Page MenuHomeCode

No OneTemporary

diff --git a/Engines/Templates/Smarty/Engine.php b/Engines/Templates/Smarty/Engine.php
index 30c9f95..444a4e4 100644
--- a/Engines/Templates/Smarty/Engine.php
+++ b/Engines/Templates/Smarty/Engine.php
@@ -1,81 +1,88 @@
<?php
namespace Zed\Engines\Templates\Smarty;
use Smarty;
class Engine {
///
/// Private members
///
/**
* @var Smarty
*/
private $smarty;
/**
* @var Configuration
*/
private $config;
///
/// Singleton
///
/**
* @var Engine
*/
private static $instance = null;
public static function load () : Engine {
if (self::$instance === null) {
self::$instance = new self;
}
return self::$instance;
}
///
/// Constructor
///
public function __construct (
?Smarty $smarty = null,
?Configuration $config = null
) {
$this->config = $config ?? Configuration::load();
$this->smarty = $smarty ?? $this->buildDefaultSmarty();
}
///
/// Factory methods
///
private function buildDefaultSmarty () : Smarty {
$smarty = new Smarty();
$smarty
->setCacheDir($this->config->getCacheDirectory())
->setCompileDir($this->config->getCompileDirectory())
->setTemplateDir($this->config->getTemplateDirectory())
->setConfigDir($this->config->getApplicationDirectory())
->addPluginsDir($this->config->getPluginsDirectory());
+ self::initializeDefaultVariables($smarty);
+
$smarty->config_vars += [
'StaticContentURL' => $this->config->getStaticContentURL(),
];
return $smarty;
}
+ private static function initializeDefaultVariables (Smarty $smarty) {
+ $smarty->assign("PAGE_CSS", []);
+ $smarty->assign("PAGE_JS", []);
+ }
+
///
/// Getters
///
public function getSmarty () : Smarty {
return $this->smarty;
}
}
diff --git a/controllers/home.php b/controllers/home.php
index 3413614..f5debad 100644
--- a/controllers/home.php
+++ b/controllers/home.php
@@ -1,102 +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.
*/
//
// Gets and manage messages
//
require_once('includes/objects/message.php');
//Deletes a message if user have clicked the X
-if ($_GET['action'] == 'msg_delete') {
+$action = $_GET['action'] ?? "";
+if ($action === 'msg_delete') {
//Deletes message $_GET['id']
$id = $_GET['id'];
$messageToDelete = new Message($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);
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);
$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 60116c5..8499b13 100644
--- a/controllers/motd.php
+++ b/controllers/motd.php
@@ -1,54 +1,54 @@
<?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
*/
//
// Handles form
//
-if ($_REQUEST['text']) {
+if (isset($_REQUEST['text'])) {
require_once('includes/objects/motd.php');
$motd = new MOTD();
$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/profile.php b/controllers/profile.php
index e11fad7..080087c 100644
--- a/controllers/profile.php
+++ b/controllers/profile.php
@@ -1,353 +1,359 @@
<?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
*/
//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->sql_query_express("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);
if ($perso->lastError) {
message_die(GENERAL_ERROR, $perso->lastError, "Error");
}
$smarty->assign('perso', $perso);
//Gets profile
$profile = new Profile($perso->id);
//Handles form
-if ($_POST['EditProfile']) {
+$message_type = $_POST['message_type'] ?? "";
+if (isset($_POST['EditProfile'])) {
$profile->load_from_form();
$profile->updated = time();
$profile->save_to_database();
$mode = 'view';
-} elseif ($_POST['UserAccount']) {
+} elseif (isset($_POST['UserAccount'])) {
$smarty->assign('WAP', "This form have been deprecated. You can write instead settings in the SmartLine");
-} elseif ($_POST['message_type'] == 'private_message') {
+} elseif ($message_type === 'private_message') {
//Sends a message
require_once('includes/objects/message.php');
$msg = new Message();
$msg->from = $CurrentPerso->id;
$msg->to = $perso->id;
$msg->text = $_POST['message'];
$msg->send();
if ($msg->from == $msg->to) {
$smarty->assign('NOTIFY', lang_get('MessageSentSelf'));
} else {
$smarty->assign('NOTIFY', lang_get('MessageSent'));
}
-} elseif ($_POST['message_type'] == 'profile_comment') {
+} elseif ($message_type== 'profile_comment') {
//New profile comment
$comment = new ProfileComment();
$comment->author = $CurrentPerso->id;
$comment->perso_id = $perso->id;
$comment->text = $_POST['message'];
$comment->publish();
$smarty->assign('NOTIFY', lang_get('CommentPublished'));
-} elseif ($_FILES['photo']) {
+} elseif (isset($_FILES['photo'])) {
#We've a file !
$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->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 ($_POST['id']) {
+} 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
-if ($profile->text) {
- //Profile
- $smarty->assign('PROFILE_TEXT', $profile->text);
- $smarty->assign('PROFILE_FIXEDWIDTH', $profile->fixedwidth);
-}
+$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;
//Gets profiles comments, photos, tags
$comments = ProfileComment::get_comments($profile->perso_id);
$photos = ProfilePhoto::get_photos($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->username);
+
+ $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);
- $css[] = THEME . '/forms.css';
+ $smarty->append('PAGE_CSS', THEME . '/forms.css');
$template = 'profile_edit.tpl';
break;
case 'account':
$smarty->assign('user', $CurrentUser);
$smarty->assign('DIJIT', true);
- $css[] = THEME . '/forms.css';
+ $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);
- switch ($action = $url[3]) {
+
+ $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 (!$id = $url[4]) {
+ if (!is_null($id)) {
$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 {
//Photo information edit form
$smarty->assign('photo', $photo);
$template = 'profile_photo_edit.tpl';
}
}
break;
case 'avatar':
//Promotes a picture to avatar
- if (!$id = $url[4]) {
+ if (!is_null($id)) {
$smarty->assign('WAP', "URL error. Parameter missing: picture id.");
} else {
$photo = new ProfilePhoto($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 (!$template) {
+ if (!isset($template)) {
$photos = ProfilePhoto::get_photos($profile->perso_id);
- if (!$smarty->tpl_vars['NOTIFY']) {
+ 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
//
-//Photos
-if (count($photos) || $photo) {
- $smarty->assign('URL_PICS', PHOTOS_URL);
- $css[] = 'lightbox.css';
- $smarty->assign('PAGE_JS', ['prototype.js', 'effects.js', 'lightbox.js']);
- $smarty->assign('PICS', $photos);
+$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
-$css[] = THEME . "/profile.css";
-$smarty->assign('PAGE_CSS', $css);
+$smarty->append('PAGE_CSS', THEME . "/profile.css");
$smarty->assign('PAGE_TITLE', $perso->name);
include('header.php');
//Serves content
-if ($template) {
+if (isset($template)) {
$smarty->display($template);
}
//Serves footer
include('footer.php');
diff --git a/controllers/request.php b/controllers/request.php
index 9174fdd..db603a5 100644
--- a/controllers/request.php
+++ b/controllers/request.php
@@ -1,96 +1,97 @@
<?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
//
if (count($url) < 3) {
message_die(HACK_ERROR, "Expected URL: /request/code_to/code_object");
}
+$request = new Request();
+
//
// Handles or print form
//
if (false) {
//Saves the request reply
-} elseif ($_POST['title'] || $_POST['message']) {
+} elseif (isset($_POST['title']) || isset($_POST['message'])) {
//Saves the request
- require_once('includes/objects/request.php');
- $request = new 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->obj = $url[2];
//Checks if the request template exists
if (!file_exists(sprintf("skins/%s/requests/%s.tpl", THEME, $request->obj))) {
message_die(HACK_ERROR, "$url[2] isn't a valid request object code");
}
$template = "requests/$request->obj.tpl";
switch ($request->obj) {
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/settings.php b/controllers/settings.php
index 89141d2..7b96b1e 100644
--- a/controllers/settings.php
+++ b/controllers/settings.php
@@ -1,95 +1,96 @@
<?php
/**
* Settings
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This controller allows user to set its preferences, according the Settings
* classes and the preferences.xml document.
*
* It handles the /settings 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 replace the on the fly preferences.xml code generation by a cached code generation
* @todo reduce the number of for loops in this controller
*/
//
// Loads settings
//
lang_load('settings.conf');
include('includes/settings/settings.php');
$settings = new Settings('includes/settings/preferences.xml');
//Selects relevant settings page
$pages = $settings->pages;
if (count($url) > 1) {
//From url: /settings/account -> page account
if (array_key_exists($url[1], $settings->pages)) {
$page = $pages[$url[1]];
} else {
message_die(GENERAL_ERROR, "/settings/$url[1] isn't a valid setting page");
}
} else {
//Default page
$page = array_shift($pages);
}
//Pages links
foreach ($settings->pages as $tmppage) {
$pagesLinks[$tmppage->id] = $tmppage->title;
}
//
// Handles form
//
if (array_key_exists('settings_page', $_POST)) {
if ($_POST['settings_page'] == $page->id) {
//Updates settings
$errors = [];
$page->handle_form($errors);
if (count($errors)) {
//Prints error message
$smarty->assign('WAP', implode('<br />', $errors));
}
} else {
//The field settings.page isn't the current page
//Prints an HACK_ERROR to avoid to save properties with the same names.
$id_current = $page->id;
$id_toSave = $_POST['settings_page'];
message_die(HACK_ERROR, "You're on /settings/$id_current but you want to update /settings/$id_toSave");
}
}
//
// HTML output
//
//Serves header
define('DIJIT', true);
$title = lang_get('Settings');
$smarty->assign('PAGE_TITLE', $title);
include('header.php');
//Serves settings page;
$smarty->assign('page', $page);
$smarty->assign('pages', $pagesLinks);
$smarty->display('settings_page.tpl');
//Servers footer
+$smarty->assign('screen', "Settings > " . $page->title);
include('footer.php');
diff --git a/controllers/usersearch.php b/controllers/usersearch.php
index 4a0f4a3..ba5659b 100644
--- a/controllers/usersearch.php
+++ b/controllers/usersearch.php
@@ -1,118 +1,118 @@
<?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');
//
// 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->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
}
$i = 0;
while ($row = $db->sql_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->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql);
}
$i = 0;
while ($row = $db->sql_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);
}
}
$template = 'usersearch.tpl';
$smarty->assign('TITLE', $title);
$smarty->assign('USERS', $users);
break;
}
//
// HTML output
//
//Serves header
-$smarty->assign('PAGE_CSS', 'usersearch.css');
+$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/includes/SmartLine/SmartLine.php b/includes/SmartLine/SmartLine.php
index 1db8969..afa9fb6 100644
--- a/includes/SmartLine/SmartLine.php
+++ b/includes/SmartLine/SmartLine.php
@@ -1,543 +1,544 @@
<?php
/**
* SmartLine 0.1
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* 0.1 2007-07-28 01:36 [DcK] Initial release
* 2010-07-02 00:39 [Dck] Documentation
*
* @package Zed
* @subpackage SmartLine
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2007 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @link http://bitbucket.org/dereckson/smartline
* @filesource
///////////////////////////////////////////////////////////////////////////////
// SECTION I - INITIALIZATION
///////////////////////////////////////////////////////////////////////////////
//Constants
/**
* The standard, regular output (like STDOUT on POSIX systems)
*/
if (!defined('STDOUT')) {
- define('STDOUT', 1, true);
+ define('STDOUT', 1);
}
/**
* The error output (like STDERR on POSIX systems)
*/
if (!defined('STDERR')) {
- define('STDERR', -1, true);
+ define('STDERR', -1);
}
///////////////////////////////////////////////////////////////////////////////
// SECTION Ibis - L10n
///////////////////////////////////////////////////////////////////////////////
//Ensures $lang is a standard array
if (empty($lang) || !is_array($lang)) {
$lang = [];
}
$lang = array_merge($lang, [
//Errors
'InvalidCommand' => "Invalid command %s. Use <strong>showcommands</strong> to show all commands.",
'RegisteredButNotExistingCommand' => "[CRITICAL ERROR] The command %s has correctly been registered but its method or class doesn't exist.",
'NotYetHelpForThiscommand' => "This command hasn't been documented yet.",
//Help
'DefaultHelp' => "This SmartLine is a command line interface.
<br /><br /><strong>showcommands</strong> prints the list.
<br /><strong>help &lt;command&gt;</strong> prints help for this command.",
'Help' => [
'help' => "<strong>help &lt;command&gt;</strong> prints command help.",
'showcommands' => 'show available commands'
]
]);
///////////////////////////////////////////////////////////////////////////////
// SECTION II - HELPERS FUNCTIONS
///////////////////////////////////////////////////////////////////////////////
/**
* Error handler called during SmartLine command execution.
*
* Any error occurring during command execution will be set in STDERR.
*
* To get an array with all the errors:
* <code>$errors = $yourSmartLine->gets_all(STDERR)</code>
*
* Or to prints all the error:
* <code>$yourSmartLine->prints_all(STDERR)</code>
*
* Or to pops (gets and deletes) only the last error:
* <code>$lastError = $yourSmartLine->gets(STDERR)</code>
*
* @link http://www.php.net/manual/en/function.set-error-handler.php set_error_handler, PHP manual
* @link http://www.php.net/manual/en/errorfunc.examples.php Error handling examples, PHP manual
*
* @param int $level The PHP error level
* @param string $error The error description
* @param string $file The script where the error occurred
* @param int $line The line where the error occurred
*/
function SmartLineHandler($level, $error, $file, $line) {
switch ($level) {
case E_NOTICE:
$type = 'Notice';
break;
case E_WARNING:
$type = 'Warning';
break;
case E_ERROR:
$type = 'Error';
break;
default:
$type = "#$level";
}
$_SESSION['SmartLineOutput'][STDERR][] = "[PHP $type] $error in $file line $line.";
return true;
}
///////////////////////////////////////////////////////////////////////////////
// SECTION III - BASE CLASSES
///////////////////////////////////////////////////////////////////////////////
//SmartLineCommand is a class implementing a SmartLine command.
//If you want to create a more complex command, extends this class.
/**
* The SmartLine command base class.
*
* To add a command, create an instance of the class, like:
* <code>
* class HelloWorldSmartLineCommand extends SmartLineCommand {
* public function run ($argv, $argc) {
* $this->SmartLine->puts('Hello World!');
* }
* }
* </code>
*
* Then, registers your command:
* <code>
* $yourSmartLine->register_object('hello', 'HelloWorldSmartLineCommand');
* </code>
*
* @see SmartLine::register_object
*/
class SmartLineCommand {
/**
* Initializes a new instance of the SmartLine Command
*
* @param SmartLine $SmartLine the SmartLine the command belongs
*/
public function __construct ($SmartLine) {
$this->SmartLine = $SmartLine;
}
/**
* Gets the command help text or indicates help should be fetched from $lang array
*
* @return string|bool a string containing the command help or the bool value false, to enable the default behavior (ie prints $lang['help']['nameOfTheCommand'])
*/
public function help () {
return false;
}
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
}
/**
* The SmartLine where this instance of the command is registered
*
* @var SmartLine
*/
public $SmartLine;
}
/**
* This class represents a SmartLine instance
*
* If you use only register_object, you can use it directly.
* If you use register_method, extends this class in your SmartLine.
*/
class SmartLine {
/**
* Initializes a new instance of the SmartLine object.
*/
public function __construct () {
//Assumes we've an empty array where store registered commands.
$this->commands = [];
//Let's register standard commands
$this->register_object('showcommands', 'ShowCommandsSmartLineCommand');
$this->register_object('help', 'HelpSmartLineCommand');
}
/**
* Registers a private method as command.
*
* @param string $command The name of the command to register
* @param string $method The method to register [OPTIONAL]. If omitted, the method registered will be the method having the same name as the command.
* @param bool $useArgvArgc If true, indicates the method uses $argv, $argc as parameters. If false, indicates the method uses its parameters (default behavior). [OPTIONAL]
*
* @return bool true if the command have successfully been registered ; otherwise, false.
*/
public function register_method ($command, $method = null, $useArgvArgc = false) {
if (is_null($function)) {
$method = $command;
}
if (!method_exists($this, $method)) {
$this->lastError = "Registration failed. Unknown method $method";
return false;
}
$className = ucfirst($method) . 'SmartLineCommand';
//If class exists, add a uniqid after function
while (class_exists($method)) {
$className = uniqid(ucfirst($method)) . 'SmartLineCommand';
}
//Creates the class
if ($useArgvArgc) {
$call = "$this->SmartLine->$method(\$argv, \$argc);";
} else {
//We don't know how many args we've, so we use call_user_func_array
$call = "array_shift(\$argv);
call_user_func_array(
array(&\$this->SmartLine, '$method'),
\$argv
);";
}
$code = "class $className extends SmartLineCommand {
public function run (\$argv, \$argc) {
$call
}
}";
eval($code);
$this->register_object($command, $className);
return true;
}
/**
* Registers an object extending SmartLineCommand as command.
*
* @param string $command The name of the command to register
* @param SmartLineCommand|string $object The object extending SmartLineCommand. This can be the name of the class (string) or an instance already initialized of the object (SmartLineCommand).
* @return bool true if the command have successfully been registered ; otherwise, false.
*/
public function register_object ($command, $object) {
if (is_object($object)) {
//Sets SmartLine property
$object->SmartLine = $this;
} elseif (is_string($object) && class_exists($object)) {
//Creates a new instance of $object
$object = new $object($this);
} else {
$this->lastError = "Registration failed. register_object second parameter must be a class name (string) or an already initialized instance of such class (object) and not a " . gettype($object);
return false;
}
if (!$this->caseSensitive) {
$command = strtolower($command);
}
$this->commands[$command] = $object;
return true;
}
/**
* Determines whether the specified command have been registered.
*
* @param string $command The name of the command to check
* @return true if the specified command have been registered ; otherwise, false.
*/
public function isRegistered ($command) {
if (!$this->caseSensitive) {
$command = strtolower($command);
}
return array_key_exists($command, $this->commands);
}
/**
* Executes the specified expression.
*
* If an error occurs during the command execution:
* the STDERR output will contains the errors,
* the value returned by this methods will be false.
*
* To execute the command and prints error:
* <code>
* $fooSmartLine = new SmartLine();
* //...
* $result = $fooSmartLine->execute($expression);
* $fooSmartLine->prints_all();
* if (!$result) {
* //Errors!
* echo "<h3>Errors</h3>";
* $fooSmartLine->prints_all(STDERR);
* }
* </code>
*
* @param string $expression The expression containing the command to execute
* @return bool true if the command have been successfully executed ; otherwise, false.
*/
public function execute ($expression) {
//Does nothing if blank line
if (!$expression) {
return;
}
//Prepares $argv and $argc
$argv = $this->expression2argv($expression);
$argc = count($argv);
//Gets command
$command = $this->caseSensitive ? $argv[0] : strtolower($argv[0]);
//If command doesn't exist, throws an error
if (!array_key_exists($command, $this->commands)) {
global $lang;
$this->puts(sprintf($lang['InvalidCommand'], $command), STDERR);
return false;
}
//Executes command, intercepting error and returns result
set_error_handler("SmartLineHandler");
try {
$result = $this->commands[$command]->run($argv, $argc);
} catch (Exception $ex) {
$this->puts("<pre>$ex</pre>", STDERR);
}
restore_error_handler();
return $result;
}
/**
* Adds a message to the specified output queue.
*
* @param string $message the message to queue
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
*/
public function puts ($message, $output = STDOUT) {
//
$_SESSION['SmartLineOutput'][$output][] = $message;
}
/**
* Truncates the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
*/
public function truncate ($output = STDOUT) {
unset($_SESSION['SmartLineOutput'][$output]);
}
/**
* Pops (gets and clears) the first message from the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
* @return string the message
*/
public function gets ($output = STDOUT) {
if ($this->count($output) > 0) {
return array_pop($_SESSION['SmartLineOutput'][$output]);
}
}
/**
* Gets the number of messages in the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
*/
public function count ($output = STDOUT) {
return isset($_SESSION['SmartLineOutput'][$output])
? count($_SESSION['SmartLineOutput'][$output])
: 0;
}
/**
* Gets all the message from the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
* @param string $prefix The string to prepend each message with. It's an optional parameter ; if omitted, '<p>'.
* @param string $suffix The string to append each message with. It's an optional parameter ; if omitted, '</p>'.
* @return Array an array of string, each item a message from the specified output queue
*/
public function gets_all ($output = STDOUT, $prefix = '<p>', $suffix = '</p>') {
$count = $this->count($output);
if ($count == 0) {
return;
}
+
+ $buffer = "";
for ($i = 0 ; $i < $count ; $i++) {
$buffer .= $prefix . $_SESSION['SmartLineOutput'][$output][$i] . $suffix;
}
unset ($_SESSION['SmartLineOutput'][$output]);
return $buffer;
}
/**
* Prints all the message from the specified output queue.
*
* @param int $output The output queue (common values are STDERR and STDOUT constants). It's an optional parameter ; if omitted, the default value will be STDOUT.
* @param string $prefix The string to prepend each message with. It's an optional parameter ; if omitted, '<p>'.
* @param string $suffix The string to append each message with. It's an optional parameter ; if omitted, '</p>'.
*/
public function prints_all ($output = STDOUT, $prefix = '<p>', $suffix = '</p>') {
$count = $this->count($output);
if ($count == 0) {
return;
}
for ($i = 0 ; $i < $count ; $i++) {
echo $prefix, $_SESSION['SmartLineOutput'][$output][$i], $suffix;
}
unset ($_SESSION['SmartLineOutput'][$output]);
}
/**
* Gets the command help
*
* @param string $command The command to get help from
* @param string The command help
*/
public function gethelp ($command) {
return $this->commands[$command]->help();
}
/**
* Gets an an argv array from the specified expression
*
* @param string $expression The expression to transform into a argv array
* @return Array An array of string, the first item the command, the others those arguments.
*/
private function expression2argv ($expression) {
//Checks if expression contains "
$pos1 = strpos($expression, '"');
//We isolate "subexpression"
if ($pos1 !== false) {
$pos2 = $pos1;
do {
$pos2 = strpos($expression, '"', $pos2 + 1);
} while ($pos2 !== false && ($expression[$pos2 - 1] == "\\" && $expression[$pos2 - 2] != "\\"));
if ($pos2 === false) {
//If final quote is missing, throws a warning and autoadds it.
$this->puts("[Warning] Final \" missing in $expression.", STDERR);
$argv = $this->expression2argv(substr($expression, 0, $pos1));
$argv[] = substr($expression, $pos1 + 1);
return $argv;
}
return array_merge(
$this->expression2argv(substr($expression, 0, $pos1)),
[substr($expression, $pos1 + 1, $pos2 - $pos1 - 1)],
$this->expression2argv(substr($expression, $pos2 + 1))
);
}
//Standard expression (ie without ")
$argv = [];
$items = explode(' ', $expression);
foreach ($items as $item) {
$item = trim($item);
if (!$item) {
//blank, we ignore
continue;
}
$argv[] = $item;
}
return $argv;
}
//Contains last error
public $lastError = '';
//If true, command isn't equal to Command
public $caseSensitive = true;
}
///////////////////////////////////////////////////////////////////////////////
// SECTION IV - STANDARD COMMANDS
///////////////////////////////////////////////////////////////////////////////
/*
* These commands are available in all default smartlines instance
*/
/**
* The standard command "showcommands"
*
* This command returns a list, with all the available commands
*/
class ShowCommandsSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
$commands = array_keys($this->SmartLine->commands);
sort($commands);
$this->SmartLine->puts(implode(' ', $commands));
}
}
/**
* The standard command "help"
*
* This command prints command help.
*
* Help could be defined
* in the command classes, as a return value from the help method ;
* in the $lang['Help'] array, at the command key (e.g. $lang['Help']['quux'] for the quux command).
*/
class HelpSmartLineCommand extends SmartLineCommand {
/**
* Runs the command
*
* @param array $argv an array of string, each item a command argument
* @param int $argc the number of arguments
*/
public function run ($argv, $argc) {
global $lang;
if ($argc == 1) {
$this->SmartLine->puts($lang['DefaultHelp']);
} elseif (!$this->SmartLine->isRegistered($argv[1])) {
$this->SmartLine->puts(sprintf($lang['InvalidCommand'], str_replace(' ', '&nbsp;', $argv[1])), STDERR);
} else {
$command = strtolower($argv[1]);
if (!$help = $this->SmartLine->gethelp($command)) {
if (array_key_exists($command, $lang['Help'])) {
$help = $lang['Help'][$command];
} else {
$help = $lang['NotYetHelpForThiscommand'];
}
}
$this->SmartLine->puts($help);
}
}
}
///////////////////////////////////////////////////////////////////////////////
-
diff --git a/includes/SmartLine/ZedSmartLine.php b/includes/SmartLine/ZedSmartLine.php
index 1fdb3a3..f2131a2 100644
--- a/includes/SmartLine/ZedSmartLine.php
+++ b/includes/SmartLine/ZedSmartLine.php
@@ -1,108 +1,110 @@
<?php
/**
* The Zed SmartLine subcontroller.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* This is the SmartLine subcontroller.
*
* The SmartLine is a widget allowing to add some basic CLI capability.
*
* It executes any command given in GET or POST request (parameter C).
*
* This files also provides SmartLine history helper: a method log_C to log
* a SmartLine command and some procedural code assigning a SmartLineHistory.
*
* This code is inspired from Viper, a corporate PHP intranet I wrote in 2004.
* There, the SmartLine allowed to change color theme or to find quickly user,
* account, order or server information in a CRM context.
*
* @package Zed
* @subpackage SmartLine
* @author Sébastien Santoro aka Dereckson <dereckson@espace-win.org>
* @copyright 2010 Sébastien Santoro aka Dereckson
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version 0.1
* @link http://scherzo.dereckson.be/doc/zed
* @link http://zed.dereckson.be/
* @filesource
*
* @todo Caches SmartLine history
*/
///
/// Helpers
///
/**
* Logs a Smartline command
*
* @param string $command the command to log
* @param bool $isError indicates if the command is an error
*/
function log_C ($command, $isError = false) {
global $db, $CurrentPerso;
$isError = $isError ? 1 : 0;
$command = $db->sql_escape($command);
$sql = "INSERT INTO " . TABLE_LOG_SMARTLINE . " (perso_id, command_time, command_text, isError)
VALUES ($CurrentPerso->id, UNIX_TIMESTAMP(), '$command', $isError)";
if (!$db->sql_query($sql)) {
message_die(SQL_ERROR, "Can't log SmartLine command", '', __LINE__, __FILE__, $sql);
}
}
///
/// Executes command
///
-if ($C = $_REQUEST['C']) {
+if (isset($_REQUEST['C'])) {
+ $command = $_REQUEST['C'];
+
//Initializes SmartLine object
require_once("SmartLine.php");
$smartLine = new SmartLine();
require_once("ZedCommands.php");
//Executes SmartLine
$controller = '';
- $smartLine->execute($C);
+ $smartLine->execute($command);
$error = $smartLine->count(STDERR) > 0;
if ($smartLine->count(STDOUT) > 0) {
$smarty->assign("SmartLine_STDOUT", $smartLine->gets_all(STDOUT, '', '<br />'));
}
if ($error) {
$smarty->assign("SmartLine_STDERR", $smartLine->gets_all(STDERR, '', '<br />'));
}
if ($controller != '') {
include($controller);
}
- log_C($C, $error);
+ log_C($command, $error);
}
///
/// Gets SmartLine history
///
$perso_id = $db->sql_escape($CurrentPerso->id);
$sql = "SELECT command_time, command_text FROM log_smartline
WHERE isError = 0 AND perso_id = '$perso_id'
ORDER BY command_time DESC LIMIT 100";
if (!$result = $db->sql_query($sql)) {
message_die(SQL_ERROR, "Can't get SmartLine history", '', __LINE__, __FILE__, $sql);
}
$i = 0;
while ($row = $db->sql_fetchrow($result)) {
$commands[$i]['time'] = get_hypership_time($row['command_time']);
$commands[$i]['text'] = $row['command_text'];
$i++;
}
$smarty->assign("SmartLineHistory", $commands);
diff --git a/includes/cache/void.php b/includes/cache/void.php
index 082250a..ded28ed 100644
--- a/includes/cache/void.php
+++ b/includes/cache/void.php
@@ -1,80 +1,80 @@
<?php
/**
* "Blackhole" void cache.
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* Cache class: void
*
* 0.1 2010-07-06 22:55 Initial version [DcK]
*
* @package Zed
* @subpackage Cache
* @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
*/
/**
* "blackhole" void cache
*
* This class doesn't cache information, it'a void wrapper
* get will always return null
* set and delete do nothing
*
* It will be used by default if no cache is specified.
*
* This class implements a singleton pattern.
*/
class CacheVoid {
/**
* @var CacheVoid the current cache instance
*/
static $instance = null;
/**
* Gets the cache instance, initializing it if needed
*
* @return Cache the cache instance, or null if nothing is cached
*/
static function load () {
if (self::$instance === null) {
self::$instance = new CacheVoid();
}
return self::$instance;
}
/**
* Gets the specified key's data
*
* @param string $key the key to fetch
* @return mixed the data at the specified key
*/
function get ($key) {
- return null;
+ return false;
}
/**
* Sets the specified data at the specified key
*
* @param string $key the key where to store the specified data
* @param mixed $value the data to store
*/
function set ($key, $value) { }
/**
* Deletes the specified key's data
*
* @param string $key the key to delete
*/
function delete ($key) { }
}
diff --git a/includes/geo/scene.php b/includes/geo/scene.php
index c72b859..391a6bc 100644
--- a/includes/geo/scene.php
+++ b/includes/geo/scene.php
@@ -1,212 +1,213 @@
<?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');
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 {
/**
* 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
*/
public $location;
/**
* Initializes a new GeoScene instance
*
* @param GeoLocation $location location the scene is to print
*/
function __construct ($location) {
$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);
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 ($file) {
+ public static function get_file_extension (string $file) : string {
$pathinfo = pathinfo($file);
- return $pathinfo['extension'];
+
+ return $pathinfo['extension'] ?? "";
}
/**
* Renders the file
*
* @todo Add standard code to render .swf Flash/ShockWave files.
*/
public function render () {
if ($file = $this->sceneFile) {
switch ($ext = GeoScene::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));
} elseif ($this->location->ship) {
$smarty->assign("location", new GeoLocation($this->location->ship->location));
}
//Gets zone information
require_once('includes/content/zone.php');
if ($zone = ContentZone::at($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__);
}
echo "\n\n";
}
}
/**
* Tries to get the scene file.
*
* It will tries 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/includes/geo/sceneindex.php
index 557db30..bd8c8f8 100644
--- a/includes/geo/sceneindex.php
+++ b/includes/geo/sceneindex.php
@@ -1,218 +1,222 @@
<?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');
/**
* Geo scene class
*
* This class provides an index of available scene template files.
*/
class GeoSceneIndex {
/**
* Global location templates array
*
* Keys are global location codes.
* Values the relevant template file.
*
* @var Array
*/
public $global_templates;
/**
* Local location templates 2D array
*
* Keys are global and local location codes.
* Values the relevant template file.
*
* e.g. $local_templates['B00017001']['(10, 50, 8)'] => 'B00017_port.tpl'
*
* @var Array
*/
public $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
*/
static $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);
}
return self::$instance[$directory];
}
/**
* Initializes a new GeoSceneIndex 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 ($index = unserialize($cache->get('zed_sceneindex'))) {
- $index = unserialize($cache->get('zed_sceneindex'));
- $this->global_templates = $index->global_templates;
- $this->local_templates = $index->local_templates;
- $this->updated = $index->updated;
- $this->directory = $index->directory;
- return true;
+ $cached_index = $cache->get('zed_sceneindex');
+
+ if ($cached_index === false) {
+ return 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') {
$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 contains 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/profile.php b/includes/objects/profile.php
index 4fce0ae..6cc22d6 100644
--- a/includes/objects/profile.php
+++ b/includes/objects/profile.php
@@ -1,156 +1,156 @@
<?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
*/
/**
* Profile class
*
* This class maps the profiles table.
*
* The class also provides methods to handle and cache tags.
*/
class Profile {
public $perso_id;
- public $text;
+ public string $text = "";
public $updated;
public $fixedwidth;
/**
* Initializes a new instance of the Profile class
*
* @param int $perso_id the perso ID
*/
function __construct ($perso_id) {
$this->perso_id = $perso_id;
$this->load_from_database();
}
/**
* Loads the object Profile (ie fill the properties) from the $_POST array
*/
function load_from_form ($read_boolean = true) {
if (array_key_exists('perso_id', $_POST)) {
$this->perso_id = $_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->sql_escape($this->perso_id);
$sql = "SELECT * FROM " . TABLE_PROFILES . " WHERE perso_id = '$id'";
if ( !($result = $db->sql_query($sql)) ) {
message_die(SQL_ERROR, "Unable to query azhar_profiles", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->sql_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;
$perso_id = $db->sql_escape($this->perso_id);
$text = $db->sql_escape($this->text);
$updated = $db->sql_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->sql_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->sql_escape($this->perso_id);
$sql = "SELECT tag_code, tag_class FROM " . TABLE_PROFILES_TAGS
. " WHERE perso_id = '$id'";
if (!$result = $db->sql_query($sql)) {
message_die(SQL_ERROR, "Can't get tags", '', __LINE__, __FILE__, $sql);
}
$tags = [];
while ($row = $db->sql_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/includes/objects/profilecomment.php b/includes/objects/profilecomment.php
index 7adec57..c342af3 100644
--- a/includes/objects/profilecomment.php
+++ b/includes/objects/profilecomment.php
@@ -1,138 +1,140 @@
<?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
*/
/**
* Profile comments class
*
* This class maps the profiles_comments table.
*/
class ProfileComment {
public $id;
public $perso_id;
public $author;
public $authorname; //should be read-only
public $date;
public $text;
/**
* Initializes a new instance of the ProfileComment class
*
* @param int $id the comment ID
*/
function __construct ($id = '') {
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 () {
global $db;
$id = $db->sql_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->sql_query($sql)) ) {
message_die(SQL_ERROR, "Unable to query azhar_profiles_comments", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->sql_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;
$id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL';
$perso_id = $db->sql_escape($this->perso_id);
$author = $db->sql_escape($this->author);
$date = $db->sql_escape($this->date);
$text = $db->sql_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->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->sql_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;
$sql = "SELECT comment_id FROM " . TABLE_PROFILES_COMMENTS . " WHERE perso_id = " . $db->sql_escape($perso_id);
if (!$result = $db->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to get comments", '', __LINE__, __FILE__, $sql);
}
+
+ $comments = [];
while ($row = $db->sql_fetchrow($result)) {
$comments[] = new ProfileComment($row[0]);
}
return $comments;
}
}
diff --git a/includes/objects/profilephoto.php b/includes/objects/profilephoto.php
index f84ffc0..b07cacd 100644
--- a/includes/objects/profilephoto.php
+++ b/includes/objects/profilephoto.php
@@ -1,214 +1,217 @@
<?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
*/
/**
* 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 {
public $id;
public $perso_id;
public $name;
public $description;
public $avatar;
/**
* Initializes a new instance of the ProfilePhoto class
*/
function __construct ($id = '') {
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;
$id = $db->sql_escape($this->id);
$sql = "SELECT * FROM " . TABLE_PROFILES_PHOTOS . " WHERE photo_id = '" . $id . "'";
if ( !($result = $db->sql_query($sql)) ) {
message_die(SQL_ERROR, "Unable to query azhar_profiles_photos", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->sql_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;
//1 - locally
$sql = "UPDATE " . TABLE_PROFILES_PHOTOS . " SET photo_avatar = 0 WHERE perso_id = " . $this->perso_id;
$db->sql_query_express($sql);
$this->avatar = true;
//2 - in perso table
$perso = Perso::get($this->perso_id);
$perso->avatar = $this->name;
$perso->saveToDatabase();
}
/**
* Saves the object to the database
*/
function save_to_database () {
global $db;
//Escapes fields
$id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL';
$perso_id = $db->sql_escape($this->perso_id);
$name = $db->sql_escape($this->name);
$description = $db->sql_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->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$id) {
//Gets new record id value
$this->id = $db->sql_nextid();
}
}
/**
* Deletes the photo
*/
function delete () {
global $db;
//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->sql_escape($this->id);
$sql = "DELETE FROM " . TABLE_PROFILES_PHOTOS . " WHERE photo_id = '$id' LIMIT 1";
if (!$db->sql_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) {
+ static function get_photos ($perso_id, $allowUnsafe = true) : array {
global $db;
$sql = "SELECT photo_id FROM " . TABLE_PROFILES_PHOTOS . " WHERE perso_id = " . $db->sql_escape($perso_id);
if (!$allowUnsafe) {
$sql .= " AND photo_safe = 0";
}
if (!$result = $db->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to get photos", '', __LINE__, __FILE__, $sql);
}
+
+ $photos = [];
while ($row = $db->sql_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;
$perso_id = $db->sql_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->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to get avatar", '', __LINE__, __FILE__, $sql);
}
if ($row = $db->sql_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'];
return "<img src=\"$url\" title=\"$username\" alt=\"$description\" />";
} else {
return null;
}
}
}
diff --git a/includes/objects/request.php b/includes/objects/request.php
index 3ff377a..deaa118 100644
--- a/includes/objects/request.php
+++ b/includes/objects/request.php
@@ -1,143 +1,143 @@
<?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
*/
/**
* Request class
*
* This class maps the requests table.
*/
class Request {
public $id;
public $code;
public $title;
public $date;
public $author;
public $to;
public $message;
public $location_global;
public $location_local;
public $status;
/**
* Initializes a new instance
* @param int $id the primary key
*/
function __construct ($id = null) {
if ($id) {
$this->id = $id;
$this->load_from_database();
} else {
$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;
$id = $db->sql_escape($this->id);
$sql = "SELECT * FROM " . TABLE_REQUESTS . " WHERE request_id = '" . $id . "'";
if (!$result = $db->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to query requests", '', __LINE__, __FILE__, $sql);
}
if (!$row = $db->sql_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;
$id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL';
$code = $db->sql_escape($this->code);
$title = $db->sql_escape($this->title);
$date = $db->sql_escape($this->date);
$author = $db->sql_escape($this->author);
$message = $db->sql_escape($this->message);
$to = $db->sql_escape($this->to);
$location_global = $db->sql_escape($this->location_global);
$location_local = $db->sql_escape($this->location_local);
$status = $db->sql_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')";
+ $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->sql_query($sql)) {
message_die(SQL_ERROR, "Unable to save", '', __LINE__, __FILE__, $sql);
}
if (!$this->id) {
//Gets new record id value
$this->id = $db->sql_nextid();
}
}
}
diff --git a/includes/settings/settings.php b/includes/settings/settings.php
index 1503736..5cede5a 100644
--- a/includes/settings/settings.php
+++ b/includes/settings/settings.php
@@ -1,83 +1,83 @@
<?php
/**
* Settings
*
* Zed. The immensity of stars. The HyperShip. The people.
*
* (c) 2010, Dereckson, some rights reserved.
* Released under BSD license.
*
* @package Zed
* @subpackage Settings
* @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
*/
/**
* The method to call in your objects, to save data.
*/
define("SETTINGS_SAVE_METHOD", "save_to_database");
require_once("page.php");
/**
* Settings
*
* This class maps the Settings format (preferences.xml)
*
* It allows to generate settings web forms and handle replies, from a
* XML document.
*/
class Settings {
/**
* The file path
*
* @var string
*/
public $file;
/**
* A collection of SettingsPage items
*
- * @var Array
+ * @var SettingsPage[]
*/
public $pages;
/**
* Initializes a new instance of Settings class
*
* @param string $xmlFile The XML document which defines the settings.
*/
function __construct ($xmlFile) {
//Opens .xml
if (!file_exists($xmlFile)) {
message_die(GENERAL_ERROR, "$xmlFile not found.", "Settings load error");
}
$this->file = $xmlFile;
//Parses it
$this->parse();
}
/**
* Parses XML file
*/
function parse () {
//Parses it
$xml = simplexml_load_file($this->file);
foreach ($xml->page as $page) {
//Gets page
$page = SettingsPage::from_xml($page);
//Adds to sections array
$this->pages[$page->id] = $page;
}
}
}
diff --git a/skins/zed/error.tpl b/skins/zed/error.tpl
index 01beb4b..57d0906 100644
--- a/skins/zed/error.tpl
+++ b/skins/zed/error.tpl
@@ -1,60 +1,60 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{#SiteTitle#}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="{#StaticContentURL#}/css/960.css" media="screen" />
<link rel="stylesheet" href="{#StaticContentURL#}/css/zed/theme.css" />
</head>
<body>
<!-- Header -->
<div id="header">
<div id="header_content">
<div class="container_16">
<div class="grid_9">
<div id="HypershipTime">{get_hypership_time()}</div>
</div>
<div class="grid_7">
<a href="{get_url()}"><img src="{#StaticContentURL#}/img/zed/logo.png" src="Zed logo" border="0" /></a>
</div>
</div>
</div>
</div>
<div class="clear"></div>
<div class="container_16">
-{if $WAP}
+{if isset($WAP)}
<!-- WAP -->
<div class="grid_16 alpha omega">
<div class="wap">{$WAP}</div>
</div>
{/if}
-{if $NOTIFY}
+{if isset($NOTIFY)}
<!-- Notify -->
<div class="grid_16 alpha omega">
<div class="notify">{$NOTIFY}</div>
</div>
{/if}
<!-- Error -->
<div class="content_wrapper">
<h1>{$TITLE}</h1>
<div class="content">
<p>{$ERROR_TEXT}</p>
<p><a href="{get_url()}">{#BackToHome#}</a></p>
</div>
</div>
<div class="clear"></div>
<hr />
<div id="footer">
<div class="grid_12 alpha">
<p>[ {#Product#} / {#FatalErrorScreen#} ]</p>
</div>
<div class="grid_4 omega">
<p style="text-align: right">[ <a href="{get_url()}?action=user.logout">{#Logout#}</a> ]</p>
</div>
</div>
</div>
</body>
-</html>
\ No newline at end of file
+</html>
diff --git a/skins/zed/footer.tpl b/skins/zed/footer.tpl
index e8eaf40..1b95d45 100644
--- a/skins/zed/footer.tpl
+++ b/skins/zed/footer.tpl
@@ -1,20 +1,20 @@
<div class="clear"></div>
{if $SmartLinePrint}
{include file="smartline.tpl"}
{/if}
<!-- Footer -->
<hr />
<div id="footer">
<div class="grid_8 alpha">
- <p>[ {#Product#} / {$CurrentPerso->location_global} {$CurrentPerso->location_local} / {if $screen}{$screen}{else}Untitled screen{/if} ]</p>
+ <p>[ {#Product#} / {$CurrentPerso->location_global} {$CurrentPerso->location_local} / {if isset($screen)}{$screen}{else}Untitled screen{/if} ]</p>
</div>
<div class="grid_8 omega" style="float: right">
<p style="text-align: right">[ {if $MultiPerso}<a href="{get_url()}?action=perso.logout">{sprintf(#SwapPerso#, $CurrentPerso->name)}</a> | {/if}<a href="{get_url()}?action=user.logout">{#Logout#}</a> ]</p>
</div>
</div>
</div>
</body>
-</html>
\ No newline at end of file
+</html>
diff --git a/skins/zed/header.tpl b/skins/zed/header.tpl
index 8c6ebc1..57a1126 100644
--- a/skins/zed/header.tpl
+++ b/skins/zed/header.tpl
@@ -1,69 +1,68 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{$PAGE_TITLE} - {#SiteTitle#}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="{#StaticContentURL#}/css/960.css" media="screen" />
<link rel="stylesheet" href="{#StaticContentURL#}/css/zed/theme.css" />
<script type="text/javascript" src="{#StaticContentURL#}/js/misc.js"></script>
{foreach from=$PAGE_CSS item=css}
<link rel="stylesheet" href="{#StaticContentURL#}/css/{$css}" />
{/foreach}
{foreach from=$PAGE_JS item=js}
<script src="{#StaticContentURL#}/js/{$js}"></script>
{/foreach}
{if $DOJO}
<!-- DOJO -->
<script type="text/javascript" src="{#StaticContentURL#}/js/dojo/dojo/dojo.js" djConfig="isDebug:false, parseOnLoad: true" ></script>
-{if $DIJIT}
+{if isset($DIJIT)}
<link rel="stylesheet" type="text/css" href="{#StaticContentURL#}/css/zed/forms.css" />
{/if}
{/if}
</head>
-<body{if $DIJIT} class="tundra"{/if}>
+<body{if isset($DIJIT)} class="tundra"{/if}>
<!-- Header -->
<div id="header">
<div id="header_content">
<div class="container_16">
<div class="grid_9">
<div class="wall" id="header_wall">
{$WALL_TEXT}
<br />
<span class="wall_info">- - <a href="{$WALL_USER_URL}">{$WALL_USER}</a></span>
</div>
<div class="clear"></div>
<div id="HypershipTime">{get_hypership_time()}</div>
</div>
<div class="grid_7">
<a href="{get_url()}"><img src="{#StaticContentURL#}/img/zed/logo.png" alt="Zed logo" border="0" /></a>
</div>
<div class="clear"></div>
</div>
</div>
</div>
<div class="clear"></div>
<!-- Content -->
<div class="container_16">
-{if $WAP}
+{if isset($WAP)}
<!-- WAP -->
<div class="grid_16 alpha omega">
<div class="wap">{$WAP}</div>
</div>
<div class="clear"></div>
{/if}
-{if $NOTIFY}
+{if isset($NOTIFY)}
<!-- Notify -->
<div class="grid_16 alpha omega">
<div class="notify">{$NOTIFY}</div>
</div>
<div class="clear"></div>
{/if}
-{if $SmartLine_STDOUT || $SmartLine_STDERR}
+{if isset($SmartLine_STDOUT) || isset($SmartLine_STDERR)}
{include file="smartline_results.tpl"}
{/if}
-
diff --git a/skins/zed/profile.tpl b/skins/zed/profile.tpl
index b181e4c..a924e9e 100644
--- a/skins/zed/profile.tpl
+++ b/skins/zed/profile.tpl
@@ -1,80 +1,80 @@
<!-- Profile -->
<div class="clear">&nbsp;</div>
<div class="grid_11 alpha">
<!-- Profile header -->
<div class="profile_id clearfix">
<h1 class="profile_nick">{$perso->name}</h1>
<div class="profile_info">
{$perso->location}&nbsp;<br />
{if $perso->is_online()}{#Online#}{/if}
</div>
</div>
<div class="clear">&nbsp;</div>
<div class="profile">
-{if $PICS}
+{if isset($PICS)}
<!-- Photos -->
<div class="profile_photos">
{foreach from=$PICS item=photo}
<a rel="lightbox" href="{$URL_PICS}/{$photo->name}" title="{$photo->description}"><img src="{$URL_PICS}/tn/{$photo->name}" alt="{$photo->description}" /></a>
{/foreach}
</div>
{/if}
<!-- Text -->
<div class="profile_text{if $PROFILE_FIXEDWIDTH} fixedwidth{/if}">{if $PROFILE_TEXT != ""}{if $PROFILE_FIXEDWIDTH}{$PROFILE_TEXT}{else}{$PROFILE_TEXT|nl2br}{/if}{else}{if $PROFILE_SELF}<a href="{get_url('who')}/edit/profile">{/if}<img src="{#StaticContentURL#}/img/zed/empty_profile.png" width="642" height="392" alt="Be creative ! Fill this space with your best words." />{if $PROFILE_SELF}</a>{/if}{/if}</div>
{$PROFILE_TAGS}
<!-- Leave a message -->
<div class="profile_separator_light"></div>
<div class="profile_message">
<h2 id="Message">{#DropMessage#}</h2>
<form method="post" action="{get_url('who')}/{$perso->nickname}">
<div class="grid_4 alpha">
<input type="radio" name="message_type" value="private_message" checked onclick="document.getElementById('MessageSubmit').value = '{#Send#}';">{sprintf(#SendMessage#, $NAME)}
</div>
<div class="grid_6 omega">
<input type="radio" name="message_type" value="profile_comment" onclick="document.getElementById('MessageSubmit').value = '{#Publish#}';">{sprintf(#AddComment#, $NAME)}
</div>
<p><textarea rows="7" cols="64" name="message"></textarea></p>
<p><input id="MessageSubmit" type="submit" name="MessageSubmit" value="{#Send#}" /></p>
</form>
</div>
</div>
</div>
<!-- User content -->
<div class="grid_5 omega">
<div class="sidebar_border"></div>
<div id="sidebar">
<div class="border_top"></div>
<div class="sidebar_content">
{if $PROFILE_SELF}
<!-- {{counter name=section}|romanize}. edit profile, account, photos -->
<h2>{#EditMyPage#}</h2>
<ul>
<li><a href="{get_url('who','edit','profile')}">{#EditProfile#}</a></li>
<li><a href="{get_url('settings','perso')}">{#EditAccount#}</a></li>
- <li><a href="{get_url('who','edit','photos')}">{if $PICS}{#ManagePhotos#}{else}{#AddPhoto#}{/if}</a></li>
+ <li><a href="{get_url('who','edit','photos')}">{if isset($PICS)}{#ManagePhotos#}{else}{#AddPhoto#}{/if}</a></li>
</ul>
{/if}
<!-- {{counter name=section}|romanize}. sidebar placeholder/explanation -->
<h2>Sidebar</h2>
<p>Here will be published new art submission, request/offers post it, external content imported, etc.</p>
</div>
<div class="border_bottom"></div>
</div>
</div>
{if $PROFILE_COMMENTS}
<!-- Profile comments -->
<div class="grid_16 alpha omega profile_comments" id="comments" style="margin-bottom: 1em;">
{foreach from=$PROFILE_COMMENTS item=comment}
<div class="comment black">
<div class="profile_comments_text"><p>{$comment->text|nl2br}</p></div>
<div class="profile_comments_info">-- <a href="{get_url('who')}/{$comment->author}">{$comment->authorname}</a>, {get_hypership_time($comment->date)}</div>
</div>
{/foreach}
</div>
{/if}
diff --git a/skins/zed/profile_edit.tpl b/skins/zed/profile_edit.tpl
index 595b91c..4d3b91a 100644
--- a/skins/zed/profile_edit.tpl
+++ b/skins/zed/profile_edit.tpl
@@ -1,59 +1,59 @@
<!-- Calls dojo -->
<script src="/js/dojo/dojo/dojo.js" type="text/javascript"
djConfig="isDebug: false, parseOnLoad: true"></script>
<script type="text/javascript">
dojo.require("dijit.form.Form");
dojo.require("dijit.form.CheckBox");
dojo.require("dijit.form.Button");
function SetWidgetFont(id, font) {
//TODO: document.getElementById(id).style.font = font;
}
</script>
<!-- Edit profile -->
<div class="grid_11 alpha profile">
<div class="profile_id clearfix">
<h1 class="profile_nick" id="UserLongname">{$USERNAME}</h1>
</div>
<div class="profile_separator"></div>
<div class="profile_text">
<form action="" method="post">
<input type="hidden" name="EditProfile" value="1" />
<h2>{#ProfileTextTitle#}</h2>
<textarea style="font-family: Calibri" id="ProfileText" rows="16" cols="72" name="text" class="text">{$PROFILE_TEXT}</textarea><br />
<div class="row" style="background-color: white; color: black;">
<span>{#ProfileFont#}{#_t#}</span>
<input type="radio" name="fixedwidth" id="fixedwidthNo" value="0" dojoType="dijit.form.RadioButton" {if !$PROFILE_FIXEDWIDTH}checked{/if} onclick="SetWidgetFont('ProfileText', 'Calibri')" />
<label for="fixedwidthNo"><span style="font-family: Calibri, Arial; font-weight: 100; font-size: 1.25em; color: black;">{#Calibri#}</span></label>
<input type="radio" name="fixedwidth" id="fixedwidthYes" value="1" dojoType="dijit.form.RadioButton" {if $PROFILE_FIXEDWIDTH}checked={/if} onclick="SetWidgetFont('ProfileText', 'FixedSys')" />
<label for="fixedwidthYes"><span style='font-family: "Fixedsys Excelsior 3.01", Fixedsys, Fixed; font-weight: 100; color: black;'>{#FixedSys#}</span></label>
</div>
<div class="row">
<button dojoType="dijit.form.Button" iconClass="dijitEditorIcon dijitEditorIconSave" type=submit onclick="document.forms[0].submit()">
{#SaveProfile#}
</button>
<noscript>
<input type="submit" value="{#SaveProfile#} {#JavaScriptSafeMessage#}" />
</noscript>
</div>
</form>
</div>
</div>
<!-- Faerie content -->
<div class="grid_5 omega">
<div class="sidebar_border"></div>
<div id="sidebar">
<div class="border_top"></div>
<div class="sidebar_content">
<h2>{#EditMyPage#}</h2>
<ul>
<li>{#EditProfile#}</li>
<li><a href="{get_url('settings','perso')}">{#EditAccount#}</a></li>
- <li><a href="{get_url('who')}/edit/photos">{if $PICS}{#ManagePhotos#}{else}{#AddPhoto#}{/if}</a></li>
+ <li><a href="{get_url('who')}/edit/photos">{if isset($PICS)}{#ManagePhotos#}{else}{#AddPhoto#}{/if}</a></li>
</ul>
</div>
<div class="border_bottom"></div>
</div>
- </div>
\ No newline at end of file
+ </div>
diff --git a/skins/zed/profile_photo.tpl b/skins/zed/profile_photo.tpl
index 34dbe5f..4d140bd 100644
--- a/skins/zed/profile_photo.tpl
+++ b/skins/zed/profile_photo.tpl
@@ -1,50 +1,50 @@
<!-- Add a photo -->
<div class="grid_11 alpha profile">
<div class="profile_id clearfix">
<h1 class="profile_nick" id="UserLongname">{$USERNAME}</h1>
</div>
<div class="profile_separator"></div>
<div class="profile_text">
<h2>{#AddPhotoToProfile#}</h2>
<form name="PhotoUpload" method="post" enctype="multipart/form-data">
<p>{#AddPhotoExplanations#}</p>
<p><label>Photo{#_t#}</label> <input type="file" name="photo" /></p>
<p><label>{#ShortDescription#}{#_t#}</label> <input type="text" maxlength="63" size="32" name="description" /></p>
<p><input type="checkbox" name="SafeForWork" id="SafeForWork" value="0" /> <label for="SafeForWork">{#SafeForWorkLabel#}</label></p>
<p><input type="submit" value="{#Upload#}" /></p>
</form>
</div>
</div>
<div class="grid_5 omega">
<div class="sidebar_border"></div>
<div id="sidebar" style="min-height: inherit;">
<div class="border_top"></div>
<div class="sidebar_content">
<h2>{#EditMyPage#}</h2>
<ul>
<li><a href="{get_url('who')}/edit/profile">{#EditProfile#}</a></li>
<li><a href="{get_url('settings','perso')}">{#EditAccount#}</a></li>
- <li>{if $PICS}{#ManagePhotos#}{else}{#AddPhoto#}{/if}</li>
+ <li>{if isset($PICS)}{#ManagePhotos#}{else}{#AddPhoto#}{/if}</li>
</ul>
</div>
<div class="border_bottom"></div>
</div>
</div>
-{if $PICS}
+{if isset($PICS)}
<!-- Manage current photos -->
<div class="grid_16 alpha omega profile_comments">
<h2>{#ManageCurrentPhotos#}</h2>
<div class="photos">
{foreach from=$PICS item=photo}
<div class="photo" style="float: left">
<a rel="lightbox" href="{$URL_PICS}/{$photo->name}" title="{$photo->description}"><img src="{$URL_PICS}/tn/{$photo->name}" alt="{$photo->description}" /></a>
<br />
<a href="{get_url('who')}/edit/photos/edit/{$photo->id}" title="{#EditPictureProperties#}"><img src="/skins/VacuumCleanerBridge/images/open.gif" alt="{#PictureProperties#}"></a>
<a href="{get_url('who')}/edit/photos/delete/{$photo->id}" title="{#DeleteThisPicture#}"><img src="/skins/VacuumCleanerBridge/images/del.gif" alt="{#Delete#}"></a>
</div>
{/foreach}
</div>
{/if}
-</div>
\ No newline at end of file
+</div>
diff --git a/skins/zed/smartline_results.tpl b/skins/zed/smartline_results.tpl
index b539520..22d6378 100644
--- a/skins/zed/smartline_results.tpl
+++ b/skins/zed/smartline_results.tpl
@@ -1,13 +1,12 @@
<!-- SmartLine output -->
<div id="SmartLineResults" class="black">
-{if $SmartLine_STDOUT}
+{if isset($SmartLine_STDOUT)}
<p>{$SmartLine_STDOUT}</p>
{/if}
-{if $SmartLine_STDERR}
+{if isset($SmartLine_STDERR)}
<p class="error">{$SmartLine_STDERR}</p>
{/if}
</div>
<p class="clear"></p>
-

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 5, 04:37 (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
21176
Default Alt Text
(108 KB)

Event Timeline