Page MenuHomeCode

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/Engines/Builder/Map/OctocubeBuilder.php b/Engines/Builder/Map/OctocubeBuilder.php
index 70afefb..5d2f082 100644
--- a/Engines/Builder/Map/OctocubeBuilder.php
+++ b/Engines/Builder/Map/OctocubeBuilder.php
@@ -1,62 +1,62 @@
<?php
namespace Zed\Engines\Builder\Map;
use Hypership\Geo\Octocube;
use Hypership\Geo\Point3D;
-use GeoLocation;
+use Zed\Models\Geo\Location;
class OctocubeBuilder {
private Point3D $localLocation;
const HYPERSHIP_CORE_LOCATION = "B00001002";
- public function __construct (GeoLocation $location) {
+ public function __construct (Location $location) {
$this->localLocation = Point3D::fromString($location->local);
}
private function getSector () : int {
return Octocube::getSectorFromPoint3D($this->localLocation);
}
/**
* Gets SQL RLIKE pattern for the specified sector
*
* @return string a SQL clause like "([0-9]+, -[0,9]+, [0,9]+)"
*/
public function getRlikePattern() : string {
$sector = $this->getSector();
if ($sector == 0) {
return "(0, 0, 0)";
}
$vector = Octocube::getBaseVector($sector);
//x
if ($vector[0] == 1) {
$query = "([0-9]+, ";
} else {
$query = "(-[0-9]+, ";
}
//y
if ($vector[1] == 1) {
$query .= "[0-9]+, ";
} else {
$query .= "-[0-9]+, ";
}
//z
$query .= $this->localLocation->z . ")";
return $query;
}
static public function canBuildAt($location) : bool {
return $location->global === self::HYPERSHIP_CORE_LOCATION;
}
}
diff --git a/Engines/Perso/Events/BaseEvent.php b/Engines/Perso/Events/BaseEvent.php
index 2f532d5..7fd1ea4 100644
--- a/Engines/Perso/Events/BaseEvent.php
+++ b/Engines/Perso/Events/BaseEvent.php
@@ -1,25 +1,30 @@
<?php
namespace Zed\Engines\Perso\Events;
+use Keruald\Database\DatabaseEngine;
use Zed\Engines\Perso\PersoSelector;
abstract class BaseEvent {
protected PersoSelector $selector;
public function __construct (PersoSelector $selector) {
$this->selector = $selector;
}
public function run () : void {
if ($this->isTriggered()) {
$this->handle();
}
}
abstract public function isTriggered() : bool;
abstract public function handle() : void;
+ public function getDatabase () : DatabaseEngine {
+ return $this->selector->db;
+ }
+
}
diff --git a/Engines/Perso/Events/Create.php b/Engines/Perso/Events/Create.php
index 04fc610..866e3b6 100644
--- a/Engines/Perso/Events/Create.php
+++ b/Engines/Perso/Events/Create.php
@@ -1,52 +1,53 @@
<?php
namespace Zed\Engines\Perso\Events;
-use Perso;
+use Zed\Models\Objects\Perso;
/**
* This event is triggered when a perso is created,
* for example after a new perso form posted.
*/
class Create extends BaseEvent {
private ?Perso $createdPerso = null;
private array $errors = [];
public function isTriggered () : bool {
return
array_key_exists('form', $_POST)
&&
$_POST['form'] === 'perso.create';
}
public function handle () : void {
$isCreated = Perso::create_perso_from_form(
+ $this->getDatabase(),
$this->selector->user,
$this->createdPerso,
$this->errors
);
if ($isCreated) {
// We've got a winner.
$this->login();
} else {
// Let's try again.
$this->printAgainCreatePersoForm();
}
}
private function login () : void {
$this->selector->smarty
->assign('NOTIFY', lang_get('NewCharacterCreated'));
$this->selector->selectAndSetPerso($this->createdPerso);
}
private function printAgainCreatePersoForm () : void {
$this->selector->smarty
->assign('WAP', join("<br />", $this->errors))
->assign('perso', $this->createdPerso);
}
}
diff --git a/Engines/Perso/Events/ReadFromSession.php b/Engines/Perso/Events/ReadFromSession.php
index 91545df..79d7ba0 100644
--- a/Engines/Perso/Events/ReadFromSession.php
+++ b/Engines/Perso/Events/ReadFromSession.php
@@ -1,19 +1,22 @@
<?php
namespace Zed\Engines\Perso\Events;
-use Perso;
+use Zed\Models\Objects\Perso;
class ReadFromSession extends BaseEvent {
public function isTriggered (): bool {
return isset($this->selector->user->session['perso_id']);
}
public function handle (): void {
// Gets perso ID from the session data
- $perso = Perso::get($this->selector->user->session['perso_id']);
+ $perso = Perso::get(
+ $this->getDatabase(),
+ $this->selector->user->session['perso_id']
+ );
$this->selector->setPerso($perso);
}
}
diff --git a/Engines/Perso/Events/Select.php b/Engines/Perso/Events/Select.php
index 81fe8fb..6f72d97 100644
--- a/Engines/Perso/Events/Select.php
+++ b/Engines/Perso/Events/Select.php
@@ -1,33 +1,32 @@
<?php
namespace Zed\Engines\Perso\Events;
use InvalidArgumentException;
-
-use Perso;
+use Zed\Models\Objects\Perso;
class Select extends BaseEvent {
public function isTriggered () : bool {
return array_key_exists('action', $_GET)
&& $_GET['action'] == 'perso.select';
}
public function handle () : void {
// User has explicitly selected a perso
if (!array_key_exists('perso_id', $_GET)) {
throw new InvalidArgumentException(
"The perso ID is missing from the URL ('perso_id')."
);
}
- $perso = new Perso($_GET['perso_id']);
+ $perso = new Perso($this->getDatabase(), $_GET['perso_id']);
if ($perso->user_id !== $this->selector->user->id) {
message_die(HACK_ERROR, "This isn't your perso.");
}
$this->selector->selectAndSetPerso($perso);
}
}
diff --git a/Engines/Perso/Events/TryAutoSelect.php b/Engines/Perso/Events/TryAutoSelect.php
index 653355a..f25f52a 100644
--- a/Engines/Perso/Events/TryAutoSelect.php
+++ b/Engines/Perso/Events/TryAutoSelect.php
@@ -1,49 +1,52 @@
<?php
namespace Zed\Engines\Perso\Events;
-use Perso;
+use Zed\Models\Objects\Perso;
class TryAutoSelect extends BaseEvent {
/**
* @var Perso[]
*/
private array $persos;
public function isTriggered (): bool {
return !$this->selector->hasPerso;
}
public function handle () : void {
- $this->persos = Perso::get_persos($this->selector->user->id);
+ $this->persos = Perso::get_persos(
+ $this->getDatabase(),
+ $this->selector->user
+ );
$count = count($this->persos);
if ($count === 0) {
$this->askUserToCreatePerso();
} elseif ($count === 1) {
$this->autoselect();
} else {
$this->askUserToSelectPerso();
}
}
private function askUserToCreatePerso () : never {
$this->selector->smarty
->display("perso_create.tpl");
exit;
}
private function autoselect () : void {
$this->selector->selectAndSetPerso($this->persos[0]);
}
private function askUserToSelectPerso () : never {
$this->selector->smarty
->assign("PERSOS", $this->persos)
->display("perso_select.tpl");
$_SESSION['UserWithSeveralPersos'] = true;
exit;
}
}
diff --git a/Engines/Perso/PersoSelector.php b/Engines/Perso/PersoSelector.php
index 36de588..ccd6169 100644
--- a/Engines/Perso/PersoSelector.php
+++ b/Engines/Perso/PersoSelector.php
@@ -1,122 +1,122 @@
<?php
namespace Zed\Engines\Perso;
+use Keruald\Database\DatabaseEngine;
+use LogicException;
use Smarty;
-
use Zed\Engines\Perso\Events\BaseEvent;
use Zed\Engines\Perso\Events\Create;
use Zed\Engines\Perso\Events\Logout;
use Zed\Engines\Perso\Events\ReadFromSession;
use Zed\Engines\Perso\Events\Select;
use Zed\Engines\Perso\Events\TryAutoSelect;
-
-use Perso;
-use User;
-
-use LogicException;
+use Zed\Models\Objects\Perso;
+use Zed\Models\Objects\User;
class PersoSelector {
///
/// Properties
///
+ public DatabaseEngine $db;
public User $user;
public Smarty $smarty;
public Perso $perso;
public bool $hasPerso = false;
///
/// Constructors
//
/**
* @param User $user The currently logged user
*/
- public function __construct (User $user, Smarty $smarty) {
+ public function __construct (DatabaseEngine $db, User $user, Smarty $smarty) {
+ $this->db = $db;
$this->smarty = $smarty;
$this->user = $user;
}
///
/// Events processing
///
/**
* Select a perso using several strategies in a sequential order.
*
* Order matters: for example, to be able to log a perso out,
* we first need to select it from the session.
*
* @return BaseEvent[]
*/
private function getDefaultEvents () : array {
return [
// Strategy 1. Look in session if the perso is already selected.
new ReadFromSession($this),
// Strategy 2. Process forms and actions from URL.
new Create($this),
new Logout($this),
new Select($this),
// Strategy 3. Try to autoselect a perso or ask user for one.
new TryAutoSelect($this),
];
}
private function handleEvents () : void {
$events = $this->getDefaultEvents();
foreach ($events as $event) {
$event->run();
}
}
/**
* Run all the workflow to get a perso.
*/
- public static function load (User $user, Smarty $smarty) : Perso {
- $selector = new self($user, $smarty);
+ public static function load (DatabaseEngine $db, User $user, Smarty $smarty) : Perso {
+ $selector = new self($db, $user, $smarty);
$selector->handleEvents();
if (!$selector->hasPerso) {
throw new LogicException(<<<'EOD'
The selector has processed the different events and scenarii
to pick a perso. The expectation after all events have been
handled is we've selected a perso or have printed any view
to create or select one.
As such, this code should be unreachable. Debug the different
'isTriggered' and 'handle' methods of the events to ensure the
last event exit or return a perso.
EOD);
}
$smarty->assign('CurrentPerso', $selector->perso);
return $selector->perso;
}
///
/// Properties
///
/**
* Select a perso, as a fresh login.
*
* If the perso is already logged in,
* we can call PersoSelector::setPerso instead.
*/
public function selectAndSetPerso (Perso $perso) : void {
$perso->on_select();
$this->setPerso($perso);
}
public function setPerso (Perso $perso) : void {
$this->perso = $perso;
$this->hasPerso = true;
}
}
diff --git a/Engines/Templates/Smarty/Plugins/modifier.sector.php b/Engines/Templates/Smarty/Plugins/modifier.sector.php
index 2b01819..68a0988 100644
--- a/Engines/Templates/Smarty/Plugins/modifier.sector.php
+++ b/Engines/Templates/Smarty/Plugins/modifier.sector.php
@@ -1,27 +1,29 @@
<?php
declare(strict_types=1);
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
use Hypership\Geo\Octocube;
+use Zed\Models\Geo\Location;
+
/**
* Smarty sector modifier plugin
*
* Type: modifier<br>
* Name: sector<br>
* Purpose: prints the sector from a location
*/
-function smarty_modifier_sector (GeoLocation $location) : string {
+function smarty_modifier_sector (Location $location) : string {
$xyz = explode(',', substr($location->local, 1, -1));
$x = (int)$xyz[0];
$y = (int)$xyz[1];
$z = (int)$xyz[2];
return (string)Octocube::getSector($x, $y, $z);
}
diff --git a/Models/Base/Entity.php b/Models/Base/Entity.php
new file mode 100644
index 0000000..8fcf791
--- /dev/null
+++ b/Models/Base/Entity.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Zed\Models\Base;
+
+abstract class Entity implements EntityDatabaseInterface {
+
+ ///
+ /// Database layer
+ ///
+
+ use WithDatabase;
+
+}
diff --git a/Models/Base/EntityDatabaseInterface.php b/Models/Base/EntityDatabaseInterface.php
new file mode 100644
index 0000000..d8794d1
--- /dev/null
+++ b/Models/Base/EntityDatabaseInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Zed\Models\Base;
+
+use Keruald\Database\DatabaseEngine;
+
+/*
+ * The save_to_database and load_from_database methods
+ * can use $db = $this->getDatabase();
+ */
+
+interface EntityDatabaseInterface {
+
+ public function save_to_database () : void;
+
+ public function load_from_database () : bool;
+
+ public function getDatabase () : DatabaseEngine;
+
+}
diff --git a/Models/Base/WithDatabase.php b/Models/Base/WithDatabase.php
new file mode 100644
index 0000000..04024f0
--- /dev/null
+++ b/Models/Base/WithDatabase.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Zed\Models\Base;
+
+use Keruald\Database\DatabaseEngine;
+
+trait WithDatabase {
+
+ private DatabaseEngine $db;
+
+ /**
+ * @return DatabaseEngine
+ */
+ public function getDatabase () : DatabaseEngine {
+ return $this->db;
+ }
+
+ /**
+ * @param DatabaseEngine $db
+ */
+ public function setDatabase(DatabaseEngine $db) : void {
+ $this->db = $db;
+ }
+
+}
diff --git a/includes/content/file.php b/Models/Content/File.php
similarity index 92%
rename from includes/content/file.php
rename to Models/Content/File.php
index c1a4b10..278d397 100644
--- a/includes/content/file.php
+++ b/Models/Content/File.php
@@ -1,284 +1,296 @@
<?php
/**
* Content file class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2012-12-03 02:57 Forked from Content
*
* @package Zed
* @subpackage Content
* @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
*/
+namespace Zed\Models\Content;
+
+use Keruald\Database\DatabaseEngine;
+
+use Zed\Models\Base\Entity;
+use Zed\Models\Base\WithDatabase;
+
/**
* Content class
*
* This class maps the content_files table.
*
*/
-class ContentFile {
+class File extends Entity {
+
+ use WithDatabase;
/* -------------------------------------------------------------
Properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
public $id;
public $path;
public $user_id;
public $perso_id;
public $title;
/* -------------------------------------------------------------
Constructor, __toString
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
- * Initializes a new ContentFile instance
+ * Initializes a new File instance
*
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
- * Returns a string representation of current Content instance
+ * Returns a string representation of current File instance
*
* @return string the content title or path if title is blank.
*/
function __toString () {
return $this->title ?: $this->path;
}
/* -------------------------------------------------------------
Load/save class
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
- * Loads the object ContentFile (ie fill the properties) from the $_POST array
+ * Loads the object File (ie fill the properties) from the $_POST array
*
* @param boolean $allowSensibleFields if false, allow only title to be defined ; otherwise, allow all fields.
*/
function load_from_form ($allowSensibleFields = false) {
if (array_key_exists('title', $_POST)) {
$this->title = $_POST['title'];
}
if ($allowSensibleFields) {
if (array_key_exists('path', $_POST)) {
$this->path = $_POST['path'];
}
if (array_key_exists('user_id', $_POST)) {
$this->user_id = $_POST['user_id'];
}
if (array_key_exists('perso_id', $_POST)) {
$this->perso_id = $_POST['perso_id'];
}
}
}
/**
- * Loads the object ContentFile (ie fill the properties) from the database
+ * Loads the object File (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database () : bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM content_files WHERE content_id = '" . $id . "'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Unable to query content", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Content unknown: " . $this->id;
return false;
}
$this->load_from_row($row);
return true;
}
/**
* Loads the object from row
*/
function load_from_row ($row) {
$this->id = $row['content_id'];
$this->path = $row['content_path'];
$this->user_id = $row['user_id'];
$this->perso_id = $row['perso_id'];
$this->title = $row['content_title'];
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database () : void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$path = $db->escape($this->path);
$user_id = $db->escape($this->user_id);
$perso_id = $db->escape($this->perso_id);
$title = $db->escape($this->title);
//Updates or inserts
$sql = "REPLACE INTO content_files (`content_id`, `content_path`, `user_id`, `perso_id`, `content_title`) VALUES ($id, '$path', '$user_id', '$perso_id', '$title')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save content", '', __LINE__, __FILE__, $sql);
}
if (!$this->id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/* -------------------------------------------------------------
File handling helper methods
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Determines if the extension is valid
*
* @param string $ext The extension (without dot)
* @return boolean true if this extension is valid ; otherwise, false.
*/
function is_valid_extension ($ext) {
$ext = strtolower($ext);
return (is_valid_image_extension($ext) || is_valid_audio_extension($ext)
|| is_valid_video_extension($ext));
}
/**
* Determines if the extension is valid
*
* @param string $ext The extension (without dot)
* @return boolean true if this extension is valid ; otherwise, false.
*/
function is_valid_image_extension ($ext) {
switch ($ext = strtolower($ext)) {
//Pictures
case 'jpg':
case 'gif':
case 'png':
case 'bmp':
case 'xbm':
return true;
//Denied extension
default:
return false;
}
}
/**
* Determines if the extension is a valid audio file one
*
* @param string $ext The extension (without dot)
* @return boolean true if this extension is valid ; otherwise, false.
*/
function is_valid_audio_extension ($ext) {
switch ($ext = strtolower($ext)) {
//Sounds (HTML5 <audio> formats)
case 'mp3':
case 'ogg':
case 'aac':
case 'wav':
case 'wave':
return true;
//Denied extension
default:
return false;
}
}
/**
* Determines if the extension is a valid video file one
*
* @param string $ext The extension (without dot)
* @return boolean true if this extension is valid ; otherwise, false.
*
* @todo add H.264 extension
*/
function is_valid_video_extension ($ext) {
switch ($ext = strtolower($ext)) {
//Video (HTML5 <video> formats)
case 'ogg':
case 'webm':
return true;
//Denied extension
default:
return false;
}
}
/**
* Creates a directory
*
* @param string $dir the directory to create
*
* @todo set contents chmod in config
*/
function create_directory ($directory) {
if (!file_exists($directory)) {
@mkdir($directory); //Creates new directory, chmod 777
}
}
/**
* Handles uploaded file
*
* @return bool true if the file have been handled
*/
function handle_uploaded_file ($fileArray) {
if (count($fileArray) && $fileArray['error'] == 0) {
$this->create_directory("content/users/$this->user_id");
$this->path = "content/users/$this->user_id/$fileArray[name]";
if (!self::is_valid_extension(get_extension($fileArray[name]))) {
return false;
}
if (move_uploaded_file($fileArray['tmp_name'], $this->path)) {
return true;
} else {
$this->path = null;
return false;
}
} else {
return false;
}
}
/**
* Generates a thumbnail using ImageMagick binary
*
* @return boolean true if the thumbnail command returns 0 as program exit code ; otherwise, false
*/
function generate_thumbnail () {
global $Config;
//Builds thumbnail filename
$sourceFile = $this->path;
$pos = strrpos($this->path, '.');
$thumbnailFile = substr($sourceFile, 0, $pos) . 'Square' . substr($sourceFile, $pos);
//Executes imagemagick command
$command = $Config['ImageMagick']['convert'] . " \"$sourceFile\" -resize 162x162 \"$thumbnailFile\"";
@system($command, $code);
//Returns true if the command have exited with errorcode 0 (= ok)
return ($code == 0);
}
}
diff --git a/includes/content/location.php b/Models/Content/Location.php
similarity index 88%
rename from includes/content/location.php
rename to Models/Content/Location.php
index dd88a08..06030de 100644
--- a/includes/content/location.php
+++ b/Models/Content/Location.php
@@ -1,233 +1,244 @@
<?php
/**
* Content location class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-12-03 2:58 Forked from Content class
*
* @package Zed
* @subpackage Content
* @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
*/
+namespace Zed\Models\Content;
+
+use Keruald\Database\DatabaseEngine;
+
+use Zed\Models\Base\Entity;
+
/**
* Content location class
*
* This class maps the content_locations table.
*
* A content location is defined by 3 parameters:
* - location_global
* - location_local
* - location_k, an index for the content at the specified location
*
* This class allows to get or set the content_id at this
* (global, local, k) location.
*
* This class also provides a static helper method to
* get local content from a specific location.
*/
-class ContentLocation {
+class Location extends Entity {
/* -------------------------------------------------------------
Properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
public $location_global = null;
public $location_local = null;
public $location_k = null;
public $content_id;
/* -------------------------------------------------------------
Constructor, __toString
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
- * Initializes a new ContentLocation instance
+ * Initializes a new Location instance
*
* @param string $location_global the global location
* @param string $location_local the local location
* @param int $location_k the item indice for the specified location
*/
- function __construct ($location_global = null, $location_local = null, $location_k = null) {
+ function __construct (DatabaseEngine $db, $location_global = null, $location_local = null, $location_k = null) {
+ $this->setDatabase($db);
+
$this->location_global = $location_global;
$this->location_local = $location_local;
if ($location_k) {
$this->location_k = $location_k;
$this->load_from_database();
} else {
$this->location_k = self::get_free_location_k($location_global, $location_local);
}
}
/**
- * Returns a string representation of current Content instance
+ * Returns a string representation of current Location instance
*
* @return string the content title or path if title is blank.
*/
function __toString () {
$location_global = $this->location_global ?: '?';
$location_local = $this->location_local ?: '?';
$location_k = $this->location_k ?: '?';
return "($location_global, $location_local, $location_k)";
}
/* -------------------------------------------------------------
Load/save class
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
- * Loads the object ContentLocation (ie fill the properties) from the database
+ * Loads the object Location (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database () : bool {
+ $db = $this->getDatabase();
+
$location_global = "'" . $db->escape($this->location_global) . "'";
$location_local = "'" . $db->escape($this->location_local) . "'";
$location_k = "'" . $db->escape($this->location_k) . "'";
$sql = "SELECT * FROM content_locations WHERE location_global = '$location_global' AND location_local = '$location_local' AND location_k = '$location_k'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Unable to query content", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Content location unknown: " . $this->content_id;
return false;
}
$this->load_from_row($row);
return true;
}
/**
* Loads the object from row
*/
function load_from_row ($row) {
$this->content_id = $row['content_id'];
$this->location_global = $row['location_global'];
$this->location_local = $row['location_local'];
$this->location_k = $row['location_k'];
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database () : void {
+ $db = $this->getDatabase();
$location_global = "'" . $db->escape($this->location_global) . "'";
$location_local = "'" . $db->escape($this->location_local) . "'";
$location_k = "'" . $db->escape($this->location_k) . "'";
$content_id = $this->content_id ? "'" . $db->escape($this->content_id) . "'" : 'NULL';
$sql = "REPLACE INTO content_locations (location_global, location_local, location_k, content_id) VALUES ($location_global, $location_local, $location_k, $content_id)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save content location", '', __LINE__, __FILE__, $sql);
}
}
/* -------------------------------------------------------------
Helper methods
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Gets the next k free value for the specified location
*
* @param string $location_global the global location
* @param string $location_local the local location
*
* @param int $location_k the next free local content indice
*/
function get_free_location_k ($location_global, $location_local) {
+ $db = $this->getDatabase();
+
$location_global = "'" . $db->escape($location_global) . "'";
$location_local = "'" . $db->escape($location_local) . "'";
$sql = "SELECT MAX(location_k) + 1 FROM content_locations WHERE location_global = '$location_global' AND location_local = '$location_local'";
- if (!$result = $db->sql_query($sql)) {
+ if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get content location k", '', __LINE__, __FILE__, $sql);
}
- $row = $db->sql_fetchrow($result);
+ $row = $db->fetchRow($result);
return $row[0];
}
/**
* Deletes this content location from the database
*/
function delete() {
+ $db = $this->getDatabase();
+
$location_global = "'" . $db->escape($this->location_global) . "'";
$location_local = "'" . $db->escape($this->location_local) . "'";
$location_k = "'" . $db->escape($this->location_k) . "'";
$sql = "DELETE FROM content_locations WHERE location_global = '$location_global' AND location_local = '$location_local' AND location_k = '$location_k' LIMIT 1";
- if (!$db->sql_query($sql)) {
+ if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't delete current content location", '', __LINE__, __FILE__, $sql);
}
}
/**
* Moves the content into new location
*
* @param string $location_global the target global location
* @param string $location_local the target local location
* @param int $location_k the target local content indice [facultative]
*/
function move ($location_global, $location_local, $location_k = null) {
if ($this->content_id) {
$this->delete();
}
if ($location_k) {
$this->location_k = $location_k;
} else {
$this->location_k = self::get_free_location_k($location_global, $location_local);
}
if ($this->content_id) {
$this->save_to_database();
}
}
/* -------------------------------------------------------------
Gets content
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Gets content at specified location
*
* @param string $location_global global content location
* @param string $location_local local content location
- * @return Array array of ContentFile instances
+ * @return File[]
*
* The returned array indices are the local_k.
*/
- static function get_local_content ($location_global, $location_local) {
- global $db;
-
+ static function get_local_content (DatabaseEngine $db, string $location_global, string $location_local) : array {
//Get contents at this location
$location_global = $db->escape($location_global);
$location_local = $db->escape($location_local);
$sql = "SELECT c.* FROM content c WHERE c.location_global = '$location_global' AND c.location_local = '$location_local' ORDER BY location_k ASC";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get content", '', __LINE__, __FILE__, $sql);
}
//Fills content array
$contents = [];
while ($row = $db->fetchRow($result)) {
$k = $row['location_k'];
- $contents[$k] = new ContentFile();
+ $contents[$k] = new File($db);
$contents[$k]->load_from_row($row);
}
return $contents;
}
}
diff --git a/includes/content/zone.php b/Models/Content/Zone.php
similarity index 84%
rename from includes/content/zone.php
rename to Models/Content/Zone.php
index caf0345..27e0792 100644
--- a/includes/content/zone.php
+++ b/Models/Content/Zone.php
@@ -1,203 +1,228 @@
<?php
/**
* Zone class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Content
* @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
*/
+namespace Zed\Models\Content;
+
+use Keruald\Database\DatabaseEngine;
+
+use Zed\Models\Base\Entity;
+use Zed\Models\Base\WithDatabase;
+
/**
* Content zone class
*
* A zone is a physical place, independent of the location.
* This mechanism allows to more easily move zones.
*
* This class maps the content_zones table.
*/
-class ContentZone {
+class Zone extends Entity {
+
+ use WithDatabase;
+
+ ///
+ /// Properties
+ ///
public $id;
public $title;
public $type;
public $params;
public $deleted = false;
+ ///
+ /// Constructor
+ ///
+
/**
* Initializes a new instance of a zone object
*
* @param int $id The zone ID
*/
- function __construct ($id = '') {
+ function __construct (DatabaseEngine $db, $id = '') {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
+ ///
+ /// Helper methods
+ ///
+
/**
* Loads the object zone (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('title', $_POST)) {
$this->user_id = $_POST['title'];
}
if (array_key_exists('type', $_POST)) {
$this->user_id = $_POST['type'];
}
if (array_key_exists('params', $_POST)) {
$this->user_id = $_POST['params'];
}
if (array_key_exists('deleted', $_POST)) {
$this->user_id = $_POST['deleted'];
}
}
/**
* Loads the object zone (ie fill the properties) from the $row array
*/
function load_from_row ($row) {
$this->id = $row['zone_id'];
$this->title = $row['zone_title'];
$this->type = $row['zone_type'];
$this->params = $row['zone_params'];
$this->deleted = (bool)$row['zone_deleted'];
}
/**
* Loads the object zone (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database () : bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM " . TABLE_CONTENT_ZONES . " WHERE zone_id = '" . $id . "'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, 'Unable to query content_zones', '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = 'Zone unknown: ' . $this->id;
return false;
}
$this->load_from_row($row);
return true;
}
/**
* Saves the object to the database
*/
- function save_to_database () {
- global $db;
+ function save_to_database () : void {
+ $db = $this->getDatabase();
+
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$title = $db->escape($this->title);
$type = $db->escape($this->type);
$params = $db->escape($this->params);
$deleted = $this->deleted ? 1 : 0;
$sql = "REPLACE INTO " . TABLE_CONTENT_ZONES . " (`zone_id`, `zone_title`, `zone_type`, `zone_params`, `zone_deleted`) VALUES ($id, '$title', '$type', '$params', $deleted)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$this->id) {
$this->id = $db->nextId();
}
}
/**
* Assigns the zone at the specified location.
*
* @param string $location_global the global location
* @param string $location_global the local location
* @param bool $delete_old_locations if true, delete old locations
*/
function assign_to ($location_global, $location_local, $delete_old_locations = true) {
+ $db = $this->getDatabase();
+
if (!$this->id) {
$this->save_to_database();
}
- global $db;
+
if ($delete_old_locations) {
$sql = "DELETE FROM " . TABLE_CONTENT_ZONES_LOCATIONS . " WHERE zone_id = " . $this->id;
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to delete", '', __LINE__, __FILE__, $sql);
}
}
$g = $db->escape($location_global);
$l = $db->escape($location_local);
$sql = "REPLACE INTO " . TABLE_CONTENT_ZONES_LOCATIONS . " (location_global, location_local, zone_id) VALUES ('$g', '$l', $this->id)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to set zone location", '', __LINE__, __FILE__, $sql);
}
}
/**
* Gets the zone at specified location
*
* @param string $location_global the global location
* @param string $location_local the local location
* @param bool $create if the zone doesn't exist, create it [optional] [default value: false]
- * @return ContentZone the zone, or null if the zone doesn't exist and $create is false
+ * @return Zone the zone, or null if the zone doesn't exist and $create is false
*/
- static function at ($location_global, $location_local, $create = false) {
- global $db;
+ static function at (DatabaseEngine $db, $location_global, $location_local, $create = false) {
$g = $db->escape($location_global);
$l = $db->escape($location_local ?? "");
$sql = "SELECT * FROM " . TABLE_CONTENT_ZONES_LOCATIONS . " WHERE location_global = '$g' AND location_local = '$l'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to set zone location", '', __LINE__, __FILE__, $sql);
}
if ($row = $db->fetchRow($result)) {
- return new ContentZone($row['zone_id']);
+ return new Zone($db, $row['zone_id']);
} elseif ($create) {
- $zone = new ContentZone();
+ $zone = new Zone($db);
$zone->assign_to($location_global, $location_local);
return $zone;
} else {
return null;
}
}
/**
* Gets all the zones matching the specified location queries
*
* @param string $location_global_query the global location query
* @param string $location_local_query the local location query
- * @return Array a ContentZone array, with each item a zone found
+ * @return Zone[]
*
* [SECURITY] They are passed as is in SQL [R]LIKE queries, you can't inject users expression.
*
- * The following properties are added to the ContentZone items of the returned array:
+ * The following properties are added to the Zone items of the returned array:
* - location_global
* - location_local
*/
- static function search ($location_global_query, $location_local_query, $use_regexp_for_local = false) {
- global $db;
+ static function search (DatabaseEngine $db, $location_global_query, $location_local_query, $use_regexp_for_local = false) {
$zones = [];
$op = $use_regexp_for_local ? 'RLIKE' : 'LIKE';
$sql = "SELECT * FROM " . TABLE_CONTENT_ZONES_LOCATIONS . " WHERE location_global LIKE '$location_global_query' AND location_local $op '$location_local_query'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to set zone location", '', __LINE__, __FILE__, $sql);
}
while ($row = $db->fetchRow($result)) {
- $zone = new ContentZone($row['zone_id']);
+ $zone = new Zone($db, $row['zone_id']);
$zone->location_global = $row['location_global'];
$zone->location_local = $row['location_local'];
$zones[] = $zone;
}
return $zones;
}
}
diff --git a/includes/geo/body.php b/Models/Geo/Body.php
similarity index 71%
rename from includes/geo/body.php
rename to Models/Geo/Body.php
index 1436e31..50bb74f 100644
--- a/includes/geo/body.php
+++ b/Models/Geo/Body.php
@@ -1,194 +1,207 @@
<?php
/**
* Geo body class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* A 3D grid of objects
*
* 0.1 2010-01-27 21:51 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Geo
* @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
*/
+namespace Zed\Models\Geo;
+
+use Keruald\Database\DatabaseEngine;
+use Keruald\OmniTools\Collections\Vector;
+
+use Zed\Models\Base\Entity;
+
/**
* Geo body class
*/
-class GeoBody {
+class Body extends Entity {
- public $code;
- public $name;
+ public string $code;
+ public string $name;
- public $hypership;
- public $asteroid;
- public $moon;
- public $planet;
- public $star;
- public $orbital;
- public $hidden;
+ public bool $hypership = false;
+ public bool $asteroid = false;
+ public bool $moon = false;
+ public bool $planet = false;
+ public bool $star = false;
+ public bool $orbital = false;
+ public bool $hidden = false;
public $location;
- public $description;
+ public string $description;
- public $lastError;
+ public string $lastError = "";
/**
* Initializes a new instance
- *
- * @param int $code the primary key
*/
- function __construct ($code = null) {
+ function __construct (DatabaseEngine $db, string|int $code = null) {
+ $this->setDatabase($db);
+
if ($code) {
- $this->code = $code;
+ $this->code = (string)$code;
$this->load_from_database();
}
}
/**
* Loads the object body (ie fill the properties) from the $_POST array
*
* @param bool $readBoolean if false, don't read any form item matching a boolean field to avoid to set it to false if there are absent from a form.
*/
function load_from_form ($readBoolean = true) {
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if ($readBoolean) {
if (array_key_exists('hypership', $_POST)) {
$this->hypership = $_POST['hypership'];
}
if (array_key_exists('star', $_POST)) {
- $this->start = $_POST['star'];
+ $this->star = $_POST['star'];
}
if (array_key_exists('asteroid', $_POST)) {
- $this->hypership = $_POST['asteroid'];
+ $this->asteroid = $_POST['asteroid'];
}
if (array_key_exists('moon', $_POST)) {
- $this->start = $_POST['moon'];
+ $this->moon = $_POST['moon'];
}
if (array_key_exists('planet', $_POST)) {
- $this->start = $_POST['planet'];
+ $this->planet = $_POST['planet'];
}
if (array_key_exists('orbital', $_POST)) {
- $this->start = $_POST['orbital'];
+ $this->orbital = $_POST['orbital'];
}
if (array_key_exists('hidden', $_POST)) {
- $this->start = $_POST['hidden'];
+ $this->hidden = $_POST['hidden'];
}
}
if (array_key_exists('location', $_POST)) {
$this->location = $_POST['location'];
}
if (array_key_exists('description', $_POST)) {
$this->description = $_POST['description'];
}
}
/**
* Loads the object body (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database () : bool {
+ $db = $this->getDatabase();
+
$sql = "SELECT * FROM " . TABLE_BODIES . " WHERE body_code = '" . $this->code . "'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Unable to query geo_bodies", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "body unknown: " . $this->code;
return false;
}
$this->name = $row['body_name'];
$this->location = $row['body_location'];
- $this->description = $row['body_description'];
+ $this->description = $row['body_description'] ?? "";
if ($row['body_status']) {
$flags = explode(',', $row['body_status']);
foreach ($flags as $flag) {
$this->$flag = true;
}
}
return true;
}
/**
* Gets the status field
*
* @return string the status field value (e.g. "asteroid,hidden")
*/
- function getStatus () {
+ function getStatus () : string {
$flags = ['hypership','asteroid','moon','planet','star','orbital','hidden'];
- foreach ($flags as $flag) {
- if ($this->$flag) {
- $status[] = $flag;
- }
- }
- return implode(',', $status);
+
+ return Vector::from($flags)
+ ->filter(function ($flag) {
+ return $this->$flag;
+ })
+ ->implode(",")
+ ->getValue();
}
/**
* Gets the kind of place the body is (e.g. asteroid)
*
* @return string the kind of place
*/
- function kind () {
+ function kind () : string {
//If a location can be described by 2 flags, order the relevant flags list
//by priority, as it'll return the first triggered.
//e.g. a moon converted in hypership will be "hypership" and not "moon".
$relevantFlags = ['hypership','asteroid','moon','planet','star','orbital'];
- foreach ($relevantFlags as $flag) {
- if ($this->$flag) {
- return $flag;
- }
- }
- return "";
+
+ return Vector::from($relevantFlags)
+ ->filter(function ($flag) {
+ return $this->$flag;
+ })
+ ->firstOr("");
}
/**
* Gets the name of the body, as a string representation of the object
*
* @return string the name of the body
*/
function __toString () {
return $this->name;
}
+ public function getFullCode () : string {
+ return sprintf("B%05d", $this->code);
+ }
+
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database () : void {
+ $db = $this->getDatabase();
$code = $this->code ? "'" . $db->escape($this->code) . "'" : 'NULL';
$name = $db->escape($this->name);
- $status = getStatus();
+ $status = $this->getStatus();
$location = $db->escape($this->location);
$description = $db->escape($this->description);
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_BODIES . " (`body_code`, `body_name`, `body_status`, `body_location`, `body_description`) VALUES ($code, '$name', '$status', '$location', '$description')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$code) {
//Gets new record code value
$this->code = $db->nextId();
}
}
}
diff --git a/Models/Geo/Entity.php b/Models/Geo/Entity.php
new file mode 100644
index 0000000..fb99b51
--- /dev/null
+++ b/Models/Geo/Entity.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Zed\Models\Geo;
+
+use Zed\Models\Base\WithDatabase;
+
+abstract class Entity {
+
+ ///
+ /// Database layer
+ ///
+
+ use WithDatabase;
+
+}
diff --git a/includes/geo/galaxy.php b/Models/Geo/Galaxy.php
similarity index 92%
rename from includes/geo/galaxy.php
rename to Models/Geo/Galaxy.php
index d85d438..0630472 100644
--- a/includes/geo/galaxy.php
+++ b/Models/Geo/Galaxy.php
@@ -1,60 +1,62 @@
<?php
/**
* Geo galaxy class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* A 3D grid of objects
*
* 0.1 2010-02-08 14:02 Initial version [DcK]
* 0.2 2010-07-25 9:20 Spherical conversion, get objects
*
* @package Zed
* @subpackage Geo
* @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
*/
+namespace Zed\Models\Geo;
+
use Hypership\Geo\Point3D;
+use Keruald\Database\DatabaseEngine;
/**
* Geo galaxy class
*/
-class GeoGalaxy {
+class Galaxy {
/*
* ----------------------------------------------------------------------- *
* Objects fetchers
* ----------------------------------------------------------------------- *
*/
/**
* Gets all the coordinates of the objects in the galaxy.
*
* @return array An array of array. Each item is [string object_name, string object_type, Point3D coordinates]
*/
- static function getCoordinates () {
- global $db;
+ static function getCoordinates (DatabaseEngine $db) {
$sql = "SELECT * FROM geo_coordinates";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't query geo_coordinates view.", '', __LINE__, __FILE__, $sql);
}
$objects = [];
while ($row = $db->fetchRow($result)) {
//Demios ship xyz: [-50, 30, 40]
//Kaos asteroid xyz: [150, -129, 10]
$objects[] = [$row[0], $row[1], Point3D::fromString($row[2])];
}
return $objects;
}
}
diff --git a/includes/geo/location.php b/Models/Geo/Location.php
similarity index 88%
rename from includes/geo/location.php
rename to Models/Geo/Location.php
index 8c9c187..34d3df8 100644
--- a/includes/geo/location.php
+++ b/Models/Geo/Location.php
@@ -1,446 +1,448 @@
<?php
/**
* Geo location class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-28 18:52 DcK
*
* @package Zed
* @subpackage Geo
* @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;
+namespace Zed\Models\Geo;
-require_once('body.php');
-require_once('place.php');
-require_once('includes/objects/ship.php');
+use Exception;
+use Hypership\Geo\Point3D;
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Objects\Ship;
/**
* Geo location class
*
* This class contains properties to get, set or compare a location and
* explore the geo classes linked to.
*
* It quickly allows to parse through the location classes in templates and
* controllers.
*
* @todo Initialize $point3D from $body or $ship own locations
- * @todo Improve GeoLocation documentation (especially magic properties)
+ * @todo Improve documentation (especially magic properties)
*/
-class GeoLocation {
+class Location extends Entity {
/**
* An array of strings containing location data.
*
* In the current class implementation,
* the first element is the global location
* and the second element is the local location.
- *
- * @var Array
*/
- private $data;
+ private array $data;
/**
* A body object
*
* It contains a GeoBody value when the global location is a body
* ie if $this->data[0][0] == 'B'
*
* Otherwise, its value is null.
*
- * @var GeoBody
+ * @var Body
*/
public $body = null;
/**
* A place object
*
- * It contains a GeoPlacevalue when the global location is a place
+ * It contains a Place value when the global location is a place
* ie if $this->data[0][0] == 'B' && strlen($this->data[0]) == 9
*
* Otherwise, its value is null.
- *
- * @var GeoPlace
*/
- public $place = null;
+ public ?Place $place = null;
/**
* A point identified by x, y, z coordinates
*/
public Point3D|null $point3D = null;
/**
* A ship object
*
* It contains a Ship value when the global location is a ship
* ie if $this->data[0][0] == 'S'
*
* Otherwise, its value is null.
*
* @var Ship
*/
public $ship = null;
/**
* Initializes a new location instance
*
- * @param string $global the global location
- * @param string local the locallocation
- *
* @todo Improve local location handling
*/
- function __construct ($global = null, $local = null) {
+ function __construct (DatabaseEngine $db, string $global = null, string $local = null) {
+ $this->setDatabase($db);
+
if (!$global) {
$this->data = [];
} elseif (preg_match("/[BS][0-9]{5}[0-9]{3}/", $global)) {
$this->data[0] = $global;
} elseif (preg_match("/[BS][0-9]{5}/", $global)) {
$this->data[0] = $global;
} elseif (preg_match("/^xyz\:/", $global)) {
$coords = sscanf($global, "xyz: [%d, %d, %d]");
if (count($coords) == 3) {
$this->data[0] = $global;
} else {
throw new Exception("Invalid expression: $global");
}
} else {
global $db;
$name = $db->escape($global);
$sql = "SELECT location_code FROM " . TABLE_LOCATIONS . " WHERE location_name LIKE '$name'";
$code = $db->queryScalar($sql);
if ($code) {
$this->data[0] = $code;
return;
}
throw new Exception("Invalid expression: $global");
}
//TODO: handle $local in a better way: from the global location, gets
//a local location handler. Or a some inheritance, like a class
- //HypershipGeoLocation extending GeoLocation.
+ //HypershipLocation extending Location.
if ($local !== null) {
$this->data[1] = $local;
}
$this->load_classes();
}
/**
* Gets $place, $body and $ship instances if they're needed
*/
function load_classes () {
+ $db = $this->getDatabase();
+
//No data, no class to load
if (!count($this->data)) {
return;
}
//Loads global classes
$global = $this->data[0];
$code = substr($global, 1, 5);
switch ($global[0]) {
case 'B':
switch (strlen($global)) {
case 9:
- $this->place = GeoPlace::from_code($global);
+ $this->place = Place::from_code($db, $global);
case 6:
- $this->body = new GeoBody($code);
+ $this->body = new Body($db, $code);
break;
}
break;
case 'S':
- $this->ship = new Ship($code);
+ $this->ship = new Ship($db, $code);
break;
case 'x':
$coords = sscanf($global, "xyz: [%f, %f, %f]");
if (count($coords) == 3) {
$this->point3D = new Point3D(...$coords);
}
break;
}
}
/**
* Magic method called when a unknown property is get.
*
* Handles $global, $local, $type, $body_code, $ship_code, $place_code,
* $body_kind, $containsGlobalLocation, $containsLocalLocation.
*/
function __get ($variable) {
switch ($variable) {
/* main variables */
case 'global':
return $this->data[0];
break;
case 'local':
return (count($this->data) > 1) ? $this->data[1] : null;
break;
/* global location */
case 'type':
return $this->data[0][0];
case 'body_code':
if ($this->data[0][0] == 'B') {
return substr($this->data[0], 1, 5);
}
return null;
case 'place_code':
if ($this->data[0][0] == 'B') {
return substr($this->data[0], 6, 3);
}
return null;
case 'ship_code':
if ($this->data[0][0] == 'S') {
return substr($this->data[0], 1, 5);
}
return null;
case 'body_kind':
if ($this->data[0][0] == 'B' && $this->body != null) {
if ($kind = $this->body->kind()) {
return $kind;
}
} elseif ($this->data[0][0] == 'S') {
return 'ship';
}
return 'place';
case 'containsGlobalLocation':
return count($this->data) > 0;
case 'containsLocalLocation':
return count($this->data) > 1;
default:
throw new Exception("Unknown variable: $variable");
break;
}
}
/**
* Checks if the place exists
*
* @return bool true if the place exists ; otherwise, false.
*
* @todo Handle alias
*/
function exists () {
+ $db = $this->getDatabase();
+
$n = count($this->data);
//If not defined, it doesn't exist
if ($n == 0) {
return false;
}
//Checks global location
switch ($this->data[0][0]) {
case 'B':
switch (strlen($this->data[0])) {
case 9:
- if (!$place = GeoPlace::from_code($this->data[0])) {
+ if (!$place = Place::from_code($db, $this->data[0])) {
return false;
}
break;
case 6:
- $body = new GeoBody(substr($this->data[0], 1));
+ $body = new Body($db, substr($this->data[0], 1));
if ($body->lastError) {
return false;
}
break;
default:
- message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__);
+ message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "Location exists method", __LINE__, __FILE__);
}
break;
case 'S':
- $ship = new Ship(substr($this->data[0], 1));
- if ($body->lastError) {
+ $ship = new Ship($db, substr($this->data[0], 1));
+ if ($ship->lastError) {
return false;
}
break;
default:
- message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__);
+ message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "Location exists method", __LINE__, __FILE__);
return false;
}
if ($n > 1) {
if (!isset($place)) {
- message_die(GENERAL_ERROR, "Can't check if a local place exists for the following location: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__);
+ message_die(GENERAL_ERROR, "Can't check if a local place exists for the following location: " . $this->data[0], "Location exists method", __LINE__, __FILE__);
}
if (!$place->is_valid_local_location($this->data[1])) {
return false;
}
}
return true;
}
/**
* Checks if the place is equals at the specified expression or place
*
* @return bool true if the places are equals ; otherwise, false.
*
* @todo Create a better set of rules to define when 2 locations are equal.
*/
function equals ($expression) {
//Are global location equals?
//TODO: create a better set of rules to define when 2 locations are equal.
- if (is_a($expression, 'GeoLocation')) {
+ if ($expression instanceof Location) {
if (!$this->equals($expression->data[0])) {
return false;
}
if (count($expression->data) + count($this->data) > 2) {
return $expression->data[1] == $this->data[1];
}
}
if ($expression == $this->data[0]) {
return true;
}
$n1 = strlen($expression);
$n2 = strlen($this->data[0]);
if ($n1 > $n2) {
return substr($expression, 0, $n2) == $this->data[0];
}
return false;
}
/**
* Represents the current location instance as a string
*
* @return string a string representing the current location
*/
function __toString () {
if (!$this->data[0]) {
return "";
}
+ $db = $this->getDatabase();
+
switch ($this->data[0][0]) {
case 'S':
- $ship = new Ship($this->ship_code);
+ $ship = new Ship($db, $this->ship_code);
$location[] = $ship->name;
break;
case 'B':
- $body = new GeoBody($this->body_code);
+ $body = new Body($db, $this->body_code);
$location[] = $body->name ?: lang_get('UnknownBody');
if (strlen($this->data[0]) == 9) {
- $place = GeoPlace::from_code($this->data[0]);
+ $place = Place::from_code($db, $this->data[0]);
$location[] = $place->name ?: lang_get('UnknownPlace');
}
break;
case 'x':
$pt = $this->point3D->toSpherical();
return sprintf("(%d, %d°, %d°)", $pt[0], $pt[1], $pt[2]);
default:
message_die(GENERAL_ERROR, "Unknown location identifier: $type.<br />Expected: B or S.");
}
return implode(", ", array_reverse($location));
}
/**
* Magic method called when a unknown property is set.
*
* Handles $global, $local, $type, $body_code, $ship_code, $place_code
*/
function __set ($variable, $value) {
switch ($variable) {
/* main variables */
case 'global':
$this->data[0] = $value;
break;
case 'local':
$this->data[1] = $value;
break;
/* global location */
case 'type':
if ($value == 'B' || $value == 'S') {
if (!$this->data[0]) {
$this->data[0] = $value;
} else {
$this->data[0][0] = $value;
}
}
break;
case 'body_code':
if (preg_match("/[0-9]{1,5}/", $value)) {
$value = sprintf("%05d", $value);
if (!$this->data[0]) {
$this->data[0] = "B" . $value;
return;
} elseif ($this->data[0][0] == 'B') {
$this->data[0] = "B" . $value . substr($this->data[0], 6);
return;
}
throw new Exception("Global location isn't a body.");
}
throw new Exception("$value isn't a valid body code");
case 'ship_code':
if (preg_match("/[0-9]{1,5}/", $value)) {
$value = sprintf("%05d", $value);
if (!$this->data[0]) {
$this->data[0] = "S" . $value;
return;
} elseif ($this->data[0][0] == 'S') {
$this->data[0] = "S" . $value . substr($this->data[0], 6);
return;
}
throw new Exception("Global location isn't a ship.");
}
throw new Exception("$value isn't a valid ship code");
case 'place_code':
if (!preg_match("/[0-9]{1,3}/", $value)) {
throw new Exception("$value isn't a valid place code");
}
$value = sprintf("%03d", $value);
if ($this->data[0][0] == 'B') {
- $this->data[0] = substr($this->data[0], 0, 6) . $value;
+ $this->data[0] = location . phpsubstr($this->data[0], 0, 6) . $value;
}
throw new Exception("Global location isn't a body.");
default:
throw new Exception("Unknown variable: $variable");
break;
}
}
}
diff --git a/includes/geo/place.php b/Models/Geo/Place.php
similarity index 89%
rename from includes/geo/place.php
rename to Models/Geo/Place.php
index 3fa5dea..6d6338d 100644
--- a/includes/geo/place.php
+++ b/Models/Geo/Place.php
@@ -1,232 +1,247 @@
<?php
/**
* Geo place class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-28 01:48 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Geo
* @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
*/
+namespace Zed\Models\Geo;
+
+use Keruald\Database\DatabaseEngine;
+
+use Zed\Models\Base\Entity;
+use Zed\Models\Base\WithDatabase;
+
/**
* Default local location format
*
* The local_location format is a PCRE regular expression
*
* By default, local_location format is an (x, y, z) expression
*/
-define('LOCATION_LOCAL_DEFAULT_FORMAT', '/^\([0-9]+( )*,( )*[0-9]+( )*,( )*[0-9]+\)$/');
/**
* Geo place
*
* A place is a city or a hypership district.
*
* It's identified by a 9 chars geocode like B0001001.
* The 5 first chars indicates the body (class GeoBody) where the place is and
* the 3 last digits is the place number.
*
* This class maps the geo_places table.
*/
-class GeoPlace {
+class Place extends Entity {
+
+ use WithDatabase;
+
+ ///
+ /// Constants
+ ///
+
+ const LOCATION_LOCAL_DEFAULT_FORMAT = "/^\([0-9]+( )*,( )*[0-9]+( )*,( )*[0-9]+\)$/";
public $id;
public $body_code;
public $code;
public $name;
public $description;
public $location_local_format;
public $start;
public $hidden;
public string $lastError = "";
/**
* Initializes a new instance
*
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct ($db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Loads the object place (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('body_code', $_POST)) {
$this->body_code = $_POST['body_code'];
}
if (array_key_exists('code', $_POST)) {
$this->code = $_POST['code'];
}
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('description', $_POST)) {
$this->description = $_POST['description'];
}
if (array_key_exists('status', $_POST)) {
$this->status = $_POST['status'];
}
if (array_key_exists('location_local_format', $_POST)) {
$this->location_local_format = $_POST['location_local_format'];
}
}
/**
* Loads the object place (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database () : bool {
+ $db = $this->getDatabase();
+
$sql = "SELECT * FROM " . TABLE_PLACES . " WHERE place_id = '" . $this->id . "'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Unable to query geo_places", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "place unknown: " . $this->id;
return false;
}
$this->body_code = $row['body_code'];
$this->code = $row['place_code'];
$this->name = $row['place_name'];
$this->description = $row['place_description'];
$this->location_local_format = $row['location_local_format'];
//Explodes place_status SET field in boolean variables
if ($row['place_status']) {
$flags = explode(',', $row['place_status']);
foreach ($flags as $flag) {
$this->$flag = true;
}
}
return true;
}
/**
* Gets status field value
*
* @return string the status field value (e.g. "requiresPTA,default")
*/
function getStatus () {
$flags = ['start', 'hidden'];
foreach ($flags as $flag) {
if ($this->$flag) {
$status[] = $flag;
}
}
return implode(',', $status);
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database () : void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$body_code = $db->escape($this->body_code);
$code = $db->escape($this->code);
$name = $db->escape($this->name);
$description = $db->escape($this->description);
$status = $this->getStatus();
$location_local_format = $db->escape($this->location_local_format);
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_PLACES . " (`place_id`, `body_code`, `place_code`, `place_name`, `place_description`, `place_status`, `location_local_format`) VALUES ($id, '$body_code', '$code', '$name', '$description', '$status', '$location_local_format')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/**
* Determines if the specified local location looks valid
*
* @param string $local_location the local location
* @return boolean true if the specified local location looks valid ; otherwise, false.
*/
function is_valid_local_location ($local_location) {
- $format = $this->location_local_format ?: LOCATION_LOCAL_DEFAULT_FORMAT;
+ $format = $this->location_local_format ?: self::LOCATION_LOCAL_DEFAULT_FORMAT;
return preg_match($format, $local_location) > 0;
}
/**
* Gets a string representation of the current place
*
* @return string A Bxxxxxyyy string like B00001001, which represents the current place.
*/
function __tostring () {
return 'B' . $this->body_code . $this->code;
}
/**
* Creates a Place instance, from the specified body/place code
*
* @param $code the place's code
- * @return GeoPlace the place instance
+ * @return Place the place instance
*/
- static function from_code ($code) {
- global $db;
+ static function from_code (DatabaseEngine $db, $code) {
$sql = "SELECT * FROM " . TABLE_PLACES . " WHERE CONCAT('B', body_code, place_code) LIKE '$code'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query geo_places", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
return null;
}
- $place = new GeoPlace();
+ $place = new Place($db);
$place->id = $row['place_id'];
$place->body_code = $row['body_code'];
$place->code = $row['place_code'];
$place->name = $row['place_name'];
$place->description = $row['place_description'];
$place->location_local_format = $row['location_local_format'];
//Explodes place_status SET field in boolean variables
if ($row['place_status']) {
$flags = explode(',', $row['place_status']);
foreach ($flags as $flag) {
$place->$flag = true;
}
}
return $place;
}
/**
* Gets a start location
*
* @return string The global location code of a start location
*
* @TODO sql optimisation (query contains ORDER BY RAND())
*/
- static function get_start_location () {
- global $db;
+ static function get_start_location (DatabaseEngine $db) {
$sql = "SELECT CONCAT('B', body_code, place_code) FROM " . TABLE_PLACES . " WHERE FIND_IN_SET('start', place_status) > 0 ORDER BY rand() LIMIT 1";
return $db->queryScalar($sql);
}
}
diff --git a/includes/objects/port.php b/Models/Geo/Port.php
similarity index 85%
rename from includes/objects/port.php
rename to Models/Geo/Port.php
index 3d24223..a1c3b78 100644
--- a/includes/objects/port.php
+++ b/Models/Geo/Port.php
@@ -1,213 +1,220 @@
<?php
/**
* Port class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-02-09 19:17 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Model
* @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
*/
-require_once("includes/geo/location.php");
+namespace Zed\Models\Geo;
+
+use Keruald\Database\DatabaseEngine;
+
+use Zed\Models\Base\Entity;
/**
* Port class
*
* This class maps the ports table.
*
* The class also provides helper methods to handle ports at specified location.
*/
-class Port {
+class Port extends Entity {
public $id;
public $location_global;
public $location_local;
public $name;
public $hidden;
public $requiresPTA;
public $default;
public string $lastError = "";
/**
* Initializes a new instance
- * @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, int $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Loads the object Port (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('location_global', $_POST)) {
$this->location_global = $_POST['location_global'];
}
if (array_key_exists('location_local', $_POST)) {
$this->location_local = $_POST['location_local'];
}
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('hidden', $_POST)) {
$this->hidden = (bool)$_POST['hidden'];
}
if (array_key_exists('requiresPTA', $_POST)) {
$this->requiresPTA = (bool)$_POST['requiresPTA'];
}
if (array_key_exists('default', $_POST)) {
$this->hidden = (bool)$_POST['default'];
}
}
/**
* Loads the object Port (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM " . TABLE_PORTS . " WHERE port_id = '" . $id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query ports", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Port unknown: " . $this->id;
return false;
}
$this->location_global = $row['location_global'];
$this->location_local = $row['location_local'];
$this->name = $row['port_name'];
//Explodes place_status SET field in boolean variables
if ($row['place_status']) {
$flags = explode(',', $row['port_status']);
foreach ($flags as $flag) {
$this->$flag = true;
}
}
return true;
}
/**
* Gets status field value
*
* @return string the status field value (e.g. "requiresPTA,default")
*/
- function getStatus () {
+ function getStatus (): string {
$flags = ['hidden', 'requiresPTA', 'default'];
foreach ($flags as $flag) {
if ($this->$flag) {
$status[] = $flag;
}
}
return implode(',', $status);
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$location_global = $db->escape($this->location_global);
$location_local = $db->escape($this->location_local);
$name = $db->escape($this->name);
$status = $this->getStatus();
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_PORTS . " (`port_id`, `location_global`, `location_local`, `port_name`, `port_status`) VALUES ($id, '$location_global', '$location_local', '$name', '$status')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/**
* Determines if the specified location have a port
*
* @param string $location_global the global location
* @return boolean true if there is a spatioport exactly at the specified location ; otherwise, false.
*/
static function have_port ($location_global) {
return (get_port_id($location_global) !== null);
}
/**
* Gets the port situated exactly at the specified global location
*
* @param string $location_global the global location
- * @return int the port ID
+ * @return int|null the port ID, or null if none found
*/
- static function get_port_id ($location_global) {
- global $db;
+ static function get_port_id (DatabaseEngine $db, string $location_global): ?int {
$location_global = $db->escape($location_global);
$sql = "SELECT port_id FROM " . TABLE_PORTS . " WHERE location_global = '$location_global'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to get ports", '', __LINE__, __FILE__, $sql);
}
+
if ($row = $db->fetchRow($result)) {
- return $row['port_id'];
+ return (int)$row['port_id'];
}
+
return null;
}
/**
* Gets default port, from specified global location
*
+ * @param DatabaseEngine $db
* @param string $location_global the global location
- * @return Port the port near this location ; null if there isn't port there.
+ * @return Port|null the port near this location ; null if there isn't port there.
*/
- static function from_location ($location_global) {
+ static function from_location (DatabaseEngine $db, string $location_global): ?self {
$havePlace = strlen($location_global) == 9;
$port_id = null;
if ($havePlace) {
//Checks if there's a port at specified location
- $port_id = self::get_port_id($location_global);
+ $port_id = self::get_port_id($db, $location_global);
}
if ($port_id == null) {
//Nearest default port.
//If place have been specified (B0001001), we've to found elsewhere
//==> B00001%
- global $db;
$loc = $db->escape(substr($location_global, 0, 6));
$sql = "SELECT port_id FROM " . TABLE_PORTS . " WHERE location_global LIKE '$loc%'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get port", '', __LINE__, __FILE__, $sql);
}
if ($row = $db->fetchRow($result)) {
$port_id = $row['port_id'];
} else {
return null;
}
}
- return new Port($port_id);
- }
+ return new Port($db, $port_id);
+ }
}
diff --git a/includes/geo/scene.php b/Models/Geo/Scene.php
similarity index 87%
rename from includes/geo/scene.php
rename to Models/Geo/Scene.php
index 6d6c881..3381d09 100644
--- a/includes/geo/scene.php
+++ b/Models/Geo/Scene.php
@@ -1,212 +1,215 @@
-
<?php
/**
* Geo scene class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Geo
* @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
*/
-require_once('location.php');
-require_once('sceneindex.php');
+namespace Zed\Models\Geo;
+
+use Zed\Models\Content\Zone;
if (!defined('SCENE_DIR')) {
/**
* The directory containing scenes files
*/
define('SCENE_DIR', 'content/scenes');
}
/**
* Geo scene class
*
* This class provides methods to determine and renders the local scene.
*/
-class GeoScene {
+class Scene extends Entity {
/**
* Last error or warning
*
* @var string
*/
public $lastError = "";
/**
* File scene to serve
*
* @var string
*/
public $sceneFile;
/**
* The location of the scene to print
*
- * @var GeoLocation
+ * @var Location
*/
public $location;
/**
- * Initializes a new GeoScene instance
+ * Initializes a new Scene instance
*
- * @param GeoLocation $location location the scene is to print
+ * @param Location $location location the scene is to print
*/
function __construct ($location) {
+ $this->setDatabase($location->getDatabase());
+
$this->location = $location;
//Gets local scene
if ($location->containsLocalLocation) {
if ($this->get_local_scene()) {
return;
}
}
//Gets global scene
if ($location->containsGlobalLocation) {
if ($this->get_global_scene()) {
return;
}
}
//If not scene found, let's set a warning
$this->lastError = "No scene found.";
}
/**
* Gets local scene
*
* @return boolean true if a scene have been found ; otherwise, false.
*/
private function get_local_scene () {
//From the index
- $index = GeoSceneIndex::Load(SCENE_DIR);
+ $index = SceneIndex::Load(SCENE_DIR);
if ($tpl = $index->get_local_template($this->location->global, $this->location->local)) {
$this->sceneFile = SCENE_DIR . '/' . $tpl;
return true;
}
//From filename
$expression = $this->location->global . ' ' . $this->location->local;
if ($this->try_get_scene($expression)) {
return true;
}
return false;
}
/**
* Gets global scene
*
* @return boolean true if a scene have been found ; otherwise, false.
*/
private function get_global_scene () {
$location = $this->location;
if ($location->place) {
if ($this->try_get_scene($location->global)) {
return true;
}
}
if ($location->body) {
if ($this->try_get_scene('B' . $location->body->code)) {
return true;
}
}
return false;
}
/**
* Gets file extension
*
* @param string $file the file path
* @return string the file extension
*/
public static function get_file_extension (string $file) : string {
$pathinfo = pathinfo($file);
return $pathinfo['extension'] ?? "";
}
/**
* Renders the file
*/
public function render () {
+ $db = $this->getDatabase();
+
if ($file = $this->sceneFile) {
- switch ($ext = GeoScene::get_file_extension($file)) {
+ switch ($ext = Scene::get_file_extension($file)) {
case 'png':
case 'jpg':
case 'gif':
case 'bmp':
echo "<img src=\"$file\" />";
break;
case 'tpl':
global $smarty, $Config;
//$this->location is the object reference
//Some objects like the hypership move, so we also need to know where there are.
//From the template, this object location is assigned to $location
//To get $this->location from template, use $CurrentPerso->location
if ($this->location->body) {
- $smarty->assign("location", new GeoLocation($this->location->body->location));
+ $smarty->assign("location", new Location($db, $this->location->body->location));
} elseif ($this->location->ship) {
- $smarty->assign("location", new GeoLocation($this->location->ship->location));
+ $smarty->assign("location", new Location($db, $this->location->ship->location));
}
//Gets zone information
- require_once('includes/content/zone.php');
- if ($zone = ContentZone::at($this->location->global, $this->location->local)) {
+ if ($zone = Zone::at($db, $this->location->global, $this->location->local)) {
$smarty->assign('zone', $zone);
}
//Scene-specific variables
$smarty->assign("SCENE_URL", defined('SCENE_URL') ? SCENE_URL : '/' . SCENE_DIR);
if ($Config['builder']['hotglue']['enable']) {
$smarty->assign("HOTGLUE", $Config['builder']['hotglue']['URL']);
}
lang_load('scenes.conf', $this->location->global);
//Displays scene
$smarty->display($file);
break;
case 'php':
message_die(HACK_ERROR, ".php scene files not allowed without review", '', __LINE__, __FILE__);
default:
- message_die(GENERAL_ERROR, "Can't handle $ext extension for $file scene", 'GeoScene render error', __LINE__, __FILE__);
+ message_die(GENERAL_ERROR, "Can't handle $ext extension for $file scene", 'Scene render error', __LINE__, __FILE__);
}
echo "\n\n";
}
}
/**
* Tries to get the scene file.
*
* It will try to find in the scene directory a file with $code as name,
* and .tpl .png .gif .bmp .swf .html or .php as extension.
*
* @param string the location code (and filename)
* @return bool true if a scene file have been found and set ; otherwise, false.
*/
private function try_get_scene ($code) {
$file = SCENE_DIR . "/$code";
$extensions = ['tpl', 'png', 'jpg', 'gif', 'bmp', 'swf', 'html', 'php'];
foreach ($extensions as $ext) {
if (file_exists("$file.$ext")) {
$this->sceneFile = "$file.$ext";
return true;
}
}
return false;
}
}
diff --git a/includes/geo/sceneindex.php b/Models/Geo/SceneIndex.php
similarity index 92%
rename from includes/geo/sceneindex.php
rename to Models/Geo/SceneIndex.php
index 1b24e1d..34d50bd 100644
--- a/includes/geo/sceneindex.php
+++ b/Models/Geo/SceneIndex.php
@@ -1,222 +1,220 @@
<?php
/**
* Geo scene index class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Geo
* @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
*
* This class implements a singleton pattern.
*/
-require_once('includes/cache/cache.php');
+namespace Zed\Models\Geo;
+
+use Cache;
+
+use Exception;
/**
* Geo scene class
*
* This class provides an index of available scene template files.
*/
-class GeoSceneIndex {
+class SceneIndex {
/**
* Global location templates array
*
* Keys are global location codes.
* Values the relevant template file.
- *
- * @var Array
*/
- public $global_templates;
+ public array $global_templates;
/**
* Local location templates 2D array
*
* Keys are global and local location codes.
- * Values the relevant template file.
+ * Values the srelevant template file.
*
* e.g. $local_templates['B00017001']['(10, 50, 8)'] => 'B00017_port.tpl'
- *
- * @var Array
*/
- public $local_templates;
+ public array $local_templates;
/**
* Time of the last updated file in the scenes directory
*
* @var int
*/
public $updated;
/**
* The directory where templates are stored
*
* @var string
*/
public $directory;
/**
* The current index instance array
*
* Keys are scene directories (string)
- * Items are GeoSceneIndex instances
- *
- * @var Array
+ * Items are SceneIndex instances
*/
- static $instance = [];
+ static array $instance = [];
/**
* Gets the index instance, initializing it if needed
*
* @return index the index instance
*/
static function load ($directory) {
//Creates the index object if needed
if (!array_key_exists($directory, self::$instance)) {
- self::$instance[$directory] = new GeoSceneIndex($directory);
+ self::$instance[$directory] = new SceneIndex($directory);
}
return self::$instance[$directory];
}
/**
- * Initializes a new GeoSceneIndex instance
+ * Initializes a new SceneIndex instance
*
* @param string $directory the scene templates directory
*/
public function __construct ($directory) {
$this->directory = $directory;
if (!$this->get_cached_information() || !$this->is_up_to_date()) {
$this->refresh_information();
$this->set_cached_information();
}
}
/**
* Caches index data
*/
public function set_cached_information () {
$cache = Cache::load();
$cache->set('zed_sceneindex', serialize($this));
}
/**
* Gets index from cache
*/
public function get_cached_information () {
$cache = Cache::load();
$cached_index = $cache->get('zed_sceneindex');
if ($cached_index === false) {
return false;
}
$index = unserialize($cached_index);
$this->global_templates = $index->global_templates;
$this->local_templates = $index->local_templates;
$this->updated = $index->updated;
$this->directory = $index->directory;
return true;
}
/**
* Reads scene templates and indexes information
*/
public function refresh_information () {
$this->global_templates = [];
$this->local_templates = [];
$this->updated = filemtime($this->directory);
if ($handle = opendir($this->directory)) {
while (false !== ($file = readdir($handle))) {
- if (GeoScene::get_file_extension($file) == 'tpl') {
+ if (Scene::get_file_extension($file) == 'tpl') {
$template = file_get_contents($this->directory . '/' . $file, false, null, 0, 1024);
$location = self::get_template_location($template);
if ($location[1] !== null) {
$this->local_templates[$location[0]][$location[1]] = $file;
} elseif ($location[0] != null) {
$this->global_templates[$location[0]] = $file;
}
}
}
closedir($handle);
}
}
/**
* Determines if the information is still up to date
*
* @return bool true if the information is up to date ; otherwise, false.
*/
public function is_up_to_date () {
return ($this->updated == filemtime($this->directory));
}
/**
* Gets template location
*
* @return Array an string array of the location (two items; global, local)
* At key 0, a string with global location, or NULL if not specified
* At key 1, a string with local location, or NULL if not specified
*/
private static function get_template_location ($template) {
$location = [null, null];
//Gets global location
$pos1 = strpos($template, "Global location: ");
if ($pos1 === false) {
throw new Exception("No location in template. Any template file must contain a comment line with the string 'Global location: ' followed by the global location matching the template. It should also contain a line 'Local location: ' when applicable.");
}
$pos1 += 17;
$pos2 = strpos($template, "\n", $pos1);
$location[0] = trim(substr($template, $pos1, $pos2 - $pos1));
//Gets local location
$pos1 = strpos($template, "Local location: ");
if ($pos1 !== false) {
$pos1 += 16;
$pos2 = strpos($template, "\n", $pos1);
$location[1] = trim(substr($template, $pos1, $pos2 - $pos1));
}
return $location;
}
/**
* Gets local template file from index
*
* @param string $location_global the global location
* @param string $location_global the local location
* @return string the relevant template scene file, or NULL if not existing
*/
public function get_local_template ($location_global, $location_local) {
if (isset($this->local_templates[$location_global][$location_local])) {
return $this->local_templates[$location_global][$location_local];
}
return null;
}
/**
* Gets global template file from index
*
* @param string $location_global the global location
* @return string the relevant template scene file, or NULL if not existing
*/
public function get_global_template ($location_global) {
if (isset($this->global_templates[$location_global])) {
return $this->global_templates[$location_global];
}
return null;
}
}
diff --git a/includes/objects/message.php b/Models/Messages/Message.php
similarity index 69%
rename from includes/objects/message.php
rename to Models/Messages/Message.php
index c8dc343..b36a4f6 100644
--- a/includes/objects/message.php
+++ b/Models/Messages/Message.php
@@ -1,172 +1,221 @@
- <?php
+<?php
/**
* Message class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-28 01:47 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Messages;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+use Zed\Models\Objects\Perso;
+
/**
* Message class
*
* This class maps the messages table.
*
* It also provides a static method to get perso's messages.
*/
-class Message {
+class Message extends Entity {
public $id;
public $date;
- public $from;
+ private $from;
public $to;
public $text;
public $flag;
+ public MessageSource $source;
+ public ?Perso $perso;
+
public string $lastError = "";
/**
* Initializes a new instance
*
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
} else {
$this->date = time();
$this->flag = 0; //unread
}
}
/**
* Loads the object Message (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('date', $_POST)) {
$this->date = $_POST['date'];
}
if (array_key_exists('from', $_POST)) {
$this->from = $_POST['from'];
}
if (array_key_exists('to', $_POST)) {
$this->to = $_POST['to'];
}
if (array_key_exists('text', $_POST)) {
$this->text = $_POST['text'];
}
if (array_key_exists('flag', $_POST)) {
$this->flag = $_POST['flag'];
}
}
/**
* Loads the object Message (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$sql = "SELECT * FROM messages WHERE message_id = '" . $this->id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query messages", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Message unknown: " . $this->id;
return false;
}
$this->date = $row['message_date'];
- $this->from = $row['message_from'];
+ $this->from = (int)$row['message_from'];
$this->to = $row['message_to'];
$this->text = $row['message_text'];
$this->flag = $row['message_flag'];
+
+ $this->computeSource();
+
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$date = $db->escape($this->date);
- $from = $db->escape($this->from);
+ $from = match ($this->source) {
+ MessageSource::System => 0,
+ MessageSource::Perso => $this->perso->id,
+ };
$to = $db->escape($this->to);
$text = $db->escape($this->text);
$flag = $db->escape($this->flag);
//Updates or inserts
$sql = "REPLACE INTO messages (`message_id`, `message_date`, `message_from`, `message_to`, `message_text`, `message_flag`) VALUES ($id, '$date', '$from', '$to', '$text', '$flag')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
+ public function setAsSystem () {
+ $this->source = MessageSource::System;
+ $this->perso = null;
+ }
+
+ public function setFrom (Perso $perso) {
+ $this->source = MessageSource::Perso;
+ $this->perso = $perso;
+ }
+
+ private function computeSource () {
+ $db = $this->getDatabase();
+
+ $this->source = match ($this->from) {
+ 0 => MessageSource::System,
+ default => MessageSource::Perso,
+ };
+
+ $this->perso = match ($this->from) {
+ 0 => null,
+ default => Perso::get($db, $this->from),
+ };
+ }
+
/**
* Sends the message
*/
- function send () {
+ function send (): void {
$this->save_to_database();
//TODO: triggers new message notifier
}
/**
* Deletes the message
*/
- function delete () {
+ function delete (): void {
//A message is deleted if its flag value is 2
if ($this->flag != 2) {
$this->flag = 2;
$this->save_to_database();
}
}
/**
* Gets messages from the specified perso
*/
- static function get_messages ($perso_id, $mark_as_read = true, &$countNewMessages = 0) {
- global $db;
+ static function get_messages (DatabaseEngine $db, Perso $perso, bool $mark_as_read = true, int &$countNewMessages = 0) {
$ids = [];
- $sql = "SELECT message_id FROM " . TABLE_MESSAGES . " WHERE message_to = " . $db->escape($perso_id) . " AND message_flag < 2 ORDER BY message_id DESC";
+ $sql = "SELECT message_id FROM " . TABLE_MESSAGES . " WHERE message_to = " . $db->escape($perso->id) . " AND message_flag < 2 ORDER BY message_id DESC";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to get messages", '', __LINE__, __FILE__, $sql);
}
$messages = [];
while ($row = $db->fetchRow($result)) {
- $message = new Message($row[0]);
+ $message = new Message($db, $row[0]);
$messages[] = $message;
$ids[] = $message->id;
if ($message->flag == 0) {
//New message
$countNewMessages++;
}
}
if ($mark_as_read && count($ids)) {
$ids = join(', ', $ids);
$sql = "UPDATE " . TABLE_MESSAGES . " SET message_flag = '1' WHERE message_id IN ($ids)";
$db->query($sql);
}
return $messages;
}
+
+ public function isSelf (): bool {
+ return match ($this->source) {
+ MessageSource::System => false,
+ MessageSource::Perso => $this->perso->id == $this->to,
+ };
+ }
+
}
diff --git a/Models/Messages/MessageSource.php b/Models/Messages/MessageSource.php
new file mode 100644
index 0000000..fe30989
--- /dev/null
+++ b/Models/Messages/MessageSource.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Zed\Models\Messages;
+
+enum MessageSource {
+ /**
+ * Identifies a system message, like an automated notification.
+ */
+ case System;
+
+ /**
+ * Identifies a message sent by a being.
+ */
+ case Perso;
+}
diff --git a/includes/objects/application.php b/Models/Objects/Application.php
similarity index 84%
rename from includes/objects/application.php
rename to Models/Objects/Application.php
index 2849508..032078f 100644
--- a/includes/objects/application.php
+++ b/Models/Objects/Application.php
@@ -1,192 +1,190 @@
<?php
/**
* Application class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-02-10 02:40 Autogenerated by Pluton Scaffolding
* Some API bits
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+
require_once('perso.php');
/**
* Application class
*
* This class maps the Application table.
*
* This class also provides methods application API helper methods, to generate
* an application user key or to gets the perso ID from such a key.
*/
-class Application {
+class Application extends Entity {
public $id;
public $code;
public $name;
public $api_key;
public $description;
public string $lastError = "";
/**
* Initializes a new instance
- *
- * @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Loads the object Application (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('code', $_POST)) {
$this->code = $_POST['code'];
}
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('api_key', $_POST)) {
$this->api_key = $_POST['api_key'];
}
if (array_key_exists('description', $_POST)) {
$this->description = $_POST['description'];
}
}
/**
* Loads the object Application (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM applications WHERE application_id = '" . $id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query applications", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Application unknown: " . $this->id;
return false;
}
$this->code = $row['application_code'];
$this->name = $row['application_name'];
$this->api_key = $row['api_key'];
$this->description = $row['application_description'];
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$code = $db->escape($this->code);
$name = $db->escape($this->name);
$api_key = $db->escape($this->api_key);
$description = $db->escape($this->description);
//Updates or inserts
$sql = "REPLACE INTO applications (`application_id`, `application_code`, `application_name`, `api_key`, `application_description`) VALUES ($id, '$code', '$name', '$api_key', '$description')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/*
* ----------------------------------------------------------------------- *
* Application API methods
* ----------------------------------------------------------------------- *
*/
/**
* Loads an Application object from its API key
*
* @param string Application API key GUID
* @return Application the application matching the API key
*/
- static function from_api_key ($key) {
- global $db;
+ static function from_api_key (DatabaseEngine $db, string $key): ?self {
$key = $db->escape($key);
$sql = "SELECT * FROM applications WHERE api_key = '" . $key . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query applications", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
return null;
}
//Fills app information
- $app = new Application();
+ $app = new Application($db);
$app->id = $row['application_id'];
$app->code = $row['application_code'];
$app->name = $row['application_name'];
$app->api_key = $row['api_key'];
$app->description = $row['application_description'];
return $app;
}
/**
* Gets the perso ID from an application user key
*
* @param string $userkey User application key GUID
* @return int the perso ID
*/
function get_perso_id ($userkey) {
- global $db;
+ $db = $this->getDatabase();
$id = $db->escape($this->id);
$userkey = $db->escape($userkey);
$sql = "SELECT perso_id FROM applications_userkeys WHERE api_userkey = '$userkey' AND application_id = '$id'";
return $db->queryScalar($sql);
}
/**
* Generates a key for the specified perso and current application.
- *
- * @param int $perso_id The perso ID
- * @param string $userkey User application key GUID (optional)
- * @return Application User application key GUID
*/
- function generate_userkey ($perso_id = null, $userkey = null) {
- global $CurrentPerso;
-
+ function generate_userkey (Perso $perso, ?string $userKey = null): string {
//Ensures we've a key and someone to be assigned it
- if ($userkey === null) {
- $userkey = new_guid();
+ if ($userKey === null) {
+ $userKey = new_guid();
}
- $perso = ($perso_id === null) ? $CurrentPerso : Perso::get($perso_id);
//Saves key
- $perso->set_flag('api.app.keys.' . $this->id, $userkey);
+ $perso->set_flag('api.app.keys.' . $this->id, $userKey);
//Returns it
- return $userkey;
+ return $userKey;
}
}
diff --git a/includes/objects/content.php b/Models/Objects/Content.php
similarity index 83%
rename from includes/objects/content.php
rename to Models/Objects/Content.php
index 95ff668..484fcfc 100644
--- a/includes/objects/content.php
+++ b/Models/Objects/Content.php
@@ -1,346 +1,353 @@
<?php
/**
* Content class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-02-24 15:57 Autogenerated by Pluton Scaffolding
- *
+ *
* @package Zed
* @subpackage Model
* @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
*
* @deprecated
*/
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
+
+use Zed\Models\Base\Entity;
+
+use InvalidArgumentException;
+
/**
* Content class
*
* This class maps the content view.
*
* This view shows the content_files and content_locations tables.
*
* This class also provides helper methods, to handle files, generate thumbnails
* or get local content from a specific location.
*
* [DESIGN BY CONTRACT] This class works only with the following assertions:
* i. Each content have EXACTLY ONE location
* ii. Location fields will not be modified
*
* If a content have more than one location, only the first occurrence in
* content_locations table will be considered.
*
* If a content have no location, it will be ignored.
*
* If you edit content location, then call saveToDatabase, you will create
* a new location but future instances will contain first not deleted location.
*
* @todo remove dbc temporary limitations (cf. /do.php upload_content and infra)
- * @todo create a class ContentLocation and move location fields there
+ * @todo create a class Location and move location fields there
* @todo validate SQL schema and add in config.php TABLE_CONTENT tables
*
* @deprecated
*/
-class Content {
+class Content extends Entity {
-/* -------------------------------------------------------------
- Properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* -------------------------------------------------------------
+ Properties
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
public $id;
public $path;
public $user_id;
public $perso_id;
public $title;
public $location_global = null;
- public $location_local = null;
- public $location_k = null;
+ public $location_local = null;
+ public $location_k = null;
public $perso_name;
public $perso_nickname;
public string $lastError = "";
-/* -------------------------------------------------------------
- Constructor, __toString
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* -------------------------------------------------------------
+ Constructor, __toString
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Initializes a new Content instance
*
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Returns a string representation of current Content instance
*
* @return string the content title or path if title is blank.
*/
function __toString () {
return $this->title ?: $this->path;
}
-/* -------------------------------------------------------------
- Load/save class
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* -------------------------------------------------------------
+ Load/save class
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Loads the object Content (ie fill the properties) from the $_POST array
*
* @param boolean $allowSensibleFields if false, allow only location_local, location_k and title to be defined ; otherwise, allow all fields.
*/
function load_from_form ($allowSensibleFields = false) {
if (array_key_exists('title', $_POST)) {
$this->title = $_POST['title'];
}
if (array_key_exists('location_local', $_POST)) {
$this->location_local = $_POST['location_local'];
}
if (array_key_exists('location_k', $_POST)) {
$this->location_k = $_POST['location_k'];
}
if ($allowSensibleFields) {
if (array_key_exists('path', $_POST)) {
$this->path = $_POST['path'];
}
if (array_key_exists('user_id', $_POST)) {
$this->user_id = $_POST['user_id'];
}
if (array_key_exists('perso_id', $_POST)) {
$this->perso_id = $_POST['perso_id'];
}
if (array_key_exists('location_global', $_POST)) {
$this->location_global = $_POST['location_global'];
}
}
}
/**
* Loads the object Content (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM content WHERE content_id = '" . $id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query content", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Content unknown: " . $this->id;
return false;
}
$this->load_from_row($row);
return true;
}
/**
* Loads the object from row
*/
function load_from_row ($row) {
$this->id = $row['content_id'];
$this->path = $row['content_path'];
$this->user_id = $row['user_id'];
$this->perso_id = $row['perso_id'];
$this->title = $row['content_title'];
$this->location_global = $row['location_global'];
$this->location_local = $row['location_local'];
$this->location_k = $row['location_k'];
if (array_key_exists('perso_name', $row)) {
$this->perso_name = $row['perso_name'];
}
if (array_key_exists('perso_nickname', $row)) {
$this->perso_nickname = $row['perso_nickname'];
}
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$path = $db->escape($this->path);
$user_id = $db->escape($this->user_id);
$perso_id = $db->escape($this->perso_id);
$title = $db->escape($this->title);
$location_global = ($this->location_global !== null) ? "'" . $db->escape($this->location_global) . "'" : 'NULL';
$location_local = ($this->location_local !== null) ? "'" . $db->escape($this->location_local) . "'" : 'NULL';
$location_k = ($this->location_k !== null) ? "'" . $db->escape($this->location_k) . "'" : 'NULL';
//Updates or inserts
$sql = "REPLACE INTO content_files (`content_id`, `content_path`, `user_id`, `perso_id`, `content_title`) VALUES ($id, '$path', '$user_id', '$perso_id', '$title')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save content", '', __LINE__, __FILE__, $sql);
}
if (!$this->id) {
//Gets new record id value
$this->id = $db->nextId();
}
//Saves location
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$sql = "REPLACE INTO content_locations (location_global, location_local, location_k, content_id) VALUES ($location_global, $location_local, $location_k, $id)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save content location", '', __LINE__, __FILE__, $sql);
}
}
-/* -------------------------------------------------------------
- File handling helper methods
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* -------------------------------------------------------------
+ File handling helper methods
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Determines if the extension is valid
*
* @param string $ext The extension (without dot)
* @return boolean true if this extension is valid ; otherwise, false.
*/
public static function is_valid_extension ($ext) {
return match (strtolower($ext)) {
'jpg', 'jpeg', 'gif', 'svg', 'webp',
'tiff', 'png', 'bmp', 'xbm' => true,
default => false,
};
}
/**
* Creates a directory
*
* @param string $dir the directory to create
*/
function create_directory ($directory) {
if (!file_exists($directory)) {
@mkdir($directory); //Creates new directory, chmod 777
}
}
/**
* Handles uploaded file
*
* @return bool true if the file have been handled
*/
function handle_uploaded_file ($fileArray) {
if (!is_array($fileArray) || $fileArray['error'] != 0) {
return false;
}
$this->path = $this->getLogicalPath($fileArray["name"]);
if (!self::is_valid_extension(get_extension($this->path))) {
return false;
}
$storagePath = $this->getStoragePath();
if (move_uploaded_file($fileArray['tmp_name'], $storagePath)) {
return true;
}
return false;
}
/**
* @return string The storage path for this file on disk
*/
- function getStoragePath () : string {
+ function getStoragePath (): string {
$storageDir = CONTENT_USERS_DIR . "/" . $this->user_id;
$this->create_directory($storageDir);
$logicalDir = 'content/users/' . $this->user_id;
if (!str_starts_with($this->path, $logicalDir)) {
throw new InvalidArgumentException(<<<EOF
Logical path is '$this->path', but should start with '$logicalDir'.
EOF
-);
+ );
}
return str_replace($logicalDir, $storageDir, $this->path);
}
/**
* @param string $name The final destination filename
*
* @return string The URL to use to serve the file.
*/
- function getLogicalPath (string $name) : string {
+ function getLogicalPath (string $name): string {
return "content/users/$this->user_id/$name";
}
/**
* Generates a thumbnail using ImageMagick binary
*
* @return boolean true if the thumbnail command returns 0 as program exit code ; otherwise, false
*/
function generate_thumbnail () {
global $Config;
//Builds thumbnail filename
$sourceFile = $this->getStoragePath();
$pos = strrpos($this->path, '.');
$thumbnailFile = substr($sourceFile, 0, $pos) . 'Square' . substr($sourceFile, $pos);
//Executes imagemagick command
$command = $Config['ImageMagick']['convert'] . " \"$sourceFile\" -resize 162x162 \"$thumbnailFile\"";
@system($command, $code);
//Returns true if the command have exited with errorcode 0 (= ok)
return ($code == 0);
}
-/* -------------------------------------------------------------
- Gets content
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ /* -------------------------------------------------------------
+ Gets content
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
- * Gets content at specified location
+ * Gets all content at specified location
*
- * @param string $location_global global content location
- * @param string $location_local local content location
- * @return Array array of Content instances
+ * @return Content[] array of Content instances
*/
- static function get_local_content ($location_global, $location_local) {
- global $db;
-
+ static function get_local_content (DatabaseEngine $db, string $location_global, string $location_local) {
//Get contents at this location
$location_global = $db->escape($location_global);
- $location_local = $db->escape($location_local);
+ $location_local = $db->escape($location_local);
$sql = "SELECT c.*, p.perso_nickname, p.perso_name FROM content c, persos p WHERE c.location_global = '$location_global' AND c.location_local = '$location_local' AND p.perso_id = c.perso_id ORDER BY location_k ASC";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get content", '', __LINE__, __FILE__, $sql);
}
//Fills content array
$contents = [];
while ($row = $db->fetchRow($result)) {
- $content = new Content();
+ $content = new Content($db);
$content->load_from_row($row);
$contents[] = $content;
}
return $contents;
}
}
diff --git a/includes/objects/invite.php b/Models/Objects/Invite.php
similarity index 75%
rename from includes/objects/invite.php
rename to Models/Objects/Invite.php
index 717309b..bedc270 100644
--- a/includes/objects/invite.php
+++ b/Models/Objects/Invite.php
@@ -1,201 +1,200 @@
<?php
/**
* User invite class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-06-29 02:13 Initial version [DcK]
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
use Keruald\OmniTools\Identifiers\Random;
+use Zed\Models\Base\Entity;
/**
* User invite class
*
* This class maps the users_invites table.
*/
-class Invite {
+class Invite extends Entity {
public $code;
public $date;
public $from_user_id;
public $from_perso_id;
public string $lastError = "";
/**
* The user_id who have been claimed the invite
* Will be NULL as long as the invite haven't been claimed
*
* @var int
*/
public $to_user_id = null;
/**
* Initializes a new instance
- *
- * @param int $code the primary key
*/
- function __construct ($code = null) {
+ function __construct (DatabaseEngine $db, $code = null) {
+ $this->setDatabase($db);
+
if ($code) {
$this->code = $code;
$this->load_from_database();
} else {
//New invite code
$this->generate_code();
$this->date = time();
}
}
/**
* Generates a unique invite code and sets it in the code property.
*/
function generate_code () {
- global $db;
+ $db = $this->getDatabase();
do {
$this->code = Random::generateString("AAA111");
$sql = "SELECT COUNT(*) FROM " . TABLE_USERS_INVITES . " WHERE invite_code = '$this->code' LOCK IN SHARE MODE;";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't access invite users table", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
} while ($row[0]);
}
/**
* Loads the object Invite (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$code = $db->escape($this->code);
$sql = "SELECT * FROM " . TABLE_USERS_INVITES . " WHERE invite_code = '" . $code . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query invite codes", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Invite code unknown: " . $this->code;
return false;
}
$this->code = $row['invite_code'];
$this->date = $row['invite_date'];
$this->from_user_id = $row['invite_from_user_id'];
$this->from_perso_id = $row['invite_from_perso_id'];
$this->to_user_id = $row['invite_to_user_id'];
return true;
}
/**
* Determines whether the current invite code have been claimed by an user.
*
* @return true if the code have been claimed ; otherwise, false.
*/
- function is_claimed () {
+ function is_claimed (): bool {
return (bool)$this->to_user_id;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$code = $db->escape($this->code);
$date = $db->escape($this->date);
$from_user_id = $db->escape($this->from_user_id);
$from_perso_id = $db->escape($this->from_perso_id);
$to_user_id = $this->to_user_id ? "'" . $db->escape($this->to_user_id) . "'" : 'NULL';
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_USERS_INVITES . " (`invite_code`, `invite_date`, `invite_from_user_id`, `invite_from_perso_id`, `invite_to_user_id`) VALUES ('$code', '$date', '$from_user_id', '$from_perso_id', $to_user_id)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save invite code", '', __LINE__, __FILE__, $sql);
}
}
/**
* Deletes the invite
*/
function delete () {
- global $db;
+ $db = $this->getDatabase();
+
$code = $db->escape($this->code);
$sql = "DELETE FROM " . TABLE_USERS_INVITES . " WHERE invite_code = '$code'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save delete code", '', __LINE__, __FILE__, $sql);
}
}
/**
* Creates an invite code
- *
- * @param int $user_id user id
- * @param int $perso_id perso id
- * @return string the invite code
*/
- static function create ($user_id, $perso_id) {
- $invite = new Invite();
- $invite->from_perso_id = $perso_id;
- $invite->from_user_id = $user_id;
+ static function create (DatabaseEngine $db, Perso $perso): string {
+ $invite = new Invite($db);
+ $invite->from_perso_id = $perso->id;
+ $invite->from_user_id = $perso->user_id;
$invite->save_to_database();
+
return $invite->code;
}
/**
* Gets invites generated by the specified perso ID
*
- * @param int $perso_id the perso whom to get the invites
- * @return Array an array of string, each line being an invite code
+ * @return string[]
*/
- static function get_invites_from ($perso_id) {
- global $db;
+ static function get_invites_from (DatabaseEngine $db, Perso $perso): array {
+ $perso_id = $perso->id;
$sql = "SELECT invite_code FROM " . TABLE_USERS_INVITES
- . " WHERE invite_from_perso_id = $perso_id AND invite_to_user_id IS NULL ORDER BY invite_date ASC";
+ . " WHERE invite_from_perso_id = $perso_id AND invite_to_user_id IS NULL ORDER BY invite_date ASC";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't access invite users table", '', __LINE__, __FILE__, $sql);
}
+
$codes = [];
while ($row = $db->fetchRow($result)) {
$codes[] = $row['invite_code'];
}
return $codes;
}
/**
* Gets the perso ID who invited the specified perso
*
- * @param int $perso_id the perso whom to get the invites
* @return int|null the perso whom to get the invites ; or null, if nobody have invited him
*/
- static function who_invited ($perso_id) {
- global $db;
- $perso = Perso::get($perso_id);
+ static function who_invited (DatabaseEngine $db, Perso $perso): ?int {
+ $user_id = $perso->user_id;
- if ($user_id = $perso->user_id) {
- $sql = "SELECT invite_from_perso_id FROM " . TABLE_USERS_INVITES . " WHERE invite_to_user_id = '$user_id'";
- if (!$result = $db->query($sql)) {
- message_die(SQL_ERROR, "Can't access invite users table", '', __LINE__, __FILE__, $sql);
- }
- if ($row = $db->fetchRow($result)) {
- return $row[0];
- }
+ $sql = "SELECT invite_from_perso_id FROM " . TABLE_USERS_INVITES . " WHERE invite_to_user_id = '$user_id'";
+ if (!$result = $db->query($sql)) {
+ message_die(SQL_ERROR, "Can't access invite users table", '', __LINE__, __FILE__, $sql);
+ }
+ if ($row = $db->fetchRow($result)) {
+ return $row[0];
}
return null;
}
}
diff --git a/includes/objects/motd.php b/Models/Objects/MOTD.php
similarity index 85%
rename from includes/objects/motd.php
rename to Models/Objects/MOTD.php
index ef8bac3..149ad0e 100644
--- a/includes/objects/motd.php
+++ b/Models/Objects/MOTD.php
@@ -1,103 +1,110 @@
<?php
/**
* MOTD class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-02-03 21:11 Import from Azhàr code
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+
/**
* MOTD class
*
* This class maps the motd table.
*/
-class MOTD {
+class MOTD extends Entity {
public $id;
public $perso_id;
public $text;
public $date;
public string $lastError = "";
/**
* Initializes a new instance of a MOTD object
- *
- * @param int $id The MOTD ID
*/
- function __construct ($id = '') {
- if ($id) {
+ function __construct (DatabaseEngine $db, int $id = null) {
+ $this->setDatabase($db);
+
+ if ($id !== null) {
$this->id = $id;
return $this->load_from_database();
} else {
$this->date = time();
return true;
}
}
/**
* Loads the object MOTD (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('perso_id', $_POST)) {
$this->user_id = $_POST['user_id'];
}
if (array_key_exists('text', $_POST)) {
$this->text = $_POST['text'];
}
if (array_key_exists('date', $_POST)) {
$this->date = $_POST['date'];
}
}
/**
* Loads the object MOTD (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM " . TABLE_MOTD . " WHERE motd_id = '" . $id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query azhar_motd", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "MOTD unknown: " . $this->id;
return false;
}
$this->perso_id = $row['perso_id'];
$this->text = $row['motd_text'];
$this->date = $row['motd_date'];
return true;
}
/**
* Saves the object to the database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
+
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$perso_id = $db->escape($this->perso_id);
$text = $db->escape($this->text);
$date = $db->escape($this->date);
$sql = "REPLACE INTO " . TABLE_MOTD . " (`motd_id`, `perso_id`, `motd_text`, `motd_date`) VALUES ($id, '$perso_id', '$text', '$date')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
}
}
diff --git a/includes/objects/perso.php b/Models/Objects/Perso.php
similarity index 82%
rename from includes/objects/perso.php
rename to Models/Objects/Perso.php
index 3727cde..376b616 100644
--- a/includes/objects/perso.php
+++ b/Models/Objects/Perso.php
@@ -1,612 +1,615 @@
<?php
/**
* Perso class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-27 00:39 Autogenerated by Pluton Scaffolding
* 0.2 2010-01-29 14:39 Adding flags support
* 0.3 2010-02-06 17:50 Adding static perso hashtable
* 0.4 2012-07-04 11:37 Refactoring: moving code from index.php
*
* @package Zed
* @subpackage Model
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010, 2012 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
*/
-require_once("includes/geo/location.php");
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+use Zed\Models\Geo\Location;
+use Zed\Models\Messages\Message;
/**
* Perso class
*
* This class maps the persos table.
*
* The class also provides methods
* to move or locate a perso,
* to gets and sets perso's flags and notes (tables persos_flags and persos_notes),
* to gets user's perso or check if a perso is online,
* to handle on select and logout events.
*
*/
-class Perso {
+class Perso extends Entity {
public $id;
public $user_id;
public $name;
public $nickname;
public $race;
public $sex;
public string $avatar = "";
public $location;
public $location_global;
public $location_local;
public $flags;
public string $lastError = "";
public static $hashtable_id = [];
public static $hashtable_name = [];
/**
* Initializes a new instance
- *
- * @param mixed $data perso ID or nickname
*/
- function __construct ($data = null) {
+ function __construct (DatabaseEngine $db, mixed $data = null) {
+ $this->setDatabase($db);
+
if ($data) {
if (is_numeric($data)) {
$this->id = $data;
} else {
$this->nickname = $data;
}
if (!$this->load_from_database()) {
message_die(GENERAL_ERROR, $this->lastError, "Can't authenticate perso");
}
} else {
$this->generate_id();
}
}
/**
* Initializes a new Perso instance if needed or get already available one.
*
- * @param mixed $data perso ID or nickname
- * @return Perso the perso instance
+ * @deprecated Move to an entities repository
*/
- static function get ($data = null) : Perso {
+ static function get (DatabaseEngine $db, mixed $data = null): Perso {
if ($data) {
//Checks in the hashtables if we already have loaded this instance
if (is_numeric($data)) {
if (array_key_exists($data, Perso::$hashtable_id)) {
return Perso::$hashtable_id[$data];
}
} else {
if (array_key_exists($data, Perso::$hashtable_name)) {
return Perso::$hashtable_name[$data];
}
}
}
- return new Perso($data);
+ return new Perso($db, $data);
}
/**
* Loads the object Perso (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('user_id', $_POST)) {
$this->user_id = $_POST['user_id'];
}
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('nickname', $_POST)) {
$this->nickname = $_POST['nickname'];
}
if (array_key_exists('race', $_POST)) {
$this->race = $_POST['race'];
}
if (array_key_exists('sex', $_POST)) {
$this->sex = $_POST['sex'];
}
if (array_key_exists('avatar', $_POST)) {
$this->avatar = $_POST['avatar'];
}
if (array_key_exists('location_global', $_POST)) {
$this->location_global = $_POST['location_global'];
}
if (array_key_exists('location_local', $_POST)) {
$this->location_local = $_POST['location_local'];
}
}
/**
* Loads the object Perso (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
//Gets perso
$sql = "SELECT * FROM " . TABLE_PERSOS;
if ($this->id) {
$id = $db->escape($this->id);
$sql .= " WHERE perso_id = '" . $id . "'";
} else {
$nickname = $db->escape($this->nickname);
$sql .= " WHERE perso_nickname = '" . $nickname . "'";
}
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query persos", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Perso unknown: " . $this->id;
return false;
}
$this->id = $row['perso_id'];
$this->user_id = $row['user_id'];
$this->name = $row['perso_name'];
$this->nickname = $row['perso_nickname'];
$this->race = $row['perso_race'];
$this->sex = $row['perso_sex'];
$this->avatar = $row['perso_avatar'];
$this->location_global = $row['location_global'];
$this->location_local = $row['location_local'];
//Gets flags
$sql = "SELECT flag_key, flag_value FROM " . TABLE_PERSOS_FLAGS .
- " WHERE perso_id = $this->id";
+ " WHERE perso_id = $this->id";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get flags", '', __LINE__, __FILE__, $sql);
}
while ($row = $db->fetchRow($result)) {
$this->flags[$row["flag_key"]] = $row["flag_value"];
}
//Gets location
- $this->location = new GeoLocation(
+ $this->location = new Location(
+ $db,
$this->location_global,
$this->location_local
);
//Puts object in hashtables
Perso::$hashtable_id[$this->id] = $this;
Perso::$hashtable_name[$this->nickname] = $this;
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$user_id = $db->escape($this->user_id);
$name = $db->escape($this->name);
$nickname = $db->escape($this->nickname);
$race = $db->escape($this->race);
$sex = $db->escape($this->sex);
$avatar = $db->escape($this->avatar);
- $location_global = $this->location_global ? "'" . $db->escape($this->location_global) . "'" : 'NULL';
- $location_local = $this->location_local ? "'" . $db->escape($this->location_local) . "'" : 'NULL';
+ $location_global = $this->location_global ? "'" . $db->escape($this->location_global) . "'" : 'NULL';
+ $location_local = $this->location_local ? "'" . $db->escape($this->location_local) . "'" : 'NULL';
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_PERSOS . " (`perso_id`, `user_id`, `perso_name`, `perso_nickname`, `perso_race`, `perso_sex`, `perso_avatar`, `location_global`, `location_local`) VALUES ($id, '$user_id', '$name', '$nickname', '$race', '$sex', '$avatar', $location_global, $location_local)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/**
* Updates the specified field in the database record
- *
- * @param string $field The field to save
*/
- function save_field ($field) {
- global $db;
+ function save_field (string $field): void {
+ $db = $this->getDatabase();
+
if (!$this->id) {
message_die(GENERAL_ERROR, "You're trying to update a perso record not yet saved in the database: $field");
}
$id = $db->escape($this->id);
$value = $db->escape($this->$field);
$sql = "UPDATE " . TABLE_PERSOS . " SET `$field` = '$value' WHERE perso_id = '$id'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save $field field", '', __LINE__, __FILE__, $sql);
}
}
/**
* Gets perso location
*
* @return string The location names
*/
- public function where () {
+ public function where (): string {
return $this->location->__toString();
}
/**
* Moves the perso to a new location
*
- * @param string $global the global target location
- * @param string $global the local target location
+ * @param string|null $global the global target location
+ * @param string|null $local the local target location
*/
- public function move_to ($global = null, $local = null) {
+ public function move_to (string $global = null, string $local = null): void {
//Sets global location
- if ($global != null) {
+ if ($global !== null) {
$this->location_global = $global;
}
//Sets local location
- if ($local != null) {
+ if ($local !== null) {
$this->location_local = $local;
}
//Updates database record
- if ($global != null && $local != null) {
- global $db;
+ if ($global !== null && $local !== null) {
+ $db = $this->getDatabase();
+
$perso_id = $db->escape($this->id);
$g = $db->escape($this->location_global);
$l = $db->escape($this->location_local);
$sql = "UPDATE " . TABLE_PERSOS .
- " SET location_global = '$g', location_local = '$l'" .
- " WHERE perso_id = '$perso_id'";
+ " SET location_global = '$g', location_local = '$l'" .
+ " WHERE perso_id = '$perso_id'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save new $global $local location.", '', __LINE__, __FILE__, $sql);
}
} elseif ($global != null) {
$this->save_field('location_global');
} elseif ($local != null) {
$this->save_field('location_local');
}
//Updates location member
- $this->location = new GeoLocation(
+ $this->location = new Location(
+ $this->getDatabase(),
$this->location_global,
$this->location_local
);
}
/**
* Gets the specified flag value
*
* @param string $key flag key
* @param mixed $defaultValue default value if the flag doesn't exist
* @return mixed the flag value (string) or null if not existing
*/
public function get_flag ($key, $defaultValue = null) {
return $this->flag_exists($key) ? $this->flags[$key] : $defaultValue;
}
/**
* Determines if the specified flag exists
*
* @param string $key the flag key to check
* @return boolean true if the specified flag exists ; otherwise, false.
*/
public function flag_exists ($key) {
return array_key_exists($key, $this->flags);
}
/**
* Sets the specified flag
*
* @param string $key flag key
* @param string $value flag value (optional, default value: 1)
*/
public function set_flag ($key, $value = 1) {
//Checks if flag isn't already set at this value
if ($this->flags != null && array_key_exists($key, $this->flags) && $this->flags[$key] === $value) {
return;
}
//Saves flag to database
global $db;
$id = $db->escape($this->id);
$key = $db->escape($key);
$value = $db->escape($value);
$sql = "REPLACE " . TABLE_PERSOS_FLAGS . " SET perso_id = '$id', flag_key = '$key', flag_value = '$value'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save flag", '', __LINE__, __FILE__, $sql);
}
//Sets flag in this perso instance
$this->flags[$key] = $value;
}
/**
* Deletes the specified flag
*
* @param string $key flag key
*/
public function delete_flag ($key) {
global $db;
if (!array_key_exists($key, $this->flags)) {
return;
}
$id = $db->escape($this->id);
$key = $db->escape($key);
- $sql = "DELETE FROM " . TABLE_PERSOS_FLAGS .
- " WHERE flag_key = '$key' AND perso_id = '$id' LIMIT 1";
+ $sql = "DELETE FROM " . TABLE_PERSOS_FLAGS .
+ " WHERE flag_key = '$key' AND perso_id = '$id' LIMIT 1";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't delete flag", '', __LINE__, __FILE__, $sql);
}
}
/**
* Ensures the current perso have the specified flag or exits.
*
*
* @param string $flag the flag to assert
* @param int $threshold value the flags must strictly be greater than (optional, the default value is 0)
*
* Example:
* <code>
* $perso->set_flag('quux.foo', 1);
* //The perso wants to read quux, which we allow with the flag quux.foo
* $perso->request_flag('quux.foo'); //will be okay
*
* //The perso wants also to write quux, which we all allow if quux.foo = 2
* //The threshold will so be 1, as 2 > 1
* $perso->request_flag('quux.foo', 1); //Will exits, with a "You don't have quux.foo permission" message
* </code>
*/
public function request_flag ($flag, $threshold = 0) {
if (!array_key_exists($flag, $this->flags) || $this->flags[$flag] <= $threshold) {
message_die(HACK_ERROR, "You don't have $flag permission.", "Permissions");
}
}
/**
* Gets the specified note
*
* @param string $code the note code
* @return string the note content
*/
public function get_note ($code) {
global $db;
$id = $db->escape($this->id);
$code = $db->escape($code);
$sql = "SELECT note_text FROM " . TABLE_PERSOS_NOTES . " WHERE perso_id = '$id' AND note_code LIKE '$code'";
return $db->queryScalar($sql);
}
/**
* Sets the specified note
*
* @param string $code the note code
* @param string $text the note content
*/
public function set_note ($code, $text) {
global $db;
$id = $db->escape($this->id);
$code = $db->escape($code);
$text = $db->escape($text);
- $sql = "REPLACE INTO " . TABLE_PERSOS_NOTES . " (perso_id, note_code, note_text) VALUES ('$id', '$code', '$text')";
+ $sql = "REPLACE INTO " . TABLE_PERSOS_NOTES . " (perso_id, note_code, note_text) VALUES ('$id', '$code', '$text')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save note", '', __LINE__, __FILE__, $sql);
}
}
/**
* Counts the amount of notes the perso have saved
*
* @return int the amount of notes assigned to the perso
*/
public function count_notes () {
global $db;
$id = $db->escape($this->id);
$sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS_NOTES . " WHERE perso_id = '$id'";
return $db->queryScalar($sql);
}
/*
* Determines if the specified ID is available
*
* @param integer $id The perso ID to check
* @return boolean true if the specified ID is available ; otherwise, false
*/
public static function is_available_id ($id) {
global $db;
$sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE perso_id = $id LOCK IN SHARE MODE";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't access users table", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return ($row[0] == 0);
}
/**
* Generates a unique ID for the current object
*/
private function generate_id () {
do {
$this->id = rand(2001, 5999);
} while (!Perso::is_available_id($this->id));
}
/**
* Checks if the nickname is available
*
* @param string $nickname the nickname to check
*/
public static function is_available_nickname ($nickname) {
global $db;
$nickname = $db->escape($nickname);
$sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE perso_nickname LIKE '$nickname' LOCK IN SHARE MODE;";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return ($row[0] == 0);
}
/**
* Counts the perso a user have
*
* @param int user_id the user ID
* @return int the user's perso count
*/
- public static function get_persos_count ($user_id) : int {
+ public static function get_persos_count ($user_id): int {
global $db;
$sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE user_id = $user_id";
return (int)$db->queryScalar($sql);
}
/**
* Gets an array with all the perso of the specified user
- *
- * @param int $user_id the user ID
*/
- public static function get_persos (int $user_id) : array {
- global $db;
- $user_id = $db->escape($user_id);
+ public static function get_persos (DatabaseEngine $db, User $user): array {
+ $user_id = $db->escape($user->id);
$sql = "SELECT perso_id FROM " . TABLE_PERSOS . " WHERE user_id = $user_id";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get persos", '', __LINE__, __FILE__, $sql);
}
$persos = [];
while ($row = $db->fetchRow($result)) {
- $persos[] = Perso::get($row['perso_id']);
+ $persos[] = Perso::get($db, $row['perso_id']);
}
return $persos;
}
/**
* Gets the first perso a user have
* (typically to be used when get_persos_count returns 1 to autoselect)
*
- * @param int user_id the user ID
+ * @deprecated This case is now handled by the TryAutoSelect class.
*/
- public static function get_first_perso ($user_id) {
- global $db;
- $sql = "SELECT perso_id FROM " . TABLE_PERSOS ." WHERE user_id = $user_id LIMIT 1";
+ public static function get_first_perso (DatabaseEngine $db, int $user_id) {
+ $sql = "SELECT perso_id FROM " . TABLE_PERSOS . " WHERE user_id = $user_id LIMIT 1";
if ($perso_id = $db->queryScalar($sql)) {
- return new Perso($perso_id);
+ return new Perso($db, $perso_id);
}
}
/**
* Determines whether the perso is online
*
* @return bool true if the perso is online ; otherwise, false.
*/
public function is_online () {
global $db;
$id = $db->escape($this->id);
- $sql = "SELECT MAX(online) FROM " . TABLE_SESSIONS ." WHERE perso_id = $id";
+ $sql = "SELECT MAX(online) FROM " . TABLE_SESSIONS . " WHERE perso_id = $id";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return ($row[0] == 1);
}
/**
* This event method is called when the user selects a new perso
*/
public function on_select () {
//Session
set_info('perso_id', $this->id);
$this->set_flag("site.lastlogin", $_SERVER['REQUEST_TIME']);
define("PersoSelected", true);
}
/**
* This event method is called when the user logs off its account or perso
*/
public function on_logout () {
//Clears perso information in $_SESSION and session table
set_info('perso_id', null);
clean_session();
}
/**
* This event method is called when the perso is created
*/
public function on_create () {
//Notifies host
$this->notify_inviter();
}
- /**
- * Creates a new perso, from a parameter form
- *
- * @param User $user The user to attach the perso to
- * @param Perso $perso A reference to the created perso (don't initialize it, give it a null value)
- * @param array $errors A reference to the arrays containing errors (should be an empty array, or the method will always return false)
- * @return boolean true if the perso has ben created ; otherwise, false
- */
- public static function create_perso_from_form (User $user, &$perso, &$errors) {
- $perso = new Perso();
+ /**
+ * Creates a new perso, from a parameter form
+ *
+ * @param DatabaseEngine $db
+ * @param User $user The user to attach the perso to
+ * @param Perso $perso A reference to the created perso (don't initialize it, give it a null value)
+ * @param array $errors A reference to the arrays containing errors (should be an empty array, or the method will always return false)
+ * @return boolean true if the perso has ben created ; otherwise, false
+ */
+ public static function create_perso_from_form (DatabaseEngine $db, User $user, &$perso, &$errors): bool {
+ $perso = new Perso($db);
$perso->load_from_form();
$perso->user_id = $user->id;
//Validates forms
if (!$perso->name) {
$errors[] = lang_get("NoFullnameSpecified");
}
if (!$perso->race) {
$errors[] = lang_get("NoRaceSpecified");
$perso->race = "being";
}
if (!$perso->sex) {
$errors[] = lang_get("NoSexSpecified");
}
if (!$perso->nickname) {
$errors[] = lang_get("NoNicknameSpecified");
} elseif (!Perso::is_available_nickname($perso->nickname)) {
$errors[] = lang_get("UnavailableNickname");
}
- if (count($errors)) {
+ if (count($errors)) {
return false;
- }
+ }
- //Creates perso
- $perso->save_to_database();
- $perso->on_create();
- return true;
+ //Creates perso
+ $perso->save_to_database();
+ $perso->on_create();
+ return true;
}
/**
* Notifies the person having invited this perso
*/
- public function notify_inviter() {
- require_once('includes/objects/message.php');
- require_once('includes/objects/invite.php');
- $message = new Message();
- $message->from = 0;
- $message->to = invite::who_invited($this->id);
- $message->text = sprintf(
+ public function notify_inviter () {
+ $db = $this->getDatabase();
+
+ $message = new Message($db);
+ $message->setAsSystem();
+ $message->to = Invite::who_invited($db, $this);
+ $message->text = sprintf(
lang_get('InvitePersoCreated'),
$this->name,
get_server_url() . get_url('who', $this->nickname)
);
$message->send();
}
}
diff --git a/includes/objects/ship.php b/Models/Objects/Ship.php
similarity index 85%
rename from includes/objects/ship.php
rename to Models/Objects/Ship.php
index d7bbad6..f55be2b 100644
--- a/includes/objects/ship.php
+++ b/Models/Objects/Ship.php
@@ -1,342 +1,353 @@
<?php
/**
* Ship class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-02-05 18:51 Autogenerated by Pluton Scaffolding
* 0.2 2010-02-06 17:30 Ship API
*
* @package Zed
* @subpackage Model
* @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
*/
-require_once("perso.php");
-require_once("includes/geo/location.php");
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
+use the;
+use Zed\Models\Base\Entity;
+use Zed\Models\Geo\Location;
/**
* Ship class
*
* This class maps the ship table.
*
* It also provides helper methods to handle landing and fly out,
* or locate the ship.
*
* The class also provides methods for the ship API.
*/
-class Ship {
+class Ship extends Entity {
/*
* ----------------------------------------------------------------------- *
* Ship class definition
* ----------------------------------------------------------------------- *
*/
public $id;
public $name;
public $location_global;
public $location_local;
public $api_key;
public $description;
public string $lastError = "";
private static $hashtable = [];
/**
* Initializes a new instance
*
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
if (preg_match("/^S[0-9]{5}$/", $id)) {
$id = substr($id, 1);
}
$this->id = $id;
$this->load_from_database();
}
}
/**
* Initializes a new Ship instance if needed or gets already available one.
*
* @param mixed $data ship ID
* @return Ship the ship instance
+ *
+ * @deprecated Use an entities repository
*/
- static function get ($data = null) {
+ static function get (DatabaseEngine $db, $data = null) {
if ($data !== null) {
if (preg_match("/^S[0-9]{5}$/", $id)) {
$id = substr($data, 1);
} else {
$id = $data;
}
//Checks in the hashtable if we already have loaded this instance
if (array_key_exists($id, self::$hashtable)) {
return self::$hashtable[$data];
}
}
- return new Ship($data);
+ return new Ship($db, $data);
}
/**
* Loads the object Ship (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('location_global', $_POST)) {
- $this->location = $_POST['location_global'];
+ $this->location_global = $_POST['location_global'];
}
if (array_key_exists('location_local', $_POST)) {
- $this->location = $_POST['location_local'];
+ $this->location_local = $_POST['location_local'];
}
if (array_key_exists('api_key', $_POST)) {
$this->api_key = $_POST['api_key'];
}
if (array_key_exists('description', $_POST)) {
$this->description = $_POST['description'];
}
}
/**
* Loads the object Ship (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM ships WHERE ship_id = '" . $id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query Ships", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Ship unknown: " . $this->id;
return false;
}
$this->name = $row['ship_name'];
$this->location_global = $row['location_global'];
$this->location_local = $row['location_local'];
$this->api_key = $row['api_key'];
$this->description = $row['ship_description'];
-
//Puts object in hashtable
self::$hashtable[$this->id] = $this;
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$name = $db->escape($this->name);
$location_global = $db->escape($this->location_global);
$location_local = $db->escape($this->location_local);
$api_key = $db->escape($this->api_key);
$description = $db->escape($this->description);
//Updates or inserts
$sql = "REPLACE INTO ships (`ship_id`, `ship_name`, `location_global`, `location_local`, `api_key`, `ship_description`) VALUES ($id, '$name', '$location_global', '$location_location', '$api_key', '$description')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/**
* Gets the ship code, as a string representation of the instance.
*
* @return the ship's code
*/
function __toString () {
return $this->get_code();
}
/**
* Get ships at specified location
*
- * @param string $location_global global location
- * @param string $location_local local location
- * @return array An array of Ship items, each one a ship at the specified location
+ * @return Ship[]
+ * @throws Exception
*/
- static function get_ships_at ($location_global, $location_local = null) {
- global $db;
-
+ static function get_ships_at (DatabaseEngine $db, string $location_global, string $location_local = null): array {
//Gets ships
$sql = "SELECT ship_id, location_global, location_local FROM " . TABLE_SHIPS . " WHERE location_global IS NOT NULL";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get ships", '', __LINE__, __FILE__, $sql);
}
+
$ships = [];
- $location = new GeoLocation($location_global, $location_local);
+ $location = new Location($db, $location_global, $location_local);
while ($row = $db->fetchRow($result)) {
- $shipLocation = new GeoLocation($row['location_global'], $row['location_local']);
+ $shipLocation = new Location($db, $row['location_global'], $row['location_local']);
if ($location->equals($shipLocation)) {
- $ships[] = self::get($row['ship_id']);
+ $ships[] = self::get($db, $row['ship_id']);
}
}
return $ships;
}
/*
* ----------------------------------------------------------------------- *
* Helper methods
* ----------------------------------------------------------------------- *
*/
/**
* Gets ship code, e.g. S00001
*
* @return string the ship code
*/
function get_code () {
return sprintf("S%05d", $this->id);
}
/**
* Determines if the ship is at a spatioport (or assimilated)
*
* @return bool true if the ship is at a spatioport ; false if the ship is in space
*/
function in_spatioport () {
return $this->location_local !== null;
}
/**
* Flies in the sip
*
* @param string $location_local the spatioport location
*
* @todo Completes location global e.g. B00001 -> B00001003
*/
function fly_in ($location_local = null) {
$this->location_local = ($location_local == null) ? 0 : $location_local;
}
/**
* Flies out.
*/
function fly_out () {
$this->location_local = null;
}
/*
* ----------------------------------------------------------------------- *
* Ship API methods
* ----------------------------------------------------------------------- *
*/
/**
* Requests the specified perso to authenticate to this ship
*
* @param mixed $perso_data the perso id or name
*/
function request_perso_authenticate ($perso_data) {
- $perso = Perso::get($perso_data);
+ $perso = Perso::get($this->getDatabase(), $perso_data);
$flag = sprintf("request.api.ship.auth.%s", $this->get_code());
+
$perso->set_flag($flag);
$perso->set_flag("site.requests");
}
/**
* Determines if a perso is authenticated to this ship
*
* @param mixed $perso_data the perso id or name
* @return boolean true if the specified perso name is authenticated to this ship ; otherwise, false.
*/
function is_perso_authenticated ($perso_data) {
+ $perso = Perso::get($this->getDatabase(), $perso_data);
$flag = sprintf("api.ship.auth.%s", $this->get_code());
- return Perso::get($perso_data)->flags[$flag] == 1;
+
+ return $perso->flags[$flag] == 1;
}
/**
* Requests the specified perso to confirm the ship API session
*
* @param string $session_id a session ID provided by calling application
* @param mixed $perso_data the perso id or name
*/
function request_perso_confirm_session ($session_id, $perso_data) {
- $perso = Perso::get($perso_data);
+ $perso = Perso::get($this->getDatabase(), $perso_data);
$flag = sprintf("request.api.ship.session.%s.%s", $this->get_code(), $session_id);
+
+
$perso->set_flag($flag);
$perso->set_flag("site.requests");
}
/**
* Cleans ship API temporary sessions
*/
- static function clean_ship_sessions () {
+ private function clean_ship_sessions (): void {
//Cleans old sessions
- global $db;
+ $db = $this->getDatabase();
$sql = "DELETE FROM " . TABLE_REGISTRY . " WHERE registry_key LIKE 'api.ship.session.%' AND registry_updated < NOW() - 7200";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't delete old ship API sessions", '', __LINE__, __FILE__, $sql);
}
}
/**
* Gets perso id from specified session
*
* @param string $session_id a session ID provided by calling application
* @return mixed the session
*/
function get_perso_from_session ($session_id) {
//Cleans old session
- self::clean_ship_sessions();
+ $this->clean_ship_sessions();
//Reads api.ship.session.S00001.$session_id
//This registry key contains perso_id if it exists and valid
$key = sprintf("api.ship.session.%s.%s", $this->get_code(), $session_id);
- return registry_get($key);
+ return registry_get($this->getDatabase(), $key);
}
/**
* Loads a Ship object from its API key
*
* @param string $key API key GUID
* @return Ship the ship matching the API key
*/
static function from_api_key ($key) {
global $db;
$key = $db->escape($key);
$sql = "SELECT * FROM ships WHERE api_key = '" . $key . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query ships", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
return null;
}
//Fills ship information
- $ship = new Ship();
+ $ship = new Ship($db);
$ship->id = $row['ship_id'];
$ship->name = $row['ship_name'];
$ship->location = $row['ship_location'];
$ship->api_key = $row['api_key'];
$ship->description = $row['ship_description'];
return $ship;
}
}
diff --git a/includes/objects/user.php b/Models/Objects/User.php
similarity index 89%
rename from includes/objects/user.php
rename to Models/Objects/User.php
index b7b1e01..e517e19 100644
--- a/includes/objects/user.php
+++ b/Models/Objects/User.php
@@ -1,266 +1,288 @@
<?php
/**
* User class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* [DESIGN BY CONTRACT NOTE] No more than one OpenID per user
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Objects;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+use Zed\Models\Base\WithDatabase;
+
/**
* User class
*
* This class maps the users and users_openid tables.
*
* It also provides helper methods to check if a login is available,
* or to retrieve a username from e-mail address.
*/
-class User {
+class User extends Entity {
+
+ use WithDatabase;
+
+ ///
+ /// Properties
+ ///
public $id;
public $name;
public $password;
public $active = 0;
public string $actkey = "";
public $email;
public $regdate;
public string $lastError = "";
public static $hashtable_id = [];
public static $hashtable_name = [];
public array $session = [];
+ ///
+ /// Constructors
+ ///
+
/**
* Initializes a new instance
*
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Initializes a new User instance if needed or get already available one.
*
* @param mixed $data user ID or name
* @return User the user instance
*/
- static function get ($data = null) {
+ static function get (DatabaseEngine $db, $data = null) {
if ($data) {
//Checks in the hashtables if we already have loaded this instance
if (is_numeric($data)) {
if (array_key_exists($data, User::$hashtable_id)) {
return User::$hashtable_id[$data];
}
} else {
if (array_key_exists($data, User::$hashtable_name)) {
return User::$hashtable_name[$data];
}
}
}
- $user = new User($data);
- return $user;
+ return new User($db, $data);
}
+ ///
+ /// Helper methods
+ ///
+
/**
* Loads the object User (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('password', $_POST)) {
$this->password = $_POST['password'];
}
if (array_key_exists('active', $_POST)) {
$this->active = $_POST['active'];
}
if (array_key_exists('actkey', $_POST)) {
$this->actkey = $_POST['actkey'];
}
if (array_key_exists('email', $_POST)) {
$this->email = $_POST['email'];
}
if (array_key_exists('regdate', $_POST)) {
$this->regdate = $_POST['regdate'];
}
}
/**
* Loads the object User (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$sql = "SELECT * FROM " . TABLE_USERS . " WHERE user_id = '" . $this->id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query users", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "User unknown: " . $this->id;
return false;
}
$this->name = $row['username'];
$this->password = $row['user_password'];
$this->active = $row['user_active'];
$this->actkey = $row['user_actkey'];
$this->email = $row['user_email'];
$this->regdate = $row['user_regdate'];
//Puts object in hashtables
User::$hashtable_id[$this->id] = $this;
User::$hashtable_name[$this->name] = $this;
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$name = $db->escape($this->name);
$password = $db->escape($this->password);
$active = $db->escape($this->active);
$actkey = $db->escape($this->actkey);
$email = $db->escape($this->email);
$regdate = $this->regdate ? "'" . $db->escape($this->regdate) . "'" : 'NULL';
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_USERS . " (`user_id`, `username`, `user_password`, `user_active`, `user_actkey`, `user_email`, `user_regdate`) VALUES ($id, '$name', '$password', '$active', '$actkey', '$email', $regdate)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/**
* Updates the specified field in the database record
*/
function save_field ($field) {
- global $db;
+ $db = $this->getDatabase();
+
if (!$this->id) {
message_die(GENERAL_ERROR, "You're trying to update a record not yet saved in the database");
}
$id = $db->escape($this->id);
$value = $db->escape($this->$field);
$sql = "UPDATE " . TABLE_USERS . " SET `$field` = '$value' WHERE user_id = '$id'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save $field field", '', __LINE__, __FILE__, $sql);
}
}
/**
* Generates a unique user id
*/
function generate_id () {
- global $db;
+ $db = $this->getDatabase();
do {
$this->id = rand(2001, 5999);
$sql = "SELECT COUNT(*) FROM " . TABLE_USERS . " WHERE user_id = $this->id LOCK IN SHARE MODE;";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't access users table", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
} while ($row[0]);
}
/**
* Fills password field with encrypted version of the specified clear password
*
* @param string $newpassword The user's new password
*/
public function set_password ($newpassword) {
$this->password = md5($newpassword);
}
/**
* Deletes OpenID for this user
*/
public function delete_OpenID () {
$this->set_OpenID('');
}
/**
* Sets OpenID for this user
*
* @param string $url OpenID endpoint URL
*/
public function set_OpenID ($url) {
- global $db;
+ $db = $this->getDatabase();
+
if (!$this->id) {
$this->save_to_database();
}
$url = $db->escape($url);
$sql = "DELETE FROM " . TABLE_USERS_AUTH . " WHERE auth_type = 'OpenID' AND user_id = $this->id";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't delete old OpenID", '', __LINE__, __FILE__, $sql);
}
if ($url != '') {
$sql = "INSERT INTO " . TABLE_USERS_AUTH . " (auth_type, auth_identity, user_id) VALUES ('OpenID', '$url', $this->id)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't add new OpenID", '', __LINE__, __FILE__, $sql);
}
}
}
/**
* Checks if a login is available
*
* @param string $login the login to check
* @return bool true if the specified login is available ; otherwise, false.
*/
- public static function is_available_login ($login) : bool {
- global $db;
+ public static function is_available_login (DatabaseEngine $db, $login): bool {
$sql = "SELECT COUNT(*) FROM " . TABLE_USERS . " WHERE username LIKE '$login' LOCK IN SHARE MODE;";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return !$row[0];
}
/**
* Gets username from specified e-mail
*
* @param string $mail the mail to search
* @return string|bool the username matching the mail if found ; otherwise, false.
*/
- public static function get_username_from_email ($mail) {
- global $db;
+ public static function get_username_from_email (DatabaseEngine $db, $mail) {
$sql = "SELECT username FROM " . TABLE_USERS . " WHERE user_email LIKE '$mail' LOCK IN SHARE MODE;";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql);
}
if ($row = $db->fetchRow($result)) {
return $row['username'];
}
return false;
}
}
diff --git a/includes/objects/profile.php b/Models/Profile/Profile.php
similarity index 80%
rename from includes/objects/profile.php
rename to Models/Profile/Profile.php
index 3dfd103..cc2c3c4 100644
--- a/includes/objects/profile.php
+++ b/Models/Profile/Profile.php
@@ -1,158 +1,172 @@
<?php
/**
* Profile class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-02 16:49 Autogenerated by Pluton Scaffolding
* Import from Azhàr
* 0.2 2010-07-05 03:56 Tags
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Profile;
+
+use Cache;
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+use Zed\Models\Base\WithDatabase;
+use Zed\Models\Objects\Perso;
+
/**
* Profile class
*
* This class maps the profiles table.
*
* The class also provides methods to handle and cache tags.
*/
-class Profile {
+class Profile extends Entity {
- public $perso_id;
+ use WithDatabase;
+
+ public Perso $perso;
public string $text = "";
public $updated;
public $fixedwidth;
public string $lastError = "";
/**
* Initializes a new instance of the Profile class
- *
- * @param int $perso_id the perso ID
*/
- function __construct ($perso_id) {
- $this->perso_id = $perso_id;
+ function __construct (DatabaseEngine $db, Perso $perso) {
+ $this->setDatabase($db);
+
+ $this->perso = $perso;
$this->load_from_database();
}
/**
* Loads the object Profile (ie fill the properties) from the $_POST array
*/
function load_from_form ($read_boolean = true) {
+ $db = $this->getDatabase();
+
if (array_key_exists('perso_id', $_POST)) {
- $this->perso_id = $_POST['perso_id'];
+ $this->perso = Perso::get($db, $_POST['perso_id']);
}
if (array_key_exists('text', $_POST)) {
$this->text = $_POST['text'];
}
if (array_key_exists('updated', $_POST)) {
$this->updated = $_POST['updated'];
}
if ($read_boolean) {
if (array_key_exists('fixedwidth', $_POST)) {
$this->fixedwidth = $_POST['fixedwidth'];
}
}
}
/**
* Loads the object Profile (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
- $id = $db->escape($this->perso_id);
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
+ $id = $db->escape($this->perso->id);
$sql = "SELECT * FROM " . TABLE_PROFILES . " WHERE perso_id = '$id'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query azhar_profiles", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Profile unknown: " . $this->perso_id;
return false;
}
$this->text = $row['profile_text'];
$this->updated = $row['profile_updated'];
$this->fixedwidth = $row['profile_fixedwidth'];
return true;
}
/**
* Saves the object to the database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
- $perso_id = $db->escape($this->perso_id);
+ $perso_id = $db->escape($this->perso->id);
$text = $db->escape($this->text);
$updated = $db->escape($this->updated);
$fixedwidth = $this->fixedwidth ? 1 : 0;
$sql = "REPLACE INTO " . TABLE_PROFILES . " (`perso_id`, `profile_text`, `profile_updated`, `profile_fixedwidth`) VALUES ('$perso_id', '$text', '$updated', '$fixedwidth')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
}
///
/// Tags
///
/**
* Gets the profile's tags
*
* @return string The profile's tags
*/
function get_tags () {
- global $db;
- $id = $db->escape($this->perso_id);
+ $db = $this->getDatabase();
+
+ $id = $db->escape($this->perso->id);
$sql = "SELECT tag_code, tag_class FROM " . TABLE_PROFILES_TAGS
- . " WHERE perso_id = '$id'";
+ . " WHERE perso_id = '$id'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't get tags", '', __LINE__, __FILE__, $sql);
}
$tags = [];
while ($row = $db->fetchRow($result)) {
$tags[$row['tag_class']][] = $row['tag_code'];
}
return $tags;
}
/**
* Gets the profile's cached tags
*
* @return string The profile's tags
*/
function get_cached_tags () {
require_once('includes/cache/cache.php');
$cache = Cache::load();
- $key = 'zed_profile_tags_' . $this->perso_id;
+ $key = 'zed_profile_tags_' . $this->perso->id;
if (!$tags_html = $cache->get($key)) {
//Regenerates tags cached html snippet
global $smarty;
$tags = $this->get_tags();
if (count($tags)) {
$smarty->assign('tags', $tags);
$tags_html = $smarty->fetch('profile_tags.tpl');
} else {
$tags_html = " ";
}
$cache->set($key, $tags_html);
}
return $tags_html;
}
}
diff --git a/includes/objects/profilecomment.php b/Models/Profile/ProfileComment.php
similarity index 80%
rename from includes/objects/profilecomment.php
rename to Models/Profile/ProfileComment.php
index b752474..40e7804 100644
--- a/includes/objects/profilecomment.php
+++ b/Models/Profile/ProfileComment.php
@@ -1,142 +1,156 @@
<?php
/**
* Profile comments class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-03 01:02 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Profile;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+use Zed\Models\Base\WithDatabase;
+
/**
* Profile comments class
*
* This class maps the profiles_comments table.
*/
-class ProfileComment {
+class ProfileComment extends Entity {
+
+ use WithDatabase;
+
+ ///
+ /// Properties
+ ///
public $id;
public $perso_id;
public $author;
public $authorname; //should be read-only
public $date;
public $text;
public string $lastError = "";
/**
* Initializes a new instance of the ProfileComment class
*
* @param int $id the comment ID
*/
- function __construct ($id = '') {
+ function __construct (DatabaseEngine $db, $id = '') {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
} else {
$this->date = time();
}
}
/**
* Loads the object comment (ie fill the properties) from the $_POST array
*/
function load_from_form () {
- if (array_key_exists('perso_id', $_POST)) {
+ if (array_key_exists('perso_id', $_POST)) {
$this->perso_id = $_POST['perso_id'];
- }
- if (array_key_exists('author', $_POST)) {
+ }
+ if (array_key_exists('author', $_POST)) {
$this->author = $_POST['author'];
- }
- if (array_key_exists('date', $_POST)) {
+ }
+ if (array_key_exists('date', $_POST)) {
$this->date = $_POST['date'];
- }
- if (array_key_exists('text', $_POST)) {
+ }
+ if (array_key_exists('text', $_POST)) {
$this->text = $_POST['text'];
- }
+ }
}
/**
* Loads the object comment (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT c.*, p.perso_name as author FROM " . TABLE_PROFILES_COMMENTS . " c, " . TABLE_PERSOS . " p WHERE c.comment_id = '$id' AND p.perso_id = c.comment_author";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query azhar_profiles_comments", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "comment unknown: " . $this->id;
return false;
}
$this->perso_id = $row['perso_id'];
$this->author = $row['comment_author'];
$this->authorname = $row['author'];
$this->date = $row['comment_date'];
$this->text = $row['comment_text'];
return true;
}
/**
* Saves the object to the database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$perso_id = $db->escape($this->perso_id);
$author = $db->escape($this->author);
$date = $db->escape($this->date);
$text = $db->escape($this->text);
$sql = "REPLACE INTO " . TABLE_PROFILES_COMMENTS . " (`comment_id`, `perso_id`, `comment_author`, `comment_date`, `comment_text`) VALUES ($id, '$perso_id', '$author', '$date', '$text')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
/**
* Publishes the comment
* @todo Add events on publish
*/
function publish () {
$this->save_to_database();
}
/**
* Gets comments
*
* @param int $perso_id The Perso ID
*/
- static function get_comments ($perso_id) {
- global $db;
+ static function get_comments (DatabaseEngine $db, $perso_id) {
$sql = "SELECT comment_id FROM " . TABLE_PROFILES_COMMENTS . " WHERE perso_id = " . $db->escape($perso_id);
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to get comments", '', __LINE__, __FILE__, $sql);
}
$comments = [];
while ($row = $db->fetchRow($result)) {
- $comments[] = new ProfileComment($row[0]);
+ $comments[] = new ProfileComment($db, $row[0]);
}
return $comments;
}
}
diff --git a/includes/objects/profilephoto.php b/Models/Profile/ProfilePhoto.php
similarity index 88%
rename from includes/objects/profilephoto.php
rename to Models/Profile/ProfilePhoto.php
index 4533273..ae25f33 100644
--- a/includes/objects/profilephoto.php
+++ b/Models/Profile/ProfilePhoto.php
@@ -1,219 +1,225 @@
<?php
/**
* Profile photo class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-01-03 21:00 Autogenerated by Pluton Scaffolding
* 0.2 2010-02-02 00:52 Thumbnail ImageMagick generation code
*
* @package Zed
* @subpackage Model
* @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
*/
+namespace Zed\Models\Profile;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+use Zed\Models\Objects\Perso;
+
/**
* Profile photo class
*
* This class maps the profile_photos table.
*
* It also provides helper methods to handle avatars or get all the photos
* from a specified perso.
*/
-class ProfilePhoto {
+class ProfilePhoto extends Entity {
public $id;
public $perso_id;
public $name;
public $description;
public $avatar;
public string $lastError = "";
/**
* Initializes a new instance of the ProfilePhoto class
*/
- function __construct ($id = '') {
+ function __construct (DatabaseEngine $db, $id = '') {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Loads the object photo (ie fill the properties) from the $_POST array
*
* @param bool $readBoolean if false, don't read the bool avatar field to avoid to set by error false if the field weren't in the form.
*/
function load_from_form ($readBoolean = true) {
if (array_key_exists('perso_id', $_POST)) {
$this->perso_id = $_POST['perso_id'];
}
if (array_key_exists('name', $_POST)) {
$this->name = $_POST['name'];
}
if (array_key_exists('description', $_POST)) {
$this->description = $_POST['description'];
}
if ($readBoolean) {
$this->avatar = (bool)$_POST['avatar'];
}
}
/**
* Loads the object photo (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database () : bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM " . TABLE_PROFILES_PHOTOS . " WHERE photo_id = '" . $id . "'";
- if ( !($result = $db->query($sql)) ) {
+ if (!($result = $db->query($sql))) {
message_die(SQL_ERROR, "Unable to query azhar_profiles_photos", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "photo unknown: " . $this->id;
return false;
}
$this->perso_id = $row['perso_id'];
$this->name = $row['photo_name'];
$this->description = $row['photo_description'];
$this->avatar = $row['photo_avatar'];
return true;
}
/**
* Promotes the photo to avatar
*/
function promote_to_avatar () {
- global $db;
+ $db = $this->getDatabase();
//1 - locally
$sql = "UPDATE " . TABLE_PROFILES_PHOTOS . " SET photo_avatar = 0 WHERE perso_id = " . $this->perso_id;
$db->queryScalar($sql);
$this->avatar = true;
//2 - in perso table
- $perso = Perso::get($this->perso_id);
+ $perso = Perso::get($db, $this->perso_id);
$perso->avatar = $this->name;
- $perso->saveToDatabase();
+ $perso->save_to_database();
}
/**
* Saves the object to the database
*/
- function save_to_database () {
- global $db;
+ function save_to_database () : void {
+ $db = $this->getDatabase();
//Escapes fields
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$perso_id = $db->escape($this->perso_id);
$name = $db->escape($this->name);
$description = $db->escape($this->description);
$avatar = $this->avatar ? 1 : 0;
//Saves
$sql = "REPLACE INTO " . TABLE_PROFILES_PHOTOS . " (`photo_id`, `perso_id`, `photo_name`, `photo_description`, `photo_avatar`) VALUES ($id, '$perso_id', '$name', '$description', $avatar)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->nextId();
- }
+ }
}
/**
* Deletes the photo
*/
function delete () {
- global $db;
+ $db = $this->getDatabase();
//Deletes from disk
$pic_tn = PHOTOS_DIR . '/' . $this->name;
$pic_genuine = PHOTOS_DIR . '/tn/' . $this->name;
unlink($pic_tn);
unlink($pic_genuine);
//Deletes from database
$id = $db->escape($this->id);
$sql = "DELETE FROM " . TABLE_PROFILES_PHOTOS . " WHERE photo_id = '$id' LIMIT 1";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't delete photo", '', __LINE__, __FILE__, $sql);
}
}
/**
* Generates a thumbnail using ImageMagick binary
*
* @return boolean true if the thumbnail command returns 0 as program exit code ; otherwise, false
*/
function generate_thumbnail () {
global $Config;
$sourceFile = PHOTOS_DIR . DIRECTORY_SEPARATOR . $this->name;
$thumbnailFile = PHOTOS_DIR . DIRECTORY_SEPARATOR . 'tn' . DIRECTORY_SEPARATOR . $this->name;
$command = $Config['ImageMagick']['convert'] . " $sourceFile -resize 1000x80 $thumbnailFile";
@system($command, $code);
return ($code == 0);
}
/**
* Gets photos from the specified perso
*
* @param int $perso_id the perso ID
* @param bool $allowUnsafe if false, don't include not safe for work photos
* @return ProfilePhoto[]
*/
- static function get_photos ($perso_id, $allowUnsafe = true) : array {
- global $db;
+ static function get_photos (DatabaseEngine $db, $perso_id, $allowUnsafe = true): array {
$sql = "SELECT photo_id FROM " . TABLE_PROFILES_PHOTOS . " WHERE perso_id = " . $db->escape($perso_id);
if (!$allowUnsafe) {
$sql .= " AND photo_safe = 0";
}
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to get photos", '', __LINE__, __FILE__, $sql);
}
$photos = [];
while ($row = $db->fetchRow($result)) {
$photos[] = new ProfilePhoto($row[0]);
}
return $photos;
}
/**
* Gets perso avatar
*
* @param integer $perso_id the perso to get the avatar ID
* @param string $username the username to put in title tag
*/
- static function get_avatar ($perso_id, $username = '') {
- global $db;
-
+ static function get_avatar (DatabaseEngine $db, $perso_id, $username = '') {
$perso_id = $db->escape($perso_id);
$sql = "SELECT photo_description, photo_name FROM " . TABLE_PROFILES_PHOTOS . " WHERE perso_id = '$perso_id' and photo_avatar = 1";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to get avatar", '', __LINE__, __FILE__, $sql);
}
if ($row = $db->fetchRow($result)) {
if (!$username) {
$username = get_name($perso_id);
}
$description = $row['photo_description'] ? "$row[photo_description] ($username's avatar)" : "$username's avatar";
- $url = PHOTOS_URL . '/tn/' . $row['photo_name'];
+ $url = PHOTOS_URL . '/tn/' . $row['photo_name'];
return "<img src=\"$url\" title=\"$username\" alt=\"$description\" />";
} else {
return null;
}
}
}
diff --git a/includes/objects/request.php b/Models/Requests/Request.php
similarity index 90%
rename from includes/objects/request.php
rename to Models/Requests/Request.php
index 98f10a4..4bc9a58 100644
--- a/includes/objects/request.php
+++ b/Models/Requests/Request.php
@@ -1,145 +1,153 @@
<?php
/**
* Request class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2011, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2011-06-13 21:16 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Model
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2011 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
*/
+namespace Zed\Models\Requests;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+
/**
* Request class
*
* This class maps the requests table.
*/
-class Request {
+class Request extends Entity {
public $id;
public $code;
public $title;
public $date;
public $author;
public $to;
public $message;
public $location_global;
public $location_local;
public $status;
public string $lastError = "";
/**
* Initializes a new instance
* @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
} else {
- $this->date = time();
- $this->status = 'NEW';
+ $this->date = time();
+ $this->status = 'NEW';
}
}
/**
* Loads the object Request (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('code', $_POST)) {
$this->code = $_POST['code'];
}
if (array_key_exists('title', $_POST)) {
$this->title = $_POST['title'];
}
if (array_key_exists('date', $_POST)) {
$this->date = $_POST['date'];
}
if (array_key_exists('author', $_POST)) {
$this->author = $_POST['author'];
}
if (array_key_exists('to', $_POST)) {
$this->to = $_POST['to'];
}
if (array_key_exists('message', $_POST)) {
$this->message = $_POST['message'];
}
if (array_key_exists('location_global', $_POST)) {
$this->location_global = $_POST['location_global'];
}
if (array_key_exists('location_local', $_POST)) {
$this->location_local = $_POST['location_local'];
}
if (array_key_exists('status', $_POST)) {
$this->status = $_POST['status'];
}
}
/**
* Loads the object Request (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM " . TABLE_REQUESTS . " WHERE request_id = '" . $id . "'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query requests", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "Request unknown: " . $this->id;
return false;
}
$this->code = $row['request_code'];
$this->title = $row['request_title'];
$this->date = $row['request_date'];
$this->author = $row['request_author'];
$this->message = $row['request_message'];
$this->to = $row['request_to'];
$this->location_global = $row['location_global'];
$this->location_local = $row['location_local'];
$this->status = $row['request_status'];
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$code = $db->escape($this->code);
$title = $db->escape($this->title);
$date = $db->escape($this->date);
$author = $db->escape($this->author);
$message = $db->escape($this->message);
$to = $db->escape($this->to);
$location_global = $db->escape($this->location_global);
$location_local = $db->escape($this->location_local);
$status = $db->escape($this->status);
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_REQUESTS . " (`request_id`, `request_code`, `request_title`, `request_date`, `request_author`, `request_message`, `request_to`, `location_global`, `location_local`, `request_status`) VALUES ($id, '$code', '$title', '$date', '$author', '$message', '$to', '$location_global', '$location_local', '$status')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$this->id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
}
diff --git a/includes/objects/requestreply.php b/Models/Requests/RequestReply.php
similarity index 89%
rename from includes/objects/requestreply.php
rename to Models/Requests/RequestReply.php
index 8dee932..f1d8e2d 100644
--- a/includes/objects/requestreply.php
+++ b/Models/Requests/RequestReply.php
@@ -1,112 +1,119 @@
<?php
/**
* RequestReply class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2011, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2011-06-13 21:20 Autogenerated by Pluton Scaffolding
*
* @package Zed
* @subpackage Model
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2011 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
*/
+namespace Zed\Models\Requests;
+
+use Keruald\Database\DatabaseEngine;
+use Zed\Models\Base\Entity;
+
/**
* RequestReply class
*
* This class maps the requests_replies table.
*/
-class RequestReply {
+class RequestReply extends Entity {
public $id;
public $request_id;
public $author;
public $date;
public $text;
public string $lastError = "";
/**
* Initializes a new instance
- * @param int $id the primary key
*/
- function __construct ($id = null) {
+ function __construct (DatabaseEngine $db, $id = null) {
+ $this->setDatabase($db);
+
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* Loads the object RequestReply (ie fill the properties) from the $_POST array
*/
function load_from_form () {
if (array_key_exists('request_id', $_POST)) {
$this->request_id = $_POST['request_id'];
}
if (array_key_exists('author', $_POST)) {
$this->author = $_POST['author'];
}
if (array_key_exists('date', $_POST)) {
$this->date = $_POST['date'];
}
if (array_key_exists('text', $_POST)) {
$this->text = $_POST['text'];
}
}
/**
* Loads the object RequestReply (ie fill the properties) from the database
*/
- function load_from_database () {
- global $db;
+ function load_from_database (): bool {
+ $db = $this->getDatabase();
+
$id = $db->escape($this->id);
$sql = "SELECT * FROM " . TABLE_REQUESTS_REPLIES . " WHERE request_reply_id = '" . $id . "'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query requests_replies", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
$this->lastError = "RequestReply unknown: " . $this->id;
return false;
}
$this->request_id = $row['request_id'];
$this->author = $row['request_reply_author'];
$this->date = $row['request_reply_date'];
$this->text = $row['request_reply_text'];
return true;
}
/**
* Saves to database
*/
- function save_to_database () {
- global $db;
+ function save_to_database (): void {
+ $db = $this->getDatabase();
$id = $this->id ? "'" . $db->escape($this->id) . "'" : 'NULL';
$request_id = $db->escape($this->request_id);
$author = $db->escape($this->author);
$date = $db->escape($this->date);
$text = $db->escape($this->text);
//Updates or inserts
$sql = "REPLACE INTO " . TABLE_REQUESTS_REPLIES . "(`request_reply_id`, `request_id`, `request_reply_author`, `request_reply_date`, `request_reply_text`) VALUES ('$id', '$request_id', '$author', '$date', '$text')";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$this->id) {
//Gets new record id value
$this->id = $db->nextId();
}
}
}
diff --git a/api.php b/api.php
index d9f2777..5d099ab 100644
--- a/api.php
+++ b/api.php
@@ -1,336 +1,338 @@
<?php
/**
* API entry point
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @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
* @todo Consider to output documentation on / and /ship queries
* @todo /app/getdata
*/
use Zed\Engines\Database\Database;
+use Zed\Models\Geo\Galaxy;
+use Zed\Models\Geo\Location;
+use Zed\Models\Objects\Application;
+use Zed\Models\Objects\Perso;
+use Zed\Models\Objects\Ship;
//API preferences
define('URL', 'http://' . $_SERVER['HTTP_HOST'] . '/index.php');
//Pluton library
require_once('includes/core.php');
require_once('includes/config.php');
//API libs
require_once('includes/api/api_helpers.php');
require_once('includes/api/cerbere.php');
//Use our URL controller method if you want to mod_rewrite the API
$Config['SiteURL'] = get_server_url() . $_SERVER["PHP_SELF"];
$url = get_current_url_fragments();
//Database
$db = Database::load($Config['database']);
switch ($module = $url[0]) {
/* -------------------------------------------------------------
Site API
/time
/location
/coordinates
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case '':
//Nothing to do
//TODO: offer documentation instead
die();
case 'time':
//Hypership time
api_output(get_hypership_time(), "time");
break;
case 'location':
//Checks credentials
cerbere();
//Gets location info
- require_once("includes/geo/location.php");
- $location = new GeoLocation($url[1], $url[2]);
+ $location = new Location($db, $url[1], $url[2]);
api_output($location, "location");
break;
case 'coordinates':
//Checks credentials
cerbere();
//Get coordinates
- api_output(GeoGalaxy::getCoordinates(), 'galaxy', 'object');
+ api_output(Galaxy::getCoordinates($db), 'galaxy', 'object');
break;
/* -------------------------------------------------------------
Ship API
/authenticate
/appauthenticate
/appauthenticated
/move
/land
/flyout
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case 'ship':
//Ship API
//Gets ship from Ship API key (distinct of regular API keys)
- require_once('includes/objects/ship.php');
$ship = Ship::from_api_key($_REQUEST['key']) or cerbere_die("Invalid ship API key");
switch ($command = $url[1]) {
case '':
//Nothing to do
//TODO: offer documentation instead
die();
case 'authenticate':
//TODO: web authenticate
break;
case 'appauthenticate':
//Allows desktop application to authenticate an user
$tmp_session_id = $url[2] or cerbere_die("/appauthenticate/ must be followed by any session identifier");
if ($_REQUEST['name']) {
//Perso will be offered auth invite at next login.
//Handy for devices like PDA, where it's not easy to auth.
- $perso = new Perso($_REQUEST['name']);
+ $perso = new Perso($db, $_REQUEST['name']);
if ($perso->lastError) {
cerbere_die($perso->lastError);
}
if (!$ship->is_perso_authenticated($perso->id)) {
$ship->request_perso_authenticate($perso->id);
}
$ship->request_perso_confirm_session($tmp_session_id, $perso->id);
} else {
//Delivers an URL. App have to redirects user to this URL
//launching a browser or printing the link.
$ship_code = $ship->get_code();
- registry_set("api.ship.session.$ship_code.$tmp_session_id", -1);
+ registry_set($db, "api.ship.session.$ship_code.$tmp_session_id", -1);
$url = get_server_url() . get_url() . "?action=api.ship.appauthenticate&session_id=" . $tmp_session_id;
api_output($url, "URL");
}
break;
case 'appauthenticated':
//Checks the user authentication
$tmp_session_id = $url[2] or cerbere_die("/appauthenticated/ must be followed by any session identifier you used in /appauthenticate");
$perso_id = $ship->get_perso_from_session($tmp_session_id);
if (!$isPersoAuth = $ship->is_perso_authenticated($perso_id)) {
//Global auth not ok/revoked.
$auth->status = -1;
} else {
- $perso = Perso::get($perso_id);
+ $perso = Perso::get($db, $perso_id);
$auth->status = 1;
$auth->perso->id = $perso->id;
$auth->perso->nickname = $perso->nickname;
$auth->perso->name = $perso->name;
//$auth->perso->location = $perso->location;
//Is the perso on board? Yes if its global location is S...
$auth->perso->onBoard = (
$perso->location_global[0] == 'S' &&
substr($perso->location_global, 1, 5) == $ship->id
);
if ($auth->perso->onBoard) {
//If so, give local location
$auth->perso->location_local = $perso->location_local;
}
}
api_output($auth, "auth");
break;
case 'move':
//Moves the ship to a new location, given absolute coordinates
//TODO: handle relative moves
if (count($url) < 2) {
cerbere_die("/move/ must be followed by a location expression");
}
//Gets location class
//It's allow: (1) to normalize locations between formats
// (2) to ensure the syntax
- //==> if the ship want to communicate free forms coordinates, must be added on GeoLocation a free format
+ //==> if the ship want to communicate free forms coordinates, must be added on Location a free format
try {
- $location = new GeoLocation($url[2]);
+ $location = new Location($db, $url[2]);
} catch (Exception $ex) {
$reply->success = 0;
$reply->error = $ex->getMessage();
api_output($reply, "move");
break;
}
$ship->location_global = $location->global;
$ship->save_to_database();
$reply->success = 1;
$reply->location = $ship->location;
api_output($reply, "move");
break;
case 'land':
case 'flyin':
//Flies in
try {
- $location = new GeoLocation($location);
+ $location = new Location($db, $location);
} catch (Exception $ex) {
$reply->success = 0;
$reply->error = $ex->getMessage();
api_output($reply, "land");
break;
}
break;
case 'flyout':
//Flies out
break;
}
break;
/* -------------------------------------------------------------
Application API
/checkuserkey
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case 'app':
//Application API
- require_once("includes/objects/application.php");
- $app = Application::from_api_key($_REQUEST['key']) or cerbere_die("Invalid application API key");
+ $app = Application::from_api_key($db, $_REQUEST['key']) or cerbere_die("Invalid application API key");
switch ($command = $url[1]) {
case '':
//Nothing to do
//TODO: offer documentation instead
die();
case 'checkuserkey':
if (count($url) < 2) {
cerbere_die("/checkuserkey/ must be followed by an user key");
}
$reply = (boolean)$app->get_perso_id($url[2]);
api_output($reply, "check");
break;
case 'pushuserdata':
if (count($url) < 3) {
cerbere_die("/pushuserdata/ must be followed by an user key");
}
$perso_id = $app->get_perso_id($url[2]) or cerbere_die("Invalid application user key");
//then, falls to 'pushdata'
case 'pushdata':
$data_id = $_REQUEST['data'] ?: new_guid();
//Gets data
switch ($mode = $_REQUEST['mode']) {
case '':
cerbere_die("Add in your data posted or in the URL mode=file to read data from the file posted (one file per api call) or mode=request to read data from \$_REQUEST['data'].");
case 'request':
$data = $_REQUEST['data'];
$format = "raw";
break;
case 'file':
$file = $_FILES['datafile']['tmp_name'] or cerbere_die("File is missing");
if (!is_uploaded_file($file)) {
cerbere_die("Invalid form request");
}
$data = "";
if (preg_match('/\.tar$/', $file)) {
$format = "tar";
$data = file_get_contents($file);
} elseif (preg_match('/\.tar\.bz2$/', $file)) {
$format = "tar";
} elseif (preg_match('/\.bz2$/', $file)) {
$format = "raw";
} else {
$format = "raw";
$data = file_get_contents($file);
}
if ($data === "") {
//.bz2
$bz = bzopen($file, "r") or cerbere_die("Couldn't open $file");
while (!feof($bz)) {
$data .= bzread($bz, BUFFER_SIZE);
}
bzclose($bz);
}
unlink($file);
break;
default:
cerbere_die("Invalid mode. Expected: file, request");
}
//Saves data
global $db;
$data_id = $db->escape($data_id);
$data = $db->escape($data);
$perso_id = $perso_id ?: 'NULL';
$sql = "REPLACE INTO applications_data (application_id, data_id, data_content, data_format, perso_id) VALUES ('$app->id', '$data_id', '$data', '$format', $perso_id)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Can't save data", '', __LINE__, __FILE__, $sql);
}
//cerbere_die("Can't save data");
//Returns
api_output($data_id);
break;
case 'getuserdata':
// /api.php/getuserdata/data_id/perso_key
// /api.php/getdata/data_id
if (count($url) < 3) {
cerbere_die("/getuserdata/ must be followed by an user key");
}
$perso_id = $app->get_perso_id($url[2]) or cerbere_die("Invalid application user key");
//then, falls to 'getdata'
case 'getdata':
if (count($url) < 2) {
cerbere_die('/' . $url[0] . '/ must be followed by the data ID');
}
if (!$perso_id) {
$perso_id = 'NULL';
}
$data_id = $db->escape($url[1]);
$sql = "SELECT data_content FROM applications_data WHERE application_id = '$app->id' AND data_id = '$data_id' AND perso_id = $perso_id";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
}
while ($row = $db->fetchRow($result)) {
}
break;
default:
echo "Unknown module:";
dprint_r($url);
break;
}
break;
default:
echo "Unknown module:";
dprint_r($url);
break;
}
diff --git a/composer.json b/composer.json
index 2c3e67d..d9019f0 100644
--- a/composer.json
+++ b/composer.json
@@ -1,32 +1,33 @@
{
"name": "zed/zed",
"description": "Hypership and galaxy",
"type": "project",
"require": {
"smarty/smarty": "dev-master",
"vlucas/phpdotenv": "^5.5",
"keruald/database": "^0.1.0",
"keruald/globalfunctions": "^0.5.1",
"keruald/omnitools": "^0.6.0",
"hypership/geo": "^0.1.0"
},
"require-dev": {
"nasqueron/codestyle": "^0.0.1",
"phan/phan": "^5.3.1",
"squizlabs/php_codesniffer": "^3.6",
"phpunit/phpunit": "^10.2.2"
},
"autoload": {
"psr-4": {
"Zed\\Engines\\": "Engines",
+ "Zed\\Models\\": "Models",
"Zed\\Tests\\": "dev/tests"
}
},
"license": "BSD-2-Clause",
"authors": [
{
"name": "Sébastien Santoro",
"email": "dereckson@espace-win.org"
}
]
}
diff --git a/controllers/anonymous.php b/controllers/anonymous.php
index d9f690d..a18e1a2 100644
--- a/controllers/anonymous.php
+++ b/controllers/anonymous.php
@@ -1,164 +1,164 @@
<?php
/**
* Content for anonymous users
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller handles the pages for not logged in users.
*
* It recognizes the following URLs:
* /tour a redirect to tour.html file, a visite guidée from Zed ;
* /invite the page to claim the invites.
*
* In all other cases, it prints the login form.
*
* @package Zed
* @subpackage Controllers
* @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\OmniTools\Identifiers\Random;
+use Zed\Models\Messages\Message;
+use Zed\Models\Objects\Invite;
+use Zed\Models\Objects\User;
//
// Prepares the page
//
switch ($url[0]) {
case 'tour':
//The user have forgotten .html, let's redirect him
header('Location: ' . $Config['StaticContentURL'] . '/tour.html');
exit;
case 'invite':
//Invite form
if ($_POST['form'] == 'account.create') {
//User tries to claim its invite to create an account
- require_once('includes/objects/invite.php');
- require_once('includes/objects/user.php');
//Gets invite
$invite = new Invite($_POST['invite_code']);
if ($invite->lastError != '') {
//Not existing invite.
$smarty->assign('NOTIFY', lang_get("IncorrectInviteCode"));
} elseif ($invite->is_claimed()) {
//The invitation have already claimed by someone else.
$smarty->assign('NOTIFY', lang_get("InviteCodeAlreadyClaimed"));
} else {
//Checks if the given information is correct
//We ignore bad mails. All we really need is a login and a pass.
//We fill our array $errors with all the errors
$errors = [];
if (!$_POST['username']) {
$errors[] = lang_get('MissingUsername');
- } elseif (!User::is_available_login($_POST['username'])) {
+ } elseif (!User::is_available_login($db, $_POST['username'])) {
$errors[] = lang_get('LoginUnavailable');
}
- if (User::get_username_from_email($_POST['email']) !== false) {
+ if (User::get_username_from_email($db, $_POST['email']) !== false) {
$errors[] = "There is already an account with this e-mail.";
}
if (!$_POST['passwd']) {
$errors[] = lang_get('MissingPassword');
}
if (count($errors)) {
$smarty->assign('WAP', join('<br />', $errors));
} else {
//Creates account
- $user = new User();
+ $user = new User($db);
$user->regdate = time();
$user->generate_id();
$user->name = $_POST['username'];
$user->active = 1;
$user->email = $_POST['email'];
$user->set_password($_POST['passwd']);
$user->save_to_database();
//Updates invite
$invite->to_user_id = $user->id;
$invite->save_to_database();
//Notifies host
- require_once('includes/objects/message.php');
- $message = new Message();
- $message->from = 0;
+ $message = new Message($db);
+ $message->setAsSystem();
$message->to = $invite->from_perso_id;
$message->text = sprintf(lang_get('InviteHaveBeenClaimed'), $invite->code);
$message->send();
//Logs in user
login($user->id, $user->name);
//Prints confirm message
$smarty->assign('WAP', lang_get("AccountCreated"));
//Redirects users to homepage
header('refresh: 5; url=' . get_url());
//Calls void controller
$smarty->assign('screen', 'user.create');
define('NO_FOOTER_EXTRA', true);
include("void.php");
exit;
}
}
//Keeps username, email, invite code printed on account create form
$smarty->assign('username', $_POST['username']);
$smarty->assign('invite_code', $_POST['invite_code']);
$smarty->assign('email', $_POST['email']);
}
//If the invite code is specified, checks format
//If the form has already been posted with an invite code, it doesn't matter anymore.
if ($url[1] && !isset($_POST['invite_code'])) {
if (preg_match("/^([A-Z]){3}([0-9]){3}$/i", $url[1])) {
$invite = new Invite($url[1]);
if ($invite->lastError != '') {
//Not existing invite.
$smarty->assign('NOTIFY', lang_get("IncorrectInviteCode"));
} else {
$smarty->assign('invite_code', strtoupper($url[1]));
}
} else {
$smarty->assign('NOTIFY', lang_get("IncorrectInviteCodeFormat"));
}
}
$template = 'account_create.tpl';
break;
default:
//Invite code for code comment
$code = Random::generateString("AAA111");
$smarty->assign('code', $code);
//Login form
$smarty->assign('username', $_COOKIE['LastUsername'] ?? "");
$smarty->assign('OpenID', $_COOKIE['LastOpenID'] ?? "");
$smarty->assign('LoginError', $loginError ?? "");
$template = 'login.tpl';
break;
}
//
// HTML output
//
if ($template) {
$smarty->display($template);
}
diff --git a/controllers/builder.php b/controllers/builder.php
index edc6b1c..30cfd0f 100644
--- a/controllers/builder.php
+++ b/controllers/builder.php
@@ -1,137 +1,138 @@
<?php
/**
* Builder
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Controllers
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/gpl-2.0.php GPLv2
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*/
use Zed\Engines\Builder\Map\OctocubeBuilder;
-
-require_once('includes/content/zone.php');
+use Zed\Models\Content\Zone;
+use Zed\Models\Geo\Location;
//
// Helper methods
//
/**
* Determines if a specified location is buildable
*
- * @param GeoLocation $location the location to check
+ * @param Location $location the location to check
* @param string if the location isn't buildable, a textual description of why.
* @return bool true if the location is buildable ; otherwise, false
*
* @todo create a build.xml document to set what's buildable, and by who
*/
-function is_buildable ($location, &$error = '') {
+function is_buildable (Location $location, &$error = '') {
//We currently allow build only in the hypership tower and core.
if (!$location->body->hypership) {
$error = "You can only invoke the HyperShip builder facilities inside the HyperShip.";
return false;
}
//if ($build_location->place->code == "001") {
// //Don't allow custom builds in the corridor (T?C?)
//}
if ($build_location->place->code == "003") {
message_die("Bays aren't buildable.");
return false;
}
return true;
}
//
// Determines mode and initializes resources
//
switch ($build_mode = $url[1]) {
case 'map':
$build_mode = 'map';
//Get zones at this floor
if (!OctocubeBuilder::canBuildAt($CurrentPerso->location)) {
message_die(GENERAL_ERROR, "Can't map this area.", "Builder :: Map");
}
$builder = new OctocubeBuilder($CurrentPerso->location);
- $zones = ContentZone::search(
+ $zones = Zone::search(
+ $db,
$CurrentPerso->location->global,
$builder->getRlikePattern(),
true
);
//Template
define('DOJO', true);
$smarty->assign('zones', $zones);
$template = "builder_map.tpl";
break;
case '':
case 'hotglue':
//Temporary initialization code, to allow some build during the Zed alphatest
$build_location = $CurrentPerso->location;
$build_mode = 'hotglue';
$error = '';
if (!is_buildable($build_location, $error)) {
message_die(GENERAL_ERROR, $error, "Can't build");
}
//Gets or creates a new zone at build location
- $zone = ContentZone::at($build_location->global, $build_location->local, true);
+ $zone = Zone::at($db, $build_location->global, $build_location->local, true);
switch ($zone->type) {
case 'hotglue':
//All rulez
break;
case '':
//New zone
$zone->title = "Sandbox hotglue zone for $build_location->global $build_location->local";
$zone->type = 'hotglue';
$zone->save_to_database();
break;
default:
message_die("This isn't a zone managed by hotglue.");
}
unset($error);
//Template
$smarty->assign('location', $build_location);
$smarty->assign('zone', $zone);
$smarty->assign('IFRAME_SRC', '/apps/hotglue/index.php?zone_' . $zone->id . '/edit');
$template = 'builder_hotglue.tpl';
break;
default:
message_die(GENERAL_ERROR, "Unknown build mode: $build_mode");
}
//
// HTML output
//
//Serves header
$smarty->assign('PAGE_TITLE', 'Builder');
include('header.php');
//Serves content
$smarty->display($template);
//Serves footer
include('footer.php');
diff --git a/controllers/home.php b/controllers/home.php
index f5debad..8d537b6 100644
--- a/controllers/home.php
+++ b/controllers/home.php
@@ -1,104 +1,104 @@
<?php
/**
* Homepage
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller handle the / URL.
*
* It prints:
* a scene rendering from where the perso is ;
* the home.tpl view ;
* the messages, using the messages.tpl view.
*
* The controller also handle messages, marking them red and allowing their
* suppression: /?action=msg_delete&id=8 to delete the message #8.
*
* @package Zed
* @subpackage Controllers
* @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 The homepage requires Dojo but Dojo loading here is currently a kludge, as dojo is required by hypership .tpl scene. We should create an optional .meta xml file format to set this kind of options.
*/
+use Zed\Models\Geo\Scene;
+use Zed\Models\Messages\Message;
+
//
// Gets and manage messages
//
-require_once('includes/objects/message.php');
-
//Deletes a message if user have clicked the X
$action = $_GET['action'] ?? "";
if ($action === 'msg_delete') {
//Deletes message $_GET['id']
$id = $_GET['id'];
- $messageToDelete = new Message($id);
+ $messageToDelete = new Message($db, $id);
if ($messageToDelete->to != $CurrentPerso->id) {
//Not one of user message
$smarty->assign('WAP', lang_get('NotYourMessage'));
} elseif ($messageToDelete->flag == 2) {
//Already deleted
$smarty->assign('WAP', lang_get('MessageAlreadyDeleted'));
} else {
$messageToDelete->delete();
$smarty->assign('NOTIFY', lang_get('MessageDeleted'));
}
}
//Gets messages
$newMessagesCount = 0;
-$messages = Message::get_messages($CurrentPerso->id, true, $newMessagesCount);
+$messages = Message::get_messages($db, $CurrentPerso, true, $newMessagesCount);
if ($newMessagesCount > 0) {
$smarty->assign('NOTIFY', sprintf(lang_get("NewMessages"), $newMessagesCount, s($newMessagesCount)));
}
//Gets scene
-require_once("includes/geo/scene.php");
-$scene = new GeoScene($CurrentPerso->location);
+$scene = new Scene($CurrentPerso->location);
$smarty->assign('SCENE', $scene);
//
// HTML output
//
//Serves header
//TODO: Dojo loading here is currently a kludge, as dojo is required by
//hypership .tpl scene. We should create an optional .meta xml file format
//to set this kind of options
if (!defined('DIJIT')) {
/**
* This constant indicates we need to load the Dijit (and so Dojo) library.
*/
define('DIJIT', true);
}
$smarty->assign('PAGE_TITLE', lang_get('Welcome'));
include('header.php');
//Serves content
if (!$scene->lastError) {
$scene->render();
}
$smarty->display('home.tpl');
if ($messages) {
$smarty->assign('MESSAGES', $messages);
$smarty->display('messages.tpl');
}
//Serves footer
$smarty->assign("screen", "Home console");
include('footer.php');
diff --git a/controllers/motd.php b/controllers/motd.php
index 8499b13..6c1a67d 100644
--- a/controllers/motd.php
+++ b/controllers/motd.php
@@ -1,54 +1,55 @@
<?php
/**
* MOTD
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller handle the /push "secret" URL.
*
* It allows to add a message in the MOTD (messages printed in the header on
* the top of each page).
*
* It uses the motd_add.tpl view and the MOTD class.
*
* @package Zed
* @subpackage Controllers
* @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 Zed\Models\Objects\MOTD;
+
//
// Handles form
//
if (isset($_REQUEST['text'])) {
- require_once('includes/objects/motd.php');
- $motd = new MOTD();
+ $motd = new MOTD($db);
$motd->text = $_REQUEST['text'];
$motd->perso_id = $CurrentPerso->id;
$motd->save_to_database();
$smarty->assign('WAP', lang_get('Published'));
}
//
// HTML output
//
//Serves header
$smarty->assign('PAGE_TITLE', lang_get('PushMessage'));
include('header.php');
//Serves content
$smarty->display('motd_add.tpl');
//Servers footer
include('footer.php');
diff --git a/controllers/persorequest.php b/controllers/persorequest.php
index 26e3ae1..b27edcf 100644
--- a/controllers/persorequest.php
+++ b/controllers/persorequest.php
@@ -1,166 +1,165 @@
<?php
/**
* Persos' requests
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller handle the /requests URL.
*
* It can also be called with the requests SmartLine command.
*
* It allows to prints a content page.
*
* This controllers uses the persorequests.tpl view.
*
* This controller offer AJAX capabilities but degrades gracefully in JS.
*
* @package Zed
* @subpackage Controllers
* @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 Document the request system in the API documentation
*/
///
/// Helper class and method
///
+use Zed\Models\Objects\Ship;
/**
* A perso request
*/
class PersoRequest {
public $message;
public $requestFlag;
public $flag;
public $store = 'perso';
public $value_allow = 1;
public $value_deny = 0;
/**
* Initialises a perso request
*
* @param string $requestFlag the request's flag
* @param string $message the message to print
* @param string $message the flag to set, according the request approve/denial
*/
function __construct ($requestFlag, $message, $flag) {
$this->requestFlag = $requestFlag;
$this->message = $message;
$this->flag = $flag;
}
}
/**
* Gets request allow URL
*
* @param PersoRequest $request the perso request to confirm
* @return string the URL to allow the request
*/
function get_request_allow_url ($request) {
return get_request_url($request->requestFlag, $request->store, $request->flag, $request->value_allow);
}
/**
* Gets request deny URL
*
* @param PersoRequest $request the perso request to confirm
* @return string the URL to deny the request
*/
function get_request_deny_url ($request) {
return get_request_url($request->requestFlag, $request->store, $request->flag, $request->value_deny);
}
/**
* Gets request URL
*
* @param string $store 'perso' or 'registry'
* @param string $key the perso flag or registry key
* @param string $value the value to store
* @return the request URL
*/
function get_request_url ($requestFlag, $store, $key, $value) {
global $Config;
$hash = md5($_SESSION['ID'] . $Config['SecretKey'] . $requestFlag . $store . $key . $value);
return "$Config[DoURL]/perso_request/$requestFlag/$store/$key/$value/$hash";
}
///
/// Get requests
///
//Loads perso request language file
lang_load('persorequest.conf');
//The array request will be passed to Smarty and will contain PersoRequest items.
$requests = [];
foreach ($CurrentPerso->flags as $flag => $value) {
if ($value && str_starts_with($flag, "request.")) {
if (str_starts_with($flag, 'request.api.ship.auth.')) {
//Gets ship
- require_once('include/objects/ship.php');
$ship_code = substr($flag, 22);
- $ship = Ship::get($ship_code);
+ $ship = Ship::get($db, $ship_code);
//Adds request
$message = sprintf(lang_get('RequestShipAPIAuthenticate'), $ship->name);
$requests[] = new PersoRequest($flag, $message, substr($flag, 8));
} elseif (str_starts_with($flag, 'request.api.ship.session.')) {
//Gets ship
- require_once('include/objects/ship.php');
$ship_code = substr($flag, 25, 6);
- $ship = Ship::get($ship_code);
+ $ship = Ship::get($$db, ship_code);
//Adds request
$message = sprintf(lang_get('RequestShipAPISessionConfirm'), $ship->name);
$request = new PersoRequest($flag, $message, substr($flag, 8));
$request->value_allow = $CurrentPerso->id;
$request->value_deny = -1;
$request->store = 'registry';
$requests[] = $request;
} else {
message_die(GENERAL_ERROR, "Unknown request flag: $flag. Please report this bug.");
}
}
}
///
/// Requests handling
///
if (count($requests) == 0) {
//If site.requests flag is at 1 but we don't have request, ignore processing
$CurrentPerso->set_flag('site.requests', 0);
//We don't die, so next controller takes relay
} else {
///
/// HTML output
///
//Serves header
define('DOJO', true);
$smarty->assign('PAGE_TITLE', lang_get('PersoRequests'));
include('header.php');
//Serves content
$smarty->assign('requests', $requests);
$smarty->display('persorequests.tpl');
//Serves footer
$smarty->assign("screen", "Perso requests");
include('footer.php');
//Dies
exit;
}
diff --git a/controllers/profile.php b/controllers/profile.php
index 7ccb1f6..dc3416d 100644
--- a/controllers/profile.php
+++ b/controllers/profile.php
@@ -1,361 +1,358 @@
<?php
/**
* User profile
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This is a controller allowing user profile view and edit.
*
* It handles the following URLs:
* /who/<perso nickname> views the nickname's profile,
* /who/random views a random profile,
* /who/edit/profile edits its profile
* /who/edit/account edits its account (disabled on Zed, cf. settings),
* /who/edit/photo(s) manages its profile's photos,
* /who/edit/photo/edit/<photo id> edits a photo properties,
* /who/edit/photo/delete/<photo id> deletes a photo,
* /who/edit/photo/avatar/<photo id> promotes a photo to avatar.
*
* The following views are used:
* profile.tpl,
* profile_edit.tpl,
* user_account.tpl,
* profile_photo.tpl,
* profile_photo_edit.tpl.
*
* The following models are used:
* Profile,
* ProfilePhoto,
* ProfileComment.
*
* The view profile_tags.tpl is indirectly used by the Profile model.
*
* This code is maintained in // with Azhàr.
*
* @package Zed
* @subpackage Controllers
* @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 Zed\Models\Messages\Message;
+use Zed\Models\Objects\Perso;
+use Zed\Models\Profile\Profile;
+use Zed\Models\Profile\ProfileComment;
+use Zed\Models\Profile\ProfilePhoto;
+
//Loads language file
lang_load('profile.conf');
//Gets perso nickname from URL
$who = $url[1];
switch ($who) {
case 'edit':
$mode = 'edit';
$who = $CurrentPerso->nickname;
break;
case 'random':
$mode = 'view';
$who = $db->queryScalar("SELECT perso_id FROM " . TABLE_PROFILES . " ORDER BY rand() LIMIT 1");
break;
default:
$mode = 'view';
}
if (!$who) {
message_die(GENERAL_ERROR, "Who?", "URL error");
}
-//Libs
-require_once('includes/objects/profile.php');
-require_once('includes/objects/profilecomment.php');
-require_once('includes/objects/profilephoto.php');
-
//Gets perso information
-require_once('includes/objects/perso.php');
-$perso = Perso::get($who);
+$perso = Perso::get($db, $who);
if ($perso->lastError) {
message_die(GENERAL_ERROR, $perso->lastError, "Error");
}
$smarty->assign('perso', $perso);
//Gets profile
-$profile = new Profile($perso->id);
+$profile = new Profile($db, $perso);
//Handles form
$message_type = $_POST['message_type'] ?? "";
if (isset($_POST['EditProfile'])) {
$profile->load_from_form();
$profile->updated = time();
$profile->save_to_database();
$mode = 'view';
} elseif (isset($_POST['UserAccount'])) {
$smarty->assign('WAP', "This form have been deprecated. You can write instead settings in the SmartLine");
} elseif ($message_type === 'private_message') {
//Sends a message
- require_once('includes/objects/message.php');
- $msg = new Message();
- $msg->from = $CurrentPerso->id;
+ $msg = new Message($db);
+ $msg->setFrom($CurrentPerso);
$msg->to = $perso->id;
$msg->text = $_POST['message'];
$msg->send();
- if ($msg->from == $msg->to) {
+ if ($msg->isSelf()) {
$smarty->assign('NOTIFY', lang_get('MessageSentSelf'));
} else {
$smarty->assign('NOTIFY', lang_get('MessageSent'));
}
} elseif ($message_type== 'profile_comment') {
//New profile comment
- $comment = new ProfileComment();
+ $comment = new ProfileComment($db);
$comment->author = $CurrentPerso->id;
$comment->perso_id = $perso->id;
$comment->text = $_POST['message'];
$comment->publish();
$smarty->assign('NOTIFY', lang_get('CommentPublished'));
} elseif (isset($_FILES['photo'])) {
#We've a file !
$errors = [];
$hash = md5(microtime() . serialize($_FILES));
$extension = get_extension($_FILES['photo']['name']);
$filename = $CurrentPerso->id . '_' . $hash . '.' . $extension;
#We ignore $_FILES[photo][error] 4, this means no file has been uploaded
#(so user doesn't want upload a new file)
#See http:/www.php.net/features.file-upload and http://www.php.net/manual/en/features.file-upload.errors.php about common errors
#Not valid before PHP 4.2.0
switch ($_FILES['photo']['error']) {
case 0:
#There is no error, the file uploaded with success.
if (!move_uploaded_file($_FILES['photo']['tmp_name'], PHOTOS_DIR . '/' . $filename)) {
$errors[] = "Upload successful, but error saving it.";
} else {
//Attaches the picture to the profile
- $photo = new ProfilePhoto();
+ $photo = new ProfilePhoto($db);
$photo->name = $filename;
$photo->perso_id = $CurrentPerso->id;
$photo->description = $_POST['description'];
if ($photo->avatar) {
$photo->promote_to_avatar();
}
$photo->save_to_database();
//Generates thumbnail
if (!$photo->generate_thumbnail()) {
$smarty->assign('WAP', "Error generating thumbnail.");
}
$smarty->assign('NOTIFY', lang_get('PhotoUploaded'));
$mode = 'view';
}
break;
case 1:
$errors[] = "The file is too large.";
break;
#TODO : more explicit error messages
default:
$errors[] = "Unknown error (#" . $_FILES['photo']['error'] . ")";
break;
}
if (count($errors)) {
$smarty->assign('WAP', join('<br />', $errors));
}
} elseif (isset($_POST['id'])) {
//Edits photo properties
$photo = new ProfilePhoto($_POST['id']);
if ($photo->lastError) {
$smarty->assign('WAP', $photo->lastError);
$mode = 'view';
} elseif ($photo->perso_id != $CurrentPerso->id) {
$smarty->assign('WAP', lang_get('NotYourPic'));
$mode = 'view';
} else {
//OK
$wereAvatar = $photo->avatar;
$photo->load_from_form();
if (!$wereAvatar && $photo->avatar) {
//Promote to avatar
$photo->promote_to_avatar();
}
$photo->save_to_database();
}
}
//Prepares output
$smarty->assign('PROFILE_TEXT', $profile->text);
$smarty->assign('PROFILE_FIXEDWIDTH', $profile->fixedwidth);
if ($mode == 'view') {
- require_once('includes/objects/profilephoto.php');
-
//Self profile?
- $self = $CurrentPerso->id == $profile->perso_id;
+ $self = $CurrentPerso->id == $profile->perso->id;
//Gets profiles comments, photos, tags
- $comments = ProfileComment::get_comments($profile->perso_id);
- $photos = ProfilePhoto::get_photos($profile->perso_id);
+ $comments = ProfileComment::get_comments($db, $profile->perso->id);
+ $photos = ProfilePhoto::get_photos($db, $profile->perso->id);
$tags = $profile->get_cached_tags();
//Records timestamp, to be able to track new comments
if ($self) {
$CurrentPerso->set_flag('profile.lastvisit', time());
}
//Template
$smarty->assign('PROFILE_COMMENTS', $comments);
$smarty->assign('PROFILE_SELF', $self);
if ($tags) {
$smarty->assign('PROFILE_TAGS', $tags);
}
$smarty->assign('USERNAME', $perso->nickname);
$smarty->assign('NAME', $perso->name ?: $perso->nickname);
$template = 'profile.tpl';
} elseif ($mode == 'edit') {
switch ($url[2]) {
case 'profile':
$smarty->assign('USERNAME', $perso->name);
$smarty->assign('DIJIT', true);
$smarty->append('PAGE_CSS', THEME . '/forms.css');
$template = 'profile_edit.tpl';
break;
case 'account':
$smarty->assign('user', $CurrentUser);
$smarty->assign('DIJIT', true);
$smarty->append('PAGE_CSS', THEME . '/forms.css');
$template = 'user_account.tpl';
break;
case '':
$smarty->assign('NOTIFY', "What do you want to edit ? Append /profile, /account or /photos to the URL");
break;
case 'photo':
case 'photos':
$smarty->assign('USERNAME', $perso->name);
$action = $url[3] ?? "";
$id = $url[4] ?? null;
switch ($action) {
case '':
//Nothing to do
break;
case 'delete':
//Deletes a picture
if (!$id = $url[4]) {
$smarty->assign('WAP', "URL error. Parameter missing: picture id.");
} else {
$photo = new ProfilePhoto($id);
if ($photo->lastError) {
//Probably an non existent id (e.g. double F5, photo already deleted)
$smarty->assign('WAP', $photo->lastError);
} elseif ($photo->perso_id != $CurrentPerso->id) {
$smarty->assign('WAP', lang_get('NotYourPic'));
} else {
//OK we can delete it
$photo->delete();
$smarty->assign('NOTIFY', lang_get('PictureDeleted'));
}
}
break;
case 'edit':
if (!is_null($id)) {
$smarty->assign('WAP', "URL error. Parameter missing: picture id.");
} else {
- $photo = new ProfilePhoto($id);
+ $photo = new ProfilePhoto($db, $id);
if ($photo->lastError) {
//Probably an non existent id (e.g. double F5, photo already deleted)
$smarty->assign('WAP', $photo->lastError);
} elseif ($photo->perso_id != $CurrentPerso->id) {
$smarty->assign('WAP', lang_get('NotYourPic'));
} else {
//Photo information edit form
$smarty->assign('photo', $photo);
$template = 'profile_photo_edit.tpl';
}
}
break;
case 'avatar':
//Promotes a picture to avatar
if (!is_null($id)) {
$smarty->assign('WAP', "URL error. Parameter missing: picture id.");
} else {
- $photo = new ProfilePhoto($id);
+ $photo = new ProfilePhoto($db, $id);
if ($photo->lastError) {
$smarty->assign('WAP', $photo->lastError);
} elseif ($photo->perso_id != $CurrentPerso->id) {
$smarty->assign('WAP', lang_get('NotYourPic'));
} else {
//OK, promote it to avatar
$photo->promote_to_avatar();
$photo->save_to_database();
$smarty->assign('NOTIFY', lang_get('PromotedToAvatar'));
}
}
break;
default:
$smarty->assign('WAP', "Unknown URL. To delete a picture it's /delete/<picture id>. To edit it /edit/<picture id>");
break;
}
if (!isset($template)) {
- $photos = ProfilePhoto::get_photos($profile->perso_id);
+ $photos = ProfilePhoto::get_photos($db, $profile->perso->id);
if (!array_key_exists('NOTIFY', $smarty->tpl_vars)) {
$smarty->assign('NOTIFY', "Your feedback is valued. Report any bug or suggestion on the graffiti wall.");
}
$template = 'profile_photo.tpl';
}
break;
default:
$smarty->assign('WAP', "URL error. You can use /edit with profile, account or photos.");
break;
}
}
//
// HTML output
//
$has_photos = isset($photo) || count($photos ?? []) > 0;
if ($has_photos) {
$smarty
->assign('URL_PICS', PHOTOS_URL)
->assign('PICS', $photos ?? [])
->append('PAGE_CSS', 'lightbox.css')
->append('PAGE_JS', 'prototype.js')
->append('PAGE_JS', 'effects.js')
->append('PAGE_JS', 'lightbox.js');
}
//Serves header
$smarty->append('PAGE_CSS', THEME . "/profile.css");
$smarty->assign('PAGE_TITLE', $perso->name);
include('header.php');
//Serves content
if (isset($template)) {
$smarty->display($template);
}
//Serves footer
include('footer.php');
diff --git a/controllers/request.php b/controllers/request.php
index 243f852..6cca947 100644
--- a/controllers/request.php
+++ b/controllers/request.php
@@ -1,97 +1,99 @@
<?php
/**
* Requests controller
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller allows the perso to send requests to the HyperShip,
* to a specified ship, or to a specify port requiring PTA.
*
* It handles all the forms output, handling and notifications
* for queries from users to users.
*
* It handles /request URL, is called from tutorial.
*
* @package Zed
* @subpackage Controllers
* @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 complete requests implementation
* @todo call this controller from Ship fly out if port is a PTA
* @todo call this controller from HyperShip entrance perso request
* @todo add hook to launch some events on a new request, reply or status change.
*/
//
// Prepare fields
//
+use Zed\Models\Requests\Request;
+
if (count($url) < 3) {
message_die(HACK_ERROR, "Expected URL: /request/code_to/code_object");
}
-$request = new Request();
+$request = new Request($db);
//
// Handles or print form
//
if (false) {
//Saves the request reply
} elseif (isset($_POST['title']) || isset($_POST['message'])) {
//Saves the request
$request->load_from_form();
$request->author = $CurrentPerso->id;
$request->to = $url[1];
$request->code = $url[2];
$request->location_global = $CurrentPerso->location_global;
$request->location_local = $CurrentPerso->location_local;
$request->save_to_database();
//Confirmation
$template = "requests/confirm.tpl";
} else {
$request->to = $url[1];
$request->code = $url[2];
//Checks if the request template exists
if (!file_exists(sprintf("skins/%s/requests/%s.tpl", THEME, $request->code))) {
message_die(HACK_ERROR, "$url[2] isn't a valid request object code");
}
$template = "requests/$request->code.tpl";
switch ($request->code) {
case "aid.reach":
if ($request->to == "B00001") {
$request->title = "Shuttle pick up request";
}
break;
}
}
//
// HTML output
//
//Serves header
define('DIJIT', true);
$smarty->assign('PAGE_TITLE', lang_get('Request'));
include('header.php');
//Serves content
$smarty->assign('request', $request);
$smarty->display($template);
//Serves footer
$smarty->assign("screen", "$url[2] request");
include('footer.php');
diff --git a/controllers/ship.php b/controllers/ship.php
index 1aec9e8..6a35247 100644
--- a/controllers/ship.php
+++ b/controllers/ship.php
@@ -1,88 +1,89 @@
<?php
/**
* Ship
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller handle the /ship URL
*
* It allows the user to let personal notes about the ship.
*
* It uses the Ship model and the ship.tpl view
*
* @package Zed
* @subpackage Controllers
* @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 prints information indicating if we're or not in the ship
* @todo implement a console to control the ship
*/
//
// Load library and language file
//
-require_once('includes/objects/ship.php');
+use Zed\Models\Objects\Ship;
+
lang_load('ships.conf');
//
// Ship information
//
//Gets ship from URL
if (count($url) < 2) {
//No parameter, gets ship perso is aboard
if (!$code = $CurrentPerso->location->ship_code) {
message_die(GENERAL_ERROR, lang_get("NotAboardNoCode"), lang_get("URLError"));
//"
}
$code = 'S' . $code;
} else {
//Code have been specified
$code = $url[1];
if (!preg_match("/^S[0-9]{5}$/", $code)) {
message_die(GENERAL_ERROR, lang_get("InvalidShipCode"), lang_get("URLError"));
}
}
//Gets relevant information
$ship = Ship::get($code);
$note = $CurrentPerso->get_note($code);
//Determines the spatial relation between perso and ship
//dieprint_r($CurrentPerso->location->ship_code);
//
// Actions handling
//
if ($_REQUEST['action'] == 'ship.setnote' && $_REQUEST['note'] != $note) {
//Updates note content
$CurrentPerso->set_note($code, $_REQUEST['note']);
$note = $_REQUEST['note'];
}
//
// HTML output
//
//Serves header
$smarty->assign('PAGE_TITLE', $ship->name);
include('header.php');
//Serves content
$smarty->assign('note', $note);
$smarty->assign('ship', $ship);
$smarty->display('ship.tpl');
//Serves footer
include('footer.php');
diff --git a/controllers/story.php b/controllers/story.php
index eb4f569..3e99b80 100644
--- a/controllers/story.php
+++ b/controllers/story.php
@@ -1,129 +1,128 @@
<?php
/**
* Story
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller is a storytelling engine allowing to explore a place like
* in fighting fantasy gamebooks / livres dont vous êtes le héros.
*
* This controller is called by the explore controller and handles /explore URL.
*
* It uses the Story classes, the story.tpl view and content/stories/*.xml as
* stories content datasource.
*
* @package Zed
* @subpackage Controllers
* @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 Create a separate release from Zed based on this controller and the Story library.
* @todo Add code to handle actions defined in choices like inventory management
*/
//
// Helper method
//
/**
* Gets section to print to the user
* @param Story the story the section to get (yep, we could've global $story)
* @return StorySection the section, or null if not found
*/
function get_section ($story) {
global $url, $smarty, $CurrentPerso;
//If the URL contains a choice guid, use it as story progress source
//e.g. /explore/143f7200-766b-7b8b-e3f4-9fbfeeaeb5dd
if (count($url) > 1) {
$guid = $url[1];
//Ensures we've a StorySection object in the Story variable
if (!array_key_exists('StoryChoices', $_SESSION)) {
$smarty->assign('WAP', lang_get('ExpiredStorySession'));
} else {
//Gets StoryChoice (creating a dummy section to use get_choice method)
$section = new StorySection("void");
$section->choices = $_SESSION['StoryChoices'];
if (!$choice = $section->get_choice($guid)) {
$smarty->assign('WAP', lang_get('InvalidStoryGUID'));
}
//TODO: add code here to handle actions defined in choices
//e.g. item added to inventory
//Gets section
if ($section_id = $choice->goto) {
if (!array_key_exists($section_id, $story->sections)) {
message_die(GENERAL_ERROR, "Choice <em>$choice->text</em> redirects to <em>$section_id</em> but this section doesn't exist.", "Story error");
}
return $story->sections[$section_id];
}
}
}
if (!$CurrentPerso->location_local) {
//Gets start section
return $story->get_start_section();
}
//Gets section matching perso location
return $story->get_section_from_location($CurrentPerso->location_local);
}
//
// Opens .xml file
//
$file = STORIES_DIR . '/' . $CurrentPerso->location_global . '.xml';
if (!file_exists($file)) {
message_die(GENERAL_ERROR, "If you want to write a story for this place, contact Dereckson — $file", "No story defined");
}
//
// Gets story
//
//Loads story and tries to get the section
-require_once('includes/story/story.php');
$story = new Story($file);
$section = get_section($story);
//Ensures we've a section
if (!$section) {
message_die(GENERAL_ERROR, "Nothing to do at this location. Contact Dereckson if you think it's a bug or you want to write a story here.", "Story");
}
//Performs section actions
if ($section->location_local) {
//Moves perso to section local location
$CurrentPerso->move_to(null, $section->location_local);
}
//Saves section in session, for choices handling
$_SESSION['StoryChoices'] = $section->choices;
//
// HTML output
//
//Serves header
$smarty->assign('PAGE_TITLE', $story->title);
include('header.php');
//Serves content
$smarty->assign("section", $section);
$smarty->display('story.tpl');
//Serves footer
$smarty->assign('screen', "Story, section $section->id");
include('footer.php');
diff --git a/controllers/usersearch.php b/controllers/usersearch.php
index 7affff8..f106fae 100644
--- a/controllers/usersearch.php
+++ b/controllers/usersearch.php
@@ -1,118 +1,117 @@
<?php
/**
* User search
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This is a controller doing nothing else than call header and footer.
*
* The controller uses the usersearch.tpl and directory views (cf. Azhàr code)
*
* Not yet implemented, It should handle /users URL
*
* @package Zed
* @subpackage Controllers
* @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 implement it
*/
-//Libs
-require_once('includes/objects/ProfilePhoto.php');
+use Zed\Models\Profile\ProfilePhoto;
//
// Does the search
//
//Search type
switch ($resource = $url[1]) {
case '':
break;
case 'online':
$sql = "SELECT u.username, u.user_id, u.user_longname FROM " .
TABLE_USERS . " u, " . TABLE_SESSIONS .
" s WHERE s.online = 1 AND u.user_id = s.user_id
ORDER BY HeureLimite DESC";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
}
$i = 0;
while ($row = $db->fetchRow($result)) {
$users[$i]->id = $row['user_id'];
$users[$i]->username = $row['username'];
$users[$i]->longname = $row['user_longname'];
$i++;
}
$title = sprintf(lang_get('UsersOnline'), $i, s($i));
break;
case 'directory':
$sql = 'SELECT username, user_longname FROM ' . TABLE_USERS .
' WHERE user_active < 2 ORDER by user_longname ASC';
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
}
$i = 0;
while ($row = $db->fetchRow($result)) {
$users[$i]->username = $row['username'];
$users[$i]->longname = $row['user_longname'];
$i++;
}
$title = lang_get('Directory');
$mode = 'directory';
break;
default:
$smarty->assign('WAP', lang_get('Nay'));
break;
}
switch ($mode) {
case 'directory':
$template = 'directory.tpl';
$smarty->assign('USERS', $users);
break;
default:
//Prepares avatars
if (count($users)) {
foreach ($users as $user) {
$name = $user->longname ?: $user->username;
- $user->avatar = ProfilePhoto::get_avatar($user->id, $name);
+ $user->avatar = ProfilePhoto::get_avatar($db, $user->id, $name);
}
}
$template = 'usersearch.tpl';
$smarty->assign('TITLE', $title);
$smarty->assign('USERS', $users);
break;
}
//
// HTML output
//
//Serves header
$smarty->append('PAGE_CSS', 'usersearch.css');
$smarty->assign('PAGE_TITLE', $title);
include('header.php');
//Serves content
if ($template) {
$smarty->display($template);
}
//Serves footer
include('footer.php');
diff --git a/dev/quux.php b/dev/quux.php
index 9d55faa..29fcf22 100644
--- a/dev/quux.php
+++ b/dev/quux.php
@@ -1,163 +1,163 @@
<?php
- use Hypership\Geo\PointPolarZ;
-
- require_once('includes/objects/ship.php');
- require_once('includes/objects/port.php');
- require_once('includes/objects/application.php');
- require_once('includes/objects/content.php');
- require_once('includes/objects/message.php');
- require_once('includes/objects/invite.php');
+use Hypership\Geo\PointPolarZ;
+use Zed\Models\Geo\Galaxy;
+use Zed\Models\Geo\Port;
+use Zed\Models\Geo\SceneIndex;
+use Zed\Models\Objects\Application;
+use Zed\Models\Objects\Content;
+use Zed\Models\Objects\Invite;
+use Zed\Models\Messages\Message;
+use Zed\Models\Objects\Perso;
+
require_once('includes/cache/cache.php');
$case = 'YubiCloud';
$smarty->assign('PAGE_TITLE', "Quux [$case]");
include('controllers/header.php');
switch ($case) {
case 'YubiCloud':
require_once('Auth/Yubico.php');
echo '<h2>YubiKey</h2>';
if (!array_key_exists('YubiCloud', $Config)) {
message_die(GENERAL_ERROR, "YubiCloud authentication not configured. Add \$Config['YubiCloud']['ClientID'] and \$Config['YubiCloud']['SecretKey'] to your config.");
}
if (!$key = $_GET['OTP']) {
message_die(GENERAL_ERROR, "Please add in URL ?OTP=, then put your cursor at right of the = and press your YubiKey button");
}
$yubi = new Auth_Yubico($Config['YubiCloud']['ClientID'], $Config['YubiCloud']['SecretKey']);
if (!$data = $yubi->parsePasswordOTP($key)) {
message_die(GENERAL_ERROR, "This is not an YubiKey OTP.");
}
$prefix = $data['prefix'];
$auth = $yubi->verify($key);
if (@PEAR::isError($auth)) {
if ($auth->getMessage() == 'REPLAYED_OTP') {
message_die("This OTP has already been used.");
}
message_die(HACK_ERROR, "<p>Authentication failed: " . $auth->getMessage() . "</p><p>Debug: " . $yubi->getLastResponse() . "</p>");
} else {
print "<p>You are authenticated!</p>";
}
break;
case 'PointPolarZ':
echo "<H2>PointPolarZ</H2>";
$point = PointPolarZ::fromString("(48, 30°, 3)");
printf("Secteur T%dC%d", $point->getSection(), $point->z);
dprint_r($point);
break;
case 'index_scenes':
$time[] = microtime();
- require_once('includes/geo/scene.php');
- require_once('includes/geo/sceneindex.php');
$cache = Cache::load();
- if ($index = $cache->get('GeoSceneIndex')) {
+ if ($index = $cache->get('SceneIndex')) {
$index = unserialize($index);
} else {
- $index = GeoSceneIndex::Load(SCENE_DIR);
- $cache->set('GeoSceneIndex', serialize($index));
+ $index = SceneIndex::Load(SCENE_DIR);
+ $cache->set('SceneIndex', serialize($index));
}
$time[] = microtime();
- echo '<H2>GeoSceneIndex</H2>';
+ echo '<H2>SceneIndex</H2>';
dprint_r($index);
echo '<H2>Time (ms)</H2>';
dprint_r(1000 * ($time[1] - $time[0]));
dprint_r($time);
break;
case 'travel':
require_once('includes/travel/travel.php');
$travel = Travel::load();
dieprint_r($travel);
break;
case 'spherical':
- require_once('includes/geo/galaxy.php');
echo '<H2>Spherical coordinates test</H2>';
echo '<table cellpadding=8>';
echo "<tr><th>Name</th><th>Type</th><th>Cartesian coords</th><th>Spherical I</th><th>Spherical II</th><th>Pencil coordinates</th></tr>";
- $objects = GeoGalaxy::getCoordinates();
+ $objects = Galaxy::getCoordinates($db);
foreach ($objects as $row) {
echo "<tr><th style='text-align: left'>$row[0]</th><td>$row[1]</td><td>$row[2]</td>";
$pt = $row[2];
echo '<td>(', implode(', ', $pt->toSpherical()), ')</td>';
echo '<td>(', implode(', ', $pt->toSphericalAlternative()), ')</td>';
$pt->translate(500, 300, 200, 2);
echo '<td>', $pt, '</td>';
echo '</tr>';
}
echo '</table>';
break;
case 'travel':
require_once('includes/travel/travel.php');
require_once('includes/travel/place.php');
$cache = Cache::load();
$travel = $cache->get('zed_travel');
if ($travel == '') {
$travel_nocached = new Travel();
$travel_nocached->load_xml("content/travel.xml");
$cache->set('zed_travel', serialize($travel_nocached));
} else {
$travel = unserialize($travel);
}
dieprint_r($travel);
break;
case 'perso.create.notify':
- $testperso = Perso::get(4733);
- $message = new Message();
- $message->from = 0;
- $message->to = invite::who_invited(4733);
+ $testperso = Perso::get($db, 4733);
+ $message = new Message($db);
+ $message->setAsSystem();
+ $message->to = invite::who_invited($db, $testperso);
$url = get_server_url() . get_url('who', $testperso->nickname);
$message->text = sprintf(lang_get('InvitePersoCreated'), $testperso->name, $url);
$message->send();
dieprint_r($message);
break;
case 'pushdata';
echo '
<h2>/api.php/app/pushdata</h2>
<form method="post" action="/api.php/app/pushdata?mode=file&key=37d839ba-f9fc-42ca-a3e8-28053e979b90" enctype="multipart/form-data">
<input type="file" name="datafile" /><br />
<input type="submit" value="Send file" />
</form>
';
break;
case 'thumbnail':
- $content = new Content(1);
+ $content = new Content($db, 1);
dprint_r($content);
$content->generate_thumbnail();
break;
case 'port':
echo '<h2>Port::from_location test</h2>';
$locations = array("B00002", "B00002123", "B00001001", "xyz: [800, 42, 220]");
foreach ($locations as $location) {
- dprint_r(Port::from_location($location));
+ dprint_r(Port::from_location($db, $location));
}
break;
case 'ext':
$file = 'dev/foo.tar';
echo "<h2>$file</h2>";
echo "<h3>.tar.bz2</h3>";
echo preg_match('/\.tar\.bz2$/', $file);
echo "<h3>.tar</h3>";
echo preg_match('/\.tar$/', $file);
break;
case 'app':
- echo Application::from_api_key("37d839ba-f9fc-42ca-a3e8-28053e979b90")->generate_userkey();
+ echo Application::from_api_key($db, "37d839ba-f9fc-42ca-a3e8-28053e979b90")
+ ->generate_userkey($CurrentPerso);
break;
case '':
dieprint_r("No case currently selected.");
break;
}
include('controllers/footer.php');
diff --git a/dev/tests/Engines/Templates/Smarty/Plugins/SectorModifierTest.php b/dev/tests/Engines/Templates/Smarty/Plugins/SectorModifierTest.php
index 80b409b..db10bbe 100644
--- a/dev/tests/Engines/Templates/Smarty/Plugins/SectorModifierTest.php
+++ b/dev/tests/Engines/Templates/Smarty/Plugins/SectorModifierTest.php
@@ -1,21 +1,48 @@
<?php
declare(strict_types=1);
namespace Zed\Tests\Engines\Templates\Smarty\Plugins;
-use GeoLocation;
+use Keruald\Database\Engines\MockDatabaseEngine;
-require_once 'includes/geo/location.php';
+use Zed\Models\Geo\Location;
class SectorModifierTest extends SmartyPluginTestCase {
+ const QUERY_RESULTS = [
+ // Queries for SectorModifierTest
+
+ "SELECT * FROM geo_places WHERE CONCAT('B', body_code, place_code) LIKE 'B00001002'" => [[
+ "place_id" => 2,
+ "body_code" => "00001",
+ "place_code" => "002",
+ "place_name" => "Core",
+ "place_description" => "",
+ "place_status" => null,
+ "location_local_format" => "/^\\(\\-?[0-9]+( )*,( )*\\-?[0-9]+( )*,( )*\\-?[0-9]+\\)$/",
+ ]],
+ "SELECT * FROM geo_bodies WHERE body_code = '00001'" => [[
+ "body_code" => 00001,
+ "body_name" => "Hypership",
+ "body_status" => "hypership",
+ "body_location" => "xyz: [-5,-35,10]",
+ "body_description" => null,
+ ]],
+ ];
+
public function setUp () : void {
+ define('TABLE_BODIES', 'geo_bodies');
+ define('TABLE_PLACES', 'geo_places');
+
$this->requirePlugin('modifier', 'sector');
}
public function testPluginWithValidData () {
- $location = new GeoLocation("B00001002", "(4,2,7)");
+ $db = (new MockDatabaseEngine())
+ ->withQueries(self::QUERY_RESULTS);
+
+ $location = new Location($db, "B00001002", "(4,2,7)");
$this->assertEquals("6", smarty_modifier_sector($location));
}
}
diff --git a/dev/tests/Engines/Templates/Smarty/Plugins/SmartyPluginTestCase.php b/dev/tests/Engines/Templates/Smarty/Plugins/SmartyPluginTestCase.php
index cab02b0..2166fbe 100644
--- a/dev/tests/Engines/Templates/Smarty/Plugins/SmartyPluginTestCase.php
+++ b/dev/tests/Engines/Templates/Smarty/Plugins/SmartyPluginTestCase.php
@@ -1,20 +1,20 @@
<?php
declare(strict_types=1);
namespace Zed\Tests\Engines\Templates\Smarty\Plugins;
use PHPUnit\Framework\TestCase;
abstract class SmartyPluginTestCase extends TestCase {
- protected function requirePlugin (string $type, string $name) {
+ protected function requirePlugin (string $type, string $name) : void {
require_once($this->getPluginPath($type, $name));
}
- private function getPluginPath (string $type, string $name) {
+ private function getPluginPath (string $type, string $name) : string {
return dirname(__DIR__, 6) // application root directory
. '/Engines/Templates/Smarty/Plugins/'
. "$type.$name.php";
}
}
diff --git a/dev/tests/GeoPlaceTest.php b/dev/tests/Models/Geo/PlaceTest.php
similarity index 85%
rename from dev/tests/GeoPlaceTest.php
rename to dev/tests/Models/Geo/PlaceTest.php
index 70c1ed1..739f65b 100644
--- a/dev/tests/GeoPlaceTest.php
+++ b/dev/tests/Models/Geo/PlaceTest.php
@@ -1,76 +1,85 @@
<?php
/**
- * Unit testing : class GeoPlace
+ * Unit testing : class Place
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Tests
* @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
*/
+namespace Zed\Tests\Models\Geo;
+
+use Keruald\Database\Engines\BlackholeEngine;
use PHPUnit\Framework\TestCase;
-require_once(__DIR__ . '/../../includes/geo/place.php');
+use Zed\Models\Geo\Place;
/**
- * Test cases for the class GeoPlace
+ * Test cases for the class Place
*/
-class GeoPlaceTest extends TestCase {
+class PlaceTest extends TestCase {
+ protected function setUp () : void {
+ define('TABLE_PLACES', 'geo_places');
+ }
+
/**
- * Tests the GeoPlace::is_valid_local_location($local_location) method.
+ * Tests the Place::is_valid_local_location($local_location) method.
*/
public function testIsValidLocation () {
+ $db = new BlackholeEngine;
+
//Testing HyperShip Tower T2C3 format
- $p0 = new GeoPlace();
+ $p0 = new Place($db);
$p0->location_local_format = '/^T[1-9][0-9]*C[1-6]$/';
$this->assertTrue($p0->is_valid_local_location("T1C1")); // 1
$this->assertTrue($p0->is_valid_local_location("T14C1")); // 2
$this->assertTrue($p0->is_valid_local_location("T14C6")); // 3
$this->assertTrue($p0->is_valid_local_location("T140C6")); // 4
$this->assertTrue($p0->is_valid_local_location("T14000C6")); // 5
$this->assertFalse($p0->is_valid_local_location("C1T6")); // 6
$this->assertFalse($p0->is_valid_local_location("T14000 C6")); // 7
$this->assertFalse($p0->is_valid_local_location("T4C7")); // 8
$this->assertFalse($p0->is_valid_local_location("T4C0")); // 9
$this->assertFalse($p0->is_valid_local_location("T0C0")); //10
//Unit testing is useful: this test led to fix the regexp
//from T[0-9]+C[1-6] to T[1-9][0-9]*C[1-6]
$this->assertFalse($p0->is_valid_local_location("T0C1")); //11
//Testing default format
- $p1 = new GeoPlace();
+ $p1 = new Place($db);
$this->assertTrue($p1->is_valid_local_location("(4,62,35)")); //12
$this->assertTrue($p1->is_valid_local_location("(4, 62, 35)")); //13
$this->assertTrue($p1->is_valid_local_location("(4, 62,35)")); //14
$this->assertFalse($p1->is_valid_local_location("(4,62,-35)")); //15
$this->assertFalse($p1->is_valid_local_location("(4, 62)")); //16
//Testing (x, y, -z) format
- $p2 = new GeoPlace();
+ $p2 = new Place($db);
$p2->location_local_format = '/^\(\-?[0-9]+( )*,( )*\-?[0-9]+( )*,( )*\-?[0-9]+\)$/';
$this->assertTrue($p2->is_valid_local_location("(4,62,35)")); //17
$this->assertTrue($p2->is_valid_local_location("(4, 62, 35)")); //18
$this->assertTrue($p2->is_valid_local_location("(4, 62,35)")); //19
$this->assertTrue($p2->is_valid_local_location("(4,62,-35)")); //20
$this->assertFalse($p2->is_valid_local_location("(4,62,- 35)")); //21
$this->assertFalse($p2->is_valid_local_location("(4,62, - 35)")); //22
$this->assertFalse($p2->is_valid_local_location("(4, 62)")); //23
}
}
diff --git a/do.php b/do.php
index 9063bd8..62c7395 100644
--- a/do.php
+++ b/do.php
@@ -1,471 +1,473 @@
<?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 Zed\Engines\Database\Database;
use Zed\Engines\Templates\Smarty\Engine as SmartyEngine;
-use Hypership\Geo\Point3D;
+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
-require_once('includes/objects/perso.php');
$CurrentUser = get_logged_user(); //Gets current user infos
if ($perso_id = $CurrentUser->session['perso_id']) {
- $CurrentPerso = new Perso($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 {
/**
* 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;
//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);
break;
case 'registry':
registry_set($key, $value);
break;
default:
//Unknown storage location
return false;
}
//Clears request flag
if ((string)$request_flag !== "0") {
$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 GeoLocation the current perso's GeoLocation object
+ * @return Location the current perso's Location object
*/
static function set_local_location ($location_local) {
global $CurrentPerso;
//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);
- //Returns GeoLocation relevant instance
+ //Returns Location relevant instance
return $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 GeoLocation the current perso's GeoLocation object
+ * @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;
//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)) {
$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)"));
- //Returns GeoLocation relevant instance
+ //Returns Location relevant instance
return $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 GeoLocation the current perso's GeoLocation object
+ * @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;
//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 GeoLocation the current perso's GeoLocation object
+ * @return Location the current perso's Location object
*/
static 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;
}
/**
* Handles upload content form.
*
* @return string new content path
*/
static function upload_content () {
global $CurrentPerso, $CurrentUser;
- require_once('includes/objects/content.php');
//Initializes a new content instance
- $content = new Content();
+ $content = new Content($db);
$content->load_from_form();
$content->user_id = $CurrentUser->id;
$content->perso_id = $CurrentPerso->id;
$content->location_global = $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) {
//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
- require_once('includes/objects/content.php');
return Content::get_local_content($location_global, $_REQUEST['location_local']);
+ return Content::get_local_content($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);
$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);
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 (array_key_exists('redirectTo', $_REQUEST)) {
//If user JS disabled, you can add ?redirectTo= followed by an URL
header("location: " . $_REQUEST['redirectTo']);
} else {
echo json_encode($result);
}
}
}
diff --git a/includes/SmartLine/ZedCommands.php b/includes/SmartLine/ZedCommands.php
index ab848ad..05b5ed0 100644
--- a/includes/SmartLine/ZedCommands.php
+++ b/includes/SmartLine/ZedCommands.php
@@ -1,541 +1,540 @@
<?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;
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);
}
- require_once("includes/geo/location.php");
- require_once("includes/travel/travel.php");
-
- $here = new GeoLocation($CurrentPerso->location_global, $CurrentPerso->location_local);
+ $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 GeoLocation($argv[1]);
+ $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 GeoLocation($CurrentPerso->location_global, $argv[1]);
+ $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;
$command = ($argc > 1) ? strtolower($argv[1]) : '';
switch ($command) {
case 'list':
- $codes = Invite::get_invites_from($CurrentPerso->id);
+ $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($CurrentUser->id, $CurrentPerso->id);
+ $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;
$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;
$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;
- require_once("includes/geo/location.php");
- $place = new GeoLocation($CurrentPerso->location_global);
+ $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;
$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/auth/YubiCloudAuthentication.php b/includes/auth/YubiCloudAuthentication.php
index 9ee2d58..1ad07d0 100644
--- a/includes/auth/YubiCloudAuthentication.php
+++ b/includes/auth/YubiCloudAuthentication.php
@@ -1,192 +1,195 @@
<?php
/**
* YubiCloud authentication class.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2013, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Auth
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2013 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*/
+use Zed\Models\Objects\User;
+
require_once('Auth/Yubico.php');
/**
* YubiCloudAuthentication class
*
* Authenticates a user through YubiCloud
*/
class YubiCloudAuthentication implements IAuthentication {
/**
* The key
* @var string
*/
private $key;
/**
* The username who should match the key
* @var string
*/
private $username;
/**
* The user_id
* @var int
*/
private $user_id;
/**
* Indicates if the error MUST be returned to the user
* @var string
*/
private $mustThrowError = false;
/**
* The last validation error
* @var string
*/
public $error;
/**
* Initializes a new instance of the key
*
* @param string $key The key
*/
public function __construct ($key, $username = null) {
$this->username = $username;
$this->key = $key;
}
/**
* Validates the specified key's characters to determine if it looks like an OTP
*
* @return boolean true if the input seems an OTP key; otherwise, false.
*/
function looksValidOTP () {
return preg_match("/^[cbdefghijklnrtuv]{32,48}$/", $this->key);
}
/**
* Gets public identity
*
* @return string Public identity
*/
function getPublicIdentity () {
return substr($this->key, 0, 12);
}
/**
* Validates an OTP key against the YubiCloud servers
*
* @return boolean true if the input is a valid OTP key; otherwise, false.
*/
function isValid () {
global $Config;
+ global $db;
//No need to lost time to query server if format is incorrect.
if (!$this->looksValidOTP()) {
$this->error = "Not the expected YubiKey OTP format.";
return false;
}
//Query YubiCloud. We stop validation tests if that fails.
$yubi = new Auth_Yubico(
$Config['YubiCloud']['ClientID'],
$Config['YubiCloud']['SecretKey']
);
$auth = $yubi->verify($this->key);
if (@PEAR::isError($auth)) {
$this->error = $auth->getMessage();
return false;
}
//Note: We first query the YubiCloud server, then we check if we can use the key
// as the key is an OTP (*one time* password), this allows to invalidate it.
// If we wouldn't do that, an attacker can reuse this password for another site.
if (!$this->computeUserID()) {
$this->error = "Valid YubiKey OTP. But the key doesn't match any account.";
$this->mustThrowError = true;
return false;
}
//Finally, if someone puts also a login, we'll check this user ID match this username
if ($this->username) {
- $user = User::get($this->user_id);
+ $user = User::get($db, $this->user_id);
if ($this->username != $user->name) {
$this->error = "Valid YubiKey OTP but fix or remove your username.";
$this->mustThrowError = true;
return false;
}
}
return true;
}
/**
* Gets the user_id matching the username
*
* You first need to validate the username, calling the isValid method.
*/
function computeUserID () {
global $db;
/**
* Here a MySQL record for a valid OTP
* +---------+-----------+---------------+-----------------+---------+
* | auth_id | auth_type | auth_identity | auth_properties | user_id |
* +---------+-----------+---------------+-----------------+---------+
* | 2 | YubiKey | cccccccccccc | NULL | 1234 |
* +---------+-----------+---------------+-----------------+---------+
*/
$authentication_identity = $this->getPublicIdentity();
$sql = "SELECT user_id FROM " . TABLE_USERS_AUTH
. " WHERE auth_type = 'YubiKey' AND auth_identity = '$authentication_identity'";
if (!$result = $db->query($sql)) {
message_die(SQL_ERROR, "Can't query users authentication table.", '', __LINE__, __FILE__, $sql);
}
if ($row = $db->fetchRow($result)) {
$this->user_id = $row['user_id'];
return true;
}
return false;
}
/**
* Gets the last authentication error
*
* @return string The last authentication error
*/
function getError () {
return $this->error;
}
/**
* Gets the user_id matching the key
*
* You first need to query the authentication table, calling the computeUserID method.
* This is automatically done by IsValid, as we need to validate key matches someone.
*
* @return int the user ID
*/
function getUserID () {
return $this->user_id;
}
/**
* Determines if the next authentication method could be tried if this one failed.
*
* @return bool true if authentication can go on to the next method; otherwise, false
*/
function canTryNextAuthenticationMethod () {
return !$this->mustThrowError;
}
}
diff --git a/includes/autoload.php b/includes/autoload.php
index 73c392c..76e9d86 100644
--- a/includes/autoload.php
+++ b/includes/autoload.php
@@ -1,53 +1,30 @@
<?php
spl_autoload_register(function (string $className) {
//Classes
+ //The autoload for classes in PSR-4 namespaces is handled by Composer
+ //This function takes care of the classes still in root namespace.
+
$classes['IAuthentication'] = './includes/auth/IAuthentication.php';
$classes['UserPasswordAuthentication'] = './includes/auth/UserPasswordAuthentication.php';
$classes['YubiCloudAuthentication'] = './includes/auth/YubiCloudAuthentication.php';
$classes['Cache'] = './includes/cache/cache.php';
$classes['CacheMemcached'] = './includes/cache/memcached.php';
$classes['CacheVoid'] = './includes/cache/void.php';
- $classes['ContentFile'] = './includes/content/file.php';
- $classes['ContentLocation'] = './includes/content/location.php';
- $classes['ContentZone'] = './includes/content/zone.php';
-
- $classes['GeoBody'] = './includes/geo/body.php';
- $classes['GeoGalaxy'] = './includes/geo/galaxy.php';
- $classes['GeoLocation'] = './includes/geo/location.php';
- $classes['GeoPlace'] = './includes/geo/place.php';
- $classes['GeoScene'] = './includes/geo/scene.php';
- $classes['GeoSceneIndex'] = './includes/geo/sceneindex.php';
-
- $classes['Application'] = './includes/objects/application.php';
- $classes['Content'] = './includes/objects/content.php';
- $classes['Invite'] = './includes/objects/invite.php';
- $classes['Message'] = './includes/objects/message.php';
- $classes['MOTD'] = './includes/objects/motd.php';
- $classes['Perso'] = './includes/objects/perso.php';
- $classes['Port'] = './includes/objects/port.php';
- $classes['Profile'] = './includes/objects/profile.php';
- $classes['ProfileComment'] = './includes/objects/profilecomment.php';
- $classes['ProfilePhoto'] = './includes/objects/profilephoto.php';
- $classes['Request'] = './includes/objects/request.php';
- $classes['RequestReply'] = './includes/objects/requestreply.php';
- $classes['Ship'] = './includes/objects/ship.php';
- $classes['User'] = './includes/objects/user.php';
-
$classes['StoryChoice'] = './includes/story/choice.php';
$classes['StoryHook'] = './includes/story/hook.php';
$classes['DemoStoryHook'] = './includes/story/hook_demo.php';
$classes['SpatioportStoryHook'] = './includes/story/hook_spatioport.php';
$classes['StorySection'] = './includes/story/section.php';
$classes['Story'] = './includes/story/story.php';
$classes['TravelPlace'] = './includes/travel/place.php';
$classes['Travel'] = './includes/travel/travel.php';
//Loader
if (array_key_exists($className, $classes)) {
require_once($classes[$className]);
}
});
diff --git a/includes/sessions.php b/includes/sessions.php
index 4907d4a..14bbcaf 100644
--- a/includes/sessions.php
+++ b/includes/sessions.php
@@ -1,162 +1,162 @@
<?php
/**
* Sessions
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This file provides functions to manage sessions. It's not currently properly
* documented, as it's a temporary old session file, which will be updated soon.
*
* @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
*
* @todo Replaces this code by the unified Keruald session class.
*/
use Keruald\OmniTools\Identifiers\Random;
+use Zed\Models\Objects\User;
function session_update () {
global $db, $IP, $Config;
//Nettoyage de la session
/* Initialisation */
$time_online = 5 * 60; // Temps après lequel l'utilisateur n'est plus considéré comme online
$time_session = 2 * 60 * 60; // Durée de vie de la session
$heureActuelle = time(); //Timestamp UNIX et non MySQL
/* On fait le ménage */
$sql = "UPDATE " . TABLE_SESSIONS . " SET online=0 WHERE HeureLimite < $heureActuelle";
if (!$db->query($sql)) {
message_die(SQL_ERROR, 'Impossible de mettre à jour les sessions (utilisateurs offline)', '', __LINE__, __FILE__, $sql);
}
$sql = "DELETE FROM " . TABLE_SESSIONS . " WHERE SessionLimite < $heureActuelle";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Impossible d'effacer les sessions expirées", '', __LINE__, __FILE__, $sql);
}
/* Création / mise à jour de la session utilisateur */
if (!$_SESSION['ID']) {
$_SESSION['ID'] = Random::generateHexHash();
}
$sql = "SELECT * FROM " . TABLE_SESSIONS . " WHERE session_id LIKE '$_SESSION[ID]'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Problème critique avec les sessions.", '', __LINE__, __FILE__, $sql);
}
if ($result->numRows() === 0) {
$sql = "INSERT INTO " . TABLE_SESSIONS . " (IP, session_id, `Where`, HeureLimite, SessionLimite) VALUES ('$IP', '$_SESSION[ID]', $Config[ResourceID], $heureActuelle + $time_online, $heureActuelle + $time_session)";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Impossible de créer une nouvelle session", '', __LINE__, __FILE__, $sql);
}
} else {
$sql = "UPDATE " . TABLE_SESSIONS . " SET online=1, HeureLimite = $heureActuelle + $time_online, SessionLimite= $heureActuelle + $time_session WHERE session_id = '$_SESSION[ID]'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Impossible de mettre à jour la session", '', __LINE__, __FILE__, $sql);
}
}
}
function nbc () {
//Renvoi du nombre d'usagers connectés
global $db, $Config;
$sql = "SELECT count(*) FROM " . TABLE_SESSIONS . " WHERE online=1 AND `Where` = $Config[ResourceID]";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Impossible d'obtenir le nombre d'utilisateurs connectés sur le site web", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return $row[0];
}
function get_info ($info) {
//Renvoie une variable de la session
global $db;
$sql = "SELECT $info FROM " . TABLE_SESSIONS . " WHERE session_id LIKE '$_SESSION[ID]'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Impossible d'obtenir $info", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
return $row[$info];
}
function get_logged_user () {
//Renvoie toutes les informations d'un utilisateur
global $db;
$sql = "SELECT * FROM " . TABLE_SESSIONS . " WHERE session_id LIKE '$_SESSION[ID]'";
if ( !($result = $db->query($sql)) ) {
message_die(SQL_ERROR, "Impossible d'obtenir les informations de l'utilisateur", '', __LINE__, __FILE__, $sql);
}
$row = $db->fetchRow($result);
- require_once('includes/objects/user.php');
- $user = User::get($row['user_id']);
+ $user = User::get($db, $row['user_id']);
$user->session = $row;
return $user;
}
function set_info ($info, $value) {
//Définit une variable session
global $db;
$value = ($value === null) ? 'NULL' : "'" . $db->escape($value) . "'";
$sql = "UPDATE " . TABLE_SESSIONS . " SET $info = $value WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Impossible de définir $info", '', __LINE__, __FILE__, $sql);
}
}
/**
* Destroys $_SESSION array values, help ID
*/
function clean_session () {
foreach ($_SESSION as $key => $value) {
if ($key != 'ID') {
unset($_SESSION[$key]);
}
}
}
/**
* Logs in user
*/
function login ($user_id, $username) {
global $db;
$sql = "UPDATE " . TABLE_SESSIONS . " SET user_id = '$user_id' WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Impossible de procéder à la connexion", '', __LINE__, __FILE__, $sql);
}
//We send a cookie to print automatically the last username on the login
//page during 30 days.
if ($username) {
setcookie("LastUsername", $username, time() + 2592000);
}
}
/**
* Logs out user
*/
function logout () {
//Anonymous user in session table
global $db;
$sql = "UPDATE " . TABLE_SESSIONS . " SET user_id = '-1', perso_id = NULL WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->query($sql)) {
message_die(SQL_ERROR, "Impossible de procéder à la déconnexion", '', __LINE__, __FILE__, $sql);
}
clean_session();
}
diff --git a/includes/story/hook.php b/includes/story/hook.php
index 0ba4177..12e815c 100644
--- a/includes/story/hook.php
+++ b/includes/story/hook.php
@@ -1,103 +1,105 @@
<?php
/**
* Story hook class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Story
* @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 Zed\Models\Objects\Perso;
+
/**
* Story hook class
*
* This class allows to hook PHP code to a textual story.
*
* It allows the story to be completed by site elements.
*
* For a class implementation example:
* @see DemoStoryHook
*/
abstract class StoryHook {
/**
* The current story
*
* @var Story
*/
public $story;
/*
* The current story section
*
* @var StorySection
*/
public $section;
/**
* The character involved in the story
*
* @var Perso
*/
public $perso;
/**
* Constructor
*
* @param Story $story The story including this hook
* @param StorySection $section The section including this hook
*/
function __construct ($story, $section) {
$this->story = $story;
$this->section = $section;
$this->perso = $GLOBALS['CurrentPerso'];
$this->initialize();
}
/**
* Initializes hook. Called after constructor.
*/
abstract function initialize () : void;
/**
* Gets choices extra links
*
* @param string[] $links the hooks links array
*/
function get_choices_links (array &$links) : void {
}
/**
* Updates description
*
* @param string $description the description text (from section and previous hooks)
*/
function update_description (string &$description) : void {
}
/**
* Adds HTML code *AT THE END* of the story content block
*/
function add_content (): string {
return "";
}
/**
* Adds HTML code *AFTER* the content block
*/
function add_html (): string {
return "";
}
}
diff --git a/includes/story/hook_spatioport.php b/includes/story/hook_spatioport.php
index 8f03fd3..3e1f3cd 100644
--- a/includes/story/hook_spatioport.php
+++ b/includes/story/hook_spatioport.php
@@ -1,130 +1,134 @@
<?php
/**
* Story hook class :: spatioport
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This class allows to hook spatioport content to a story.
*
* It lists the ship inside the spatioport and in the surrounding space.
*
* @package Zed
* @subpackage Story
* @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 Adds spatioport services, like ship repair & co
* @todo Adds a map of the sky, with ship around
* @todo Consider to use the Port class instead and to move get_ships methods there.
*/
-require_once('includes/objects/ship.php');
-require_once('includes/geo/location.php');
+use Zed\Models\Geo\Location;
+use Zed\Models\Objects\Ship;
$class = 'SpatioportStoryHook';
/**
* Spatioport story hook class
*/
class SpatioportStoryHook extends StoryHook {
/**
* The spatioport location
*
- * @var GeoLocation
+ * @var Location
*/
public $location;
/**
* The spatioport global location
*
* @var string
*/
public $location_global;
/**
* The spatioport local location
*
* @var string
*/
public $location_local;
/**
* Updates and gets the current section choices
*
* @param Array $links The story links
*/
function get_choices_links (&$links) {
//$links[] = array('Examiner les vaisseaux', get_url('port','ships'));
}
/**
* Initializes instance location properties
*/
function initialize () {
$this->location_global = $this->perso->location_global;
$this->location_local = $this->section->location_local;
- $this->location = new GeoLocation($this->location_global, $this->location_local);
+ $this->location = new Location($db, $this->location_global, $this->location_local);
}
/**
* Appends ship list to the story description
*/
function add_content () {
$ships = $this->get_ships();
if (count($ships)) {
echo "\n<h2>Ships</h2>";
echo "<p>Amongst the ships are at the spatioport:</p>";
echo "\n<ul>";
foreach ($ships as $ship) {
$url = get_url('ship', $ship);
echo "\n\t<li><a href=\"$url\">$ship->name</a></li>";
}
echo "\n</ul>";
}
$ships = $this->get_ships_in_space();
if (count($ships)) {
echo "\n<h2>In orbit</h2>";
$place = (string)$this->location->body;
echo "<p>Those ships are in space around $place:</p>";
echo "\n<ul>";
foreach ($ships as $ship) {
$url = get_url('ship', $ship);
echo "\n\t<li><a href=\"$url\">$ship->name</a></li>";
}
echo "\n</ul>";
}
}
/**
* Get ships in the spatioports
*
* @param string $location_global global location
* @param string $location_local local location
* @return array The ships in the spatioport
*/
private function get_ships () {
- return Ship::get_ships_at($this->location_global, $this->location_local);
+ global $db;
+
+ return Ship::get_ships_at($db, $this->location_global, $this->location_local);
}
/**
* Get ships in the space surrounding the spatioport
*
* @param string $location_global global location
* @param string $location_local local location
* @return array The ships in the space around the spatioport
*/
private function get_ships_in_space () {
- return Ship::get_ships_at($this->location_global, null);
+ global $db;
+
+ return Ship::get_ships_at($db, $this->location_global, null);
}
}
diff --git a/includes/travel/place.php b/includes/travel/place.php
index ca67540..a808078 100644
--- a/includes/travel/place.php
+++ b/includes/travel/place.php
@@ -1,150 +1,150 @@
<?php
/**
* TravelPlace class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-07-19 22:10 DcK
*
* @package Zed
* @subpackage Travel
* @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
*/
/**
* TravelPlace class
*
* The TravelPlace class is a set of rules determining what moves are valid
* in a specific place.
*
- * @see GeoPlace
+ * @see Place
*
*/
class TravelPlace {
/**
* The place code
*
* @var string
*/
public $code;
/**
* Determines if any local location move is valid
*
* @var bool
*/
public $freeLocalMove = false;
/**
* Array of strings, each item another place reachable
*
* This matches GlobalTravelTo XML tags.
*
* @var Array
*/
public $globalTravelTo = [];
/**
* Array of array, containing [location, alias, name] entries
*
* This matches LocalMove XML tags.
*
* @var Array
*/
public $localMoves = [];
/**
* Array of array, containing [expression, global_location, local_location] entries
*
* This matches RewriteRule XML tags.
*
* @var Array
*/
public $rewriteRules = [];
/**
* Initializes a new TravelPlace instance, from the specified XML fragment
*
* @param string $xml the XML fragment to parse
* @return TravelPlace the TravelPlace instance matching the specified XML fragment
*/
static function from_xml ($xml) {
$travelPlace = new TravelPlace();
//Reads attributes: <TravelPlace code="B00001001" freeLocalMove="true">
foreach ($xml->attributes() as $key => $value) {
switch ($key) {
case 'code':
$travelPlace->code = (string)$value;
break;
case 'freeLocalMove':
$travelPlace->freeLocalMove = (boolean)$value;
break;
}
}
//<GlobalTravelTo code="B00001002" />
foreach ($xml->GlobalTravelTo as $globalTravelToXml) {
foreach ($globalTravelToXml->attributes() as $key => $value) {
if ($key == "code") {
$travelPlace->globalTravelTo[] = (string)$value;
}
}
}
//<LocalMove local_location="(0, 0, 0)" alias="C0" name="Core" />
foreach ($xml->LocalMove as $localMoveXml) {
$localMove = [null, null, null];
foreach ($localMoveXml->attributes() as $key => $value) {
switch ($key) {
case 'local_location':
$localMove[0] = (string)$value;
break;
case 'alias':
$localMove[1] = (string)$value;
break;
case 'name':
$localMove[2] = (string)$value;
break;
}
}
$travelPlace->localMoves[] = $localMove;
}
//<RewriteRule expression="/^T([1-9][0-9]*)$/" global_location="B00001001" local_location="T$1C1" />
foreach ($xml->RewriteRule as $rewriteRuleXml) {
$rewriteRule = [null, null, null];
foreach ($rewriteRuleXml->attributes() as $key => $value) {
switch ($key) {
case 'expression':
$rewriteRule[0] = (string)$value;
break;
case 'global_location':
$rewriteRule[1] = (string)$value;
break;
case 'local_location':
$rewriteRule[2] = (string)$value;
break;
}
}
$travelPlace->rewriteRules[] = $rewriteRule;
}
return $travelPlace;
}
}
diff --git a/includes/travel/travel.php b/includes/travel/travel.php
index 1fbe4e9..2e23452 100644
--- a/includes/travel/travel.php
+++ b/includes/travel/travel.php
@@ -1,173 +1,177 @@
<?php
/**
* Travel helper class
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2010-07-18 22:05 DcK
*
* @package Zed
* @subpackage Travel
* @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 Zed\Models\Geo\Location;
+
require_once('place.php');
/**
* Travel helper class
*
* The Travel class reads content/travel.xml to get travel special rules
*
* It's so able to provide methods determining if a move is or not valid.
*
* This class implements a singleton pattern.
*/
class Travel {
/**
* Array of TravelPlace, each one a custom travel rule
*
* This array is indexed by TravelPlace code.
*
* @var Array
*/
public $globalTravelTo;
/**
* Constructor
*/
function __construct () {
//Initializes array
$this->globalTravelTo = [];
}
/**
* Gets and initializes if needed the Travel instance
*
* @return Travel the Travel instance
*/
static function load () {
require_once('includes/cache/cache.php');
$cache = Cache::load();
if (!$travel = $cache->get('zed_travel')) {
//Initializes resource and caches it
$travel = new Travel();
$travel->load_xml(CONTENT_DIR . "/travel.xml");
$cache->set('zed_travel', serialize($travel));
return $travel;
}
return unserialize($travel);
}
/**
* Loads a travel configuration XML file
*
* @param string the path to the travel XML file
*/
function load_xml ($file) {
$xml = simplexml_load_file($file);
foreach ($xml->TravelPlace as $travelPlaceXml) {
$travelPlace = TravelPlace::from_xml($travelPlaceXml);
$this->globalTravelTo[$travelPlace->code] = $travelPlace;
}
}
/**
* Tries to parse the specified expression, according the rewrite rules
* (for example defined by the <RewriteRule> xml tags)
*
* @param string $expression the expression to parse
- * @param GeoLocation the location where the perso is
- * @param GeoLocation the location where the perso wants to go
+ * @param Location the location where the perso is
+ * @param Location the location where the perso wants to go
*
* @return boolean true if the expression have been parsed ; otherwise, false.
*/
function try_parse_rewrite_rule ($expression, $from, &$to) {
+ global $db;
+
//Relevant write rules depends on the location the perso is ($from)
if (!array_key_exists($from->global, $this->globalTravelTo)) {
return false;
}
$travelPlace = $this->globalTravelTo[$from->global];
foreach ($travelPlace->rewriteRules as $rule) {
//$rule is an array [expression, global_location, local_location]
$subpatterns = [];
$result = preg_match($rule[0], $expression, $subpatterns);
if ($result > 0) {
//$subpatterns is an array with:
// - at indice 0, the full matched regexp
// - from 1 to n, the (groups) inside the regexp
//We need so to replace $1 by $subpatterns[1] and so on.
for ($i = count($subpatterns) - 1 ; $i > 0 ; $i--) {
$rule[1] = str_replace('$' . $i, $subpatterns[$i], $rule[1]);
$rule[2] = str_replace('$' . $i, $subpatterns[$i], $rule[2]);
}
- $to = new GeoLocation($rule[1], $rule[2]);
+ $to = new Location($db, $rule[1], $rule[2]);
return true;
}
}
return false;
}
/**
* Determines if a perso can travel from $from to $to
*
* If an alias have been used for $to local location, set correct location.
*
- * @param GeoLocation the location where the perso is
- * @param GeoLocation the location where the perso wants to go
+ * @param Location the location where the perso is
+ * @param Location the location where the perso wants to go
* @return boolean true if the travel move is valid ; otherwise, false.
*
* @todo From B00001002, goto C1 doesn't work. Alias seems ignored.
*/
function can_travel ($from, &$to) {
if ($from->global != $to->global) {
//Checks if we can locally from $from to $to place
if (!array_key_exists($from->global, $this->globalTravelTo)) {
return false;
}
$travelPlace = $this->globalTravelTo[$from->global];
if (!in_array($to->global, $travelPlace->globalTravelTo)) {
return false;
}
}
if ($to->containsLocalLocation) {
//Determines if we've custom rules about local moves in $to
if (!array_key_exists($to->global, $this->globalTravelTo)) {
return false;
}
$travelPlace = $this->globalTravelTo[$to->global];
//Is it's an especially allowed movement?
foreach ($travelPlace->localMoves as $move) {
//move is a [location, alias, name] array
//If any of those 3 parameters matches $to->local, it's okay
if (in_array($to->local, $move)) {
$to->local = $move[0];
return true;
}
}
if ($travelPlace->freeLocalMove) {
//We can move freely, perfect
return true;
}
//Local move not allowed
return false;
}
return true;
}
}
diff --git a/index.php b/index.php
index 4573118..d4205f8 100644
--- a/index.php
+++ b/index.php
@@ -1,136 +1,136 @@
<?php
/**
* Application entry point
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @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 Zed\Engines\Database\Database;
use Zed\Engines\Perso\PersoSelector;
use Zed\Engines\Templates\Smarty\Engine as SmartyEngine;
+use Zed\Models\Geo\Place;
////////////////////////////////////////////////////////////////////////////////
///
/// Initialization
///
//Keruald (formerly Pluton) library
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
include("includes/login.php"); //login/logout
$CurrentUser = get_logged_user(); //Gets current user information
//Skin and accent to load
define('THEME', $CurrentUser->session['Skin']);
define('ACCENT', $CurrentUser->session['Skin_accent']);
//Loads Smarty
$smarty = SmartyEngine::load()->getSmarty();
//Loads language files
initialize_lang();
lang_load('core.conf');
//Gets URL
$url = get_current_url_fragments();
//If the user isn't logged in (is anonymous), prints login/invite page & dies.
if ($CurrentUser->id < 1000) {
include('controllers/anonymous.php');
exit;
}
////////////////////////////////////////////////////////////////////////////////
///
/// Perso (=character) selector
///
-$CurrentPerso = PersoSelector::load($CurrentUser, $smarty);
+$CurrentPerso = PersoSelector::load($db, $CurrentUser, $smarty);
////////////////////////////////////////////////////////////////////////////////
///
/// Tasks to execute before calling the URL controller:
/// - assert the perso is somewhere
/// - executes the smartline
///
//If the perso location is unknown, ejects it to an asteroid
if (!$CurrentPerso->location_global) {
- require_once('includes/geo/place.php');
$smarty->assign('NOTIFY', lang_get('NewLocationNotify'));
- $CurrentPerso->move_to(GeoPlace::get_start_location());
+ $CurrentPerso->move_to(Place::get_start_location($db));
}
//SmartLine
include("includes/SmartLine/ZedSmartLine.php");
//Redirects user to user request controller if site.requests flag on
if (defined('PersoSelected') && array_key_exists('site.requests', $CurrentPerso->flags) && $CurrentPerso->flags['site.requests']) {
include('controllers/persorequest.php');
}
////////////////////////////////////////////////////////////////////////////////
///
/// Calls the specific controller to serve the requested page
///
switch ($controller = $url[0]) {
case '':
include('controllers/home.php');
break;
case 'builder':
case 'explore':
case 'page':
case 'request':
case 'settings':
case 'ship':
include("controllers/$controller.php");
break;
case 'who':
include('controllers/profile.php'); //Azhàr controller
break;
case 'push':
include('controllers/motd.php'); //Azhàr controller
break;
case 'quux':
//It's like a test/debug console/sandbox, you put what you want into
if (file_exists('dev/quux.php')) {
include('dev/quux.php');
} else {
message_die(GENERAL_ERROR, "Quux lost in Hollywood.", "Nay");
}
break;
default:
//TODO: returns a prettier 404 page
header("Status: 404 Not Found");
dieprint_r($url, 'Unknown URL');
}
diff --git a/skins/zed/messages.tpl b/skins/zed/messages.tpl
index 7826221..533f128 100644
--- a/skins/zed/messages.tpl
+++ b/skins/zed/messages.tpl
@@ -1,21 +1,20 @@
<!-- Messages -->
<div class="grid_16 alpha omega">
{foreach from=$MESSAGES item=message}
-{if $message->from == 0}
+{if $message->source == Zed\Models\MessageSource::System}
<div class="message red">
<div class="message_info">{#SystemNotice#} | {get_hypership_time($message->date)} | <a href="?action=msg_delete&id={$message->id}" title="{#DeleteThisMessage#}">X</a></div>
<div class="message_text">{$message->text|text2html}</div>
</div>
{else}
-{$perso = Perso::get($message->from)}
-{$who = $perso->name}
-{$url = get_url('who', $perso->nickname)}
+{$who = $message->perso->name}
+{$url = get_url('who', $message->perso->nickname)}
<div class="message {cycle values="light,dark"}">
<div class="message_info"><a href="{$url}">{$who}</a> | {get_hypership_time($message->date)} | <a href="{$url}#Message">{#Reply#}</a> | <a href="?action=msg_delete&id={$message->id}" title="{#DeleteThisMessage#}">X</a></div>
<div class="message_text">{$message->text|text2html}</div>
</div>
{/if}
{/foreach}
</div>
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 08:40 (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
20764
Default Alt Text
(361 KB)

Event Timeline