diff --git a/controllers/profile.php b/controllers/profile.php index d0826db..42f42cc 100644 --- a/controllers/profile.php +++ b/controllers/profile.php @@ -1,344 +1,345 @@ 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/ edits a photo properties, * /who/edit/photo/delete/ deletes a photo, * /who/edit/photo/avatar/ 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 * @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']) { $profile->load_from_form(); $profile->updated = time(); $profile->save_to_database(); $mode = 'view'; } elseif ($_POST['UserAccount']) { $smarty->assign('WAP', "This form have been deprecated. You can write instead settings in the SmartLine"); } elseif ($_POST['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') { //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']) { #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($errors, '
')); } } elseif ($_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); } 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('NAME', $perso->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'; $template = 'profile_edit.tpl'; break; case 'account': $smarty->assign('user', $CurrentUser); $smarty->assign('DIJIT', true); $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]) { 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]) { $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]) { $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/. To edit it /edit/"); break; } if (!$template) { $photos = ProfilePhoto::get_photos($profile->perso_id); - if (!$smarty->tpl_vars['NOTIFY']) + if (!$smarty->tpl_vars['NOTIFY']) { $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); } //Serves header $css[] = THEME . "/profile.css"; $smarty->assign('PAGE_CSS', $css); $smarty->assign('PAGE_TITLE', $perso->name); include('header.php'); //Serves content if ($template) $smarty->display($template); //Serves footer include('footer.php'); diff --git a/includes/content/location.php b/includes/content/location.php index 05ed461..9a25e53 100755 --- a/includes/content/location.php +++ b/includes/content/location.php @@ -1,228 +1,231 @@ * @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 */ /** * Content location class * * This class maps the content_locations table. * * A content location is defined by 3 parameters: * - location_global * - location_local * - location_k, an index for the content at the specified location * * This class allows to get or set the content_id at this * (global, local, k) location. * * This class also provides a static helper method to * get local content from a specific location. */ class ContentLocation { /* ------------------------------------------------------------- Properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ public $location_global = null; public $location_local = null; public $location_k = null; public $content_id; /* ------------------------------------------------------------- Constructor, __toString - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Initializes a new ContentLocation instance * * @param string $location_global the global location * @param string $location_local the local location * @param int $location_k the item indice for the specified location */ function __construct ($location_global = null, $location_local = null, $location_k = null) { $this->location_global = $location_global; $this->location_local = $location_local; if ($location_k) { $this->location_k = $location_k; $this->load_from_database(); } else { $this->location_k = self::get_free_location_k($location_global, $location_local); } } /** * Returns a string representation of current Content instance * * @return string the content title or path if title is blank. */ function __toString () { $location_global = $this->location_global ? $this->location_global : '?'; $location_local = $this->location_local ? $this->location_local : '?'; $location_k = $this->location_k ? $this->location_k : '?'; return "($location_global, $location_local, $location_k)"; } /* ------------------------------------------------------------- Load/save class - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Loads the object ContentLocation (ie fill the properties) from the database */ function load_from_database () { global $db; $location_global = "'" . $db->sql_escape($this->location_global) . "'"; $location_local = "'" . $db->sql_escape($this->location_local) . "'"; $location_k = "'" . $db->sql_escape($this->location_k) . "'"; $sql = "SELECT * FROM content_locations WHERE location_global = '$location_global' AND location_local = '$location_local' AND location_k = '$location_k'"; if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query content", '', __LINE__, __FILE__, $sql); if (!$row = $db->sql_fetchrow($result)) { $this->lastError = "Content location unknown: " . $this->content_id; return false; } $this->load_from_row($row); return true; } /** * Loads the object from row */ function load_from_row ($row) { $this->content_id = $row['content_id']; $this->location_global = $row['location_global']; $this->location_local = $row['location_local']; $this->location_k = $row['location_k']; } /** * Saves to database */ function save_to_database () { global $db; $location_global = "'" . $db->sql_escape($this->location_global) . "'"; $location_local = "'" . $db->sql_escape($this->location_local) . "'"; $location_k = "'" . $db->sql_escape($this->location_k) . "'"; $content_id = $this->content_id ? "'" . $db->sql_escape($this->content_id) . "'" : 'NULL'; $sql = "REPLACE INTO content_locations (location_global, location_local, location_k, content_id) VALUES ($location_global, $location_local, $location_k, $content_id)"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't save content location", '', __LINE__, __FILE__, $sql); + } } /* ------------------------------------------------------------- Helper methods - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Gets the next k free value for the specified location * * @param string $location_global the global location * @param string $location_local the local location * * @param int $location_k the next free local content indice */ function get_free_location_k ($location_global, $location_local) { $location_global = "'" . $db->sql_escape($location_global) . "'"; $location_local = "'" . $db->sql_escape($location_local) . "'"; $sql = "SELECT MAX(location_k) + 1 FROM content_locations WHERE location_global = '$location_global' AND location_local = '$location_local'"; - if (!$result = $db->sql_query($sql)) + if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't get content location k", '', __LINE__, __FILE__, $sql); + } $row = $db->sql_fetchrow($result); return $row[0]; } /** * Deletes this content location from the database */ function delete() { $location_global = "'" . $db->sql_escape($this->location_global) . "'"; $location_local = "'" . $db->sql_escape($this->location_local) . "'"; $location_k = "'" . $db->sql_escape($this->location_k) . "'"; $sql = "DELETE FROM content_locations WHERE location_global = '$location_global' AND location_local = '$location_local' AND location_k = '$location_k' LIMIT 1"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't delete current content location", '', __LINE__, __FILE__, $sql); + } } /** * Moves the content into new location * * @param string $location_global the target global location * @param string $location_local the target local location * @param int $location_k the target local content indice [facultative] */ function move ($location_global, $location_local, $location_k = null) { if ($this->content_id) { $this->delete(); } if ($location_k) { $this->location_k = $location_k; } else { $this->location_k = self::get_free_location_k($location_global, $location_local); } if ($this->content_id) { $this->save_to_database(); } } /* ------------------------------------------------------------- Gets content - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Gets content at specified location * * @param string $location_global global content location * @param string $location_local local content location * @return Array array of ContentFile instances * * The returned array indices are the local_k. */ static function get_local_content ($location_global, $location_local) { global $db; //Get contents at this location $location_global = $db->sql_escape($location_global); $location_local = $db->sql_escape($location_local); $sql = "SELECT c.* FROM content c WHERE c.location_global = '$location_global' AND c.location_local = '$location_local' ORDER BY location_k ASC"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't get content", '', __LINE__, __FILE__, $sql); } //Fills content array $contents = []; while ($row = $db->sql_fetchrow($result)) { $k = $row['location_k']; $contents[$k] = new ContentFile(); $contents[$k]->load_from_row($row); } return $contents; } } diff --git a/includes/core.php b/includes/core.php index 858003f..0e95e94 100755 --- a/includes/core.php +++ b/includes/core.php @@ -1,664 +1,672 @@ * @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 */ //////////////////////////////////////////////////////////////////////////////// /// /// /// Configures PHP and loads site-wide used libraries /// /// /// //////////////////////////////////////////////////////////////////////////////// error_reporting(E_ALL & ~E_NOTICE); include_once("config.php"); include_once("error.php"); include_once("db/Database.php"); $db = Database::load(); Database::cleanupConfiguration(); include_once("sessions.php"); include_once("autoload.php"); //////////////////////////////////////////////////////////////////////////////// /// /// /// Information helper methods /// /// /// //////////////////////////////////////////////////////////////////////////////// /** * Gets the nickname from the specified perso ID * * @param integer $perso_id The specified perso ID * @return string The perso's nickname */ function get_name ($perso_id) { global $db; $perso_id = $db->sql_escape($perso_id); $sql = 'SELECT perso_nickname FROM '. TABLE_PERSOS . " WHERE perso_id = '$perso_id'"; if (!$result = $db->sql_query($sql)) message_die(SQL_ERROR, "Can't query persos table.", '', __LINE__, __FILE__, $sql); $row = $db->sql_fetchrow($result); return $row['perso_nickname']; } /** * Gets the user ID from the specified username * * @param string $username The username * @return integer the user ID */ function get_userid ($username) { global $db; $username = $db->sql_escape($username); $sql = 'SELECT user_id FROM '. TABLE_USERS . " WHERE username LIKE '$username'"; if (!$result = $db->sql_query($sql)) message_die(SQL_ERROR, "Can't query users table.", '', __LINE__, __FILE__, $sql); $row = $db->sql_fetchrow($result); return $row['user_id']; } /** * Gets an information from the application global registry * * @param string $key the registry's key * @return string The key value */ function registry_get ($key) { global $db; $key = $db->sql_escape($key); $sql = "SELECT registry_value FROM " . TABLE_REGISTRY . " WHERE registry_key = '$key'"; if (!$result = $db->sql_query($sql)) message_die(SQL_ERROR, "Can't read registry.", '', __LINE__, __FILE__, $sql); $row = $db->sql_fetchrow($result); return $row['registry_value']; } /** * Sets an information in the application global registry * * @param string $key the registry key * @param string $value the value to store at the specified registry key */ function registry_set ($key, $value) { global $db; $key = $db->sql_escape($key); $value = $db->sql_escape($value); $sql = "REPLACE INTO " . TABLE_REGISTRY . " (registry_key, registry_value) VALUES ('$key', '$value')"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't update registry", '', __LINE__, __FILE__, $sql); + } } //////////////////////////////////////////////////////////////////////////////// /// /// /// Misc helper methods /// /// /// //////////////////////////////////////////////////////////////////////////////// /** * Generates a random string, according the specified format. * * * echo generate_random_string('AAA111'); //this could output SDQ245. * * * @author Pierre Habart * * @param string $format The format e.g. AAA111 * @return string a random string */ function generate_random_string ($format) { mt_srand((double)microtime()*1000000); $str_to_return=""; $t_alphabet=explode(",", "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"); $t_number=explode(",", "1,2,3,4,5,6,7,8,9,0"); for ($i=0;$i= 2 || $amount <= -2) return "s"; } /** * Returns "x" when the $amount request a plural * This function is a French plural helper. * * @param $amount the amount of objects * @return string 'x' if $amount implies a plural ; '' if it implies a singular. */ function x ($amount) { if ($amount >= 2 || $amount <= -2) return "x"; } //Debug /** * Prints human-readable information about a variable. * * It behaves like the print_r command, but the output is enclosed in pre tags, * to have a preformatted HTML output. * * @param mixed $expression The expression to be printed */ function dprint_r ($expression) { echo '
';
     print_r($expression);
     echo '
'; } //GUID /** * Generates a GUID, or more precisely an UUID * @link http://en.wikipedia.org/wiki/Universally_Unique_Identifier Wikipedia, Universally Unique Identifier. * * A UUID is a 36 chars string of 32 hexadecimal and 4 dashes, with a * very high probability to be unique. * * @return string the UUID */ function new_guid() { $characters = explode(",", "a,b,c,d,e,f,0,1,2,3,4,5,6,7,8,9"); $guid = ""; for ($i = 0 ; $i < 36 ; $i++) { if ($i == 8 || $i == 13 || $i == 18 || $i == 23) { $guid .= "-"; } else { $guid .= $characters[mt_rand() % sizeof($characters)]; } } return $guid; } /** * Determines if the expression is a valid UUID (a guid without {}). * @see new_guid * * @param string $expression the expression to check * @return boolean true if the specified expression is a valid UUID ; otherwise, false. */ function is_guid ($expression) { //We avoid regexp to speed up the check //A guid is a 36 characters string if (strlen($expression) != 36) return false; $expression = strtolower($expression); for ($i = 0 ; $i < 36 ; $i++) { if ($i == 8 || $i == 13 || $i == 18 || $i == 23) { //with dashes if ($expression[$i] != "-") return false; } else { //and numbers if (!is_numeric($expression[$i]) && $expression[$i] != 'a' && $expression[$i] != 'b' && $expression[$i] != 'c' && $expression[$i] != 'd' && $expression[$i] != 'e' && $expression[$i] != 'f' ) return false; } } return true; } /** * Gets file extension * * @param string $file the file to get the extension * @return string the extension from the specified file */ function get_extension ($file) { $dotPosition = strrpos($file, "."); return substr($file, $dotPosition + 1); } /** * Determines if a string starts with specified substring * * @param string $haystack the string to check * @param string $needle the substring to determines if it's the start * @param boolean $case_sensitive determines if the search must be case sensitive * @return boolean true if $haystack starts with $needle ; otherwise, false. */ function string_starts_with ($haystack, $needle, $case_sensitive = true) { if (!$case_sensitive) { $haystack = strtoupper($haystack); $needle = strtoupper($needle); } if ($haystack == $needle) return true; return strpos($haystack, $needle) === 0; } /** * Inserts a message into the supralog * * @param string $category the entry category * @param string $message the message to log * @param string $source the entry source. */ function supralog ($category, $message, $source = null) { global $db, $CurrentUser, $CurrentPerso; $category = $db->sql_query_express($category); $message = $db->sql_query_express($message); $source = $db->sql_query_express($source ? $source : $_SERVER['SERVER_ADDR']); $ip = $_SERVER['REMOTE_ADDR']; $sql = "INSERT INTO " . TABLE_LOG . " (entry_ip, user_id, perso_id, entry_category, entry_message, entry_source) VALUES ('$ip', $CurrentUser->id, $CurrentPerso->id, '$category', '$message', '$source')"; - if ( !($result = $db->sql_query($sql)) ) + if ( !($result = $db->sql_query($sql)) ) { message_die(SQL_ERROR, "Can't log this entry.", '', __LINE__, __FILE__, $sql); + } } //////////////////////////////////////////////////////////////////////////////// /// /// /// Localization (l10n) /// /// /// //////////////////////////////////////////////////////////////////////////////// /** * Defines the LANG constant, to lang to print * * This information is contained in the session, or if not yet defined, * it's to determine according the user's browser preferences. * @see find_lang */ function initialize_lang () { //If $_SESSION['lang'] doesn't exist yet, find a common language if (!array_key_exists('lang', $_SESSION)) { $lang = find_lang(); $_SESSION['lang'] = $lang ? $lang : '-'; } - if ($_SESSION['lang'] != '-') + if ($_SESSION['lang'] != '-') { define('LANG', $_SESSION['lang']); + } } /** * Gets a common lang spoken by the site and the user's browser * @see get_http_accept_languages * * @return string the language */ function find_lang () { if (file_exists('lang') && is_dir('lang')) { //Gets lang/ subdirectories: this is the list of available languages $handle = opendir('lang'); while ($file = readdir($handle)) { if ($file != '.' && $file != '..' && is_dir("lang/$file")) { $langs[] = $file; } } //The array $langs contains now the language available. //Gets the langs the user should want: - if (!$userlangs = get_http_accept_languages()) + if (!$userlangs = get_http_accept_languages()) { return; + } //Gets the intersection between the both languages arrays //If it matches, returns first result $intersect = array_intersect($userlangs, $langs); if (count($intersect)) { return $intersect[0]; } //Now it's okay with Opera and Firefox but Internet Explorer will //by default return en-US and not en or fr-BE and not fr, so second pass foreach ($userlangs as $userlang) { $lang = explode('-', $userlang); - if (count($lang) > 1) + if (count($lang) > 1) { $userlangs2[] = $lang[0]; + } } $intersect = array_intersect($userlangs2, $langs); if (count($intersect)) { return $intersect[0]; } } } /** * Gets the languages accepted by the browser, by order of priority. * * This will read the HTTP_ACCEPT_LANGUAGE variable sent by the browser in the * HTTP request. * * @return Array an array of string, each item a language accepted by browser */ function get_http_accept_languages () { //What language to print is sent by browser in HTTP_ACCEPT_LANGUAGE var. //This will be something like en,fr;q=0.8,fr-fr;q=0.5,en-us;q=0.3 if (!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) { return null; } $http_accept_language = explode(',', $_SERVER["HTTP_ACCEPT_LANGUAGE"]); foreach ($http_accept_language as $language) { $userlang = explode(';q=', $language); if (count($userlang) == 1) { $userlangs[] = [1, $language]; } else { $userlangs[] = [$userlang[1], $userlang[0]]; } } rsort($userlangs); foreach ($userlangs as $userlang) { $result[] = $userlang[1]; } return $result; } /** * Loads specified language Smarty configuration file * * @param string $file the file to load * @param mixed $sections array of section names, single section or null */ function lang_load ($file, $sections = null) { global $smarty; //Loads English file as fallback if some parameters are missing - if (file_exists("lang/en/$file")) + if (file_exists("lang/en/$file")) { $smarty->configLoad("lang/en/$file", $sections); + } //Loads wanted file (if it exists and a language have been defined) - if (defined('LANG') && LANG != 'en' && file_exists('lang/' . LANG . '/' . $file)) + if (defined('LANG') && LANG != 'en' && file_exists('lang/' . LANG . '/' . $file)) { $smarty->configLoad('lang/' . LANG . '/' . $file, $sections); + } } /** * Gets a specified language expression defined in configuration file * * @param string $key the configuration key matching the value to get * @return string The value in the configuration file */ function lang_get ($key) { global $smarty; $smartyConfValue = $smarty->config_vars[$key]; return $smartyConfValue ? $smartyConfValue : "#$key#"; } //////////////////////////////////////////////////////////////////////////////// /// /// /// Zed date and time helper methods /// /// /// //////////////////////////////////////////////////////////////////////////////// /** * Converts a YYYYMMDD or YYYY-MM-DD timestamp to unixtime * @link http://en.wikipedia.org/wiki/Unix_time Unix time * * @param string $timestamp the timestamp to convert * @return integer the unixtime */ function to_unixtime ($timestamp) { switch (strlen($timestamp)) { case 8: //YYYYMMDD return mktime(0, 0, 0, substr($timestamp, 4, 2), substr($timestamp, 6, 2), substr($timestamp, 0, 4)); case 10: //YYYY-MM-DD return mktime(0, 0, 0, substr($timestamp, 5, 2), substr($timestamp, 8, 2), substr($timestamp, 0, 4)); default: throw new Exception("timestamp is not a valid YYYYMMDD or YYYY-MM-DD timestamp: $timestamp"); } } /** * Converts a unixtime to the YYYYMMDD or YYYY-MM-DD timestamp format * @see to_unixtime * * @param int $unixtime the time to convert * @param int $format 8 or 10. If 8 (default), will output YYYYMMDD. If 10, YYYY-MM-DD. * @return string the timestamp */ function to_timestamp ($unixtime = null, $format = 8) { //If no parameter is specified (or null, or false), current time is used //==== allows to_timestamp(0) to return correct 1970-1-1 value. if ($unixtime === null || $unixtime === false) $unixtime = time(); switch ($format) { case 8: //YYYYMMDD return date('Ymd', $unixtime); case 10: //YYYY-MM-DD return date('Y-m-d', $unixtime); default: throw new Exception("format must be 8 (YYYYMMDD) or 10 (YYYY-MM-DD) and not $format."); } } /** * Converts a unixtime to the Hypership time format or gets the current hypership time. * @link http://en.wikipedia.org/wiki/Unix_time * @link http://www.purl.org/NET/Zed/blog/HyperShipTime * * @param int $unixtime The unixtime to convert to HyperShip time. If omitted, the current unixtime. * @return string The HyperShip time */ function get_hypership_time ($unixtime = null) { //If unixtime is not specified, it's now if ($unixtime === null) $unixtime = time(); //Hypership time is a count of days since launch @ 2010-07-03 00:00:00 //Followed by a fraction of the current day /1000, like the internet time //but in UTC timezone and not Switzerland CET/CEST. //We don't need to use floor(), as we output the result at int, truncating //automatically decimal values instead of round it (like in C). $seconds = $unixtime - 1278115200; $days = $seconds / 86400; $fraction = (abs($seconds) % 86400) / 86.4; return sprintf("%d.%03d", $days, $fraction); } //////////////////////////////////////////////////////////////////////////////// /// /// /// URL helpers functions /// /// /// //////////////////////////////////////////////////////////////////////////////// /** * Gets the URL matching the specified resource. * * Example: * * $url = get_url('ship', $ship); * echo $url; //if $ship contains S00001, this should print /ship/S00001 * * * @param string $resource,... the resources * @return string the URL matching the specified resource */ function get_url () { global $Config; if (func_num_args() > 0) { $pieces = func_get_args(); return $Config['BaseURL'] . '/' . implode('/', $pieces); } elseif ($Config['BaseURL'] == "" || $Config['BaseURL'] == $_SERVER["PHP_SELF"]) { return "/"; } else { return $Config['BaseURL']; } } /** * Gets the current page URL * * @return string the current page URL */ function get_page_url () { $url = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO']; if (substr($url, -10) == $_SERVER["PHP_SELF"]) { return substr($url, 0, -9); } return $url; } /** * Gets the server URL * @todo find a way to detect https:// on non standard port * * @return string the server URL */ function get_server_url () { switch ($port = $_SERVER['SERVER_PORT']) { case '80': return "http://$_SERVER[SERVER_NAME]"; case '443': return "https://$_SERVER[SERVER_NAME]"; default: return "http://$_SERVER[SERVER_NAME]:$_SERVER[SERVER_PORT]"; } } /** * Gets $_SERVER['PATH_INFO'] or computes the equivalent if not defined. * * This function allows the entry point controllers to get the current URL * in a consistent way, for any redirection configuration * * So with /foo/bar, /index.php/foo/bar, /zed/index.php/foo/bar or /zed/foo/bar * get_current_url will return /foo/bar * * @return string the relevant URL part */ function get_current_url () { global $Config; //Gets relevant URL part from relevant $_SERVER variables if (array_key_exists('PATH_INFO', $_SERVER)) { //Without mod_rewrite, and url like /index.php/controller //we use PATH_INFO. It's the easiest case. return $_SERVER["PATH_INFO"]; } //In other cases, we'll need to get the relevant part of the URL $current_url = get_server_url() . $_SERVER['REQUEST_URI']; //Relevant URL part starts after the site URL $len = strlen($Config['SiteURL']); //We need to assert it's the correct site if (substr($current_url, 0, $len) != $Config['SiteURL']) { dieprint_r(GENERAL_ERROR, "Edit includes/config.php and specify the correct site URL
Current value: $Config[SiteURL]
Expected value: a string starting by " . get_server_url(), "Setup"); } if (array_key_exists('REDIRECT_URL', $_SERVER)) { //With mod_rewrite, we can use REDIRECT_URL //We takes the end of the URL, ie *FROM* $len position return substr(get_server_url() . $_SERVER["REDIRECT_URL"], $len); } //Last possibility: use REQUEST_URI, but remove QUERY_STRING //If you need to edit here, use $_SERVER['REQUEST_URI'] //but you need to discard $_SERVER['QUERY_STRING'] //We takes the end of the URL, ie *FROM* $len position $url = substr(get_server_url() . $_SERVER["REQUEST_URI"], $len); //But if there are a query string (?action=... we need to discard it) if ($_SERVER['QUERY_STRING']) { return substr($url, 0, strlen($url) - strlen($_SERVER['QUERY_STRING']) - 1); } return $url; } /** * Gets an array of url fragments to be processed by controller * @see get_current_url * * This method is used by the controllers entry points to know the URL and * call relevant subcontrollers. * * @return Array an array of string, one for each URL fragment */ function get_current_url_fragments () { $url_source = get_current_url(); if ($url_source == $_SERVER["PHP_SELF"]) return []; return explode('/', substr($url_source, 1)); } //////////////////////////////////////////////////////////////////////////////// /// /// /// URL xmlHttpRequest helpers functions /// /// /// //////////////////////////////////////////////////////////////////////////////// /** * Gets an hash value to check the integrity of URLs in /do.php calls * * @param Array $args the args to compute the hash * @return the hash parameter for your xmlHttpRequest url */ function get_xhr_hash ($args) { global $Config; array_shift($args); return md5($_SESSION['ID'] . $Config['SecretKey'] . implode('', $args)); } /** * Gets the URL to call do.php, the xmlHttpRequest controller * * @return string the xmlHttpRequest url, with an integrity hash */ function get_xhr_hashed_url () { global $Config; $args = func_get_args(); $args[] = get_xhr_hash($args); return $Config['DoURL'] . '/' . implode('/', $args); } /** * Gets the URL to call do.php, the xmlHttpRequest controller * * @return string the xmlHttpRequest url */ function get_xhr_url () { global $Config; $args = func_get_args(); return $Config['DoURL'] . '/' .implode('/', $args); } diff --git a/includes/error.php b/includes/error.php index 577f5b7..2f3b0a4 100755 --- a/includes/error.php +++ b/includes/error.php @@ -1,195 +1,197 @@ * @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 delete old_message_die method and write alternative HTML textual output * in the message_die method */ /// /// Error constants /// /** * SQL_ERROR is the constant meaning the error is a SQL error. * * As a message_die function parameter, it allows to add SQL specific debug information. */ define ("SQL_ERROR", 65); /** * HACK_ERROR is the constant meaning access is non authorized to the resource. * * It encompasses two problematics: * the URL points to a resource belonging to another user or for the current user have no access right (for malformed URL, pick instead GENERAL_ERROR) ; * the user is anonymous, instead to be logged in. * * A suggested way to handle the second problematic is to store in hidden input * fields or better in the session the previous form data, and to print a login * form. * * If you implement this, you don't even need to distinguishes between the two * cases, as once logged in, the regular HACK_ERROR could also be printed. */ define ("HACK_ERROR", 99); /** * GENERAL_ERROR is the constant meaning the error is general, ie not covered by * another more specific error constant. */ define ("GENERAL_ERROR", 117); /// /// Error helper functions /// /** * Output a general error, with human-readable information about the specified * expression as error message ; terminates the current script. * * @see message_die * * @param mixed $expression the expression to be printed * @param string $title the message title (optional, default will be 'Debug') */ function dieprint_r ($expression, $title = '') { if (!$title) { $title = 'Debug'; //if title is omitted or false/null, default title } message_die(GENERAL_ERROR, '
' . print_r($expression, true) .'
', $title); } /** * Outputs an error message and terminates the current script. * * Error will be output through Smarty one of the following templates : * error_block.tpl if the header have already been printed ; * error.tpl if the error occurred before the header were called and printed. * * If smarty couldn't be loaded, old_message_die method will be called, which * produces a table output. * * @param int $msg_code an integer constant identifying the error (HACK_ERROR, SQL_ERROR, GENERAL_ERROR) * @param string $msg_text the error message text (optional, but recommended) * @param string $msg_title the error message title (optional) * @param int $err_line the line number of the file where the error occurred (optional, suggested value is __LINE__) * @param string $err_line the path of file where the error occurred (optional, suggested value is __FILE__) * @param string $sql the SQL query (optional, used only if msg_code is SQL_ERROR) */ function message_die ($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '') { global $smarty, $db; if ($smarty) { $debug_text = $msg_text; - if ($err_line && $err_file) + if ($err_line && $err_file) { $debug_text .= ' — ' . $err_file. ', ' . lang_get('line') . ' ' . $err_line ; + } switch ($msg_code) { case HACK_ERROR: $smarty->assign('TITLE', lang_get('UnauthorizedAccess')); break; case SQL_ERROR: $smarty->assign('TITLE', lang_get('SQLError')); $sql_error = $db->sql_error(); if ($sql_error['message'] != '') { $debug_text .= '
' . lang_get('Error') . ' n° ' . $sql_error['code'] . lang_get('_t') . ' ' .$sql_error['message']; } $debug_text .= "

Query:

$sql"; break; default: $smarty->assign('WAP', "Message code error.
Expected: HACK_ERROR, SQL_ERROR, GENERAL_ERROR"); //Falls to GENERAL_ERROR case GENERAL_ERROR: - if ($msg_title) + if ($msg_title) { $smarty->assign('TITLE', $msg_title); - else + } else { $smarty->assign('TITLE', lang_get('GeneralError')); + } break; } $smarty->assign('ERROR_TEXT', $debug_text); $template = (defined('HEADER_PRINTED') && HEADER_PRINTED) ? "error_block.tpl" : "error.tpl"; $smarty->display($template); exit; } else { old_message_die($msg_code, $msg_text, $msg_title, $err_line, $err_file, $sql); } } /** * Outputs an error message and terminates the current script. * * This is the message_die method from Espace Win, used on Zed as fallback if Smarty isn't initialized yet. * Former "german style" error HTML markups have been removed. * * @param int $msg_code an integer constant identifying the error (HACK_ERROR, SQL_ERROR, GENERAL_ERROR) * @param string $msg_text the error message text (optional, but recommended) * @param string $msg_title the error message title (optional) * @param int $err_line the line number of the file where the error occurred (optional, suggested value is __LINE__) * @param string $err_line the path of file where the error occurred (optional, suggested value is __FILE__) * @param string $sql the SQL query (optional, used only if msg_code is SQL_ERROR) * * @deprecated since 0.1 */ function old_message_die($msg_code, $msg_text = '', $msg_title = '', $err_line = '', $err_file = '', $sql = '') { global $db, $Utilisateur; $sql_store = $sql; if ($msg_code == HACK_ERROR && $Utilisateur[user_id] < 1000) { die("You must be logged in to access to this resource."); } elseif ($msg_code == HACK_ERROR) { $title = "You aren't allowed to access this resource."; $debug_text = $msg_text; } elseif ($msg_code == SQL_ERROR) { $title = "SQL error"; $sql_error = $db->sql_error(); $debug_text = $msg_text; if ($err_line != '' && $err_file != '') { $debug_text .= ' in ' . $err_file. ', line ' . $err_line ; } if ($sql_error['message'] != '') { $debug_text .= '
Error #' . $sql_error['code'] . ': ' . $sql_error['message']; } if ($sql_store != '') { $debug_text .= "
$sql_store"; } } elseif ($msg_code == GENERAL_ERROR) { $title = $msg_title; $debug_text = $msg_text; if ($err_line && $err_file) { $debug_text .= "
$err_file, line $err_line"; } } echo '

'; echo $title; echo '

'; echo $debug_text; echo '

* @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('body.php'); require_once('place.php'); require_once('point3D.php'); require_once('includes/objects/ship.php'); /** * Geo location class * * This class contains properties to get, set or compare a location and * explore the geo classes linked to. * * It quickly allow to parse through the location classes in templates and * controllers. * * @todo Initialize $point3D from $body or $ship own locations * @todo Improve GeoLocation documentation (especially magic properties) */ class GeoLocation { /** * An array of strings containing location data. * * In the current class implementation, * the first element is the global location * and the second element is the local location. * * @var Array */ private $data; /** * A body object * * It contains a GeoBody value when the global location is a body * ie if $this->data[0][0] == 'B' * * Otherwise, its value is null. * * @var GeoBody */ public $body = null; /** * A place object * * It contains a GeoPlacevalue when the global location is a place * ie if $this->data[0][0] == 'B' && strlen($this->data[0]) == 9 * * Otherwise, its value is null. * * @var GeoPlace */ public $place = null; /** * A point identified by x, y, z coordinates * * @var GeoPoint3D */ public $point3D = null; /** * A ship object * * It contains a Ship value when the global location is a ship * ie if $this->data[0][0] == 'S' * * Otherwise, its value is null. * * @var Ship */ public $ship = null; /** * Initializes a new location instance * * @param string $global the global location * @param string local the locallocation * * @todo Improve local location handling */ function __construct ($global = null, $local = null) { if (!$global) { $this->data = []; } elseif (preg_match("/[BS][0-9]{5}[0-9]{3}/", $global)) { $this->data[0] = $global; } elseif (preg_match("/[BS][0-9]{5}/", $global)) { $this->data[0] = $global; } elseif (preg_match("/^xyz\:/", $global)) { $coords = sscanf($global, "xyz: [%d, %d, %d]"); if (count($coords) == 3) { $this->data[0] = $global; } else { throw new Exception("Invalid expression: $global"); } } else { global $db; $name = $db->sql_escape($global); $sql = "SELECT location_code FROM " . TABLE_LOCATIONS . " WHERE location_name LIKE '$name'"; $code = $db->sql_query_express($sql); if ($code) { $this->data[0] = $code; return; } throw new Exception("Invalid expression: $global"); } //TODO: handle $local in a better way: from the global location, gets //a local location handler. Or a some inheritance, like a class //HypershipGeoLocation extending GeoLocation. if ($local !== null) $this->data[1] = $local; $this->load_classes(); } /** * Gets $place, $body and $ship instances if they're needed */ function load_classes () { //No data, no class to load - if (!count($this->data)) + if (!count($this->data)) { return; + } //Loads global classes $global = $this->data[0]; $code = substr($global, 1, 5); switch ($global[0]) { case 'B': switch (strlen($global)) { case 9: $this->place = GeoPlace::from_code($global); case 6: $this->body = new GeoBody($code); break; } break; case 'S': $this->ship = new Ship($code); break; case 'x': $coords = sscanf($global, "xyz: [%d, %d, %d]"); if (count($coords) == 3) { $this->point3D = new GeoPoint3D($coords[0], $coords[1], $coords[2]); } break; } } /** * Magic method called when a unknown property is get. * * Handles $global, $local, $type, $body_code, $ship_code, $place_code, * $body_kind, $containsGlobalLocation, $containsLocalLocation. */ function __get ($variable) { switch ($variable) { /* main variables */ case 'global': return $this->data[0]; break; case 'local': return (count($this->data) > 1) ? $this->data[1] : null; break; /* global location */ case 'type': return $this->data[0][0]; case 'body_code': if ($this->data[0][0] == 'B') { return substr($this->data[0], 1, 5); } return null; case 'place_code': if ($this->data[0][0] == 'B') { return substr($this->data[0], 6, 3); } return null; case 'ship_code': if ($this->data[0][0] == 'S') { return substr($this->data[0], 1, 5); } return null; case 'body_kind': if ($this->data[0][0] == 'B' && $this->body != null) { if ($kind = $this->body->kind()) { return $kind; } } elseif ($this->data[0][0] == 'S') { return 'ship'; } return 'place'; case 'containsGlobalLocation': return count($this->data) > 0; case 'containsLocalLocation': return count($this->data) > 1; default: throw new Exception("Unknown variable: $variable"); break; } } /** * Checks if the place exists * * @return bool true if the place exists ; otherwise, false. * * @todo Handle alias */ function exists () { $n = count($this->data); //If not defined, it doesn't exist if ($n == 0) return false; //Checks global location switch ($this->data[0][0]) { case 'B': switch (strlen($this->data[0])) { case 9: - if (!$place = GeoPlace::from_code($this->data[0])) + if (!$place = GeoPlace::from_code($this->data[0])) { return false; + } break; case 6: $body = new GeoBody(substr($this->data[0], 1)); if ($body->lastError) return false; break; default: message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__); } break; case 'S': $ship = new Ship(substr($this->data[0], 1)); if ($body->lastError) return false; break; default: message_die(GENERAL_ERROR, "Invalid global location expression size: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__); return false; } if ($n > 1) { if (!isset($place)) { message_die(GENERAL_ERROR, "Can't check if a local place exists for the following location: " . $this->data[0], "GeoLocation exists method", __LINE__, __FILE__); } if (!$place->is_valid_local_location($this->data[1])) { return false; } } return true; } /** * Checks if the place is equals at the specified expression or place * * @return bool true if the places are equals ; otherwise, false. * * @todo Create a better set of rules to define when 2 locations are equal. */ function equals ($expression) { //Are global location equals? //TODO: create a better set of rules to define when 2 locations are equal. if (is_a($expression, 'GeoLocation')) { if (!$this->equals($expression->data[0])) { return false; } if (count($expression->data) + count($this->data) > 2) { return $expression->data[1] == $this->data[1]; } } if ($expression == $this->data[0]) return true; $n1 = strlen($expression); $n2 = strlen($this->data[0]); if ($n1 > $n2) { return substr($expression, 0, $n2) == $this->data[0]; } return false; } /** * Represents the current location instance as a string * * @return string a string representing the current location */ function __toString () { - if (!$this->data[0]) + if (!$this->data[0]) { return ""; + } switch ($this->data[0][0]) { case 'S': $ship = new Ship($this->ship_code); $location[] = $ship->name; break; case 'B': $body = new GeoBody($this->body_code); $location[] = $body->name ? $body->name : lang_get('UnknownBody'); if (strlen($this->data[0]) == 9) { $place = GeoPlace::from_code($this->data[0]); $location[] = $place->name ? $place->name : lang_get('UnknownPlace'); } break; case 'x': $pt = $this->point3D->toSpherical(); return sprintf("(%d, %d°, %d°)", $pt[0], $pt[1], $pt[2]); default: message_die(GENERAL_ERROR, "Unknown location identifier: $type.
Expected: B or S."); } return implode(", ", array_reverse($location)); } /** * Magic method called when a unknown property is set. * * Handles $global, $local, $type, $body_code, $ship_code, $place_code */ function __set ($variable, $value) { switch ($variable) { /* main variables */ case 'global': $this->data[0] = $value; break; case 'local': $this->data[1] = $value; break; /* global location */ case 'type': if ($value == 'B' || $value == 'S') { if (!$this->data[0]) { $this->data[0] = $value; } else { $this->data[0][0] = $value; } } break; case 'body_code': if (preg_match("/[0-9]{1,5}/", $value)) { $value = sprintf("%05d", $value); if (!$this->data[0]) { $this->data[0] = "B" . $value; return; } elseif ($this->data[0][0] == 'B') { $this->data[0] = "B" . $value . substr($this->data[0], 6); return; } throw new Exception("Global location isn't a body."); } throw new Exception("$value isn't a valid body code"); case 'ship_code': if (preg_match("/[0-9]{1,5}/", $value)) { $value = sprintf("%05d", $value); if (!$this->data[0]) { $this->data[0] = "S" . $value; return; } elseif ($this->data[0][0] == 'S') { $this->data[0] = "S" . $value . substr($this->data[0], 6); return; } throw new Exception("Global location isn't a ship."); } throw new Exception("$value isn't a valid ship code"); case 'place_code': if (!preg_match("/[0-9]{1,3}/", $value)) { throw new Exception("$value isn't a valid place code"); } $value = sprintf("%03d", $value); if ($this->data[0][0] == 'B') { $this->data[0] = substr($this->data[0], 0, 6) . $value; } throw new Exception("Global location isn't a body."); default: throw new Exception("Unknown variable: $variable"); break; } } } diff --git a/includes/objects/content.php b/includes/objects/content.php index b501776..74916dc 100755 --- a/includes/objects/content.php +++ b/includes/objects/content.php @@ -1,302 +1,303 @@ * @copyright 2010 Sébastien Santoro aka Dereckson * @license http://www.opensource.org/licenses/bsd-license.php BSD * @version 0.1 * @link http://scherzo.dereckson.be/doc/zed * @link http://zed.dereckson.be/ * @filesource * * @deprecated */ /** * Content class * * This class maps the content view. * * This view shows the content_files and content_locations tables. * * This class also provides helper methods, to handle files, generate thumbnails * or get local content from a specific location. * * [DESIGN BY CONTRACT] This class works only with the following assertions: * i. Each content have EXACTLY ONE location * ii. Location fields will not be modified * * If a content have more than one location, only the first occurrence in * content_locations table will be considered. * * If a content have no location, it will be ignored. * * If you edit content location, then call saveToDatabase, you will create * a new location but future instances will contain first not deleted location. * * @todo remove dbc temporary limitations (cf. /do.php upload_content and infra) * @todo create a class ContentLocation and move location fields there * @todo validate SQL schema and add in config.php TABLE_CONTENT tables * * @deprecated */ class Content { /* ------------------------------------------------------------- Properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ public $id; public $path; public $user_id; public $perso_id; public $title; public $location_global = null; public $location_local = null; public $location_k = null; public $perso_name; public $perso_nickname; /* ------------------------------------------------------------- Constructor, __toString - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Initializes a new Content instance * * @param int $id the primary key */ function __construct ($id = null) { if ($id) { $this->id = $id; $this->load_from_database(); } } /** * Returns a string representation of current Content instance * * @return string the content title or path if title is blank. */ function __toString () { return $this->title ? $this->title : $this->path; } /* ------------------------------------------------------------- Load/save class - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Loads the object Content (ie fill the properties) from the $_POST array * * @param boolean $allowSensibleFields if false, allow only location_local, location_k and title to be defined ; otherwise, allow all fields. */ function load_from_form ($allowSensibleFields = false) { if (array_key_exists('title', $_POST)) $this->title = $_POST['title']; if (array_key_exists('location_local', $_POST)) $this->location_local = $_POST['location_local']; if (array_key_exists('location_k', $_POST)) $this->location_k = $_POST['location_k']; if ($allowSensibleFields) { if (array_key_exists('path', $_POST)) $this->path = $_POST['path']; if (array_key_exists('user_id', $_POST)) $this->user_id = $_POST['user_id']; if (array_key_exists('perso_id', $_POST)) $this->perso_id = $_POST['perso_id']; if (array_key_exists('location_global', $_POST)) $this->location_global = $_POST['location_global']; } } /** * Loads the object Content (ie fill the properties) from the database */ function load_from_database () { global $db; $id = $db->sql_escape($this->id); $sql = "SELECT * FROM content WHERE content_id = '" . $id . "'"; if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query content", '', __LINE__, __FILE__, $sql); if (!$row = $db->sql_fetchrow($result)) { $this->lastError = "Content unknown: " . $this->id; return false; } $this->load_from_row($row); return true; } /** * Loads the object from row */ function load_from_row ($row) { $this->id = $row['content_id']; $this->path = $row['content_path']; $this->user_id = $row['user_id']; $this->perso_id = $row['perso_id']; $this->title = $row['content_title']; $this->location_global = $row['location_global']; $this->location_local = $row['location_local']; $this->location_k = $row['location_k']; if (array_key_exists('perso_name', $row)) $this->perso_name = $row['perso_name']; if (array_key_exists('perso_nickname', $row)) $this->perso_nickname = $row['perso_nickname']; } /** * Saves to database */ function save_to_database () { global $db; $id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL'; $path = $db->sql_escape($this->path); $user_id = $db->sql_escape($this->user_id); $perso_id = $db->sql_escape($this->perso_id); $title = $db->sql_escape($this->title); $location_global = ($this->location_global !== null) ? "'" . $db->sql_escape($this->location_global) . "'" : 'NULL'; $location_local = ($this->location_local !== null) ? "'" . $db->sql_escape($this->location_local) . "'" : 'NULL'; $location_k = ($this->location_k !== null) ? "'" . $db->sql_escape($this->location_k) . "'" : 'NULL'; //Updates or inserts $sql = "REPLACE INTO content_files (`content_id`, `content_path`, `user_id`, `perso_id`, `content_title`) VALUES ($id, '$path', '$user_id', '$perso_id', '$title')"; if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't save content", '', __LINE__, __FILE__, $sql); } if (!$this->id) { //Gets new record id value $this->id = $db->sql_nextid(); } //Saves location $id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL'; $sql = "REPLACE INTO content_locations (location_global, location_local, location_k, content_id) VALUES ($location_global, $location_local, $location_k, $id)"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't save content location", '', __LINE__, __FILE__, $sql); + } } /* ------------------------------------------------------------- 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) { switch ($ext = strtolower($ext)) { //Pictures case 'jpg': case 'gif': case 'png': case 'bmp': case 'xbm': return true; //Denied extension default: return false; } } /** * Creates a directory * * @param string $dir the directory to create */ function create_directory ($directory) { if (!file_exists($directory)) { @mkdir($directory); //Creates new directory, chmod 777 } } /** * Handles uploaded file * * @return bool true if the file have been handled */ function handle_uploaded_file ($fileArray) { if (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); } /* ------------------------------------------------------------- Gets content - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /** * Gets content at specified location * * @param string $location_global global content location * @param string $location_local local content location * @return Array array of Content instances */ static function get_local_content ($location_global, $location_local) { global $db; //Get contents at this location $location_global = $db->sql_escape($location_global); $location_local = $db->sql_escape($location_local); $sql = "SELECT c.*, p.perso_nickname, p.perso_name FROM content c, persos p WHERE c.location_global = '$location_global' AND c.location_local = '$location_local' AND p.perso_id = c.perso_id ORDER BY location_k ASC"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't get content", '', __LINE__, __FILE__, $sql); } //Fills content array $contents = []; while ($row = $db->sql_fetchrow($result)) { $content = new Content(); $content->load_from_row($row); $contents[] = $content; } return $contents; } } diff --git a/includes/objects/perso.php b/includes/objects/perso.php index 8884bcc..71a5fee 100755 --- a/includes/objects/perso.php +++ b/includes/objects/perso.php @@ -1,579 +1,584 @@ * @copyright 2010, 2012 Sébastien Santoro aka Dereckson * @license http://www.opensource.org/licenses/bsd-license.php BSD * @version 0.1 * @link http://scherzo.dereckson.be/doc/zed * @link http://zed.dereckson.be/ * @filesource */ require_once("includes/geo/location.php"); /** * Perso class * * This class maps the persos table. * * The class also provides methods * to move or locate a perso, * to gets and sets perso's flags and notes (tables persos_flags and persos_notes), * to gets user's perso or check if a perso is online, * to handle on select and logout events. * */ class Perso { public $id; public $user_id; public $name; public $nickname; public $race; public $sex; public $avatar; public $location_global; public $location_local; public $flags; public $lastError; public static $hashtable_id = []; public static $hashtable_name = []; /** * Initializes a new instance * * @param mixed $data perso ID or nickname */ function __construct ($data = null) { if ($data) { if (is_numeric($data)) { $this->id = $data; } else { $this->nickname = $data; } if (!$this->load_from_database()) { message_die(GENERAL_ERROR, $this->lastError, "Can't authenticate perso"); } } else { $this->generate_id(); } } /** * Initializes a new Perso instance if needed or get already available one. * * @param mixed $data perso ID or nickname * @return Perso the perso instance */ static function get ($data = null) { if ($data) { //Checks in the hashtables if we already have loaded this instance if (is_numeric($data)) { if (array_key_exists($data, Perso::$hashtable_id)) { return Perso::$hashtable_id[$data]; } } else { if (array_key_exists($data, Perso::$hashtable_name)) { return Perso::$hashtable_name[$data]; } } } $perso = new Perso($data); return $perso; } /** * Loads the object Perso (ie fill the properties) from the $_POST array */ function load_from_form () { if (array_key_exists('user_id', $_POST)) $this->user_id = $_POST['user_id']; if (array_key_exists('name', $_POST)) $this->name = $_POST['name']; if (array_key_exists('nickname', $_POST)) $this->nickname = $_POST['nickname']; if (array_key_exists('race', $_POST)) $this->race = $_POST['race']; if (array_key_exists('sex', $_POST)) $this->sex = $_POST['sex']; if (array_key_exists('avatar', $_POST)) $this->avatar = $_POST['avatar']; if (array_key_exists('location_global', $_POST)) $this->location_global = $_POST['location_global']; if (array_key_exists('location_local', $_POST)) $this->location_local = $_POST['location_local']; } /** * Loads the object Perso (ie fill the properties) from the database */ function load_from_database () { global $db; //Gets perso $sql = "SELECT * FROM " . TABLE_PERSOS; if ($this->id) { $id = $db->sql_escape($this->id); $sql .= " WHERE perso_id = '" . $id . "'"; } else { $nickname = $db->sql_escape($this->nickname); $sql .= " WHERE perso_nickname = '" . $nickname . "'"; } if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query persos", '', __LINE__, __FILE__, $sql); if (!$row = $db->sql_fetchrow($result)) { $this->lastError = "Perso unknown: " . $this->id; return false; } $this->id = $row['perso_id']; $this->user_id = $row['user_id']; $this->name = $row['perso_name']; $this->nickname = $row['perso_nickname']; $this->race = $row['perso_race']; $this->sex = $row['perso_sex']; $this->avatar = $row['perso_avatar']; $this->location_global = $row['location_global']; $this->location_local = $row['location_local']; //Gets flags $sql = "SELECT flag_key, flag_value FROM " . TABLE_PERSOS_FLAGS . " WHERE perso_id = $this->id"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't get flags", '', __LINE__, __FILE__, $sql); } while ($row = $db->sql_fetchrow($result)) { $this->flags[$row["flag_key"]] = $row["flag_value"]; } //Gets location $this->location = new GeoLocation( $this->location_global, $this->location_local ); //Puts object in hashtables Perso::$hashtable_id[$this->id] = $this; Perso::$hashtable_name[$this->nickname] = $this; return true; } /** * Saves to database */ function save_to_database () { global $db; $id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL'; $user_id = $db->sql_escape($this->user_id); $name = $db->sql_escape($this->name); $nickname = $db->sql_escape($this->nickname); $race = $db->sql_escape($this->race); $sex = $db->sql_escape($this->sex); $avatar = $db->sql_escape($this->avatar); $location_global = $this->location_global ? "'" . $db->sql_escape($this->location_global) . "'" : 'NULL'; $location_local = $this->location_local ? "'" . $db->sql_escape($this->location_local) . "'" : 'NULL'; //Updates or inserts $sql = "REPLACE INTO " . TABLE_PERSOS . " (`perso_id`, `user_id`, `perso_name`, `perso_nickname`, `perso_race`, `perso_sex`, `perso_avatar`, `location_global`, `location_local`) VALUES ($id, '$user_id', '$name', '$nickname', '$race', '$sex', '$avatar', $location_global, $location_local)"; if (!$db->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(); } } /** * Updates the specified field in the database record * * @param string $field The field to save */ function save_field ($field) { global $db; if (!$this->id) { message_die(GENERAL_ERROR, "You're trying to update a perso record not yet saved in the database: $field"); } $id = $db->sql_escape($this->id); $value = $db->sql_escape($this->$field); $sql = "UPDATE " . TABLE_PERSOS . " SET `$field` = '$value' WHERE perso_id = '$id'"; if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Unable to save $field field", '', __LINE__, __FILE__, $sql); } } /** * Gets perso location * * @return string The location names */ public function where () { return $this->location->__toString(); } /** * Moves the perso to a new location * * @param string $global the global target location * @param string $global the local target location */ public function move_to ($global = null, $local = null) { //Sets global location if ($global != null) { $this->location_global = $global; } //Sets local location if ($local != null) { $this->location_local = $local; } //Updates database record if ($global != null && $local != null) { global $db; $perso_id = $db->sql_escape($this->id); $g = $db->sql_escape($this->location_global); $l = $db->sql_escape($this->location_local); $sql = "UPDATE " . TABLE_PERSOS . " SET location_global = '$g', location_local = '$l'" . " WHERE perso_id = '$perso_id'"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't save new $global $local location.", '', __LINE__, __FILE__, $sql); + } } elseif ($global != null) { $this->save_field('location_global'); } elseif ($local != null) { $this->save_field('location_local'); } //Updates location member $this->location = new GeoLocation( $this->location_global, $this->location_local ); } /** * Gets the specified flag value * * @param string $key flag key * @param mixed $defaultValue default value if the flag doesn't exist * @return mixed the flag value (string) or null if not existing */ public function get_flag ($key, $defaultValue = null) { return $this->flag_exists($key) ? $this->flags[$key] : $defaultValue; } /** * Determines if the specified flag exists * * @param string $key the flag key to check * @return boolean true if the specified flag exists ; otherwise, false. */ public function flag_exists ($key) { return array_key_exists($key, $this->flags); } /** * Sets the specified flag * * @param string $key flag key * @param string $value flag value (optional, default value: 1) */ public function set_flag ($key, $value = 1) { //Checks if flag isn't already set at this value - if ($this->flags != null && array_key_exists($key, $this->flags) && $this->flags[$key] === $value) + if ($this->flags != null && array_key_exists($key, $this->flags) && $this->flags[$key] === $value) { return; + } //Saves flag to database global $db; $id = $db->sql_escape($this->id); $key = $db->sql_escape($key); $value = $db->sql_escape($value); $sql = "REPLACE " . TABLE_PERSOS_FLAGS . " SET perso_id = '$id', flag_key = '$key', flag_value = '$value'"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't save flag", '', __LINE__, __FILE__, $sql); + } //Sets flag in this perso instance $this->flags[$key] = $value; } /** * Deletes the specified flag * * @param string $key flag key */ public function delete_flag ($key) { global $db; if (!array_key_exists($key, $this->flags)) return; $id = $db->sql_escape($this->id); $key = $db->sql_escape($key); $sql = "DELETE FROM " . TABLE_PERSOS_FLAGS . " WHERE flag_key = '$key' AND perso_id = '$id' LIMIT 1"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't delete flag", '', __LINE__, __FILE__, $sql); + } } /** * Ensures the current perso have the specified flag or exits. * * * @param string $flag the flag to assert * @param int $threshold value the flags must strictly be greater than (optional, the default value is 0) * * Example: * * $perso->set_flag('quux.foo', 1); * //The perso wants to read quux, which we allow with the flag quux.foo * $perso->request_flag('quux.foo'); //will be okay * * //The perso wants also to write quux, which we all allow if quux.foo = 2 * //The threshold will so be 1, as 2 > 1 * $perso->request_flag('quux.foo', 1); //Will exits, with a "You don't have quux.foo permission" message * */ public function request_flag ($flag, $threshold = 0) { if (!array_key_exists($flag, $this->flags) || $this->flags[$flag] <= $threshold) { message_die(HACK_ERROR, "You don't have $flag permission.", "Permissions"); } } /** * Gets the specified note * * @param string $code the note code * @return string the note content */ public function get_note ($code) { global $db; $id = $db->sql_escape($this->id); $code = $db->sql_escape($code); $sql = "SELECT note_text FROM " . TABLE_PERSOS_NOTES . " WHERE perso_id = '$id' AND note_code LIKE '$code'"; return $db->sql_query_express($sql); } /** * Sets the specified note * * @param string $code the note code * @param string $text the note content */ public function set_note ($code, $text) { global $db; $id = $db->sql_escape($this->id); $code = $db->sql_escape($code); $text = $db->sql_escape($text); $sql = "REPLACE INTO " . TABLE_PERSOS_NOTES . " (perso_id, note_code, note_text) VALUES ('$id', '$code', '$text')"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't save note", '', __LINE__, __FILE__, $sql); + } } /** * Counts the amount of notes the perso have saved * * @return int the amount of notes assigned to the this perso */ public function count_notes () { global $db; $id = $db->sql_escape($this->id); $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS_NOTES . " WHERE perso_id = '$id'"; return $db->sql_query_express($sql); } /* * Determines if the specified ID is available * * @param integer $id The perso ID to check * @return boolean true if the specified ID is available ; otherwise, false */ public static function is_available_id ($id) { global $db; $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE perso_id = $id LOCK IN SHARE MODE"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't access users table", '', __LINE__, __FILE__, $sql); } $row = $db->sql_fetchrow($result); return ($row[0] == 0); } /** * Generates a unique ID for the current object */ private function generate_id () { do { $this->id = rand(2001, 5999); } while (!Perso::is_available_id($this->id)); } /** * Checks if the nickname is available * * @param string $nickname the nickname to check */ public static function is_available_nickname ($nickname) { global $db; $nickname = $db->sql_escape($nickname); $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE perso_nickname LIKE '$nickname' LOCK IN SHARE MODE;"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql); } $row = $db->sql_fetchrow($result); return ($row[0] == 0); } /** * Counts the perso a user have * * @param int user_id the user ID * @return the user's perso count */ public static function get_persos_count ($user_id) { global $db; $sql = "SELECT COUNT(*) FROM " . TABLE_PERSOS . " WHERE user_id = $user_id"; return $db->sql_query_express($sql); } /** * Gets an array with all the perso of the specified user * * @param int $user_id the user ID */ public static function get_persos ($user_id) { global $db; $user_id = $db->sql_escape($user_id); $sql = "SELECT perso_id FROM " . TABLE_PERSOS . " WHERE user_id = $user_id"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't get persos", '', __LINE__, __FILE__, $sql); } while ($row = $db->sql_fetchrow($result)) { $persos[] = Perso::get($row[perso_id]); } return $persos; } /** * Gets the first perso a user have * (typically to be used when get_persos_count returns 1 to autoselect) * * @param int user_id the user ID */ public static function get_first_perso ($user_id) { global $db; $sql = "SELECT perso_id FROM " . TABLE_PERSOS ." WHERE user_id = $user_id LIMIT 1"; if ($perso_id = $db->sql_query_express($sql)) { return new Perso($perso_id); } } /** * Determines whether the perso is online * * @return bool true if the perso is online ; otherwise, false. */ public function is_online () { global $db; $id = $db->sql_escape($this->id); $sql = "SELECT MAX(online) FROM " . TABLE_SESSIONS ." WHERE perso_id = $id"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Unable to query the table", '', __LINE__, __FILE__, $sql); } $row = $db->sql_fetchrow($result); return ($row[0] == 1); } /** * This event method is called when the user selects a new perso */ public function on_select () { //Session set_info('perso_id', $this->id); $this->set_flag("site.lastlogin", $_SERVER['REQUEST_TIME']); define("PersoSelected", true); } /** * This event method is called when the user logs off its account or perso */ public function on_logout () { //Clears perso information in $_SESSION and session table set_info('perso_id', null); clean_session(); } /** * This event method is called when the perso is created */ public function on_create () { //Notifies host $this->notify_inviter(); } /** * Creates a new perso, from a parameter form * * @param int $user The user to attach the perso to * @param Perso $perso A reference to the created perso (don't initialize it, give it a null value) * @param array $errors A reference to the arrays containing errors (should be an empty array, or the method will always return false) * @return boolean true if the perso has ben created ; otherwise, false */ public static function create_perso_from_form ($user, &$perso, &$errors) { $perso = new Perso(); $perso->load_from_form(); $perso->user_id = $user->id; //Validates forms if (!$perso->name) $errors[] = lang_get("NoFullnameSpecified"); if (!$perso->race) { $errors[] = lang_get("NoRaceSpecified"); $perso->race = "being"; } if (!$perso->sex) $errors[] = lang_get("NoSexSpecified"); if (!$perso->nickname) { $errors[] = lang_get("NoNicknameSpecified"); } elseif (!Perso::is_available_nickname($perso->nickname)) { $errors[] = lang_get("UnavailableNickname"); } if (count($errors)) return false; //Creates perso $perso->save_to_database(); $perso->on_create(); return true; } /** * Notifies the person having invited this perso */ public function notify_inviter() { require_once('includes/objects/message.php'); require_once('includes/objects/invite.php'); $message = new Message(); $message->from = 0; $message->to = invite::who_invited($this->id); $message->text = sprintf( lang_get('InvitePersoCreated'), $this->name, get_server_url() . get_url('who', $this->nickname) ); $message->send(); } } diff --git a/includes/objects/ship.php b/includes/objects/ship.php index bb9ca42..d808746 100755 --- a/includes/objects/ship.php +++ b/includes/objects/ship.php @@ -1,325 +1,326 @@ * @copyright 2010 Sébastien Santoro aka Dereckson * @license http://www.opensource.org/licenses/bsd-license.php BSD * @version 0.1 * @link http://scherzo.dereckson.be/doc/zed * @link http://zed.dereckson.be/ * @filesource */ require_once("perso.php"); require_once("includes/geo/location.php"); /** * 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 { /* * ----------------------------------------------------------------------- * * Ship class definition * ----------------------------------------------------------------------- * */ public $id; public $name; public $location_global; public $location_local; public $api_key; public $description; private static $hashtable = []; /** * Initializes a new instance * * @param int $id the primary key */ function __construct ($id = null) { 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 */ static function get ($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]; } } $ship = new Ship($data); return $ship; } /** * Loads the object Ship (ie fill the properties) from the $_POST array */ function load_from_form () { if (array_key_exists('name', $_POST)) $this->name = $_POST['name']; if (array_key_exists('location_global', $_POST)) $this->location = $_POST['location_global']; if (array_key_exists('location_local', $_POST)) $this->location = $_POST['location_local']; if (array_key_exists('api_key', $_POST)) $this->api_key = $_POST['api_key']; if (array_key_exists('description', $_POST)) $this->description = $_POST['description']; } /** * Loads the object Ship (ie fill the properties) from the database */ function load_from_database () { global $db; $id = $db->sql_escape($this->id); $sql = "SELECT * FROM ships WHERE ship_id = '" . $id . "'"; if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query Ships", '', __LINE__, __FILE__, $sql); if (!$row = $db->sql_fetchrow($result)) { $this->lastError = "Ship unknown: " . $this->id; return false; } $this->name = $row['ship_name']; $this->location_global = $row['location_global']; $this->location_local = $row['location_local']; $this->api_key = $row['api_key']; $this->description = $row['ship_description']; //Puts object in hashtable self::$hashtable[$this->id] = $this; return true; } /** * Saves to database */ function save_to_database () { global $db; $id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL'; $name = $db->sql_escape($this->name); $location_global = $db->sql_escape($this->location_global); $location_local = $db->sql_escape($this->location_local); $api_key = $db->sql_escape($this->api_key); $description = $db->sql_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->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(); } } /** * Gets the ship code, as a string representation of the instance. * * @return the ship's code */ function __toString () { return $this->get_code(); } /** * Get ships at specified location * * @param string $location_global global location * @param string $location_local local location * @return array An array of Ship items, each one a ship at the specified location */ static function get_ships_at ($location_global, $location_local = null) { global $db; //Gets ships $sql = "SELECT ship_id, location_global, location_local FROM " . TABLE_SHIPS . " WHERE location_global IS NOT NULL"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't get ships", '', __LINE__, __FILE__, $sql); } $ships = []; $location = new GeoLocation($location_global, $location_local); while ($row = $db->sql_fetchrow($result)) { $shipLocation = new GeoLocation($row['location_global'], $row['location_local']); if ($location->equals($shipLocation)) { $ships[] = self::get($row['ship_id']); } } return $ships; } /* * ----------------------------------------------------------------------- * * Helper methods * ----------------------------------------------------------------------- * */ /** * Gets ship code, e.g. S00001 * * @return string the ship code */ function get_code () { return sprintf("S%05d", $this->id); } /** * Determines if the ship is at a spatioport (or assimilated) * * @return bool true if the ship is at a spatioport ; false if the ship is in space */ function in_spatioport () { return $this->location_local !== null; } /** * Flies in the sip * * @param string $location_local the spatioport location * * @todo Completes location global e.g. B00001 -> B00001003 */ function fly_in ($location_local = null) { $this->location_local = ($location_local == null) ? 0 : $location_local; } /** * Flies out. */ function fly_out () { $this->location_local = null; } /* * ----------------------------------------------------------------------- * * Ship API methods * ----------------------------------------------------------------------- * */ /** * Requests the specified perso to authenticate to this ship * * @param mixed $perso_data the perso id or name */ function request_perso_authenticate ($perso_data) { $perso = Perso::get($perso_data); $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) { $flag = sprintf("api.ship.auth.%s", $this->get_code()); return Perso::get($perso_data)->flags[$flag] == 1; } /** * Requests the specified perso to confirm the ship API session * * @param string $session_id a session ID provided by calling application * @param mixed $perso_data the perso id or name */ function request_perso_confirm_session ($session_id, $perso_data) { $perso = Perso::get($perso_data); $flag = sprintf("request.api.ship.session.%s.%s", $this->get_code(), $session_id); $perso->set_flag($flag); $perso->set_flag("site.requests"); } /** * Cleans ship API temporary sessions */ static function clean_ship_sessions () { //Cleans old sessions global $db; $sql = "DELETE FROM " . TABLE_REGISTRY . " WHERE registry_key LIKE 'api.ship.session.%' AND registry_updated < NOW() - 7200"; - if (!$db->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't delete old ship API sessions", '', __LINE__, __FILE__, $sql); + } } /** * Gets perso id from specified session * * @param string $session_id a session ID provided by calling application * @return mixed the session */ function get_perso_from_session ($session_id) { //Cleans old session self::clean_ship_sessions(); //Reads api.ship.session.S00001.$session_id //This registry key contains perso_id if it exists and valid $key = sprintf("api.ship.session.%s.%s", $this->get_code(), $session_id); return registry_get($key); } /** * 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->sql_escape($key); $sql = "SELECT * FROM ships WHERE api_key = '" . $key . "'"; if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query ships", '', __LINE__, __FILE__, $sql); if (!$row = $db->sql_fetchrow($result)) return null; //Fills ship information $ship = new Ship(); $ship->id = $row['ship_id']; $ship->name = $row['ship_name']; $ship->location = $row['ship_location']; $ship->api_key = $row['api_key']; $ship->description = $row['ship_description']; return $ship; } } diff --git a/includes/objects/user.php b/includes/objects/user.php index da12797..80b8276 100755 --- a/includes/objects/user.php +++ b/includes/objects/user.php @@ -1,244 +1,246 @@ * @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 */ /** * 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 { public $id; public $name; public $password; public $active = 0; public $actkey; public $email; public $regdate; public static $hashtable_id = []; public static $hashtable_name = []; /** * Initializes a new instance * * @param int $id the primary key */ function __construct ($id = null) { if ($id) { $this->id = $id; $this->load_from_database(); } } /** * Initializes a new User instance if needed or get already available one. * * @param mixed $data user ID or name * @return User the user instance */ static function get ($data = null) { if ($data) { //Checks in the hashtables if we already have loaded this instance if (is_numeric($data)) { if (array_key_exists($data, User::$hashtable_id)) { return User::$hashtable_id[$data]; } } else { if (array_key_exists($data, User::$hashtable_name)) { return User::$hashtable_name[$data]; } } } $user = new User($data); return $user; } /** * Loads the object User (ie fill the properties) from the $_POST array */ function load_from_form () { if (array_key_exists('name', $_POST)) $this->name = $_POST['name']; if (array_key_exists('password', $_POST)) $this->password = $_POST['password']; if (array_key_exists('active', $_POST)) $this->active = $_POST['active']; if (array_key_exists('actkey', $_POST)) $this->actkey = $_POST['actkey']; if (array_key_exists('email', $_POST)) $this->email = $_POST['email']; if (array_key_exists('regdate', $_POST)) $this->regdate = $_POST['regdate']; } /** * Loads the object User (ie fill the properties) from the database */ function load_from_database () { global $db; $sql = "SELECT * FROM " . TABLE_USERS . " WHERE user_id = '" . $this->id . "'"; if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Unable to query users", '', __LINE__, __FILE__, $sql); if (!$row = $db->sql_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 Perso::$hashtable_id[$this->id] = $this; Perso::$hashtable_name[$this->name] = $this; return true; } /** * Saves to database */ function save_to_database () { global $db; $id = $this->id ? "'" . $db->sql_escape($this->id) . "'" : 'NULL'; $name = $db->sql_escape($this->name); $password = $db->sql_escape($this->password); $active = $db->sql_escape($this->active); $actkey = $db->sql_escape($this->actkey); $email = $db->sql_escape($this->email); $regdate = $this->regdate ? "'" . $db->sql_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->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(); } } /** * Updates the specified field in the database record */ function save_field ($field) { global $db; if (!$this->id) { message_die(GENERAL_ERROR, "You're trying to update a record not yet saved in the database"); } $id = $db->sql_escape($this->id); $value = $db->sql_escape($this->$field); $sql = "UPDATE " . TABLE_USERS . " SET `$field` = '$value' WHERE user_id = '$id'"; if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Unable to save $field field", '', __LINE__, __FILE__, $sql); } } /** * Generates a unique user id */ function generate_id () { global $db; do { $this->id = rand(2001, 5999); $sql = "SELECT COUNT(*) FROM " . TABLE_USERS . " WHERE user_id = $this->id LOCK IN SHARE MODE;"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Can't access users table", '', __LINE__, __FILE__, $sql); } $row = $db->sql_fetchrow($result); } while ($row[0]); } /** * Fills password field with encrypted version of the specified clear password * * @param string $newpassword The user's new password */ public function set_password ($newpassword) { $this->password = md5($newpassword); } /** * Deletes OpenID for this user */ public function delete_OpenID () { $this->set_OpenID(''); } /** * Sets OpenID for this user * * @param string $url OpenID endpoint URL */ public function set_OpenID ($url) { global $db; if (!$this->id) $this->save_to_database(); $url = $db->sql_escape($url); $sql = "DELETE FROM " . TABLE_USERS_AUTH . " WHERE auth_type = 'OpenID' AND user_id = $this->id"; - if (!$db->sql_query($sql)) + if (!$db->sql_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->sql_query($sql)) + if (!$db->sql_query($sql)) { message_die(SQL_ERROR, "Can't add new OpenID", '', __LINE__, __FILE__, $sql); + } } } /** * Checks if a login is available * * @param string $login the login to check * @return bool true if the specified login is available ; otherwise, false. */ public static function is_available_login ($login) { global $db; $sql = "SELECT COUNT(*) FROM " . TABLE_USERS . " WHERE username LIKE '$login' LOCK IN SHARE MODE;"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql); } $row = $db->sql_fetchrow($result); return ($row[0] ? false : true); } /** * Gets username from specified e-mail * * @param string $mail the mail to search * @return string|bool the username matching the mail if found ; otherwise, false. */ public static function get_username_from_email ($mail) { global $db; $sql = "SELECT username FROM " . TABLE_USERS . " WHERE user_email LIKE '$mail' LOCK IN SHARE MODE;"; if (!$result = $db->sql_query($sql)) { message_die(SQL_ERROR, "Utilisateurs non parsable", '', __LINE__, __FILE__, $sql); } if ($row = $db->sql_fetchrow($result)) { return $row['username']; } return false; } } diff --git a/includes/story/section.php b/includes/story/section.php index e04de2d..ea24500 100755 --- a/includes/story/section.php +++ b/includes/story/section.php @@ -1,158 +1,159 @@ * @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('choice.php'); require_once('hook.php'); /** * Story section class * * This class is a PHP mapping from the Story XML format's
tag. * * This class also a method to get the section where a specific choice links to. */ class StorySection { /** * The section ID * * @var string */ public $id; /** * The section title * * @var string */ public $title; /** * The section description * * @var string */ public $description; /** * @var string the local location */ public $location_local; /** * @var Array the section choices (array of StoryChoice items) */ public $choices = []; /* * @var Array the section hooks (array of StoryHook items) */ public $hooks = []; /** * @var boolean if true, it's the story start ; otherwise, false; */ public $start; /** * @var Story the story calling the section */ public $story; /** * Initializes a new instance of StorySection class */ function __construct ($id, $story = null) { $this->id = $id; if ($story !== null) { $this->story = $story; } } /** * Gets choice from specified guid * * @return StoryChoice the wanted choice, or null if it doesn't exist */ function get_choice ($guid) { foreach ($this->choices as $choice) { - if ($choice->guid == $guid) + if ($choice->guid == $guid) { return $choice; + } } return null; } /** * Initializes a story section from an SimpleXMLElement XML fragment * * @param SimpleXMLElement $xml the XML fragment * @param Story $story the calling story * @return StorySection the section instance */ static function from_xml ($xml, $story = null) { //Reads attributes $id = ''; $start = false; foreach ($xml->attributes() as $key => $value) { switch ($key) { case 'start': if ($value) $start = true; break; case 'id': $id = (string)$value; break; default: message_die(GENERAL_ERROR, "Unknown attribute: $key = \"$value\"", "Story error"); } } if (!$id) { message_die(GENERAL_ERROR, "Section without id. Please add id='' in
tag", "Story error"); } $section = new StorySection($id, $story); $section->title = (string)$xml->title; $section->description = (string)$xml->description; $section->location_local = (string)$xml->local; $section->start = $start; //Adds choices if ($xml->choices) { foreach ($xml->choices->choice as $choice) { $section->choices[] = StoryChoice::from_xml($choice); } } //Adds hooks if ($xml->hooks) { foreach ($xml->hooks->hook as $hook) { // will assign 'spatioport' to $hook; $hook = (string)$hook->attributes()->type; require_once("hook_$hook.php"); $section->hooks[] = new $class($section->story, $section); } } return $section; } }