Page MenuHomeCode

No OneTemporary

diff --git a/Models/Base/WithDatabase.php b/Engines/Database/WithDatabase.php
similarity index 92%
rename from Models/Base/WithDatabase.php
rename to Engines/Database/WithDatabase.php
index 04024f0..76282be 100644
--- a/Models/Base/WithDatabase.php
+++ b/Engines/Database/WithDatabase.php
@@ -1,25 +1,25 @@
<?php
-namespace Zed\Models\Base;
+namespace Zed\Engines\Database;
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/Models/Base/Entity.php b/Models/Base/Entity.php
index 8fcf791..284e31f 100644
--- a/Models/Base/Entity.php
+++ b/Models/Base/Entity.php
@@ -1,13 +1,15 @@
<?php
namespace Zed\Models\Base;
+use Zed\Engines\Database\WithDatabase;
+
abstract class Entity implements EntityDatabaseInterface {
///
/// Database layer
///
use WithDatabase;
}
diff --git a/Models/Content/File.php b/Models/Content/File.php
index 278d397..04eaea5 100644
--- a/Models/Content/File.php
+++ b/Models/Content/File.php
@@ -1,296 +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;
+use Zed\Engines\Database\WithDatabase;
/**
* Content class
*
* This class maps the content_files table.
*
*/
class File extends Entity {
use WithDatabase;
/* -------------------------------------------------------------
Properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
public $id;
public $path;
public $user_id;
public $perso_id;
public $title;
/* -------------------------------------------------------------
Constructor, __toString
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* Initializes a new File instance
*
* @param int $id the primary key
*/
function __construct (DatabaseEngine $db, $id = null) {
$this->setDatabase($db);
if ($id) {
$this->id = $id;
$this->load_from_database();
}
}
/**
* 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 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 File (ie fill the properties) from the database
*/
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 () : 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/Models/Content/Zone.php b/Models/Content/Zone.php
index 27e0792..f04a775 100644
--- a/Models/Content/Zone.php
+++ b/Models/Content/Zone.php
@@ -1,228 +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;
+use Zed\Engines\Database\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 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 (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 () : 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 () : 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();
}
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 Zone the zone, or null if the zone doesn't exist and $create is false
*/
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 Zone($db, $row['zone_id']);
} elseif ($create) {
$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 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 Zone items of the returned array:
* - location_global
* - location_local
*/
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 Zone($db, $row['zone_id']);
$zone->location_global = $row['location_global'];
$zone->location_local = $row['location_local'];
$zones[] = $zone;
}
return $zones;
}
}
diff --git a/Models/Geo/Entity.php b/Models/Geo/Entity.php
index fb99b51..50d3a5a 100644
--- a/Models/Geo/Entity.php
+++ b/Models/Geo/Entity.php
@@ -1,15 +1,15 @@
<?php
namespace Zed\Models\Geo;
-use Zed\Models\Base\WithDatabase;
+use Zed\Engines\Database\WithDatabase;
abstract class Entity {
///
/// Database layer
///
use WithDatabase;
}
diff --git a/Models/Geo/Place.php b/Models/Geo/Place.php
index 6d6338d..3c5c87a 100644
--- a/Models/Geo/Place.php
+++ b/Models/Geo/Place.php
@@ -1,247 +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;
+use Zed\Engines\Database\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
*/
/**
* 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 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 ($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 () : 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 () : 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 ?: 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 Place the place instance
*/
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 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 (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/Models/Objects/Ship.php b/Models/Objects/Ship.php
index f55be2b..1ffb861 100644
--- a/Models/Objects/Ship.php
+++ b/Models/Objects/Ship.php
@@ -1,353 +1,354 @@
<?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
*/
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 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 (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 (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($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_global = $_POST['location_global'];
}
if (array_key_exists('location_local', $_POST)) {
$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 (): bool {
$db = $this->getDatabase();
$id = $db->escape($this->id);
$sql = "SELECT * FROM ships WHERE ship_id = '" . $id . "'";
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 (): 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
*
* @return Ship[]
* @throws Exception
*/
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 Location($db, $location_global, $location_local);
while ($row = $db->fetchRow($result)) {
$shipLocation = new Location($db, $row['location_global'], $row['location_local']);
if ($location->equals($shipLocation)) {
$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($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->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($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
*/
private function clean_ship_sessions (): void {
//Cleans old sessions
$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
$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($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))) {
message_die(SQL_ERROR, "Unable to query ships", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->fetchRow($result)) {
return null;
}
//Fills ship information
$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/Models/Objects/User.php b/Models/Objects/User.php
index f6f6f45..9dcd44f 100644
--- a/Models/Objects/User.php
+++ b/Models/Objects/User.php
@@ -1,288 +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;
+use Zed\Engines\Database\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 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 (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 (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];
}
}
}
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 (): bool {
$db = $this->getDatabase();
$sql = "SELECT * FROM " . TABLE_USERS . " WHERE user_id = '" . $this->id . "'";
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 (): 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) {
$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 () {
$db = $this->getDatabase();
do {
$this->id = rand(2001, 5999);
$sql = "SELECT COUNT(*) as is_already_used 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["is_already_used"]);
}
/**
* 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) {
$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 (DatabaseEngine $db, $login): bool {
$sql = "SELECT COUNT(*) as is_used 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["is_used"];
}
/**
* 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 (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/Models/Profile/Profile.php b/Models/Profile/Profile.php
index cc2c3c4..1038a06 100644
--- a/Models/Profile/Profile.php
+++ b/Models/Profile/Profile.php
@@ -1,172 +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\Engines\Database\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 extends Entity {
use WithDatabase;
public Perso $perso;
public string $text = "";
public $updated;
public $fixedwidth;
public string $lastError = "";
/**
* Initializes a new instance of the Profile class
*/
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 = 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 (): bool {
$db = $this->getDatabase();
$id = $db->escape($this->perso->id);
$sql = "SELECT * FROM " . TABLE_PROFILES . " WHERE perso_id = '$id'";
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 (): void {
$db = $this->getDatabase();
$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 () {
$db = $this->getDatabase();
$id = $db->escape($this->perso->id);
$sql = "SELECT tag_code, tag_class FROM " . TABLE_PROFILES_TAGS
. " 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;
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/Models/Profile/ProfileComment.php b/Models/Profile/ProfileComment.php
index 075e565..75e69a6 100644
--- a/Models/Profile/ProfileComment.php
+++ b/Models/Profile/ProfileComment.php
@@ -1,156 +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;
+use Zed\Engines\Database\WithDatabase;
/**
* Profile comments class
*
* This class maps the profiles_comments table.
*/
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 (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)) {
$this->perso_id = $_POST['perso_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 comment (ie fill the properties) from the database
*/
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))) {
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 (): 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 (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($db, $row["comment_id"]);
}
return $comments;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 17, 01:26 (1 w, 23 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
20748
Default Alt Text
(56 KB)

Event Timeline