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 @@ +
+
    + +
  • #error#
  • + +
+
\ 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 @@ +
+
+
+

Nickname ->

+

Password ->

+
+

+

+
+
+ \ 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 @@ +
+
+ + + + + + + +
#setting_name# + +
+ +
+
\ 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 @@ +
+
+
+
+ +

#last_saved#

+
+ +
+
+ \ 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