commit 62cdc8b3394528fb8614e88d07c69026e0ad3849
Author: franzz
Date: Wed Aug 7 14:39:34 2013 +0200
Initial commit
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..f3a6dd0
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,3 @@
+## Jul - deny access to the top-level git repository
+RewriteEngine On
+RewriteRule \.git - [F,L]
diff --git a/aescrawl.ttf b/aescrawl.ttf
new file mode 100755
index 0000000..fe13851
Binary files /dev/null and b/aescrawl.ttf differ
diff --git a/background.php b/background.php
new file mode 100755
index 0000000..5906aec
--- /dev/null
+++ b/background.php
@@ -0,0 +1,19 @@
+asMessageStack = array();
+ $this->asMessageStack[self::ERROR_TAB] = array();
+ $this->asMessageStack[self::WARNING_TAB] = array();
+ $this->iExtractMode = self::MODE_ARRAY;
+ }
+
+ protected function addError($sError)
+ {
+ $this->addMessage(self::ERROR_TAB, $sError);
+ }
+
+ protected function addWarning($sWarning)
+ {
+ $this->addMessage(self::WARNING_TAB, $sError);
+ }
+
+ private function addMessage($sType, $sMessage)
+ {
+ $this->asMessageStack[$sType][] = $sMessage;
+ }
+
+ protected function getCleanErrorStack()
+ {
+ return $this->getCleanMessageStack(self::ERROR_TAB);
+ }
+
+ protected function getCleanWarningStack()
+ {
+ return $this->getCleanMessageStack(self::WARNING_TAB);
+ }
+
+ private function getCleanMessageStack($sType)
+ {
+ switch($this->iExtractMode)
+ {
+ case self::MODE_TEXT:
+ $oMessageStack = implode("\n", $this->asMessageStack[$sType]);
+ break;
+ case self::MODE_ARRAY:
+ $oMessageStack = $this->asMessageStack[$sType];
+ break;
+ case self::MODE_FILE:
+ $oMessageStack = implode("
", $this->asMessageStack[$sType]);
+ break;
+ }
+ $this->asMessageStack[$sType] = array();
+ return $oMessageStack;
+ }
+
+ function __destruct()
+ {
+ $sErrorStack = $this->getCleanErrorStack();
+
+ switch($this->iExtractMode)
+ {
+ case self::MODE_TEXT:
+ echo $sErrorStack;
+ break;
+ case self::MODE_ARRAY:
+ if(count($sErrorStack)>0)
+ {
+ pre($sErrorStack, 'Error Stack');
+ }
+ break;
+ case self::MODE_FILE:
+ if($sErrorStack!='')
+ {
+ @file_put_contents('log.html', '
'.date('r')."
".$sErrorStack.'
', FILE_APPEND);
+ }
+ break;
+ }
+ }
+}
+
+class MySqlManager extends PhpObject
+{
+ const DB_SERVER = 'localhost';
+ const DB_LOGIN = 'root';
+ const DB_PASS = '0nadmin';
+ const DB_NAME = 'mythoughts';
+
+ const ID_TAG = 'id_';
+ const USERS_TABLE = 'users';
+ const THOUGHTS_TABLE = 'thoughts';
+ const SETTINGS_TABLE = 'settings';
+
+ private $oConnection;
+
+ public function __construct()
+ {
+ parent::__construct();
+ $this->oConnection = mysql_connect(self::DB_SERVER, self::DB_LOGIN, self::DB_PASS);
+ if(!$this->oConnection)
+ {
+ $this->addError('bug connection');
+ }
+ else
+ {
+ if(!mysql_select_db(self::DB_NAME, $this->oConnection))
+ {
+ $this->install();
+ }
+ }
+ }
+
+ public function __destruct()
+ {
+ mysql_close($this->oConnection);
+ }
+
+ private function install()
+ {
+ $this->setQuery("DROP DATABASE IF EXISTS ".self::DB_NAME);
+ $this->setQuery("CREATE /* ".basename(__FILE__)." ".__LINE__." */ DATABASE ".self::DB_NAME);
+ mysql_select_db(self::DB_NAME, $this->oConnection);
+ @array_walk(array_map(array($this, 'getInstallQuery'), $this->getTables()), array($this, 'setQuery'));
+ }
+
+ private function getInstallQuery($sTableName)
+ {
+ $asTableColumns = $this->getTableColumns($sTableName);
+ $sQuery = "\n".implodeAll($asTableColumns, "` ", "\n", "`", ",")."\n".implode(", \n", $this->getTableConstraints($sTableName));
+ return "CREATE /* ".basename(__FILE__)." ".__LINE__." */ TABLE `{$sTableName}` ({$sQuery})";
+ }
+
+ private function setQuery($sQuery, $sTypeQuery=__FUNCTION__)
+ {
+ return $this->getQuery($sQuery, $sTypeQuery);
+ }
+
+ private function getQuery($sQuery, $sTypeQuery=__FUNCTION__)
+ {
+ $oResult = mysql_query($sQuery, $this->oConnection);
+ if(!$oResult)
+ {
+ $this->addError("\nErreur SQL : \n".$sQuery."\n".mysql_error());
+ }
+ return $oResult;
+ }
+
+ public function getArrayQuery($sQuery, $bStringOnly=false, $sTypeQuery=__FUNCTION__)
+ {
+ $asResult = array();
+ $oResult = $this->getQuery($sQuery, true, $sTypeQuery);
+ if($oResult!==false)
+ {
+ while($asCurrentRow = mysql_fetch_array($oResult))
+ {
+ if($bStringOnly)
+ {
+ $asCurrentRow = $this->arrayKeyFilter($asCurrentRow, 'is_string');
+ }
+
+ //One column case : collapse a level
+ if(count($asCurrentRow)==1)
+ {
+ $asResult[] = array_shift($asCurrentRow);
+ }
+ else
+ {
+ $asResult[] = $asCurrentRow;
+ }
+ }
+ }
+ return $asResult;
+ }
+
+ private function arrayKeyFilter($asArray, $sCallBack)
+ {
+ $asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack));
+ return array_intersect_key($asArray, $asValidKeys);
+ }
+
+ public static function getTables()
+ {
+ return array(self::USERS_TABLE, self::THOUGHTS_TABLE, self::SETTINGS_TABLE);
+ }
+
+ public static function getId($sTableName)
+ {
+ return self::ID_TAG.substr($sTableName, 0, -1);
+ }
+
+ public static function getText($sTableName)
+ {
+ return substr($sTableName, 0, -1);
+ }
+
+ private static function isId($sColumnName, $sTableName='')
+ {
+ $asTables = ($sTableName=='')?self::getTables():array($sTableName);
+ $asTableIds = array_map(array('self', 'getId'), $asTables);
+ return in_array($sColumnName, $asTableIds);
+ }
+
+ public static function getTablecolumns($sTableName)
+ {
+ $asTableColumns = array(self::getId($sTableName));
+
+ switch($sTableName)
+ {
+ case self::USERS_TABLE:
+ $asTableColumns[] = 'user';
+ $asTableColumns[] = 'pass';
+ break;
+ case self::THOUGHTS_TABLE:
+ $asTableColumns[] = self::getId(self::USERS_TABLE);
+ $asTableColumns[] = 'thought';
+ break;
+ case self::SETTINGS_TABLE:
+ $asTableColumns[] = self::getId(self::USERS_TABLE);
+ $asTableColumns[] = 'setting';
+ $asTableColumns[] = 'value';
+ break;
+ default:
+ $this->addError('Function '.__FUNCTION__.', table '.$sTableName.' not found');
+ }
+ $asTableColumns[] = 'led';
+ $asTableName = array_fill(0, count($asTableColumns), $sTableName);
+ return array_combine($asTableColumns, array_map(array('self', 'getColumnType'), $asTableColumns, $asTableName));
+ }
+
+ private static function getColumnType($sColumnName, $sTableName)
+ {
+ $sColumnType = '';
+ switch($sColumnName)
+ {
+ case 'user':
+ $sColumnType = "varchar(20) NOT NULL";
+ break;
+ case 'pass':
+ $sColumnType = "varchar(128) NOT NULL";
+ break;
+ case 'thought':
+ $sColumnType = "longtext NOT NULL";
+ break;
+ case 'setting':
+ $sColumnType = "varchar(20) NOT NULL";
+ break;
+ case 'value':
+ $sColumnType = 'varchar(20) NOT NULL';
+ break;
+ case 'led':
+ $sColumnType = "TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP";
+ break;
+ case self::isId($sColumnName, $sTableName):
+ $sColumnType = "int(10) UNSIGNED auto_increment";
+ break;
+ case self::isId($sColumnName):
+ $sColumnType = "int(10) UNSIGNED NOT NULL";
+ break;
+ }
+ return $sColumnType;
+ }
+
+ private static function getTableConstraints($sTableName)
+ {
+ //primary key
+ $asTableConstraints = array('PRIMARY' => "PRIMARY KEY (`".self::getId($sTableName)."`)");
+
+ //other constraints
+ switch($sTableName)
+ {
+ case 'user' :
+ break;
+ case 'thought' :
+ break;
+ }
+ return $asTableConstraints;
+ }
+
+ private function addQuotes($oData)
+ {
+ return array_map_encapsulate($oData, "'");
+ }
+
+ private function getLastId()
+ {
+ return mysql_insert_id();
+ }
+
+ public function insertRow($sTableName, $asData)
+ {
+ $this->cleanSql($sTableName);
+ $this->cleanSql($asData);
+
+ $asQueryValues = $this->addQuotes($asData);
+ $sQuery = "INSERT /* ".basename(__FILE__)." ".__LINE__." */
+ INTO ".$sTableName." (`".implode("`, `", array_keys($asQueryValues))."`)
+ VALUES (".implode(", ", $asQueryValues).")";
+
+ return $this->setQuery($sQuery)?$this->getLastId():false;
+ }
+
+ public function updateRow($sTableName, $asConstraints, $asData)
+ {
+ if(!is_array($asConstraints))
+ {
+ $asConstraints = array($this->getId($sTableName)=>$asConstraints);
+ }
+ $iTableId =
+
+ $this->cleanSql($sTableName);
+ $this->cleanSql($iTableId);
+ $this->cleanSql($asData);
+ $this->cleanSql($asConstraints);
+ $asQueryValues = $this->addQuotes($asData);
+ $asConstraintsValues = $this->addQuotes($asConstraints);
+
+ $sQuery = "UPDATE /* ".basename(__FILE__)." ".__LINE__." */ $sTableName
+ SET ".implodeAll($asQueryValues, " = ", ", ")."
+ WHERE ".implodeAll($asConstraintsValues, " = ", " AND ")." LIMIT 1";
+ return $this->setQuery($sQuery)?$this->selectValue($sTableName, $this->getId($sTableName), $asConstraints):false;
+ }
+
+ public function insertUpdateRow($sTableName, $asConstraints, $asData)
+ {
+ $iTableId = $this->selectValue($sTableName, $this->getId($sTableName), $asConstraints);
+ if(!$iTableId)
+ {
+ $asData = array_merge($asConstraints, $asData);
+ return $this->insertRow($sTableName, $asData);
+ }
+ else
+ {
+ return $this->updateRow($sTableName, $asConstraints, $asData);
+ }
+ }
+
+ public function selectRow($sTableName, $asConstraints=array(), $sColumnName='*', $bStringOnly=false)
+ {
+ $asResult = $this->selectRows(array('select'=>$sColumnName, 'from'=>$sTableName, 'constraint'=>$asConstraints));
+ $iCountNb = count($asResult);
+ switch($iCountNb)
+ {
+ case 0 :
+ return false;
+ case $iCountNb > 1 :
+ $this->addError('Trop de résultats pour un selectRow() : '.$iCountNb);
+ break;
+ }
+ return array_shift($asResult);
+ }
+ function selectValue($sTableName, $sColumnName, $asConstraints)
+ {
+ if(is_numeric($asConstraints))
+ {
+ $asConstraints = array(self::getId($sTableName)=>$asConstraints);
+ }
+ return $this->selectRow($sTableName, $asConstraints, $sColumnName);
+ }
+
+ public function selectRows($asInfo, $bStringOnly=true)
+ {
+ $sAttributes = array('select'=>"SELECT", 'from'=>"FROM", 'constraint'=>"WHERE", 'groupBy'=>"GROUP BY", 'orderBy'=>"ORDER BY");
+ $asRowSeparators = array('select'=>", ", 'from'=>"", 'constraint'=>" AND ", 'groupBy'=>", ", 'orderBy'=>", ");
+ $asOperators = array('constraint'=>" = ", 'orderBy'=>" ");
+
+ $sQuery = "/* ".basename(__FILE__)." ".__LINE__." */";
+ foreach($sAttributes as $sStatement => $sKeyWord)
+ {
+ $asSelection = array_key_exists($sStatement, $asInfo)?$asInfo[$sStatement]:array();
+ if(!is_array($asSelection))
+ {
+ $asSelection = array($asSelection);
+ }
+
+ //if provided values
+ if(!empty($asSelection))
+ {
+ $this->cleanSql($asSelection);
+
+ if($sStatement=='constraint')
+ {
+ $asSelection = $this->addQuotes($asSelection);
+ }
+
+ $sQuery .= " ".$sKeyWord." ";
+
+ //in case of double value input
+ if(array_key_exists($sStatement, $asOperators))
+ {
+ $sQuery .= implodeAll($asSelection, $asOperators[$sStatement], $asRowSeparators[$sStatement]);
+ }
+ else
+ {
+ $sQuery .= implode($asRowSeparators[$sStatement], $asSelection);
+ }
+ }
+ //default value for select
+ elseif($sStatement=='select')
+ {
+ $sQuery .= " ".$sKeyWord." * ";
+ }
+ }
+
+ return $this->getArrayQuery($sQuery, $bStringOnly);
+ }
+
+ private function cleanSql(&$oData)
+ {
+ cleanData($oData, 'mysql_real_escape_string');
+ }
+}
+
+class Session extends PhpObject
+{
+ private $iUserId;
+ private $sLogin;
+ private $sToken;
+ private $sPostToken;
+
+ private $oMySql;
+
+ const SESSION_ID_USER = 'id_user';
+ const SESSION_USER = 'user';
+ const SESSION_TOKEN = 'token';
+ const SESSION_POST_TOKEN = 'post_token';
+
+ public function __construct($oMySql)
+ {
+ parent::__construct();
+ $this->iUserId = $this->sLogin = $this->sToken = $this->sPostToken = false;
+ $this->oMySql = $oMySql;
+ $this->syncSession();
+ }
+
+ public function getUserId()
+ {
+ return $this->iUserId;
+ }
+
+ private function syncSession()
+ {
+ if(isset($_SESSION[self::SESSION_ID_USER]))
+ {
+ $this->iUserId = $_SESSION[self::SESSION_ID_USER];
+ }
+ if(isset($_SESSION[self::SESSION_USER]))
+ {
+ $this->sLogin = $_SESSION[self::SESSION_USER];
+ }
+ if(isset($_SESSION[self::SESSION_TOKEN]))
+ {
+ $this->sToken = $_SESSION[self::SESSION_TOKEN];
+ }
+ if(isset($_SESSION[self::SESSION_POST_TOKEN]))
+ {
+ $this->sPostToken = $_SESSION[self::SESSION_POST_TOKEN];
+ }
+ }
+
+ private function setSession($iUserId, $sLogin)
+ {
+ $_SESSION[self::SESSION_ID_USER] = $iUserId;
+ $_SESSION[self::SESSION_USER] = $sLogin;
+
+ //Token
+ $sToken = $this->createToken();
+ $_SESSION[self::SESSION_TOKEN] = $sToken;
+ $this->setTokenCookie($sToken);
+
+ $this->syncSession();
+ }
+
+ public function register($asData)
+ {
+ $sLogin = strtolower($asData['user']);
+ $sPass = $asData['pass'];
+
+ if($sLogin=='' || $sPass=='')
+ {
+ $this->addError('Empty mandatory fields (Nickname or password)');
+ }
+ elseif(htmlspecialchars($sLogin, ENT_QUOTES)!=$sLogin)
+ {
+ $this->addError('Nickname: HTML characters are forbidden');
+ }
+ elseif($this->oMySql->selectRow(MySqlManager::USERS_TABLE, array('user'=>$sLogin)))
+ {
+ $this->addError('Nickname: This is already a user called by that name, choose a different one');
+ }
+ else
+ {
+ $asData['pass'] = self::encryptPassword($sPass);
+ $iUserId = $this->oMySql->insertRow(MySqlManager::USERS_TABLE, $asData);
+ return $this->logMeIn($sLogin, $sPass);
+ }
+ return false;
+ }
+
+ public function logMeIn($sLogin, $sPass)
+ {
+ $bResult = false;
+ $asUser = $this->oMySql->selectRow(MySqlManager::USERS_TABLE, array('user'=>$sLogin));
+ if(!$asUser)
+ {
+ $this->addError('Utilisateur inconnu');
+ }
+ elseif(!$this->checkPassword($sPass, $asUser['pass']))
+ {
+ $this->addError('mot de pass incorrect');
+ }
+ else
+ {
+ $this->setSession($asUser[MySqlManager::getId(MySqlManager::USERS_TABLE)], $sLogin);
+ $bResult = true;
+ }
+ return $bResult;
+ }
+
+ public function isLogguedIn()
+ {
+ $bLogguedIn = false;
+ if($this->iUserId && $this->sLogin && $this->sToken)
+ {
+ //check if token is set and valid
+ if($this->checkToken())
+ {
+ //Check if user got a actual account in the database
+ $bLogguedIn = $this->checkAccount($this->sLogin, $this->iUserId);
+ }
+ else
+ {
+ $this->addError('Authentication problem, please sign in again');
+ }
+ }
+ return $bLogguedIn;
+ }
+
+ private function checkAccount($sUserName, $iUserId=0)
+ {
+ $asConstraints = array('user'=>$sUserName);
+ if($iUserId>0)
+ {
+ $asConstraints[MySqlManager::getId(MySqlManager::USERS_TABLE)] = $iUserId;
+ }
+ return $this->oMySql->selectValue(MySqlManager::USERS_TABLE, 'COUNT(1)', $asConstraints);
+ }
+
+ public function logMeOut()
+ {
+ $_SESSION = array();
+ $this->setTokenCookie('', -1);
+ return session_destroy();
+ }
+
+ private static function encryptPassword($sPass)
+ {
+ $sRandomText = 'F_RA-1H"2{bvj)5f?0sd3r#fP,K]U|w}hGiN@(sZ.sDe!7*x/:Mq+&';
+ for($iIndex=0; $iIndex < strlen($sPass); $iIndex++)
+ {
+ $sPass[$iIndex] = $sRandomText[$iIndex%strlen($sRandomText)] ^ $sPass[$iIndex];
+ }
+ return md5($sPass);
+ }
+
+ private static function createToken()
+ {
+ return self::encryptPassword( $_SERVER['HTTP_USER_AGENT'].
+ $_SERVER['REMOTE_ADDR'].
+ $_SERVER['REQUEST_TIME'].
+ strstr(microtime(), ' ', true).
+ $_SERVER['SERVER_SIGNATURE'].
+ $_SERVER['SERVER_ADMIN']);
+ }
+
+ //Session Token
+
+ private static function setTokenCookie($sToken, $iTime=1)
+ {
+ setcookie(self::SESSION_TOKEN, $sToken, time()+60*60*24*$iTime);
+ $_COOKIE[self::SESSION_TOKEN] = $sToken;
+ }
+
+ private function checkToken()
+ {
+ return ($this->sToken && array_key_exists(self::SESSION_TOKEN, $_COOKIE) && $_COOKIE[self::SESSION_TOKEN] == $this->sToken);
+ }
+
+ //Post Token
+
+ private function refreshPostToken()
+ {
+ $this->sPostToken = self::createToken();
+ $_SESSION[self::SESSION_POST_TOKEN] = $this->sPostToken;
+ }
+
+ public function getNewPostToken()
+ {
+ $this->refreshPostToken();
+ return $this->sPostToken;
+ }
+
+ public function checkPostToken($sPostToken)
+ {
+ return ($this->sPostToken && $sPostToken!='' && $sPostToken == $this->sPostToken);
+ }
+
+ private static function checkPassword($sClearPass, $sEncodedPass)
+ {
+ return self::encryptPassword($sClearPass) == $sEncodedPass;
+ }
+}
+
+class Mask extends PhpObject
+{
+ public $sMaskName;
+ public $sFilePath;
+ private $sMask;
+ private $asTags;
+ private $asPartsSource;
+ private $aoInstances;
+
+ const MASK_FOLDER = 'mask/';
+ const START_TAG = 'START';
+ const END_TAG = 'END';
+ const TAG_MARK = '#';
+
+ public function __construct($sFileName='')
+ {
+ //init
+ parent::__construct();
+ $this->sMaskName = '';
+ $this->sFilePath = '';
+ $this->sMask = '';
+ $this->asTags = array();
+ $this->asPartsSource = array();
+ $this->aoInstances = array();
+ $this->sFilePath = '';
+
+ //load file
+ if($sFileName!='')
+ {
+ $this->initFile($sFileName);
+ }
+ }
+
+ public function initFile($sFileName)
+ {
+ $sFilePath = self::MASK_FOLDER.$sFileName.'.html';
+ if(file_exists($sFilePath))
+ {
+ $this->sFilePath = $sFilePath;
+ $sSource = file_get_contents($this->sFilePath);
+ $this->initMask($sFileName, $sSource);
+ }
+ else
+ {
+ $this->addError('Fichier introuvable à l\'adresse : '.$sFilePath);
+ }
+ }
+
+ public function initFileFromString($sSource, $sPartName='', $iInstanceNb=0)
+ {
+ $this->initMask($sPartName.' (from row) '.$iInstanceNb, $sSource);
+ }
+
+ private function initMask($sMaskName, $sSource)
+ {
+ $this->sMaskName = $sMaskName;
+ $this->sMask = $sSource;
+ $this->setParts();
+ }
+
+ private function setParts()
+ {
+ while(preg_match('/\<\!-- \[PART\] (?P\S+) \[START\] --\>/', $this->sMask, $asMatch))
+ {
+ $sPartName = $asMatch['part'];
+
+ $this->asPartsSource[$sPartName] = $this->getCleanPart($sPartName);
+ $this->aoInstances[$sPartName] = array();
+ }
+ }
+
+ private function getCleanPart($sPartName)
+ {
+ $iStartPos = $this->getPartStartPos($sPartName);
+ $iEndPos = $this->getPartEndPos($sPartName);
+ $sPart = substr($this->sMask, $iStartPos, $iEndPos-$iStartPos);
+ $sExtendedPart = $this->getPartPattern($sPartName, self::START_TAG).$sPart. $this->getPartPattern($sPartName, self::END_TAG);
+ $this->sMask = str_replace($sExtendedPart, $this->getPartTagPattern($sPartName), $this->sMask);
+ return $sPart;
+ }
+
+ private function getPartStartPos($sPartName)
+ {
+ $sPartStartPattern = $this->getPartPattern($sPartName, self::START_TAG);
+ return strpos($this->sMask, $sPartStartPattern) + strlen($sPartStartPattern);
+ }
+
+ private function getPartEndPos($sPartName)
+ {
+ $sPartEndPattern = $this->getPartPattern($sPartName, self::END_TAG);
+ return strpos($this->sMask, $sPartEndPattern);
+ }
+
+ private function getPartPattern($sPartName, $sAction)
+ {
+ return '';
+ }
+
+ private function getPartTagPattern($sPartName, $bMark=true)
+ {
+ $sPartTag = 'PART '.$sPartName;
+ return $bMark?$this->addTagMark($sPartTag):$sPartTag;
+ }
+
+ public function addInstance($sPartName, $asTags)
+ {
+ $this->newInstance($sPartName);
+ foreach($asTags as $sTagName=>$sTagValue)
+ {
+ $this->setInstanceTag($sPartName, $sTagName, $sTagValue);
+ }
+ }
+
+ public function newInstance($sPartName)
+ {
+ //Finding the part
+ $oMask = &$this->findPart($this, $sPartName);
+
+ //Retrieving source html
+ $sPartSource = $oMask->asPartsSource[$sPartName];
+
+ //Creating new instance
+ $oInstance = new Mask();
+ $oInstance->initFileFromString($sPartSource, $sPartName);
+ $oMask->aoInstances[$sPartName][] = $oInstance;
+ }
+
+ public function setInstanceTag($sPartName, $sTagName, $sTagValue)
+ {
+ $oMask = &$this->findPart($this, $sPartName);
+ $oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue);
+ }
+
+ private function &findPart($oMask, $sPartName)
+ {
+ if(array_key_exists($sPartName, $oMask->aoInstances))
+ {
+ return $oMask;
+ }
+ else //not tested
+ {
+ foreach($oMask->aoInstances as $sLevelPartName=>$aoInstances)
+ {
+ if(!empty($aoInstances))
+ {
+ //take last instances
+ return $this->findPart($oMask->getCurrentInstance($sLevelPartName), $sPartName);
+ }
+ }
+ }
+ $this->addError('No part found : '.$sPartName);
+ }
+
+ private function getCurrentInstance($sPartName)
+ {
+ if(!empty($this->aoInstances[$sPartName]))
+ {
+ return end($this->aoInstances[$sPartName]);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public function setTag($sTagName, $sTagValue)
+ {
+ $this->asTags[$sTagName] = $sTagValue;
+ }
+
+ public function getMask()
+ {
+ $sCompletedMask = $this->sMask;
+
+ //build parts
+ foreach($this->aoInstances as $sPart=>$aoParts)
+ {
+ $sTagValue = '';
+ foreach($aoParts as $oInstance)
+ {
+ $sTagValue .= $oInstance->getMask();
+ }
+ $this->setTag($this->getPartTagPattern($sPart, false), $sTagValue);
+ }
+
+ //replace tags
+ if(!empty($this->asTags))
+ {
+ $asTags = $this->addTagMark(array_keys($this->asTags));
+ $sCompletedMask = str_replace($asTags, $this->asTags, $sCompletedMask);
+ }
+ return $sCompletedMask;
+ }
+
+ private function addTagMark($oData)
+ {
+ return array_map_encapsulate($oData, self::TAG_MARK);
+ }
+}
+
+class MyThoughts extends PhpObject
+{
+ //Constants
+ const URL_DATE_FORMAT = 'Ymd';
+ const LAYOUT_DATE_FORMAT = 'F \t\h\e jS, Y';
+ const MYSQL_DATE_FORMAT = 'Y-m-d';
+ const LAYOUT_TIME_FORMAT = 'G:i';
+ const WELCOME_MSG_FILE = 'welcome';
+
+ //settings
+ const SETTING_LAYOUT = 'layout';
+ const LAYOUT_ONE_PAGE = '1';
+ const LAYOUT_TWO_PAGES = '2';
+ const SETTING_FONT = 'font';
+ const FONT_THOUGHTS = 'thoughts';
+ const FONT_ARIAL = 'Arial';
+ const FONT_VERDANA = 'Verdana';
+ const SETTING_SIZE = 'Size';
+ const SIZE_16 = '16';
+ const SIZE_18 = '18';
+ const SIZE_20 = '20';
+
+ //Objects
+ private $oMySql;
+ private $oSession;
+ private $oCalendar;
+ private $asSettings;
+
+ //Masks
+ private $oMainMask;
+ private $oPageMask;
+ private $oMenuMask;
+
+ function __construct()
+ {
+ parent::__construct();
+ $this->oMySql = new MySqlManager();
+ $this->oSession = new Session($this->oMySql);
+ $this->oCalendar = new Calendar($this->oMySql, $this->oSession);
+ $this->oMainMask = new Mask('index');
+ $this->oPageMask = new Mask();
+ $this->oMenuMask = new Mask();
+ $this->asSettings = array();
+ }
+
+ public function register($sLogin, $sPass)
+ {
+ $asData = array('user'=>$sLogin, 'pass'=>$sPass);
+ $bRegistered = $this->oSession->register($asData);
+ if($bRegistered)
+ {
+ $this->addThought(file_get_contents(self::WELCOME_MSG_FILE));
+ }
+ return $bRegistered;
+ }
+
+ public function logMeIn($sLogin, $sPass)
+ {
+ return $this->oSession->logMeIn($sLogin, $sPass);
+ }
+
+ public function logMeOut()
+ {
+ $this->oSession->logMeOut();
+ self::relocate();
+ }
+
+ public function isLogguedIn()
+ {
+ return $this->oSession->isLogguedIn();
+ }
+
+ public function checkPostToken($sPostToken)
+ {
+ return $this->oSession->checkPostToken($sPostToken);
+ }
+
+ public function setPage($sPage)
+ {
+ $this->oPageMask->initFile($sPage);
+ }
+
+ public function setPageTitle($sTitle)
+ {
+ $this->oMainMask->setTag('title', $sTitle);
+ }
+
+ public function setCalendarDate($iYear=0, $iMonth=0)
+ {
+ $this->oCalendar->setDate($iYear, $iMonth);
+ }
+
+ private static function relocate($sPage='', $asVar=array())
+ {
+ $asVar['p'] = $sPage;
+ header('Location:index.php?'.implodeAll($asVar, '=', '&'));
+ die();
+ }
+
+ public function addThought($sThought)
+ {
+ $asThought = array('thought'=>$this->encodeThought($sThought));
+ $asThought[MySqlManager::getId(MySqlManager::USERS_TABLE)] = $this->oSession->getUserId();
+ return $this->oMySql->insertRow(MySqlManager::THOUGHTS_TABLE, $asThought);
+ }
+
+ public function updateThought($iThoughtId, $sThought)
+ {
+ $asValues = array('thought'=>$this->encodeThought($sThought));
+ $asConstraints = array( MySqlManager::getId(MySqlManager::THOUGHTS_TABLE)=>$iThoughtId,
+ MySqlManager::getId(MySqlManager::USERS_TABLE)=>$this->oSession->getUserId());
+ $this->oMySql->updateRow(MySqlManager::THOUGHTS_TABLE, $asConstraints, $asValues);
+ }
+
+ private function encodeThought($sthought)
+ {
+ return base64_encode(serialize(explode("\n", $this->shuffleText($sthought))));
+ }
+
+ private function decodeThought($sEncodedThought)
+ {
+ return $this->shuffleText(implode("\n", unserialize(base64_decode($sEncodedThought))));
+ }
+
+ private function shuffleText($sText)
+ {
+ $sRandomText = "let's_mess%a&bit;with~it,!just§for¨the^sake*of-it";
+ for($iIndex=0; $iIndex < strlen($sText); $iIndex++)
+ {
+ $sText[$iIndex] = $sRandomText[$iIndex%strlen($sRandomText)] ^ $sText[$iIndex];
+ }
+ return $sText;
+ }
+
+ public function activateMenu()
+ {
+ $this->oMenuMask->initFile('menu');
+ }
+
+ //settings
+
+ public static function getSettingsList()
+ {
+ //TODO Save on database (param table)
+ return array(self::SETTING_FONT, self::SETTING_SIZE, self::SETTING_LAYOUT);
+ }
+
+ private static function getDefaultSetting($sSettingName)
+ {
+ switch($sSettingName)
+ {
+ case self::SETTING_FONT:
+ return self::FONT_THOUGHTS;
+ case self::SETTING_LAYOUT:
+ return self::LAYOUT_ONE_PAGE;
+ }
+ return false;
+ }
+
+ private function getSetting($sSettingName)
+ {
+ if(!array_key_exists($sSettingName, $this->asSettings))
+ {
+ $asConstraint = array(MySqlManager::getText(MySqlManager::SETTINGS_TABLE)=>$sSettingName, MySqlManager::getId(MySqlManager::USERS_TABLE)=>$this->oSession->getUserId());
+ $oValue = $this->oMySql->selectValue(MySqlManager::SETTINGS_TABLE, 'value', $asConstraint);
+ $this->asSettings[$sSettingName] = (!$oValue)?self::getDefaultSetting($sSettingName):$oValue;
+ }
+ return $this->asSettings[$sSettingName];
+ }
+
+ private function setSetting($sValue, $sSettingName)
+ {
+ $this->oMySql->insertUpdateRow(MySqlManager::SETTINGS_TABLE, array('setting'=>$sSettingName, MySqlManager::getId(MySqlManager::USERS_TABLE)=>$this->oSession->getUserId()), array('value'=>$sValue));
+ }
+
+ public function setSettings($asSettings)
+ {
+ array_walk($asSettings, array($this, 'setSetting'));
+ }
+
+ /* Pages */
+
+ public function logonPage($sPreviousLogin)
+ {
+ $this->setPageTitle('Login');
+ $this->setPage('logon');
+ $sPreviousLogin = ($sPreviousLogin=='')?'':$sPreviousLogin;
+ $this->oPageMask->setTag('login', $sPreviousLogin);
+ }
+
+ public function writingPage($iThoughtId=0)
+ {
+ $sThought = '';
+ $iThoughtTime = '';
+ if($iThoughtId!=0)
+ {
+ //load a thought
+ $asConstraints = array( MySqlManager::getId(MySqlManager::THOUGHTS_TABLE)=>$iThoughtId,
+ MySqlManager::getId(MySqlManager::USERS_TABLE)=>$this->oSession->getUserId());
+ $asThought = $this->oMySql->selectRow(MySqlManager::THOUGHTS_TABLE, $asConstraints);
+ $sThought = $this->decodeThought($asThought['thought']);
+ $iThoughtTime = 'Saved at '.date(self::LAYOUT_TIME_FORMAT, strtotime($asThought['led']));
+ }
+
+ $this->setPage('write_thought');
+ $this->oPageMask->setTag('font', $this->getSetting(self::SETTING_FONT));
+ $this->oPageMask->setTag('size', $this->getSetting(self::SETTING_SIZE));
+ $this->oPageMask->setTag('thought', $sThought);
+ $this->oPageMask->setTag('thought_id', $iThoughtId);
+ $this->oPageMask->setTag('last_saved', $iThoughtTime);
+ $this->setPageTitle('Talk to me');
+ }
+
+ public function readingPage($iTimeStamp=0)
+ {
+ if($iTimeStamp==0)
+ {
+ $iTimeStamp = strtotime('now');
+ }
+ $sMySqlDate = date(self::MYSQL_DATE_FORMAT, $iTimeStamp);
+ $sLayoutDate = date(self::LAYOUT_DATE_FORMAT, $iTimeStamp);
+
+ $asConstraints = array('DATE(led)'=>$sMySqlDate, MySqlManager::getId(MySqlManager::USERS_TABLE)=>$this->oSession->getUserId());
+ $asThougths = $this->oMySql->selectRows(array('from'=>MySqlManager::THOUGHTS_TABLE, 'constraint'=>$asConstraints));
+
+ $this->setPage('read_thought');
+ $this->setPageTitle('Thoughts on '.$sLayoutDate);
+ $this->oPageMask->setTag('date', $sLayoutDate);
+
+ foreach($asThougths as $asThought)
+ {
+ $asThoughtParagraphs = explode("\n", $this->decodeThought($asThought['thought']));
+ $this->oPageMask->newInstance('THOUGHT');
+ $this->oPageMask->setInstanceTag('THOUGHT', 'time', date(self::LAYOUT_TIME_FORMAT, strtotime($asThought['led'])));
+ foreach($asThoughtParagraphs as $sParagraph)
+ {
+ $asParagraphTags = array('thought_paragraph'=>$sParagraph);
+ $this->oPageMask->addInstance('THOUGHT_PARA', $asParagraphTags);
+ }
+ }
+
+ //calendar update
+ $this->setCalendarDate(date('Y', $iTimeStamp), date('m', $iTimeStamp));
+
+ return (count($asThougths)>0);
+ }
+
+ public function settingsPage()
+ {
+ $this->setPage('settings');
+ $this->setPageTitle('Settings');
+ $asSettingsOptions = array( self::SETTING_LAYOUT => array(
+ 'One extensible page' => self::LAYOUT_ONE_PAGE,
+ 'Two Pages, Diary like' => self::LAYOUT_TWO_PAGES),
+ self::SETTING_FONT => array(
+ 'AES Crawl' => self::FONT_THOUGHTS,
+ 'Arial' => self::FONT_ARIAL,
+ 'Verdana' => self::FONT_VERDANA),
+ self::SETTING_SIZE => array(
+ '16pt' => self::SIZE_16,
+ '18pt' => self::SIZE_18,
+ '20pt' => self::SIZE_20));
+
+ foreach(self::getSettingsList() as $sSettingName)
+ {
+ $this->oPageMask->newInstance('SETTING');
+ $this->oPageMask->setInstanceTag('SETTING', 'setting_name', $sSettingName);
+ $sUserSetting = $this->getSetting($sSettingName);
+ foreach($asSettingsOptions[$sSettingName] as $sOptionName=>$sOptionValue)
+ {
+ if($sOptionValue == self::getDefaultSetting($sSettingName))
+ {
+ $sOptionName .= ' (Default)';
+ }
+ $sSelectedOption = ($sUserSetting==$sOptionValue)?'selected':'';
+ $asSettingOptions = array( 'setting_option_value'=>$sOptionValue,
+ 'setting_option_selected'=>$sSelectedOption,
+ 'setting_option_name'=>$sOptionName);
+ $this->oPageMask->addInstance('SETTING_OPTION', $asSettingOptions);
+ }
+ }
+ }
+
+ /* Final processes */
+
+ private function collectErrors()
+ {
+ $asErrors = array_merge($this->getCleanErrorStack(),
+ $this->oMySql->getCleanErrorStack(),
+ $this->oSession->getCleanErrorStack(),
+ $this->oCalendar->getCleanErrorStack(),
+ $this->oMainMask->getCleanErrorStack(),
+ $this->oPageMask->getCleanErrorStack(),
+ $this->oMenuMask->getCleanErrorStack());
+
+ //$asErrors = array_map('getCleanErrorStack', array($this, $this->oMySql, $this->oSession, $this->oCalendar, $this->oMainMask, $this->oPageMask, $this->oMenuMask));
+ //pre($asErrors, 'static error stock', true);
+
+ $oErrorMask = new Mask();
+ if(!empty($asErrors))
+ {
+ $oErrorMask->initFile('errors');
+ foreach($asErrors as $sError)
+ {
+ $oErrorMask->addInstance('ERROR', array('error'=>$sError));
+ }
+ }
+ return $oErrorMask->getMask();
+ }
+
+ public function getPage()
+ {
+ $this->oMenuMask->setTag('calendar', $this->oCalendar->getCalendar());
+ $this->oMainMask->setTag('menu', $this->oMenuMask->getMask());
+ $this->oMainMask->setTag('content', $this->oPageMask->getMask());
+ $this->oMainMask->setTag('errors', $this->collectErrors());
+ $this->oMainMask->setTag('post_token', $this->oSession->getNewPostToken());
+ return $this->oMainMask->getMask();
+ }
+}
+
+class Calendar extends PhpObject
+{
+ const CAL_YEAR = 'cy';
+ const CAL_MONTH = 'cm';
+
+ private $oMySql;
+ private $oSession;
+ private $oMask;
+
+ private $iUserId;
+ private $iYear;
+ private $iMonth;
+
+ function __construct($oMySql, $oSession)
+ {
+ parent::__construct();
+ $this->oMySql = $oMySql;
+ $this->oSession = $oSession;
+ $this->oMask = new Mask('calendar');
+ $this->iYear = 0;
+ $this->iMonth = 0;
+ }
+
+ public function setDate($iYear=0, $iMonth=0)
+ {
+ if($iYear==0)
+ {
+ $iYear = date('Y');
+ }
+ if($iMonth==0)
+ {
+ $iMonth = date('m');
+ }
+ $this->iYear = $iYear;
+ $this->iMonth = $iMonth;
+ }
+
+ private function getThoughts()
+ {
+ //TODO essayer avec selectRows
+ $sQuery = "SELECT DATE_FORMAT(led, '%d') AS day
+ FROM ".MySqlManager::THOUGHTS_TABLE."
+ WHERE ".MySqlManager::getId(MySqlManager::USERS_TABLE)." = ".$this->oSession->getUserId()."
+ AND YEAR(led) = ".$this->iYear."
+ AND MONTH(led) = ".$this->iMonth."
+ GROUP BY day
+ ORDER BY day";
+
+ return $this->oMySql->getArrayQuery($sQuery, true);
+ }
+
+ private function getUpdatedLink($asParams)
+ {
+ $sCurrentVariables = $_SERVER['QUERY_STRING'];
+ $asCurrentVariables = explode('&', $sCurrentVariables);
+ foreach($asCurrentVariables as $sParam)
+ {
+ $sKey = strstr($sParam, '=', true);
+ $sValue = substr(strstr($sParam, '='), 1);
+ $asVariables[$sKey] = $sValue;
+ }
+ return '?'.implodeAll(array_merge($asVariables, $asParams), '=', '&');
+ }
+
+ private function getLink($iOffset)
+ {
+ $iTimeStamp = mktime(0, 0, 0, $this->iMonth + $iOffset, 1, $this->iYear);
+ return $this->getUpdatedLink(array(self::CAL_MONTH=>date('n', $iTimeStamp), self::CAL_YEAR=>date('Y', $iTimeStamp)));
+ }
+
+ private function setMaskItems()
+ {
+ //week starting on the sunday : offset = 0, monday : offset = 1
+ $iOffset = 1;
+
+ //days in the month
+ $iMonthLastDay = date('d', mktime(0, 0, 0, $this->iMonth+1, 0, $this->iYear));
+ $asDays = range(1, $iMonthLastDay);
+
+ $iDayNb = 1 - date($iOffset?'N':'w', mktime(0, 0, 0, $this->iMonth, 1, $this->iYear)) + $iOffset;
+ $iCalendarLastDay = $iMonthLastDay + (7 - date($iOffset?'N':'w', mktime(0, 0, 0, $this->iMonth+1, 0, $this->iYear))) + $iOffset;
+
+ //days with thoughts
+ $asThoughts = $this->getThoughts();
+
+ while($iDayNb < $iCalendarLastDay)
+ {
+ $iCurrentDayTimeStamp = mktime(0, 0, 0, $this->iMonth, $iDayNb, $this->iYear);
+ $sItemDate = date('d', $iCurrentDayTimeStamp);
+
+ //new week
+ if(date('w', $iCurrentDayTimeStamp) == $iOffset)
+ {
+ $this->oMask->newInstance('WEEK');
+ }
+
+ //day within month
+ if(date('n', $iCurrentDayTimeStamp)==$this->iMonth)
+ {
+ $bThoughts = in_array($iDayNb, $asThoughts);
+
+ $sItemClass = $bThoughts?'full':'empty';
+ $sItemLink = $bThoughts?$this->getUpdatedLink(array('d'=>date(MyThoughts::URL_DATE_FORMAT, $iCurrentDayTimeStamp), 'p'=>'r')):'#';
+ $sItemLinkTitle = $bThoughts?'See my thoughts on '.date(MyThoughts::LAYOUT_DATE_FORMAT, $iCurrentDayTimeStamp):'';
+ }
+ else
+ {
+ $sItemClass = 'disabled';
+ $sItemLink = '#';
+ $sItemLinkTitle = '';
+ }
+
+ $this->oMask->addInstance('DAY', array('item_day'=>$sItemDate, 'item_class'=>$sItemClass, 'item_link'=>$sItemLink, 'item_link_title'=>$sItemLinkTitle));
+ $iDayNb++;
+ }
+
+ //column titles
+ $asDayNames = array('1'=>'Mon', '2'=>'Tue', '3'=>'Wed', '4'=>'Thu', '5'=>'Fri', '6'=>'Sat', $iOffset?'7':'0'=>'Sun');
+ ksort($asDayNames);
+ foreach($asDayNames as $sDayName)
+ {
+ $this->oMask->addInstance('TITLE', array('day_name'=>$sDayName));
+ }
+
+ }
+
+ public function getCalendar()
+ {
+ $sResult = '';
+ if($this->iYear!=0 && $this->iMonth!=0)
+ {
+ $this->oMask->setTag('link_prev', $this->getLink(-1));
+ $this->oMask->setTag('current_month', date('F', mktime(0, 0, 0, $this->iMonth, 1, $this->iYear)));
+ $this->oMask->setTag('link_next', $this->getLink(1));
+ $this->setMaskItems();
+ $sResult = $this->oMask->getMask();
+ }
+ return $sResult;
+ }
+}
+
+function arrayKeyFilter($asArray, $sCallBack)
+{
+ $asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack));
+ return array_intersect_key($asArray, $asValidKeys);
+}
+
+function array_map_encapsulate($oData, $sChar)
+{
+ if(is_array($oData))
+ {
+ $asChar = array_fill(1, count($oData), $sChar);
+ return array_combine(array_keys($oData), array_map('array_map_encapsulate', $oData, $asChar));
+ }
+ else
+ {
+ return $sChar.$oData.$sChar;
+ }
+}
+
+function implodeAll($asText, $sKeyValueSeparator='', $sRowSeparator='', $sKeyPre='', $sValuePost=false)
+{
+ if($sValuePost===false)
+ {
+ $sValuePost = $sKeyPre;
+ }
+ $asCombinedText = array();
+ foreach($asText as $sKey=>$sValue)
+ {
+ $asCombinedText[] = $sKeyPre.$sKey.$sKeyValueSeparator.$sValue.$sValuePost;
+ }
+ return implode($sRowSeparator, $asCombinedText);
+}
+
+function cleanPost(&$asData)
+{
+ //get rid of magic quotes
+ if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
+ {
+ cleanData($asData, 'stripslashes');
+ }
+}
+function cleanData(&$oData, $sCleaningFunc)
+{
+ if(!is_array($oData))
+ {
+ $oData = call_user_func($sCleaningFunc, $oData);
+ }
+ elseif(!empty($oData))
+ {
+ $asKeys = array_map($sCleaningFunc, array_keys($oData));
+ $asValues = array_map($sCleaningFunc, $oData);
+ $oData = array_combine($asKeys, $asValues);
+ }
+}
+
+//debug
+function pre($sText, $sTitle='Test', $bDie=false, $bLog=false)
+{
+ if($bLog)
+ {
+ file_put_contents('log', ($sTitle!=''?$sTitle." :\n":'').print_r($sText, true)."\n\n");
+ }
+ echo ''.$sTitle.' '.print_r($sText, true).' ';
+ if($bDie)
+ {
+ die('[die() called by the test function '.__FUNCTION__.'()]');
+ }
+}
+?>
\ No newline at end of file
diff --git a/functions.js b/functions.js
new file mode 100755
index 0000000..dbb4490
--- /dev/null
+++ b/functions.js
@@ -0,0 +1,54 @@
+function emptyBox(element, text)
+{
+ //var textarea = $('#thoughts_form textarea[name="thoughts"]');
+ if(element.value == text)
+ {
+ element.value = '';
+ }
+ else if(element.value == '')
+ {
+ element.value = text;
+ }
+}
+
+function setHeight(element)
+{
+ var padtext = element.value;
+ var height = Math.max(300, 130 + Math.round((padtext.length / 85 + padtext.split("\n").length) * 20));
+ //alert(height);
+ element.style.height = height+'px';
+}
+
+function goTo(url)
+{
+ window.location.href = url;
+}
+
+function addInput(form, name, type, value)
+{
+ var registerInput = document.createElement('input');
+ registerInput.setAttribute('type', type);
+ registerInput.setAttribute('name', name);
+ registerInput.setAttribute('value', value);
+ document.forms[form].appendChild(registerInput);
+}
+
+/*
+texts = new Object();
+texts['thoughts'] = 'Talk to me.';
+texts['login'] = 'Nickname';
+texts['pass'] = 'Password';
+
+window.onload = function ()
+{
+ for (i in texts)
+ {
+ var id = document.getElementById(i);
+ if(id)
+ {
+ id.addEventListener('focus', function() {emptyBox(this, texts[this.name]);}, false);
+ id.addEventListener('blur', function() {emptyBox(this, texts[this.name]);}, false);
+ }
+ }
+};
+*/
\ No newline at end of file
diff --git a/images/bubble.png b/images/bubble.png
new file mode 100755
index 0000000..9e4dce9
Binary files /dev/null and b/images/bubble.png differ
diff --git a/images/bubble_inverted.png b/images/bubble_inverted.png
new file mode 100755
index 0000000..1837518
Binary files /dev/null and b/images/bubble_inverted.png differ
diff --git a/images/button_left.png b/images/button_left.png
new file mode 100755
index 0000000..c8f50fe
Binary files /dev/null and b/images/button_left.png differ
diff --git a/images/button_left_hover.png b/images/button_left_hover.png
new file mode 100755
index 0000000..0a82c7a
Binary files /dev/null and b/images/button_left_hover.png differ
diff --git a/images/button_middle.png b/images/button_middle.png
new file mode 100755
index 0000000..949df54
Binary files /dev/null and b/images/button_middle.png differ
diff --git a/images/button_middle_hover.png b/images/button_middle_hover.png
new file mode 100755
index 0000000..6eed1ce
Binary files /dev/null and b/images/button_middle_hover.png differ
diff --git a/images/button_right.png b/images/button_right.png
new file mode 100755
index 0000000..1b299e4
Binary files /dev/null and b/images/button_right.png differ
diff --git a/images/button_right_hover.png b/images/button_right_hover.png
new file mode 100755
index 0000000..6551e58
Binary files /dev/null and b/images/button_right_hover.png differ
diff --git a/images/error.png b/images/error.png
new file mode 100755
index 0000000..b44adf4
Binary files /dev/null and b/images/error.png differ
diff --git a/images/favicon.ico b/images/favicon.ico
new file mode 100755
index 0000000..701baa2
Binary files /dev/null and b/images/favicon.ico differ
diff --git a/images/favicon2.ico b/images/favicon2.ico
new file mode 100755
index 0000000..fe263eb
Binary files /dev/null and b/images/favicon2.ico differ
diff --git a/images/logo.png b/images/logo.png
new file mode 100755
index 0000000..69ea6b2
Binary files /dev/null and b/images/logo.png differ
diff --git a/images/minicloud.png b/images/minicloud.png
new file mode 100755
index 0000000..adf7aeb
Binary files /dev/null and b/images/minicloud.png differ
diff --git a/images/minicloud_active.png b/images/minicloud_active.png
new file mode 100755
index 0000000..758737d
Binary files /dev/null and b/images/minicloud_active.png differ
diff --git a/images/minicloud_disabled.png b/images/minicloud_disabled.png
new file mode 100755
index 0000000..5c5e06c
Binary files /dev/null and b/images/minicloud_disabled.png differ
diff --git a/images/minicloud_hover.png b/images/minicloud_hover.png
new file mode 100755
index 0000000..2d212bc
Binary files /dev/null and b/images/minicloud_hover.png differ
diff --git a/images/pad_bottom.gif b/images/pad_bottom.gif
new file mode 100755
index 0000000..5bb3777
Binary files /dev/null and b/images/pad_bottom.gif differ
diff --git a/images/pad_line.gif b/images/pad_line.gif
new file mode 100755
index 0000000..10fc49b
Binary files /dev/null and b/images/pad_line.gif differ
diff --git a/images/pad_top.gif b/images/pad_top.gif
new file mode 100755
index 0000000..e894916
Binary files /dev/null and b/images/pad_top.gif differ
diff --git a/images/writing_cloud.png b/images/writing_cloud.png
new file mode 100755
index 0000000..16f1559
Binary files /dev/null and b/images/writing_cloud.png differ
diff --git a/index.php b/index.php
new file mode 100755
index 0000000..52d725e
--- /dev/null
+++ b/index.php
@@ -0,0 +1,109 @@
+checkPostToken($sPostToken));
+
+if($bValidPost)
+{
+ if($bRegister)
+ {
+ $oMyThougths->register($sLogin, $sPass);
+ $sPage = 'r';
+ }
+ elseif($sLogin!='' && $sPass!='')
+ {
+ $oMyThougths->logMeIn($sLogin, $sPass);
+ }
+}
+
+//if loggued in
+if(!$oMyThougths->isLogguedIn())
+{
+ $oMyThougths->logonPage($sLogin);
+}
+else
+{
+ $oMyThougths->activateMenu();
+ $oMyThougths->setCalendarDate();
+ switch($sPage)
+ {
+ case 'w': //write a thought
+ if($bValidPost && $sThought!='' && $sThought!='Talk to me.')
+ {
+ if($iThoughtId==0)
+ {
+ $iThoughtId = $oMyThougths->addThought($sThought);
+ }
+ else
+ {
+ $oMyThougths->updateThought($iThoughtId, $sThought);
+ }
+ }
+ if($bFinishedWriting)
+ {
+ $oMyThougths->readingPage();
+ }
+ else
+ {
+ $oMyThougths->writingPage($iThoughtId);
+ }
+ break;
+ case 'r': //read a thought (per day)
+ if($iDay<=0 || !$oMyThougths->readingPage(strtotime($iDay)))
+ {
+ $oMyThougths->writingPage();
+ }
+ break;
+ case 's': // go to settings page
+ if($bValidPost)
+ {
+ $asSettings = array_intersect_key($_POST, array_flip($oMyThougths->getSettingsList()));
+ $oMyThougths->setSettings($asSettings);
+ $oMyThougths->writingPage();
+ }
+ else
+ {
+ $oMyThougths->settingsPage();
+ }
+ break;
+ case 'q': //quit
+ $oMyThougths->logMeOut();
+ }
+
+ if($iCalYear!=0 && $iCalMonth!=0)
+ {
+ $oMyThougths->setCalendarDate($iCalYear, $iCalMonth);
+ }
+}
+echo $oMyThougths->getPage();
+?>
\ No newline at end of file
diff --git a/mask/calendar.html b/mask/calendar.html
new file mode 100755
index 0000000..488bca6
--- /dev/null
+++ b/mask/calendar.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+ <-
+ #current_month#
+ ->
+
+
+
+
+
+
+ #day_name#
+
+
+
+
+
+ #item_day#
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mask/errors.html b/mask/errors.html
new file mode 100755
index 0000000..9766838
--- /dev/null
+++ b/mask/errors.html
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/mask/index.html b/mask/index.html
new file mode 100755
index 0000000..ed06854
--- /dev/null
+++ b/mask/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ Thoughts • #title#
+
+
+
+
+
+
+ #content#
+
+
+ #errors#
+
+
+
+
\ No newline at end of file
diff --git a/mask/logon.html b/mask/logon.html
new file mode 100755
index 0000000..ce746c0
--- /dev/null
+++ b/mask/logon.html
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/mask/menu.html b/mask/menu.html
new file mode 100755
index 0000000..d878146
--- /dev/null
+++ b/mask/menu.html
@@ -0,0 +1,4 @@
+Write .
+Settings .
+Sign out
+#calendar#
\ No newline at end of file
diff --git a/mask/read_thought.html b/mask/read_thought.html
new file mode 100755
index 0000000..388feaa
--- /dev/null
+++ b/mask/read_thought.html
@@ -0,0 +1,14 @@
+Thoughts on #date#.
+
+
+
+
At #time#
+
+
+
#thought_paragraph#
+
+
* * *
+
+
+
+
diff --git a/mask/settings.html b/mask/settings.html
new file mode 100755
index 0000000..df30cd6
--- /dev/null
+++ b/mask/settings.html
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/mask/write_thought.html b/mask/write_thought.html
new file mode 100755
index 0000000..a050e22
--- /dev/null
+++ b/mask/write_thought.html
@@ -0,0 +1,77 @@
+
+
\ No newline at end of file
diff --git a/style.css b/style.css
new file mode 100755
index 0000000..9bf3127
--- /dev/null
+++ b/style.css
@@ -0,0 +1,365 @@
+@CHARSET "ISO-8859-1";
+
+/* Colors
+bright brown : #e2ccb2
+dark brown : #584127
+blue lines : #2DCDFF
+red lines : #EC3B45
+*/
+
+/* General */
+
+@font-face {
+ font-family: 'thoughts';
+ font-style: normal;
+ font-weight: normal;
+ src: url('aescrawl.ttf') format('truetype');
+}
+
+body {
+ min-width: 700px;
+ font-family: thoughts, Arial;
+ font-size:14px;
+ background-color:#e2ccb2;
+}
+
+table {
+ border:none;
+ background:none;
+ text-align:center;
+ margin:0;
+ padding:0;
+ border-spacing:0;
+}
+
+input, textarea {
+ font-family: thoughts, Arial;
+ font-size:14px;
+}
+
+input[type=button], input[type=submit] {
+ -moz-border-radius:5px;
+ -webkit-border-radius:5px;
+ border-radius:5px;
+ font-weight:bold;
+}
+
+p {
+ margin:0;
+ padding:0;
+}
+
+a:visited, a {
+ color:black;
+}
+
+a:active, a:focus, input:active, input:focus {
+ outline: none;
+}
+
+.round {
+ -moz-border-radius:10px;
+ -webkit-border-radius:10px;
+ border-radius:10px;
+}
+.round_top {
+ -moz-border-radius:10px 10px 0 0;
+ -webkit-border-radius:10px 10px 0 0;
+ border-radius:10px 10px 0 0;
+}
+.round_right {
+ -moz-border-radius:0 10px 10px 0;
+ -webkit-border-radius:0 10px 10px 0;
+ border-radius:0 10px 10px 0;
+}
+
+button::-moz-focus-inner, input[type="reset"]::-moz-focus-inner, input[type="button"]::-moz-focus-inner, input[type="submit"]::-moz-focus-inner,input[type="file"] > input[type="button"]::-moz-focus-inner {
+ border: none;
+}
+
+/* Container */
+
+#container {
+ width:700px;
+ margin:auto;
+ /*border:1px solid black;*/
+}
+
+/* Header */
+
+#header {
+ margin-left:-100px;
+ height:203px;
+ background:url('images/logo.png') 0 0 no-repeat;
+}
+
+/* Menu */
+
+#menu {
+
+}
+
+#menu a.option {
+ display:inline-table;
+ height:50px;
+ width:90px;
+ line-height:50px;
+ text-align:center;
+ background: url("images/button_left.png") 0 0 no-repeat,
+ url("images/button_right.png") 100% 0 no-repeat,
+ url("images/button_middle.png") 0 0 repeat-x;
+ text-decoration: none;
+}
+#menu a.option:hover {
+ color:white;
+ background: url("images/button_left_hover.png") 0 0 no-repeat,
+ url("images/button_right_hover.png") 100% 0 no-repeat,
+ url("images/button_middle_hover.png") 0 0 repeat-x;
+}
+
+/* Calendar */
+
+#calendar {
+ position:absolute;
+ margin-top:-40px;
+ margin-left:560px;
+}
+
+table.calendar_list {
+ /*border:2px solid #584127;*/
+ background-color:#e2ccb2;
+}
+
+table.calendar_list tbody {
+ display:none;
+ border:2px solid #584127;
+}
+
+table.calendar_list:hover tbody {
+ display:inline-table;
+ overflow:visible;
+}
+
+table.calendar_list tr.calendar_items td {
+ background-position:-6px -7px;
+ background-repeat:no-repeat;
+ width:37px;
+ height:42px;
+ padding-top:2px;
+ padding-right:3px;
+}
+
+table.calendar_list tr th {
+ width:auto;
+ text-align:left;
+ font-size:16px;
+ /*border-bottom:2px solid #584127;*/
+ padding-top:2px;
+}
+
+table.calendar_list tr td.item_full {
+ background-image:url('images/minicloud.png');
+ cursor:pointer;
+}
+table.calendar_list tr td.item_full:hover {
+ background-image:url('images/minicloud_hover.png');
+ color:white;
+}
+table.calendar_list tr td.item_full:active {
+ background-image:url('images/minicloud_active.png');
+ background-position: 53% 53%;
+}
+table.calendar_list tr td.item_empty {
+ background-image:url('images/minicloud_disabled.png');
+ color:#e2ccb2;
+ font-weight:bold;
+}
+table.calendar_list tr td.item_disabled {
+ color:#e2ccb2;
+}
+
+a.calendar_direction {
+ text-decoration:none;
+}
+
+/* Main */
+
+#main {
+ padding-top:30px;
+}
+
+/* Log on */
+
+#logon {
+ position:relative;
+ margin: 80px auto;
+ padding-top:40px;
+ height:225px;
+ background:url('images/bubble_inverted.png') 135px 0 no-repeat;
+ text-align:center;
+}
+
+#logon div.credentials {
+ font-size:24px;
+ text-align:left;
+ margin:15px auto 0;
+ padding-left:100px;
+ width:370px;
+}
+
+#logon input {
+ font-size:24px;
+ border:none;
+ background-color:transparent;
+}
+
+#logon div.credentials input {
+ width:130px;
+ border-bottom:2px dashed black;
+ height:24px;
+}
+
+#logon input.connection {
+ position:absolute;
+ left:512px;
+ top:142px;
+ font-size:24px;
+ cursor:pointer;
+}
+
+#logon input.register {
+ margin-top:60px;
+}
+
+/* Write Thought */
+
+#write_thought {
+ text-align:center;
+}
+
+div.save {
+ position:fixed;
+ top:0;
+ margin-top:320px;
+ margin-left:2px; /*page margin*/
+ width:110px;
+ background-color:white;
+}
+
+div.save input {
+ border:1px solid #2DCDFF;
+ background-color:white;
+}
+
+div.save p {
+ color:grey;
+ font-size:12px;
+}
+
+textarea.write, div.read {
+ border:2px solid #584127;
+ /*background-color:rgba(255, 255, 255, 0.5);*/
+ background: url("images/pad_top.gif") 0 0 no-repeat,
+ url("images/pad_bottom.gif") 0 100% no-repeat,
+ url("images/pad_line.gif") 0 0 repeat-y;
+ width:580px;
+ min-height:300px;
+ padding:85px 5px 50px 115px;
+ margin:auto;
+ overflow:hidden;
+}
+
+textarea.write {
+ font-size:16px;
+ line-height:20px;
+}
+
+input.save:hover {
+ border-color:#EC3B45;
+}
+
+/* Read Thought */
+
+div.read {
+ position:relative;
+ padding-bottom:30px;
+}
+
+div.thought {
+ font-size:16px;
+ line-height:20px;
+}
+
+p.date {
+ font-size:20px;
+}
+
+div.time {
+ position:absolute;
+ margin-left:-100px;
+}
+
+div.paragraphs p {
+ margin:0 0 20px 0;
+}
+
+div.paragraphs p:first-letter {
+ font-size:59px;
+ line-height:16px;
+ margin-right:5px;
+ font-weight:400;
+ float:left;
+ text-transform:uppercase;
+}
+
+div.paragraphs p + p, div.paragraphs p + p:first-letter {
+ font-size:inherit;
+ line-height:inherit;
+ margin-right:inherit;
+ font-weight:normal;
+ float:none;
+ text-indent:40px;
+}
+
+/* Settings */
+
+#settings table tr td {
+ text-align:left;
+}
+
+/* Footer */
+
+#footer {
+ text-align:center;
+ color:grey;
+ font-size:12px;
+}
+
+#footer a {
+ color:grey;
+}
+
+/* Errors */
+
+#errors {
+ position:fixed;
+ bottom:-2px;
+ background-color:white;
+ width:696px;
+ border:2px solid red;
+}
+#errors ul li {
+ list-style-image: url(images/error.png);
+ color:red;
+ font-weight: bold;
+ font-size:24px;
+}
+
+fieldset {
+ margin:20px auto;
+ width:90%;
+}
+
+fieldset p {
+ text-align: justify;
+}
\ No newline at end of file
diff --git a/test.php b/test.php
new file mode 100755
index 0000000..1f1dc9a
--- /dev/null
+++ b/test.php
@@ -0,0 +1,36 @@
+
+
+ #legend#
+ #thought#
+
+
+BETWEEN THOUGHT1 AND THOUGHT2
+
+ THOUGHT2
+
+ THOUGHT2.1
+
+ THOUGHT2.1.1
+
+ THOUGHT2.1
+
+ BETWEEN THOUGHT2.1 AND THOUGHT2.2
+
+ THOUGHT2.2
+
+ BETWEEN THOUGHT2.2 AND THOUGHT2.3
+
+ THOUGHT2.3
+
+ THOUGHT2
+
+BETWEEN THOUGHT2 AND THOUGHT3
+
+ THOUGHT3
+
+AFTER THOUGHT3
+ */
\ No newline at end of file
diff --git a/welcome b/welcome
new file mode 100755
index 0000000..64ef21f
--- /dev/null
+++ b/welcome
@@ -0,0 +1,2 @@
+Welcome To MyThoughts Online Application! Hope you'll enjoy the effort. don't hesitate to send feedbacks: francois at lutran dot fr
+Hit the "Write" button to start writing your thoughts.
\ No newline at end of file