Page MenuHomeCode

No OneTemporary

diff --git a/content/stories/B00003001.xml b/content/stories/B00003001.xml
new file mode 100644
--- /dev/null
+++ b/content/stories/B00003001.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Zeta, Kaos
+Dereckson, 2010-01-31
+
+TODO: It's a start location. So it should provide a way to to reach hypership
+TODO: <section id="spatioport.entry"></section>
+TODO: <section id="mines.entry"></section>
+TODO: .en
+TODO: Écrire les chapitres The hole
+-->
+<story>
+ <title>Zeta, Kaos</title>
+ <section start="true" id="panorama">
+ <title>Panorama</title>
+ <description>
+ Zeta est une ville venteuse, plongée dans la nuit et le bruit.
+
+À la surface travaillent d'honnêtes mineur de cobalt et de davyrium, ceinturant le sud de la ville.
+
+Au nord de la ville, the hole, une ancienne mine dont les mineurs évitent de parler.
+
+À l'est, un petit spatioport.
+ </description>
+ <choices>
+ <choice goto="hole.entry">Se diriger vers le hole</choice>
+ <choice goto="spatioport.entry">Se rendre au spatioport</choice>
+ <choice goto="mines.entry">Vers les mines</choice>
+ </choices>
+ <local>1</local>
+ </section>
+ <section id="hole.entry">
+ <title>The hole</title>
+ <description>
+ The hole. Une ancienne mine désaffectée depuis trois siècles. Aujourd'hui, un squat. L'endroit où tout se passe sur Zeta. Refuge des rebelles. Lieu d'exil de ce secteur de la galaxie. Lieu de tous les traffics. The hole.
+
+ Une bande d'une douzaine jeunes armés de laser et de fouet neuroniques en garde l'entrée, deux vous regardant d'un œil plutôt menaçant.
+
+ Ils vous refusent le passage.
+ </description>
+ <choices>
+ <choice goto="spatioport.entry">Se rendre au spatioport</choice>
+ <choice goto="mines.entry">Vers les mines</choice>
+ </choices>
+ <local>2</local>
+ </section>
+</story>
\ No newline at end of file
diff --git a/controllers/explore.php b/controllers/explore.php
new file mode 100644
--- /dev/null
+++ b/controllers/explore.php
@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * Zed
+ * (c) 2010, Dereckson, some rights reserved
+ * Released under BSD license
+ *
+ * Explore current location
+ * For now, it's a storytelling engine like in livres dont vous êtes le héro
+ */
+
+//
+// Helper method
+//
+
+/*
+ * Gets section to print to the user
+ * @param Story the story the section to get (yep, we could've global $story)
+ * @return StorySection the section, or null if not found
+ */
+function get_section ($story) {
+ global $url, $smarty, $CurrentPerso;
+
+ //If the URL contains a choice guid, use it as story progress source
+ //e.g. /explore/143f7200-766b-7b8b-e3f4-9fbfeeaeb5dd
+ if (count($url) > 1) {
+ $guid = $url[1];
+ if (!$choice = $_SESSION['Story']->get_choice($guid)) {
+ $smarty->assign('WAP', lang_get('InvalidStoryGUID'));
+ }
+
+ //TODO: add code here to handle actions defined in choices
+ //e.g. item added to inventory
+
+ //Gets section
+ if ($section_id = $choice->goto) {
+ if (!array_key_exists($section_id, $story->sections)) {
+ message_die(GENERAL_ERROR, "Choice <em>$choice->text</em> redirects to <em>$section_id</em> but this section doesn't exist.", "Story error");
+ }
+ return $story->sections[$section_id];
+
+ }
+ }
+
+ if (!$CurrentPerso->location_local) {
+ //Gets start section
+ return $story->get_start_section();
+ }
+
+ //Gets section matching perso location
+ return $story->get_section_from_location($CurrentPerso->location_local);
+}
+
+//
+// Opens .xml file
+//
+
+if (!defined(STORIES_DIR)) define('STORIES_DIR', "content/stories");
+
+$file = STORIES_DIR . '/' . $CurrentPerso->location_global . '.xml';
+if (!file_exists($file)) {
+ message_die(GENERAL_ERROR, "If you want to write a story for this place, contact Dereckson — $file", "No story defined");
+}
+
+//
+// Gets story
+//
+
+//Loads story and tries to get the section
+require_once('includes/story/story.php');
+$story = new Story($file);
+$section = get_section($story);
+
+//Ensures we've a section
+if (!$section) {
+ message_die(GENERAL_ERROR, "Nothing to do at this location. Contact Dereckson if you think it's a bug or you want to write a story here.", "Story");
+}
+
+//Performs section actions
+if ($section->location_local) {
+ //Moves perso to section local location
+ $CurrentPerso->move_to(null, $section->location_local);
+}
+
+//Saves section in session, for choices handling
+$_SESSION['Story'] = $section;
+
+//
+// HTML output
+//
+
+//Serves header
+$smarty->assign('PAGE_TITLE', $story->title);
+include('header.php');
+
+//Serves content
+$smarty->assign("section", $section);
+$smarty->display('story.tpl');
+
+//Serves footer
+$smarty->assign('screen', "Story, section $section->id");
+include('footer.php');
+
+?>
\ No newline at end of file
diff --git a/controllers/footer.php b/controllers/footer.php
--- a/controllers/footer.php
+++ b/controllers/footer.php
@@ -1,29 +1,29 @@
<?php
/*
* Zed
* (c) 2010, Dereckson, some rights reserved
* Released under BSD license
*
* Footer
*/
///
/// Tutorials div
///
-if ($CurrentPerso->flags['hypership.reached'] < 1) {
+if ($CurrentPerso->flags['hypership.reached'] < 1 && $controller != 'explore') {
if (!DOJO) $smarty->display('tutorial/dojo.tpl');
lang_load("tutorials.conf", "ReachHypership");
$smarty->display('tutorial/hypership_reach.tpl');
}
///
/// HTML output
///
$smarty->assign('MultiPerso', isset($_SESSION['UserWithSeveralPersos']) && $_SESSION['UserWithSeveralPersos']);
lang_load('footer.conf');
-$smarty->display('footer.tpl');
+$smarty->display('footer.tpl');
?>
\ No newline at end of file
diff --git a/controllers/raw.php b/controllers/raw.php
--- a/controllers/raw.php
+++ b/controllers/raw.php
@@ -1,25 +1,25 @@
<?php
/*
- * Azhàr, faeries intranet
- * (c) 2009-2010, Wolfæym, some rights reserved
+ * Zed
+ * (c) 2010, Dereckson, some rights reserved
* Released under BSD license
*
* Raw text or HTML content
*/
//
// HTML output
//
//Serves header
$smarty->assign('PAGE_TITLE', $title);
include('header.php');
//Serves content
$smarty->display('raw.tpl');
//Serves footer
include('footer.php');
?>
\ No newline at end of file
diff --git a/includes/config.php b/includes/config.php
--- a/includes/config.php
+++ b/includes/config.php
@@ -1,56 +1,62 @@
<?php
/*
* Zed
* (c) 2010, Dereckson, some rights reserved
* Released under BSD license
*
* Autogenerable configuration file
*/
//SQL configuration
$Config['sql']['product'] = 'MySQL';
$Config['sql']['host'] = 'localhost';
$Config['sql']['username'] = 'zed';
$Config['sql']['password'] = 'zed';
$Config['sql']['database'] = 'zed';
//GRANT ALL PRIVILEGES on zed.* TO 'zed'@'localhost' IDENTIFIED by 'zed';
//SQL tables
$prefix = '';
define('TABLE_API_KEYS', $prefix . 'api_keys');
define('TABLE_COMMENTS', $prefix . 'comments');
define('TABLE_LOG_SMARTLINE', $prefix . 'log_smartline');
define('TABLE_MESSAGES', $prefix . 'messages');
define('TABLE_MOTD', $prefix . 'motd');
define('TABLE_PAGES', $prefix . 'pages');
define('TABLE_PAGES_EDITS', $prefix . 'pages_edits');
define('TABLE_PERSOS', $prefix . 'persos');
define('TABLE_PERSOS_FLAGS', $prefix . 'persos_flags');
define('TABLE_PROFILES', $prefix . 'profiles');
define('TABLE_PROFILES_COMMENTS', $prefix . 'profiles_comments');
define('TABLE_PROFILES_PHOTOS', $prefix . 'profiles_photos');
define('TABLE_SESSIONS', $prefix . 'sessions');
define('TABLE_USERS', $prefix . 'users');
define('TABLE_USERS_OPENID', $prefix . 'users_openid');
define('TABLE_BODIES', $prefix . 'geo_bodies');
define('TABLE_LOCATIONS', $prefix . 'geo_locations'); //View
define('TABLE_PLACES', $prefix . 'geo_places');
//Script URL
$Config['BaseURL'] = '/index.php';
//Default theme
$Config['DefaultTheme'] = "Zed";
//Dates
date_default_timezone_set("UTC");
//Sessions
+
//If you want to use a common table of sessions / user handling
//with several websites, specify a different resource id for each site.
$Config['ResourceID'] = 21;
+//PHP variables
+ini_set('session.serialize_handler', 'wddx');
+ini_set('session.save_path', 'cache/sessions');
+ini_set('session.gc_maxlifetime', 345600); //4 days, for week-end story pause and continue url
+
?>
\ No newline at end of file
diff --git a/includes/login.php b/includes/login.php
--- a/includes/login.php
+++ b/includes/login.php
@@ -1,104 +1,106 @@
<?php
/*
* Zed
* (c) 2010, Dereckson, some rights reserved
* Released under BSD license
*
* Login/logout
*/
if (!file_exists('/dev/urandom')) {
//We're on Windows, without reliable source of random numbers
define('Auth_OpenID_RAND_SOURCE', null);
}
require_once('Auth/OpenID/Consumer.php');
require_once('Auth/OpenID/DumbStore.php');
//require_once('Auth/OpenID/FileStore.php');
/*
function get_openid_consumer () {
if (!file_exists('/dev/urandom')) {
//We're on Windows, without reliable source of random numbers
define('Auth_OpenID_RAND_SOURCE', null);
}
$fs = new Auth_OpenID_FileStore('cache/openid');
return new Auth_OpenID_Consumer($fs);
}
*/
function openid_login ($url) {
global $db, $_SESSION, $LoginError, $LoginSuccessful;
$url = $db->sql_escape($url);
$sql = 'SELECT user_id FROM ' . TABLE_USERS_OPENID
. " WHERE openid_url LIKE '$url'";
if ($user_id = $db->sql_query_express($sql)) {
$sql = "UPDATE " . TABLE_SESSIONS . " SET user_id = '$user_id' WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible de procéder à la connexion", '', __LINE__, __FILE__, $sql);
$LoginSuccessful = true;
+ setcookie("LastOpenID", $url, time() + 2592000);
header("location: /");
} else {
$LoginError = "To join Zed, you need an invite. Read the source to get one.";
}
}
if ($_GET['action'] == 'openid.login') {
//Gets Auth_OpenID_Consumer instance
$fs = new Auth_OpenID_DumbStore("rien n'est sûr mais c'est une piste");
//$fs = new Auth_OpenID_FileStore('cache/openid');
$consumer = new Auth_OpenID_Consumer($fs);
//$consumer = get_openid_consumer();
//Completes the OpenID transaction
$reply = $consumer->complete("http://" . $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]);
if ($reply->status == Auth_OpenID_SUCCESS) {
openid_login($reply->endpoint->claimed_id);
} elseif ($reply->message) {
$LoginError = "[OpenID] $reply->message";
} else {
$LoginError = "[OpenID] $reply->status";
}
} elseif ($_POST['LogIn']) {
//User have filled login form
if ($_POST['openid']) {
//Gets Auth_OpenID_Consumer instance
$fs = new Auth_OpenID_DumbStore("rien n'est sûr mais c'est une piste");
//$fs = new Auth_OpenID_FileStore('cache/openid');
$consumer = new Auth_OpenID_Consumer($fs);
//$consumer = get_openid_consumer();
//Starts the OpenID transaction and redirects user to provider url
if ($request = $consumer->begin($_POST['openid'])) {
$url = $request->redirectURL("http://zed.dereckson.be", "http://zed.dereckson.be/?action=openid.login", false);
header("location: $url");
$LoginError = '<a href="' . $url . '">Click here to continue login</a>';
} else {
$LoginError = 'Invalid OpenID URL.';
}
} else {
//GESTION LOGIN
$Login = $_POST['username'];
$sql = "SELECT user_password, user_id FROM " . TABLE_USERS . " WHERE username = '$Login'";
if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Impossible d'interroger le listing des utilisateurs", '', __LINE__, __FILE__, $sql);
if ($row = $db->sql_fetchrow($result)) {
if (!$row['user_password']) {
$LoginError = "This account exists but haven't a password defined. Use OpenID or contact dereckson (at) espace-win.org to fix that.";
} elseif ($row['user_password'] != md5($_POST['password'])) {
//PASS NOT OK
$LoginError = "Incorrect password.";
} else {
$sql = "UPDATE " . TABLE_SESSIONS . " SET user_id = '$row[user_id]' WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible de procéder à la connexion", '', __LINE__, __FILE__, $sql);
$LoginSuccessful = true;
+ setcookie("LastUsername", $Login, time() + 2592000);
}
} else {
//Login n'existe pas
$LoginError = "Login not found.";
}
}
} elseif ($_POST['LogOut'] || $_GET['action'] == "user.logout") {
Logout();
}
?>
\ No newline at end of file
diff --git a/includes/objects/perso.php b/includes/objects/perso.php
--- a/includes/objects/perso.php
+++ b/includes/objects/perso.php
@@ -1,289 +1,290 @@
<?php
/*
* Perso class
*
* 0.1 2010-01-27 00:39 Autogenerated by Pluton Scaffolding
* 0.2 2010-01-29 14:39 Adding flags support
*
* @package Zed
* @copyright Copyright (c) 2010, Dereckson
* @license Released under BSD license
* @version 0.1
*
*/
require_once("includes/geo/location.php");
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;
/*
* Initializes a new instance
* @param int $id the primary key
*/
function __construct ($id = null) {
if ($id) {
$this->id = $id;
$this->load_from_database();
} else {
$this->generate_id();
}
}
/*
* 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
$id = $db->sql_escape($this->id);
$sql = "SELECT * FROM " . TABLE_PERSOS . " WHERE perso_id = '" . $id . "'";
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 unkwown: " . $this->id;
return false;
}
$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
);
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
*/
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_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
*/
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))
message_die(SQL_ERROR, "Can't save new $global $local location.", '', __LINE__, __FILE__, $sql);
} elseif ($global != null) {
$this->save_field('location_global');
- } elseif ($global != null) {
+ } elseif ($local != null) {
$this->save_field('location_local');
}
//Updates location member
$this->location = new GeoLocation(
$this->location_global,
$this->location_local
);
}
public function setflag ($key, $value) {
//Checks if flag isn't already set at this value
if ($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))
message_die(SQL_ERROR, "Can't save flag", '', __LINE__, __FILE__, $sql);
//Sets flag in this perso instance
$this->flags[$key] = $value;
}
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");
}
}
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
*/
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);
}
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[] = new Perso($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);
}
}
}
?>
\ No newline at end of file
diff --git a/includes/sessions.php b/includes/sessions.php
--- a/includes/sessions.php
+++ b/includes/sessions.php
@@ -1,105 +1,110 @@
<?php
function decode_ip ($int_ip) {
$hexipbang = explode('.', chunk_split($int_ip, 2, '.'));
return hexdec($hexipbang[0]). '.' . hexdec($hexipbang[1]) . '.' . hexdec($hexipbang[2]) . '.' . hexdec($hexipbang[3]);
}
function encode_ip ($dotquad_ip) {
$ip_sep = explode('.', $dotquad_ip);
return sprintf('%02x%02x%02x%02x', $ip_sep[0], $ip_sep[1], $ip_sep[2], $ip_sep[3]);
}
function session_update () {
global $db, $IP, $Config;
//Nettoyage de la session
/* Initialisation */
$time_online = 5 * 60; // Temps après lequel l'utilisateur n'est plus considéré comme online
$time_session = 2 * 60 * 60; // Durée de vie de la session
$heureActuelle = time(); //Timestamp UNIX et non MySQL
/* On fait le ménage */
$sql = "UPDATE " . TABLE_SESSIONS . " SET online=0 WHERE HeureLimite < $heureActuelle";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, 'Impossible de mettre à jour les sessions (utilisateurs offline)', '', __LINE__, __FILE__, $sql);
$sql = "DELETE FROM " . TABLE_SESSIONS . " WHERE SessionLimite < $heureActuelle";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible d'effacer les sessions expirées", '', __LINE__, __FILE__, $sql);
/* Création / mise à jour de la session utilisateur */
if (!$_SESSION[ID]) {
$_SESSION[ID] = md5(genereString("AAAA1234"));
}
$sql = "SELECT * FROM " . TABLE_SESSIONS . " WHERE session_id LIKE '$_SESSION[ID]'";
if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Problème critique avec les sessions.", '', __LINE__, __FILE__, $sql);
if ($db->sql_numrows($result) == 0) {
$sql = "INSERT INTO " . TABLE_SESSIONS . " (IP, session_id, `Where`, HeureLimite, SessionLimite) VALUES ('$IP', '$_SESSION[ID]', $Config[ResourceID], $heureActuelle + $time_online, $heureActuelle + $time_session)";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible de créer une nouvelle session", '', __LINE__, __FILE__, $sql);
} else {
$sql = "UPDATE " . TABLE_SESSIONS . " SET online=1, HeureLimite = $heureActuelle + $time_online, SessionLimite= $heureActuelle + $time_session WHERE session_id = '$_SESSION[ID]'";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible de mettre à jour la session", '', __LINE__, __FILE__, $sql);
}
}
function nbc () {
//Renvoi du nombre d'usagers connectés
global $db, $Config;
$sql = "SELECT count(*) FROM " . TABLE_SESSIONS . " WHERE online=1 AND `Where` = $Config[ResourceID]";
if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Impossible d'obtenir le nombre d'utilisateurs connectés sur le site web", '', __LINE__, __FILE__, $sql);
$row = $db->sql_fetchrow($result);
return $row[0];
}
function get_info ($info)
//Renvoie une variable de la session
{
global $db;
$sql = "SELECT $info FROM " . TABLE_SESSIONS . " WHERE session_id LIKE '$_SESSION[ID]'";
if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Impossible d'obtenir $info", '', __LINE__, __FILE__, $sql);
$row = $db->sql_fetchrow($result);
return $row[$info];
}
function get_logged_user ()
//Renvoie toutes les informations d'un utilisateur
{
global $db;
$sql = "SELECT * FROM " . TABLE_SESSIONS . " WHERE session_id LIKE '$_SESSION[ID]'";
if ( !($result = $db->sql_query($sql)) ) message_die(SQL_ERROR, "Impossible d'obtenir les informations de l'utilisateur", '', __LINE__, __FILE__, $sql);
$row = $db->sql_fetchrow($result);
require_once('includes/objects/user.php');
$user = new User($row['user_id']);
$user->session = $row;
return $user;
}
function set_info ($info, $value)
//Définit une variable session
{
global $db;
$value = ($value === null) ? 'NULL' : "'" . $db->sql_escape($value) . "'";
$sql = "UPDATE " . TABLE_SESSIONS . " SET $info = $value WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible de définir $info", '', __LINE__, __FILE__, $sql);
}
/*
+ * Destroys $_SESSION array values, help ID
+ */
+function clean_session () {
+ foreach ($_SESSION as $key => $value) {
+ if ($key != 'ID') unset($_SESSION[$key]);
+ }
+}
+
+/*
* Logs out user
*/
function logout () {
//Anonymous user in session table
global $db;
$sql = "UPDATE " . TABLE_SESSIONS . " SET user_id = '-1', perso_id = NULL WHERE session_id LIKE '$_SESSION[ID]'";
if (!$db->sql_query($sql)) message_die(SQL_ERROR, "Impossible de procéder à la déconnexion", '', __LINE__, __FILE__, $sql);
-
- //Destroys $_SESSION array values, help ID
- foreach ($_SESSION as $key => $value) {
- if ($key != 'ID') unset($_SESSION[$key]);
- }
+ clean_session();
}
?>
\ No newline at end of file
diff --git a/includes/story/choice.php b/includes/story/choice.php
new file mode 100644
--- /dev/null
+++ b/includes/story/choice.php
@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * Zed
+ * (c) 2010, Dereckson, some rights reserved
+ * Released under BSD license
+ *
+ * Story choice
+ */
+
+class StoryChoice {
+ public $goto;
+ public $text;
+ public $guid;
+
+ function __construct () {
+ //The guid allows to build temporary URLs to get to right choice
+ $this->guid = new_guid();
+ }
+
+ function __toString () {
+ return $this->text;
+ }
+
+ /*
+ * Initializes a new instance of StoryChoice class from a XML element
+ * @param SimpleXMLElement the xml element to parse
+ * @return StoryChoice the story choice class
+ */
+ static function from_xml ($xml) {
+ $choice = new StoryChoice();
+
+ //Parses attribute
+ foreach ($xml->attributes() as $key => $value) {
+ switch ($key) {
+ case 'goto':
+ $choice->$key = (string)$value;
+ break;
+
+ default:
+ message_die(GENERAL_ERROR, "Unknown attribute: $key = \"$value\"", "Story error");
+ }
+ }
+
+ //Parses content
+ $choice->text = (string)$xml;
+
+ return $choice;
+ }
+}
\ No newline at end of file
diff --git a/includes/story/section.php b/includes/story/section.php
new file mode 100644
--- /dev/null
+++ b/includes/story/section.php
@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * Zed
+ * (c) 2010, Dereckson, some rights reserved
+ * Released under BSD license
+ *
+ * Story section
+ */
+
+require_once('choice.php');
+
+class StorySection {
+ /*
+ * @var string the section ID
+ */
+ public $id;
+
+ /*
+ * @var string the section title
+ */
+ public $title;
+
+ /*
+ * @var string the section description
+ */
+ public $description;
+
+ /*
+ * @var string the local location
+ */
+ public $location_local;
+
+ /*
+ * @var Array the section choices (array of StoryChoice items)
+ */
+ public $choices = array();
+
+ /*
+ * @var boolean if true, it's the story start ; otherwise, false;
+ */
+ public $start;
+
+ function __construct ($id) {
+ $this->id = $id;
+ }
+
+ /*
+ * 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)
+ return $choice;
+ }
+
+ return null;
+ }
+
+ /*
+ * Intializes a story section from an XML document
+ * @param string $xml the XML document
+ * @return StorySection the section instance
+ */
+ static function from_xml ($xml) {
+ //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 <section> tag", "Story error");
+ }
+
+ $section = new StorySection($id);
+ $section->title = (string)$xml->title;
+ $section->description = (string)$xml->description;
+ $section->location_local = (string)$xml->local;
+ $section->start = $start;
+ if ($xml->choices) {
+ foreach ($xml->choices->choice as $choice) {
+ $section->choices[] = StoryChoice::from_xml($choice);
+ }
+ }
+ return $section;
+ }
+}
+
+?>
diff --git a/includes/story/story.php b/includes/story/story.php
new file mode 100644
--- /dev/null
+++ b/includes/story/story.php
@@ -0,0 +1,106 @@
+<?php
+
+/*
+ * Zed
+ * (c) 2010, Dereckson, some rights reserved
+ * Released under BSD license
+ *
+ * Story
+ */
+
+require_once('section.php');
+
+/*
+ * @package Zed
+ * @subpackage story
+ */
+class Story {
+ /*
+ * @var string the file path
+ */
+ public $file;
+
+ /*
+ * @var string the story title
+ */
+ public $title;
+ /*
+ * @var Array an array of StorySection elements
+ */
+ public $sections = array();
+
+ /*
+ * @var SimpleXMLElement the SimpleXML parser
+ */
+ private $xml;
+
+ /*
+ * @var string the index of start section in sections array
+ */
+ private $startSection = null;
+
+ /*
+ * @var Array an array of StorySection elements, indexed by location
+ */
+ private $sectionsByLocation = array();
+
+ function __construct ($file) {
+ //Opens .xml
+ if (!file_exists($file)) {
+ message_die(GENERAL_ERROR, "$file not found.", "Story loading error");
+ }
+
+ $this->file = $file;
+ $this->parse();
+ }
+
+ /*
+ * Gets start section
+ * @return StorySection the section where the story starts, or null if not defined
+ */
+ function get_start_section () {
+ return ($this->startSection != null) ? $this->sections[$this->startSection] : null;
+ }
+
+ /*
+ * Gets section from local location
+ * @return StorySection the default section at this location, or null if not defined
+ */
+ function get_section_from_location ($location) {
+ return array_key_exists($location, $this->sectionsByLocation) ? $this->sectionsByLocation[$location] : null;
+ }
+
+ /*
+ * Parses XML file
+ */
+ function parse () {
+ //Parses it
+ $this->xml = simplexml_load_file($this->file);
+ $this->title = (string)$this->xml->title;
+ foreach ($this->xml->section as $section) {
+ //Gets section
+ $section = StorySection::from_xml($section);
+
+ //Have we a start section?
+ if ($section->start) {
+ //Ensures we've only one start section
+ if ($this->startSection != null) {
+ message_die(GENERAL_ERROR, "Two sections have start=\"true\": $section->id and $this->startSection.", "Story error");
+ }
+ $this->startSection = $section->id;
+ }
+
+ //By location
+ if ($section->location_local) {
+ $this->sectionsByLocation[$section->location_local] = $section;
+ }
+
+ //Adds to sections array
+ $this->sections[$section->id] = $section;
+ }
+ }
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/index.php b/index.php
--- a/index.php
+++ b/index.php
@@ -1,178 +1,185 @@
<?php
/*
* Zed
* (c) 2010, Dereckson, some rights reserved
* Released under BSD license
*
* Application entry point
*/
////////////////////////////////////////////////////////////////////////////////
///
/// Initialization
///
//Pluton library
include('includes/core.php');
//Session
$IP = encode_ip($_SERVER["REMOTE_ADDR"]);
+require_once('includes/story/story.php'); //this class can be stored in session
session_start();
$_SESSION[ID] = session_id();
session_update(); //updates or creates the session
include("includes/login.php"); //login/logout
$CurrentUser = get_logged_user(); //Gets current user infos
//Gets current perso
require_once('includes/objects/perso.php');
if ($perso_id = $CurrentUser->session['perso_id']) {
$CurrentPerso = new Perso($perso_id);
}
//Skin and accent to load
define('THEME', $CurrentUser->session['Skin']);
define('ACCENT', $CurrentUser->session['Skin_accent']);
//Loads Smarty
require('includes/Smarty/Smarty.class.php');
$smarty = new Smarty();
$current_dir = dirname(__FILE__);
$smarty->template_dir = $current_dir . '/skins/' . THEME;
$smarty->compile_dir = $current_dir . '/cache/compiled';
$smarty->cache_dir = $current_dir . '/cache';
$smarty->config_dir = $current_dir;
//Loads language files
define('LANG', 'fr');
lang_load('core.conf');
-if ($CurrentUser->id < 1000) {
+if ($CurrentUser->id < 1000) {
//Anonymous user, proceed to login
+ if (array_key_exists('LastUsername', $_COOKIE))
+ $smarty->assign('username', $_COOKIE['LastUsername']);
+ if (array_key_exists('LastOpenID', $_COOKIE))
+ $smarty->assign('OpenID', $_COOKIE['LastOpenID']);
$smarty->assign('LoginError', $LoginError);
$smarty->display('login.tpl');
exit;
}
////////////////////////////////////////////////////////////////////////////////
///
/// Perso selector
///
//Handles form
if ($_POST['form'] == 'perso.create') {
$perso = new Perso();
$perso->load_from_form();
$perso->user_id = $CurrentUser->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");
} else if (!Perso::is_available_nickname($perso->nickname)) {
$errors[] = lang_get("UnavailableNickname");
}
//Save or prints again forms
if (!$errors) {
$perso->save_to_database();
$smarty->assign('NOTIFY', lang_get('NewCharacterCreated'));
$CurrentPerso = $perso;
set_info('perso_id', $perso->id);
$CurrentPerso->setflag("site.lastlogin", $_SERVER['REQUEST_TIME']);
} else {
$smarty->assign('WAP', join("<br />", $errors));
$smarty->assign('perso', $perso);
}
}
if ($_GET['action'] == 'perso.logout') {
//User wants to change perso
$CurrentPerso = null;
set_info('perso_id', null);
+ clean_session();
} elseif ($_GET['action'] == 'perso.select') {
//User have selected a perso
$CurrentPerso = new Perso($_GET['perso_id']);
if ($CurrentPerso->user_id != $CurrentUser->id) {
//Hack
message_die(HACK_ERROR, "This isn't your perso.");
}
set_info('perso_id', $CurrentPerso->id);
$CurrentPerso->setflag("site.lastlogin", $_SERVER['REQUEST_TIME']);
}
if (!$CurrentPerso) {
switch ($count = Perso::get_persos_count($CurrentUser->id)) {
case 0:
//Create a perso
$smarty->display("perso_create.tpl");
exit;
case 1:
//Autoselect
$CurrentPerso = Perso::get_first_perso($CurrentUser->id);
set_info('perso_id', $CurrentPerso->id);
$CurrentPerso->setflag("site.lastlogin", $_SERVER['REQUEST_TIME']);
break;
default:
//Pick a perso
$persos = Perso::get_persos($CurrentUser->id);
$smarty->assign("PERSOS", $persos);
$smarty->display("perso_select.tpl");
$_SESSION['UserWithSeveralPersos'] = true;
exit;
}
}
//Assigns current perso object as Smarty variable
$smarty->assign('CurrentPerso', $CurrentPerso);
////////////////////////////////////////////////////////////////////////////////
///
/// Tasks to execute before calling the URL controller:
/// - assert the perso is somewhere
/// - executes the smartline
///
//If the perso location is unknown, ejects it to an asteroid
if (!$CurrentPerso->location_global) {
require_once('includes/geo/place.php');
$smarty->assign('NOTIFY', lang_get('NewLocationNotify'));
$CurrentPerso->move_to(GeoPlace::get_start_location());
}
//SmartLine
include("includes/SmartLine/ZedSmartLine.php");
////////////////////////////////////////////////////////////////////////////////
///
/// Calls the specific controller to serve the requested page
///
$url = explode('/', substr($_SERVER['PATH_INFO'], 1));
switch ($controller = $url[0]) {
case '':
include('controllers/home.php');
break;
case 'request':
case 'page':
+ case 'explore':
include("controllers/$controller.php");
break;
default:
//TODO: returns a 404 error
dieprint_r($url, 'Unknown URL');
}
?>
\ No newline at end of file
diff --git a/lang/en/core.conf b/lang/en/core.conf
--- a/lang/en/core.conf
+++ b/lang/en/core.conf
@@ -1,108 +1,114 @@
#Zed language config file
#Language: English
#Code: en
#Author: Dereckson
###
### Site configuration
###
SiteTitle = Zed
Product = "<strong>Zed 0.1</strong>, alpha technical preview"
###
### General stuff
###
_t = ":"
###
### Login
###
Login = Login
Password = Password
OK = OK
LoginNotFound = Login not found.
IncorrectPassword = Incorrect password.
JaMata = Ja mata!
WelcomeBack = Welcome back.
OpenID = OpenID
Logout = Logout
###
### Errors
###
UnauthorizedAccess = "Unauthorized access"
SQLError = "SQL Error"
line = line
Error = Error
BackToHome = "Back to homepage"
FatalErrorScreen = Fatal error screen
FatalErrorInterrupt = Fatal error breaking screen
GeneralError = General error
PageNotFound = "Page not found"
###
### Homepage
###
Welcome = Welcome
WelcomeText = "<p>Welcome to the Zed alpha technical preview. Zed, it's a mix between a gallery, a place to meet new and existing friends, with some RPG inspiration.<br />
The concept is to have each information stored at physical places in a virtual world, like the 80s cyberspace vision. Another goal is to build a community sharing values like cooperation, freedom, ethic.</p>"
###
### Homepage - messages
###
#messages.tpl, Reply
Reply = Reply
#messages.tpl, the X link title
DeleteThisMessage = Delete this message
#home.php, messages error
MessageDeleted = Message deleted.
NotYourMessage = This message is not one of yours.
MessageAlreadyDeleted = Message already deleted.
###
### Perso create/select
###
NewCharacterCreated = New character created
CreateCharacter = Create a character
EditCharacter = Edit %s information
NoSexSpecified = "Pick a sex, or '<em>Neutral</em>' if you don't want to tell it."
NoNicknameSpecified = "You must pick a nickname, it's like your login to identify your character."
NoFullnameSpecified = "All beings must have a name."
NoRaceSpecified = "You've to specify a race: '<em>humanoid</em>' for human and co.<br />If you don't want to specify a race, use the generic '<em>being</em>'."
NicknameUnavailable = "This nickname is already used.<br />Choose a more original one."
PickPerso = Pick your perso
SwapPerso = "Swap perso (%s's logout)"
NewLocationNotify = "You're slowly awaking in a place you don't recognize."
###
### Places
###
UnknownBody = "Unknown asteroid"
UnknownPlace = "Unknown place"
WherePlace = "%s @ %s"
SpaceAround = "Space around %s"
hypership = hypership
asteroid = asteroid
moon = moon
planet = planet
star = star
-orbital = orbital
\ No newline at end of file
+orbital = orbital
+
+###
+### Stories
+###
+
+InvalidStoryGUID = "In story mode, you can't use previous/back URL."
\ No newline at end of file
diff --git a/lang/en/tutorials.conf b/lang/en/tutorials.conf
--- a/lang/en/tutorials.conf
+++ b/lang/en/tutorials.conf
@@ -1,7 +1,7 @@
[ReachHypership]
WhereYouAre = You're on %1$s, a desolate %2$s in the galaxy.
WhereTheHypershipIs = After some investigation, you learn the hypership is in a far galaxy spiral.
HowToJoinIt = "To reach it, you can: <ul><li>hitchhike, if there are other people around going to the hypership.</li>
<li>try to <a href=/index.php/request/B00001/aid.reach>contact the hypership and ask a shuttle</a></li>
- <li>hire a ship</li>
+ <li><a href=/index.php/explore>explore the place</a> and find a ship to hire</li>
</ul>"
\ No newline at end of file
diff --git a/lang/fr/core.conf b/lang/fr/core.conf
--- a/lang/fr/core.conf
+++ b/lang/fr/core.conf
@@ -1,109 +1,115 @@
#Zed language config file
#Language: English
#Code: fr
#Author: Dereckson
###
### Site configuration
###
SiteTitle = Zed
Product = "<strong>Zed 0.1</strong>, alpha technical preview"
###
### General stuff
###
_t = " :"
###
### Login
###
Login = Login
Password = Password
OK = OK
LoginNotFound = Login introuvable.
IncorrectPassword = Mot de passe incorrect.
JaMata = Ja mata!
WelcomeBack = Welcome back.
OpenID = OpenID
Logout = Déconnexion
###
### Homepage
###
Welcome = Bienvenue
WelcomeText = "<p>Bienvenue sur la version alpha de Zed, un hybride un peu étrange entre une galerie, un endroit où rencontrer ses amis ou s'en faire de nouveaux, avec une légère inspiration RPG, dans un environnement galactique inspiré des romans de la Culture de Iain M. Banks.</p>
<p>Le concept est d'expérimenter ce qui se passe lorsque chaque information est dans un espace précis, un peu comme la vision cyberpunk des années 80 du cyberespace. Un autre but est de créer une communauté partagant des valeurs de respect, de coopération, de liberté et d'éthique.</p>"
###
### Homepage - Messages
###
#messages.tpl, Reply
Reply = Répondre
#messages.tpl, the X link title
DeleteThisMessage = Effacer ce message
#home.php, messages error
MessageDeleted = Message effacé.
NotYourMessage = Hey, ce message appartient à autrui !
MessageAlreadyDeleted = Message déjà effacé
###
### Errors
###
UnauthorizedAccess = "Accès non autorisé"
SQLError = "Erreur dans la requête SQL"
line = ligne
Error = Erreur
BackToHome = "Retour à la page d'accueil"
FatalErrorScreen = Fatal error screen
FatalErrorInterrupt = Fatal error screen (interruption)
GeneralError = "Erreur"
PageNotFound = "Cette page n'existe pas."
###
### Perso create/select
###
NewCharacterCreated = Nouveau perso créé.
CreateCharacter = Nouveau perso
EditCharacter = Éditer les infos de %s
NoSexSpecified = "Pick a sex, or '<em>Neutral</em>' if you don't want to tell it."
NoNicknameSpecified = "You must pick a nickname, it's like your login to identify your character."
NoFullnameSpecified = "All beings must have a name."
NoRaceSpecified = "You've to specify a race: '<em>humanoid</em>' for human and co.<br />If you don't want to specify a race, use the generic '<em>being</em>'."
NicknameUnavailable = "This nickname is already used.<br />Choose a more original one."
PickPerso = "Sélectionnez votre perso"
SwapPerso = "Changer de perso (déco %s)"
NewLocationNotify = "Vous vous réveillez lentement dans un endroit inconnu"
###
### Places
###
UnknownBody = "Astéroïde inconnu"
UnknownPlace = "Endroit inconnu"
WherePlace = "%2$s, %1$s."
SpaceAround = "%s et l'espace autour"
hypership = hypership
asteroid = astéroïde
moon = lune
planet = planète
star = étoile
-orbital = orbitale
\ No newline at end of file
+orbital = orbitale
+
+###
+### Stories
+###
+
+InvalidStoryGUID = "En mode récit, il n'est pas possible d'utiliser les boutons précédents et suivants."
\ No newline at end of file
diff --git a/skins/zed/login.tpl b/skins/zed/login.tpl
--- a/skins/zed/login.tpl
+++ b/skins/zed/login.tpl
@@ -1,65 +1,65 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{#SiteTitle#}</title>
<link rel="Stylesheet" href="/css/zed/login.css" type="text/css" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<!-- Login form -->
<div id="LoginBox">
<form method="post" action="/">
<div class="row">
<label for="username">{#Login#}{#_t#}</label>
- <input type="text" id="username" name="username" />
+ <input type="text" id="username" name="username" value="{$username}" />
</div>
<div class="row">
<label for="password">{#Password#}{#_t#}</label>
<input type="password" id="password" name="password" />
</div>
<div class="row">
<label for="openid">{#OpenID#}{#_t#}</label>
- <input type="text" id="openid" name="openid" />
+ <input type="text" id="openid" name="openid" value="{$OpenID}" />
</div>
<div class="row">
<input type="submit" id="submit" name="LogIn" value="{#OK#}" />
</div>
</form>
{if $LoginError}
<div class=row>
<p class="error">&nbsp;&nbsp;&nbsp;&nbsp;{$LoginError}</p>
</div>
{/if}
</div>
{$code = genereString('AAA111')}
<!--
XXXXXXX XX
X X X
X X Invitation code:
X XXXXX XXXXX {$code}
X X X X X
X XXXXXXX X X
X X X X
X X X X X X
XXXXXXX XXXXX XXXXXX
Welcome to the Zed beta. We're happy you're reading the source :)
If you want to know what we're building, check http://zed.dereckson.be/tour.html
If you wish an access, send a mail to zedinvite (alt+64) dereckson.be
and specify the following code: {$code}
* * * *
Bienvenue dans la version bêta de Zed. Heureux que vous consultiez la source.
Un petit aperçu de ce que l'on crée est sur http://zed.dereckson.be/tour.html
Pour obtenir un accès, envoyez un mail à zedinvite (alt+64) dereckson.be
en spécifiant le code suivant : {$code}
-->
</body>
</html>
\ No newline at end of file
diff --git a/skins/zed/story.tpl b/skins/zed/story.tpl
new file mode 100644
--- /dev/null
+++ b/skins/zed/story.tpl
@@ -0,0 +1,13 @@
+ <!-- Story -->
+ <div class="story">
+ <h1>{$PAGE_TITLE}</h1>
+ <h2>{$section->title}</h2>
+ <p>{$section->description|trim|text2html}</p>
+{if $section->choices}
+ <ul>
+{foreach from=$section->choices item=choice}
+ <li><a href="{get_url('explore', $choice->guid)}">{$choice->text}</a></li>
+{/foreach}
+ </ul>
+{/if}
+ </div>
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 10:59 (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
21171
Default Alt Text
(51 KB)

Event Timeline