Patch multiday thoughts

This commit is contained in:
2020-03-22 17:29:50 +01:00
parent 80b9a54607
commit 1c88ce7dfa
17 changed files with 2372 additions and 2285 deletions

View File

@@ -1,216 +1,214 @@
<?php <?php
class Auth extends PhpObject class Auth extends PhpObject
{ {
const ALGO = PASSWORD_DEFAULT; const ALGO = PASSWORD_DEFAULT;
const COST = 12; const COST = 12;
const TOKEN_SEP = '|'; const TOKEN_SEP = '|';
const USER_COOKIE_PASS = 'checksum'; const USER_COOKIE_PASS = 'checksum';
const DEFAULT_ERROR = 'Unknown error'; const DEFAULT_ERROR = 'Unknown error';
/** /**
* Database Connection * Database Connection
* @var Db * @var Db
*/ */
private $oDb; private $oDb;
private $iUserId; private $iUserId;
private $sApiKey; private $sApiKey;
public function __construct($oDb, $sApiKey='', $bAutoLogin=true) public function __construct($oDb, $sApiKey='', $bAutoLogin=true)
{ {
parent::__construct(__CLASS__, Settings::DEBUG); parent::__construct(__CLASS__, Settings::DEBUG);
$this->oDb = $oDb; $this->oDb = $oDb;
$this->setUserId(0); $this->setUserId(0);
$this->sApiKey = $sApiKey; $this->sApiKey = $sApiKey;
if($bAutoLogin) $this->autoLogIn(); if($bAutoLogin) $this->autoLogIn();
} }
private function setUserId($iUserId) private function setUserId($iUserId)
{ {
$this->iUserId = $iUserId; $this->iUserId = $iUserId;
} }
public function getUserId() public function getUserId()
{ {
return $this->iUserId; return $this->iUserId;
} }
public function isLoggedIn() public function isLoggedIn()
{ {
return ($this->getUserId() > 0); return ($this->getUserId() > 0);
} }
public function logMeIn($sToken) public function logMeIn($sToken)
{ {
$sDesc = ''; $sDesc = '';
$asUser = $this->getUserFromToken($sToken); $asUser = $this->getUserFromToken($sToken);
if($asUser['success']) if($asUser['success'])
{ {
if(self::checkPassword($asUser['http_pass'], $asUser['pass'])) if(self::checkPassword($asUser['http_pass'], $asUser['pass']))
{ {
$this->setUserId($asUser[Db::getId(MyThoughts::USER_TABLE)]); $this->setUserId($asUser[Db::getId(MyThoughts::USER_TABLE)]);
$this->resetAuthCookie($this->getUserId()); $this->resetAuthCookie($this->getUserId());
} }
else $sDesc = 'wrong password'; else $sDesc = 'wrong password';
} }
else $sDesc = $asUser['desc']; else $sDesc = $asUser['desc'];
return array('success'=>$this->isLoggedIn(), 'desc'=>$sDesc); return array('success'=>$this->isLoggedIn(), 'desc'=>$sDesc);
} }
public function register($sToken, $sNickName, $bLogMeIn=false) public function register($sToken, $sNickName, $bLogMeIn=false)
{ {
$bSuccess = false; $bSuccess = false;
$sDesc = self::DEFAULT_ERROR; $sDesc = self::DEFAULT_ERROR;
$asUser = $this->getUserFromToken($sToken); $asUser = $this->getUserFromToken($sToken);
if(array_key_exists('unknown_user', $asUser)) if(array_key_exists('unknown_user', $asUser))
{ {
$iUserId = $this->addUser($asUser['username'], $sNickName, $asUser['http_pass'], $bLogMeIn); $iUserId = $this->addUser($asUser['username'], $sNickName, $asUser['http_pass'], $bLogMeIn);
if($iUserId > 0) $bSuccess = true; if($iUserId > 0) $bSuccess = true;
else $sDesc = 'Error: Could not add user'; else $sDesc = 'Error: Could not add user';
} }
else $sDesc = 'Someone is already using this nickname, sorry!'; else $sDesc = 'Someone is already using this nickname, sorry!';
$asResult = array('success'=>$bSuccess, 'desc'=>$sDesc); $asResult = array('success'=>$bSuccess, 'desc'=>$sDesc);
return $asResult; return $asResult;
} }
private function getUserFromToken($sToken) private function getUserFromToken($sToken)
{ {
$asResult = array(); $asResult = array();
$bSuccess = false; $bSuccess = false;
$sDesc = self::DEFAULT_ERROR; $sDesc = self::DEFAULT_ERROR;
if($sToken!='') if($sToken!='')
{ {
$asResult['username'] = addslashes(strstr($sToken, self::TOKEN_SEP, true)); $asResult['username'] = addslashes(strstr($sToken, self::TOKEN_SEP, true));
$asResult['http_pass'] = substr(strstr($sToken, self::TOKEN_SEP), strlen(self::TOKEN_SEP)); $asResult['http_pass'] = substr(strstr($sToken, self::TOKEN_SEP), strlen(self::TOKEN_SEP));
if($asResult['username']!='' && $asResult['http_pass']!='') if($asResult['username']!='' && $asResult['http_pass']!='')
{ {
$asUser = $this->oDb->selectRow(MyThoughts::USER_TABLE, array(Db::getText(MyThoughts::USER_TABLE)=>$asResult['username'])); $asUser = $this->oDb->selectRow(MyThoughts::USER_TABLE, array(Db::getText(MyThoughts::USER_TABLE)=>$asResult['username']));
if(!empty($asUser)) if(!empty($asUser))
{ {
$asResult += $asUser; $asResult += $asUser;
$bSuccess = true; $bSuccess = true;
} }
else else
{ {
$asResult['unknown_user'] = true; $asResult['unknown_user'] = true;
$sDesc = 'unknown nickname'; $sDesc = 'unknown nickname';
} }
} }
else $sDesc = 'corrupted token, please login again'; else $sDesc = 'corrupted token, please login again';
} }
else $sDesc = 'no credentials has been received by the server'; else $sDesc = 'no credentials has been received by the server';
$asResult['success'] = $bSuccess; $asResult['success'] = $bSuccess;
$asResult['desc'] = $sDesc; $asResult['desc'] = $sDesc;
return $asResult; return $asResult;
} }
public function autoLogIn() public function autoLogIn()
{ {
if(isset($_COOKIE[self::USER_COOKIE_PASS])) if(isset($_COOKIE[self::USER_COOKIE_PASS]))
{ {
$sCookie = $_COOKIE[self::USER_COOKIE_PASS]; $sCookie = $_COOKIE[self::USER_COOKIE_PASS];
$iUserId = addslashes(strstr($sCookie, self::TOKEN_SEP, true)); $iUserId = addslashes(strstr($sCookie, self::TOKEN_SEP, true));
$sCookie = substr(strstr($sCookie, self::TOKEN_SEP), strlen(self::TOKEN_SEP)); $sCookie = substr(strstr($sCookie, self::TOKEN_SEP), strlen(self::TOKEN_SEP));
$asEmpl = $this->oDb->selectRow(MyThoughts::USER_TABLE, array(Db::getId(MyThoughts::USER_TABLE)=>$iUserId)); $asEmpl = $this->oDb->selectRow(MyThoughts::USER_TABLE, array(Db::getId(MyThoughts::USER_TABLE)=>$iUserId));
if(!empty($asEmpl)) if(!empty($asEmpl))
{ {
if($sCookie==$asEmpl['cookie']) if($sCookie==$asEmpl['cookie'])
{ {
$this->setUserId($asEmpl[Db::getId(MyThoughts::USER_TABLE)]); $this->setUserId($asEmpl[Db::getId(MyThoughts::USER_TABLE)]);
//Reset pass once a day //Reset pass once a day
if(mb_substr($asEmpl['led'], 0, 10) != date('Y-m-d')) $this->resetAuthCookie($this->getUserId()); if(mb_substr($asEmpl['led'], 0, 10) != date('Y-m-d')) $this->resetAuthCookie($this->getUserId());
} }
else $this->addError('token corrompu pour le user '.$asEmpl[Db::getId(MyThoughts::USER_TABLE)]); else $this->addError('token corrompu pour le user '.$asEmpl[Db::getId(MyThoughts::USER_TABLE)]);
} }
else $this->addError('Utilisateur '.$iUserId.' inconnu'); else $this->addError('Utilisateur '.$iUserId.' inconnu');
} }
} }
public function addUser($sUserHash, $sNickName, $sLoginToken, $bLogMeIn=false) public function addUser($sUserHash, $sNickName, $sLoginToken, $bLogMeIn=false)
{ {
$sPass = self::hashPassword($sLoginToken); $sPass = self::hashPassword($sLoginToken);
$bExist = $this->oDb->pingValue(MyThoughts::USER_TABLE, array(Db::getText(MyThoughts::USER_TABLE)=>$sUserHash)); $bExist = $this->oDb->pingValue(MyThoughts::USER_TABLE, array(Db::getText(MyThoughts::USER_TABLE)=>$sUserHash));
if($bExist) return -1; if($bExist) return -1;
else else
{ {
$iUserId = $this->oDb->insertRow(MyThoughts::USER_TABLE, array(Db::getText(MyThoughts::USER_TABLE)=>$sUserHash, 'nickname'=>$sNickName, 'pass'=>$sPass)); $iUserId = $this->oDb->insertRow(MyThoughts::USER_TABLE, array(Db::getText(MyThoughts::USER_TABLE)=>$sUserHash, 'nickname'=>$sNickName, 'pass'=>$sPass));
if($iUserId>0 && $bLogMeIn) if($iUserId>0 && $bLogMeIn)
{ {
$this->logMeIn($sUserHash.self::TOKEN_SEP.$sPass); $this->logMeIn($sUserHash.self::TOKEN_SEP.$sPass);
} }
} }
return $iUserId; return $iUserId;
} }
//TODO integrate with logMeIn() //TODO integrate with logMeIn()
public function checkApiKey($sApiKey) public function checkApiKey($sApiKey)
{ {
return ($this->sApiKey!='' && $sApiKey==$this->sApiKey); return ($this->sApiKey!='' && $sApiKey==$this->sApiKey);
} }
private function resetPass($iUserId=0) private function resetPass($iUserId=0)
{ {
$sUserIdCol = Db::getId(MyThoughts::USER_TABLE); $sUserIdCol = Db::getId(MyThoughts::USER_TABLE);
$sUserTextCol = Db::getText(MyThoughts::USER_TABLE); $sUserTextCol = Db::getText(MyThoughts::USER_TABLE);
$asInfo = array('select'=>array($sUserIdCol, $sUserTextCol), 'from'=>MyThoughts::USER_TABLE); $asInfo = array('select'=>array($sUserIdCol, $sUserTextCol), 'from'=>MyThoughts::USER_TABLE);
if($iUserId>0) $asInfo['constraint'] = array($sUserIdCol=>$iUserId); if($iUserId>0) $asInfo['constraint'] = array($sUserIdCol=>$iUserId);
$asUsers = $this->oDb->selectRows($asInfo); $asUsers = $this->oDb->selectRows($asInfo);
foreach($asUsers as $asUser) foreach($asUsers as $asUser)
{ {
$sToken = self::hashPassword(self::getLoginToken($asUser[$sUserTextCol])); $sToken = self::hashPassword(self::getLoginToken($asUser[$sUserTextCol]));
$this->oDb->updateRow(MyThoughts::USER_TABLE, array(Db::getId(MyThoughts::USER_TABLE)=>$asUser[$sUserIdCol]), array('pass'=>$sToken)); $this->oDb->updateRow(MyThoughts::USER_TABLE, array(Db::getId(MyThoughts::USER_TABLE)=>$asUser[$sUserIdCol]), array('pass'=>$sToken));
} }
} }
public static function getLoginToken($sPass) public static function getLoginToken($sPass)
{ {
//Add Server Name //Add Server Name
$sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD']; $sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD'];
$sAppPath = $_SERVER['REQUEST_SCHEME'].'://'.str_replace(array('http://', 'https://'), '', $sServerName.dirname($_SERVER['SCRIPT_NAME'])); $sAppPath = $_SERVER['REQUEST_SCHEME'].'://'.str_replace(array('http://', 'https://'), '', $sServerName.dirname($_SERVER['SCRIPT_NAME']));
$_GET['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':''); $_GET['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':'');
return md5($sPass.$_GET['serv_name']); return md5($sPass.$_GET['serv_name']);
} }
private function resetAuthCookie($iUserId) private function resetAuthCookie($iUserId)
{ {
$sNewPass = self::getAuthCookie($iUserId); $sNewPass = self::getAuthCookie($iUserId);
$iTimeLimit = time() + 60 * 60 * 24 * 30; $iTimeLimit = time() + 60 * 60 * 24 * 30;
$this->oDb->updateRow(MyThoughts::USER_TABLE, array(Db::getId(MyThoughts::USER_TABLE)=>$iUserId), array("cookie"=>$sNewPass)); $this->oDb->updateRow(MyThoughts::USER_TABLE, array(Db::getId(MyThoughts::USER_TABLE)=>$iUserId), array("cookie"=>$sNewPass));
setcookie(self::USER_COOKIE_PASS, $iUserId.self::TOKEN_SEP.$sNewPass, $iTimeLimit); setcookie(self::USER_COOKIE_PASS, $iUserId.self::TOKEN_SEP.$sNewPass, $iTimeLimit);
} }
private static function getAuthCookie() private static function getAuthCookie()
{ {
return self::hashPassword return self::hashPassword
( (
$_SERVER['HTTP_USER_AGENT']. $_SERVER['HTTP_USER_AGENT'].
$_SERVER['REMOTE_ADDR']. $_SERVER['REMOTE_ADDR'].
$_SERVER['REQUEST_TIME']. $_SERVER['REQUEST_TIME'].
mb_strstr(microtime(), ' ', true). mb_strstr(microtime(), ' ', true).
$_SERVER['SERVER_SIGNATURE']. $_SERVER['SERVER_SIGNATURE'].
$_SERVER['SERVER_ADMIN'] $_SERVER['SERVER_ADMIN']
); );
} }
private static function hashPassword($sPass) private static function hashPassword($sPass)
{ {
return password_hash($sPass, self::ALGO, array('cost'=>self::COST)); return password_hash($sPass, self::ALGO, array('cost'=>self::COST));
} }
private static function checkPassword($sPass, $sHash) private static function checkPassword($sPass, $sHash)
{ {
return password_verify($sPass, $sHash); return password_verify($sPass, $sHash);
} }
} }
?>

View File

@@ -1,142 +1,142 @@
<?php <?php
class Calendar extends PhpObject class Calendar extends PhpObject
{ {
const CAL_YEAR = 'cy'; const CAL_YEAR = 'cy';
const CAL_MONTH = 'cm'; const CAL_MONTH = 'cm';
private $oMySql; private $oMySql;
private $oSession; private $oSession;
private $oMask; private $oMask;
private $iUserId; private $iUserId;
private $iYear; private $iYear;
private $iMonth; private $iMonth;
function __construct($oMySql, $oSession) function __construct($oMySql, $oSession)
{ {
parent::__construct(); parent::__construct();
$this->oMySql = $oMySql; $this->oMySql = $oMySql;
$this->oSession = $oSession; $this->oSession = $oSession;
$this->oMask = new Mask('calendar'); $this->oMask = new Mask('calendar');
$this->iYear = 0; $this->iYear = 0;
$this->iMonth = 0; $this->iMonth = 0;
} }
public function setDate($iYear=0, $iMonth=0) public function setDate($iYear=0, $iMonth=0)
{ {
if($iYear==0) if($iYear==0)
{ {
$iYear = date('Y'); $iYear = date('Y');
} }
if($iMonth==0) if($iMonth==0)
{ {
$iMonth = date('m'); $iMonth = date('m');
} }
$this->iYear = $iYear; $this->iYear = $iYear;
$this->iMonth = $iMonth; $this->iMonth = $iMonth;
} }
private function getThoughts() private function getThoughts()
{ {
//TODO essayer avec selectRows //TODO essayer avec selectRows
$sQuery = "SELECT DATE_FORMAT(led, '%d') AS day $sQuery = "SELECT DATE_FORMAT(led, '%d') AS day
FROM ".Db::THOUGHTS_TABLE." FROM ".Db::THOUGHTS_TABLE."
WHERE ".Db::getId(Db::USERS_TABLE)." = ".$this->oSession->getUserId()." WHERE ".Db::getId(Db::USERS_TABLE)." = ".$this->oSession->getUserId()."
AND YEAR(led) = ".$this->iYear." AND YEAR(led) = ".$this->iYear."
AND MONTH(led) = ".$this->iMonth." AND MONTH(led) = ".$this->iMonth."
GROUP BY day GROUP BY day
ORDER BY day"; ORDER BY day";
return $this->oMySql->getArrayQuery($sQuery, true); return $this->oMySql->getArrayQuery($sQuery, true);
} }
private function getUpdatedLink($asParams) private function getUpdatedLink($asParams)
{ {
$sCurrentVariables = $_SERVER['QUERY_STRING']; $sCurrentVariables = $_SERVER['QUERY_STRING'];
$asCurrentVariables = explode('&', $sCurrentVariables); $asCurrentVariables = explode('&', $sCurrentVariables);
foreach($asCurrentVariables as $sParam) foreach($asCurrentVariables as $sParam)
{ {
$sKey = strstr($sParam, '=', true); $sKey = strstr($sParam, '=', true);
$sValue = substr(strstr($sParam, '='), 1); $sValue = substr(strstr($sParam, '='), 1);
$asVariables[$sKey] = $sValue; $asVariables[$sKey] = $sValue;
} }
return '?'.implodeAll(array_merge($asVariables, $asParams), '=', '&'); return '?'.implodeAll(array_merge($asVariables, $asParams), '=', '&');
} }
private function getLink($iOffset) private function getLink($iOffset)
{ {
$iTimeStamp = mktime(0, 0, 0, $this->iMonth + $iOffset, 1, $this->iYear); $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))); return $this->getUpdatedLink(array(self::CAL_MONTH=>date('n', $iTimeStamp), self::CAL_YEAR=>date('Y', $iTimeStamp)));
} }
private function setMaskItems() private function setMaskItems()
{ {
//week starting on the sunday : offset = 0, monday : offset = 1 //week starting on the sunday : offset = 0, monday : offset = 1
$iOffset = 1; $iOffset = 1;
//days in the month //days in the month
$iMonthLastDay = date('d', mktime(0, 0, 0, $this->iMonth+1, 0, $this->iYear)); $iMonthLastDay = date('d', mktime(0, 0, 0, $this->iMonth+1, 0, $this->iYear));
$asDays = range(1, $iMonthLastDay); $asDays = range(1, $iMonthLastDay);
$iDayNb = 1 - date($iOffset?'N':'w', mktime(0, 0, 0, $this->iMonth, 1, $this->iYear)) + $iOffset; $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; $iCalendarLastDay = $iMonthLastDay + (7 - date($iOffset?'N':'w', mktime(0, 0, 0, $this->iMonth+1, 0, $this->iYear))) + $iOffset;
//days with thoughts //days with thoughts
$asThoughts = $this->getThoughts(); $asThoughts = $this->getThoughts();
while($iDayNb < $iCalendarLastDay) while($iDayNb < $iCalendarLastDay)
{ {
$iCurrentDayTimeStamp = mktime(0, 0, 0, $this->iMonth, $iDayNb, $this->iYear); $iCurrentDayTimeStamp = mktime(0, 0, 0, $this->iMonth, $iDayNb, $this->iYear);
$sItemDate = date('d', $iCurrentDayTimeStamp); $sItemDate = date('d', $iCurrentDayTimeStamp);
//new week //new week
if(date('w', $iCurrentDayTimeStamp) == $iOffset) if(date('w', $iCurrentDayTimeStamp) == $iOffset)
{ {
$this->oMask->newInstance('WEEK'); $this->oMask->newInstance('WEEK');
} }
//day within month //day within month
if(date('n', $iCurrentDayTimeStamp)==$this->iMonth) if(date('n', $iCurrentDayTimeStamp)==$this->iMonth)
{ {
$bThoughts = in_array($iDayNb, $asThoughts); $bThoughts = in_array($iDayNb, $asThoughts);
$sItemClass = $bThoughts?'full':'empty'; $sItemClass = $bThoughts?'full':'empty';
$sItemLink = $bThoughts?$this->getUpdatedLink(array('d'=>date(MyThoughts::URL_DATE_FORMAT, $iCurrentDayTimeStamp), 'p'=>'r')):'#'; $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):''; $sItemLinkTitle = $bThoughts?'See my thoughts on '.date(MyThoughts::LAYOUT_DATE_FORMAT, $iCurrentDayTimeStamp):'';
} }
else else
{ {
$sItemClass = 'disabled'; $sItemClass = 'disabled';
$sItemLink = '#'; $sItemLink = '#';
$sItemLinkTitle = ''; $sItemLinkTitle = '';
} }
$this->oMask->addInstance('DAY', array('item_day'=>$sItemDate, 'item_class'=>$sItemClass, 'item_link'=>$sItemLink, 'item_link_title'=>$sItemLinkTitle)); $this->oMask->addInstance('DAY', array('item_day'=>$sItemDate, 'item_class'=>$sItemClass, 'item_link'=>$sItemLink, 'item_link_title'=>$sItemLinkTitle));
$iDayNb++; $iDayNb++;
} }
//column titles //column titles
$asDayNames = array('1'=>'Mon', '2'=>'Tue', '3'=>'Wed', '4'=>'Thu', '5'=>'Fri', '6'=>'Sat', $iOffset?'7':'0'=>'Sun'); $asDayNames = array('1'=>'Mon', '2'=>'Tue', '3'=>'Wed', '4'=>'Thu', '5'=>'Fri', '6'=>'Sat', $iOffset?'7':'0'=>'Sun');
ksort($asDayNames); ksort($asDayNames);
foreach($asDayNames as $sDayName) foreach($asDayNames as $sDayName)
{ {
$this->oMask->addInstance('TITLE', array('day_name'=>$sDayName)); $this->oMask->addInstance('TITLE', array('day_name'=>$sDayName));
} }
} }
public function getCalendar() public function getCalendar()
{ {
$sResult = ''; $sResult = '';
if($this->iYear!=0 && $this->iMonth!=0) if($this->iYear!=0 && $this->iMonth!=0)
{ {
$this->oMask->setTag('link_prev', $this->getLink(-1)); $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('current_month', date('F', mktime(0, 0, 0, $this->iMonth, 1, $this->iYear)));
$this->oMask->setTag('link_next', $this->getLink(1)); $this->oMask->setTag('link_next', $this->getLink(1));
$this->setMaskItems(); $this->setMaskItems();
$sResult = $this->oMask->getMask(); $sResult = $this->oMask->getMask();
} }
return $sResult; return $sResult;
} }
} }
?> ?>

View File

@@ -1,242 +1,245 @@
<?php <?php
/** /**
* Main Class * Main Class
* @author franzz * @author franzz
* @version 2.0 * @version 2.0
*/ */
class MyThoughts extends Main class MyThoughts extends Main
{ {
//Interface keywords //Interface keywords
const SUCCESS = 'success'; const SUCCESS = 'success';
const ERROR = 'error'; const ERROR = 'error';
const UNAUTHORIZED = 'unauthorized'; const UNAUTHORIZED = 'unauthorized';
const NOT_FOUND = 'unknown action'; const NOT_FOUND = 'unknown action';
//SQL tables //SQL tables
const USER_TABLE = 'users'; const USER_TABLE = 'users';
const SETTINGS_TABLE = 'settings'; const SETTINGS_TABLE = 'settings';
//Mythoughts //Mythoughts
const URL_DATE_FORMAT = 'Ymd'; const URL_DATE_FORMAT = 'Ymd';
const LAYOUT_DATE_FORMAT = 'F \t\h\e jS, Y'; const LAYOUT_DATE_FORMAT = 'F \t\h\e jS, Y';
const MYSQL_DATE_FORMAT = 'Y-m-d'; const MYSQL_DATE_FORMAT = 'Y-m-d';
const LAYOUT_TIME_FORMAT = 'G:i'; const LAYOUT_TIME_FORMAT = 'G:i';
const WELCOME_MSG_FILE = 'welcome'; const WELCOME_MSG_FILE = 'welcome';
const SETTING_LAYOUT = 'layout'; const SETTING_LAYOUT = 'layout';
const LAYOUT_ONE_PAGE = '1'; const LAYOUT_ONE_PAGE = '1';
const LAYOUT_TWO_PAGES = '2'; const LAYOUT_TWO_PAGES = '2';
const SETTING_FONT = 'font'; const SETTING_FONT = 'font';
const FONT_THOUGHTS = 'thoughts'; const FONT_THOUGHTS = 'thoughts';
const FONT_ARIAL = 'Arial'; const FONT_ARIAL = 'Arial';
const FONT_VERDANA = 'Verdana'; const FONT_VERDANA = 'Verdana';
const SETTING_SIZE = 'Size'; const SETTING_SIZE = 'Size';
const SIZE_16 = '16'; const SIZE_16 = '16';
const SIZE_18 = '18'; const SIZE_18 = '18';
const SIZE_20 = '20'; const SIZE_20 = '20';
const LAST_THOUGHT_LIMIT = 60*60*24; const LAST_THOUGHT_LIMIT = 60*60*24;
//Format //Format
const OBJ = 'object'; const OBJ = 'object';
const ARRAY = 'array'; const ARRAY = 'array';
const JSON = 'json'; const JSON = 'json';
/** /**
* Auth Object * Auth Object
* @var Auth * @var Auth
*/ */
private $oAuth; private $oAuth;
/** /**
* Main constructor [to be called from index.php] * Main constructor [to be called from index.php]
* @param ClassManagement $oClassManagement * @param ClassManagement $oClassManagement
* @param string $sLang * @param string $sLang
*/ */
public function __construct($oClassManagement, $sProcessPage) public function __construct($oClassManagement, $sProcessPage)
{ {
//Load classes //Load classes
//$this->oClassManagement->incClass('calendar', true); //$this->oClassManagement->incClass('calendar', true);
$asClasses = array( array('name'=>'auth', 'project'=>true), $asClasses = array( array('name'=>'auth', 'project'=>true),
array('name'=>'thought', 'project'=>true)); array('name'=>'thought', 'project'=>true));
parent::__construct($oClassManagement, $sProcessPage, $asClasses); parent::__construct($oClassManagement, $sProcessPage, $asClasses);
//Init objects //Init objects
if($this->oDb->sDbState == Db::DB_PEACHY) $this->oAuth = new Auth($this->oDb, Settings::API_KEY); if($this->oDb->sDbState == Db::DB_PEACHY) $this->oAuth = new Auth($this->oDb, Settings::API_KEY);
} }
protected function install() protected function install()
{ {
$this->oAuth = new Auth($this->oDb, Settings::API_KEY, false); $this->oAuth = new Auth($this->oDb, Settings::API_KEY, false);
//Install DB //Install DB
$this->oDb->install(); $this->oDb->install();
} }
private function setContext($sProcessPage) private function setContext($sProcessPage)
{ {
//Browser <> PHP <> MySql synchronization //Browser <> PHP <> MySql synchronization
date_default_timezone_set(Settings::TIMEZONE); date_default_timezone_set(Settings::TIMEZONE);
ini_set('default_charset', Settings::TEXT_ENC); ini_set('default_charset', Settings::TEXT_ENC);
header('Content-Type: text/html; charset='.Settings::TEXT_ENC); header('Content-Type: text/html; charset='.Settings::TEXT_ENC);
mb_internal_encoding(Settings::TEXT_ENC); mb_internal_encoding(Settings::TEXT_ENC);
mb_http_output(Settings::TEXT_ENC); mb_http_output(Settings::TEXT_ENC);
mb_http_input(Settings::TEXT_ENC); mb_http_input(Settings::TEXT_ENC);
mb_language('uni'); mb_language('uni');
mb_regex_encoding(Settings::TEXT_ENC); mb_regex_encoding(Settings::TEXT_ENC);
$this->asContext['process_page'] = basename($sProcessPage); $this->asContext['process_page'] = basename($sProcessPage);
$sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD']; $sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD'];
$sAppPath = 'http://'.str_replace('http://', '', $sServerName.dirname($_SERVER['SCRIPT_NAME'])); $sAppPath = 'http://'.str_replace('http://', '', $sServerName.dirname($_SERVER['SCRIPT_NAME']));
$this->asContext['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':''); $this->asContext['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':'');
} }
public function addUncaughtError($sError) public function addUncaughtError($sError)
{ {
$this->addError('Uncaught errors:'."\n".$sError); $this->addError('Uncaught errors:'."\n".$sError);
} }
/* Authorizations handling */ /* Authorizations handling */
public function register($sToken, $sNickname) public function register($sToken, $sNickname)
{ {
$asResult = $this->oAuth->register($sToken, $sNickname); $asResult = $this->oAuth->register($sToken, $sNickname);
if($asResult['success']) return $this->logMeIn($sToken); if($asResult['success']) return $this->logMeIn($sToken);
else return self::getJsonResult($asResult['success'], $asResult['desc']); else return self::getJsonResult($asResult['success'], $asResult['desc']);
} }
public function isLoggedIn() public function isLoggedIn()
{ {
return $this->oAuth->isLoggedIn(); return $this->oAuth->isLoggedIn();
} }
public function logMeIn($sToken) public function logMeIn($sToken)
{ {
$asLogResult = $this->oAuth->logMeIn($sToken); $asLogResult = $this->oAuth->logMeIn($sToken);
return MyThoughts::getJsonResult($asLogResult['success'], $asLogResult['desc'], $this->getVars()); return MyThoughts::getJsonResult($asLogResult['success'], $asLogResult['desc'], $this->getVars());
} }
public function checkApiKey($sApiKey) public function checkApiKey($sApiKey)
{ {
return $this->oAuth->checkApiKey($sApiKey); return $this->oAuth->checkApiKey($sApiKey);
} }
/* Building main pages */ /* Building main pages */
public function getPage() public function getPage()
{ {
//Constants //Constants
$asGlobalVars = array( $asGlobalVars = array(
'consts' => array( 'consts' => array(
'token_sep' => Auth::TOKEN_SEP, 'token_sep' => Auth::TOKEN_SEP,
'error' => self::ERROR, 'error' => self::ERROR,
'success' => self::SUCCESS, 'success' => self::SUCCESS,
'context' => $this->asContext, 'context' => $this->asContext,
'cookie' => Auth::USER_COOKIE_PASS 'cookie' => Auth::USER_COOKIE_PASS
), ),
'vars' => $this->getVars() 'vars' => $this->getVars()
); );
//Pages //Pages
$asPages = array('logon', 'logoff', 'write', 'read', 'settings', 'template', 'editor'); $asPages = array('logon', 'logoff', 'write', 'read', 'settings', 'template', 'editor');
foreach($asPages as $sPage) $asGlobalVars['consts']['pages'][$sPage] = $this->getPageContent($sPage); foreach($asPages as $sPage) $asGlobalVars['consts']['pages'][$sPage] = $this->getPageContent($sPage);
//Main Page //Main Page
$sPage = $this->getPageContent('index'); $sPage = $this->getPageContent('index');
$sPage = str_replace('asGlobalVars', json_encode($asGlobalVars), $sPage); $sPage = str_replace('asGlobalVars', json_encode($asGlobalVars), $sPage);
return $sPage; return $sPage;
} }
private function getVars() { private function getVars() {
return array( return array(
'id' => $this->oAuth->getUserId(), 'id' => $this->oAuth->getUserId(),
'log_in' => $this->isLoggedIn() 'log_in' => $this->isLoggedIn()
); );
} }
/* DB structure. See Db::__construct */ /* DB structure. See Db::__construct */
protected function getSqlOptions() protected function getSqlOptions()
{ {
return array( return array(
'tables' => array( 'tables' => array(
self::USER_TABLE => array(Db::getText(self::USER_TABLE), 'nickname', 'pass', 'cookie'), self::USER_TABLE => array(Db::getText(self::USER_TABLE), 'nickname', 'pass', 'cookie'),
Thought::THOUGHT_TABLE => array(Db::getId(self::USER_TABLE), Db::getText(Thought::THOUGHT_TABLE), 'created'), Thought::THOUGHT_TABLE => array(Db::getId(self::USER_TABLE), Db::getText(Thought::THOUGHT_TABLE), 'created'),
self::SETTINGS_TABLE => array(Db::getId(self::USER_TABLE), Db::getText(self::SETTINGS_TABLE), 'value') self::SETTINGS_TABLE => array(Db::getId(self::USER_TABLE), Db::getText(self::SETTINGS_TABLE), 'value')
), ),
'types' => array( 'types' => array(
Db::getText(self::USER_TABLE) => "varchar(32) NOT NULL", Db::getText(self::USER_TABLE) => "varchar(32) NOT NULL",
'nickname' => "varchar(60) NOT NULL", 'nickname' => "varchar(60) NOT NULL",
'pass' => "varchar(256) NOT NULL", 'pass' => "varchar(256) NOT NULL",
'cookie' => "varchar(255)", 'cookie' => "varchar(255)",
Db::getText(Thought::THOUGHT_TABLE) => "longtext", Db::getText(Thought::THOUGHT_TABLE) => "longtext",
'created' => "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP", 'created' => "timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP",
Db::getText(self::SETTINGS_TABLE) => "varchar(20) NOT NULL", Db::getText(self::SETTINGS_TABLE) => "varchar(20) NOT NULL",
'value' => "varchar(20) NOT NULL" 'value' => "varchar(20) NOT NULL"
), ),
'constraints' => array( 'constraints' => array(
self::USER_TABLE => "UNIQUE KEY `unique_username` (`".Db::getText(self::USER_TABLE)."`)" self::USER_TABLE => "UNIQUE KEY `unique_username` (`".Db::getText(self::USER_TABLE)."`)"
), ),
'cascading_delete' => array( 'cascading_delete' => array(
self::USER_TABLE => array(self::SETTINGS_TABLE, Thought::THOUGHT_TABLE) self::USER_TABLE => array(self::SETTINGS_TABLE, Thought::THOUGHT_TABLE)
) )
); );
} }
/* Thoughts */ /* Thoughts */
public function getThought($iThoughtId, $sFormat=self::OBJ) public function getThought($iThoughtId, $sFormat=self::OBJ)
{ {
$oThought = new Thought($this->oDb, $this->oAuth->getUserId()); $oThought = new Thought($this->oDb, $this->oAuth->getUserId());
if($iThoughtId=='last') $oThought->openLast(self::LAST_THOUGHT_LIMIT); if($iThoughtId=='last') $oThought->openLast(self::LAST_THOUGHT_LIMIT);
else $oThought->open($iThoughtId); else $oThought->open($iThoughtId);
switch($sFormat) switch($sFormat)
{ {
case self::OBJ: case self::OBJ:
return $oThought; break; return $oThought; break;
case self::ARRAY: case self::ARRAY:
return $oThought->get(); break; return $oThought->get(); break;
case self::JSON: case self::JSON:
return self::getJsonResult(true, '', $oThought->get()); break; return self::getJsonResult(true, '', $oThought->get()); break;
} }
} }
public function updateThought($asOps, $iThoughtId=0) public function updateThought($asOps, $iThoughtId=0)
{ {
$oThought = new Thought($this->oDb, $this->oAuth->getUserId(), $iThoughtId); $oThought = new Thought($this->oDb, $this->oAuth->getUserId(), $iThoughtId);
$oThought->setOps($asOps); $oThought->setOps($asOps);
$iThoughtId = $oThought->save(); $iThoughtId = $oThought->save();
$bSuccess = ($iThoughtId>0); $bSuccess = ($iThoughtId>0);
$sDesc = 'thought '.($bSuccess?'':'not ').'saved'; $sDesc = 'thought '.($bSuccess?'':'not ').'saved';
return self::getJsonResult($bSuccess, $sDesc, $this->getThought($iThoughtId, self::ARRAY)); return self::getJsonResult($bSuccess, $sDesc, $this->getThought($iThoughtId, self::ARRAY));
} }
public function getThoughtDates() public function getThoughtDates()
{ {
$asThoughts = Thought::getThoughtDates($this->oDb, $this->oAuth->getUserId()); $asThoughts = Thought::getThoughtDates($this->oDb, $this->oAuth->getUserId());
foreach($asThoughts as &$asThought) $asThought['created_f'] = self::formatDate($asThought['created'], 'j M'); foreach($asThoughts as &$asThought) {
return self::getJsonResult(true, '', $asThoughts); $asThought['created_d'] = self::formatDate($asThought['created'], 'j M');
} $asThought['created_h'] = self::formatDate($asThought['created'], 'h:i');
}
/* Static toolbox functions */ return self::getJsonResult(true, '', $asThoughts);
}
public static function getSafeNickName($sNickName)
{ /* Static toolbox functions */
return $sNickName;
} public static function getSafeNickName($sNickName)
{
private static function formatDate($iTime, $sFormat, $sField='') return $sNickName;
{ }
$iTime = ($sField == '')?$iTime:$iTime[$sField];
$iTime = is_numeric($iTime)?$iTime:strtotime($iTime); private static function formatDate($iTime, $sFormat, $sField='')
return date($sFormat, $iTime); {
} $iTime = ($sField == '')?$iTime:$iTime[$sField];
} $iTime = is_numeric($iTime)?$iTime:strtotime($iTime);
return date($sFormat, $iTime);
}
}
?> ?>

View File

@@ -5,6 +5,8 @@ class Thought extends PhpObject
const THOUGHT_TABLE = 'thoughts'; const THOUGHT_TABLE = 'thoughts';
private $iId; private $iId;
private $iPrevId;
private $iNextId;
private $iUserId; private $iUserId;
private $asOps; private $asOps;
private $iCreateTimestamp; private $iCreateTimestamp;
@@ -32,6 +34,7 @@ class Thought extends PhpObject
public function setId($iId, $bOpen=true) public function setId($iId, $bOpen=true)
{ {
$this->iId = $iId; $this->iId = $iId;
$this->iNextId = 0;
if($this->iId > 0 && $bOpen) $this->open($this->iId); if($this->iId > 0 && $bOpen) $this->open($this->iId);
} }
@@ -66,6 +69,8 @@ class Thought extends PhpObject
$asWhere = array(Db::getId(self::THOUGHT_TABLE)=>$iId, Db::getId(MyThoughts::USER_TABLE) => $this->iUserId); $asWhere = array(Db::getId(self::THOUGHT_TABLE)=>$iId, Db::getId(MyThoughts::USER_TABLE) => $this->iUserId);
$asInfo = $this->oDb->selectRow(self::THOUGHT_TABLE, $asWhere); $asInfo = $this->oDb->selectRow(self::THOUGHT_TABLE, $asWhere);
$this->iPrevId = $this->getRelativeThoughtId($iId, -1);
$this->iNextId = $this->getRelativeThoughtId($iId, 1);
$this->iId = $asInfo[Db::getId(self::THOUGHT_TABLE)]; $this->iId = $asInfo[Db::getId(self::THOUGHT_TABLE)];
$this->iUserId = $asInfo[Db::getId(MyThoughts::USER_TABLE)]; $this->iUserId = $asInfo[Db::getId(MyThoughts::USER_TABLE)];
$this->asOps = self::decodeThought($asInfo[Db::getText(self::THOUGHT_TABLE)]); $this->asOps = self::decodeThought($asInfo[Db::getText(self::THOUGHT_TABLE)]);
@@ -77,6 +82,23 @@ class Thought extends PhpObject
else $this->addError('getting thought info with no thought id'); else $this->addError('getting thought info with no thought id');
} }
private function getRelativeThoughtId($iId, $iOffset) {
$iThoughtId = 0;
$asThoughtIds = $this->oDb->selectRows(array(
'select' => Db::getId(self::THOUGHT_TABLE),
'from' => self::THOUGHT_TABLE,
'constraint'=> array('id_thought'=> $iId, Db::getId(MyThoughts::USER_TABLE) => $this->iUserId),
'constOpe' => array('id_thought'=> $iOffset>0?'>':'<', Db::getId(MyThoughts::USER_TABLE) => '='),
'orderBy' => array(Db::getId(self::THOUGHT_TABLE) => $iOffset>0?'ASC':'DESC'),
'limit' => abs($iOffset)
));
$iIndex = abs($iOffset) - 1;
if(array_key_exists($iIndex, $asThoughtIds)) $iThoughtId = $asThoughtIds[$iIndex];
return $iThoughtId;
}
public function save() public function save()
{ {
$asThought = array( $asThought = array(
@@ -94,10 +116,13 @@ class Thought extends PhpObject
{ {
return array( return array(
'id' => $this->iId, 'id' => $this->iId,
'prev_id' => $this->iPrevId,
'next_id' => $this->iNextId,
'id_user' => $this->iUserId, 'id_user' => $this->iUserId,
'ops' => $this->asOps, 'ops' => $this->asOps,
'created' => $this->iCreateTimestamp, 'created' => $this->iCreateTimestamp,
'created_f' => date('l, j F', $this->iCreateTimestamp), 'created_d' => date('l, j F', $this->iCreateTimestamp),
'created_h' => date('H:i', $this->iCreateTimestamp),
'led' => $this->sLed 'led' => $this->sLed
); );
} }

390
index.php
View File

@@ -1,196 +1,196 @@
<?php <?php
/* /*
MyThoughts Project MyThoughts Project
http://git.lutran.fr/main.git https://git.lutran.fr/franzz/mythoughts
Copyright (C) 2015 François Lutran Copyright (C) 2015 François Lutran
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses along with this program. If not, see http://www.gnu.org/licenses
*/ */
/* Requests Handler */ /* Requests Handler */
//Start buffering //Start buffering
ob_start(); ob_start();
require_once '../objects/class_management.php'; require_once '../objects/class_management.php';
$oClassManagement = new ClassManagement('mythoughts'); $oClassManagement = new ClassManagement('mythoughts');
ToolBox::cleanPost($_POST); ToolBox::cleanPost($_POST);
ToolBox::cleanPost($_GET); ToolBox::cleanPost($_GET);
ToolBox::cleanPost($_REQUEST); ToolBox::cleanPost($_REQUEST);
ToolBox::fixGlobalVars(isset($argv)?$argv:array()); ToolBox::fixGlobalVars(isset($argv)?$argv:array());
//Available variables //Available variables
$sToken = isset($_REQUEST['token'])?$_REQUEST['token']:''; $sToken = isset($_REQUEST['token'])?$_REQUEST['token']:'';
$sAction = isset($_REQUEST['a'])?$_REQUEST['a']:''; $sAction = isset($_REQUEST['a'])?$_REQUEST['a']:'';
$sPage = isset($_GET['p'])?$_GET['p']:'index'; $sPage = isset($_GET['p'])?$_GET['p']:'index';
$sNickName = isset($_REQUEST['nickname'])?$_REQUEST['nickname']:''; $sNickName = isset($_REQUEST['nickname'])?$_REQUEST['nickname']:'';
$iApiKey = isset($_GET['api'])?$_GET['api']:''; $iApiKey = isset($_GET['api'])?$_GET['api']:'';
$sContent = isset($_POST['content'])?$_POST['content']:''; $sContent = isset($_POST['content'])?$_POST['content']:'';
$iId = isset($_REQUEST['id'])?$_REQUEST['id']:0; $iId = isset($_REQUEST['id'])?$_REQUEST['id']:0;
//Initiate class //Initiate class
$oMyThoughts = new MyThoughts($oClassManagement, __FILE__); $oMyThoughts = new MyThoughts($oClassManagement, __FILE__);
$bLoggedIn = $oMyThoughts->isLoggedIn(); $bLoggedIn = $oMyThoughts->isLoggedIn();
$sResult = ''; $sResult = '';
if($sAction=='logmein') $sResult = $oMyThoughts->logMeIn($sToken); if($sAction=='logmein') $sResult = $oMyThoughts->logMeIn($sToken);
elseif($sAction!='' && $bLoggedIn) elseif($sAction!='' && $bLoggedIn)
{ {
switch ($sAction) switch ($sAction)
{ {
case 'load': case 'load':
$sResult = $oMyThoughts->getThought($iId, MyThoughts::JSON); $sResult = $oMyThoughts->getThought($iId, MyThoughts::JSON);
break; break;
case 'update': case 'update':
$sResult = $oMyThoughts->updateThought($sContent, $iId); $sResult = $oMyThoughts->updateThought($sContent, $iId);
break; break;
case 'thoughts': case 'thoughts':
$sResult = $oMyThoughts->getThoughtDates(); $sResult = $oMyThoughts->getThoughtDates();
break; break;
default: default:
$sResult = MyThoughts::getJsonResult(false, MyThoughts::NOT_FOUND); $sResult = MyThoughts::getJsonResult(false, MyThoughts::NOT_FOUND);
} }
} }
elseif($sAction!='' && !$bLoggedIn) elseif($sAction!='' && !$bLoggedIn)
{ {
if($oMyThoughts->checkApiKey($iApiKey)) if($oMyThoughts->checkApiKey($iApiKey))
{ {
switch ($sAction) switch ($sAction)
{ {
case '': case '':
//$sResult = $oMyThoughts->apifunction(); //$sResult = $oMyThoughts->apifunction();
break; break;
default: default:
$sResult = MyThoughts::getJsonResult(false, MyThoughts::NOT_FOUND); $sResult = MyThoughts::getJsonResult(false, MyThoughts::NOT_FOUND);
} }
} }
elseif($sAction=='register') $sResult = $oMyThoughts->register($sToken, $sNickName); elseif($sAction=='register') $sResult = $oMyThoughts->register($sToken, $sNickName);
else $sResult = MyThoughts::getJsonResult(false, MyThoughts::UNAUTHORIZED); else $sResult = MyThoughts::getJsonResult(false, MyThoughts::UNAUTHORIZED);
} }
else $sResult = $oMyThoughts->getPage(); else $sResult = $oMyThoughts->getPage();
$sDebug = ob_get_clean(); $sDebug = ob_get_clean();
if(Settings::DEBUG && $sDebug!='') $oMyThoughts->addUncaughtError($sDebug); if(Settings::DEBUG && $sDebug!='') $oMyThoughts->addUncaughtError($sDebug);
echo $sResult; echo $sResult;
/* /*
//load classes //load classes
session_start(); session_start();
require_once 'config.php'; require_once 'config.php';
//clean sent values //clean sent values
cleanPost($_POST); cleanPost($_POST);
cleanPost($_GET); cleanPost($_GET);
cleanPost($_REQUEST); cleanPost($_REQUEST);
//general //general
$sPage = (isset($_GET['p']) && $_GET['p']!='')?$_GET['p']:'w'; $sPage = (isset($_GET['p']) && $_GET['p']!='')?$_GET['p']:'w';
$sPostToken = isset($_POST['post_token'])?$_POST['post_token']:''; $sPostToken = isset($_POST['post_token'])?$_POST['post_token']:'';
//logon //logon
$sLogin = (isset($_POST['login']) && $_POST['login']!='Nickname')?$_POST['login']:''; $sLogin = (isset($_POST['login']) && $_POST['login']!='Nickname')?$_POST['login']:'';
$sPass = (isset($_POST['pass']) && $_POST['pass']!='Password')?$_POST['pass']:''; $sPass = (isset($_POST['pass']) && $_POST['pass']!='Password')?$_POST['pass']:'';
$bRegister = (isset($_POST['register']) && $_POST['register']==1); $bRegister = (isset($_POST['register']) && $_POST['register']==1);
//writing pad //writing pad
$sThought = isset($_POST['thoughts'])?$_POST['thoughts']:''; $sThought = isset($_POST['thoughts'])?$_POST['thoughts']:'';
$iThoughtId = (isset($_POST['thought_id']) && $_POST['thought_id']!='')?$_POST['thought_id']:0; //update or insert $iThoughtId = (isset($_POST['thought_id']) && $_POST['thought_id']!='')?$_POST['thought_id']:0; //update or insert
$bFinishedWriting = isset($_POST['finished']); $bFinishedWriting = isset($_POST['finished']);
//calendar //calendar
$iDay = isset($_GET['d'])?$_GET['d']:date(MyThoughts::URL_DATE_FORMAT); //d = yyyymmdd $iDay = isset($_GET['d'])?$_GET['d']:date(MyThoughts::URL_DATE_FORMAT); //d = yyyymmdd
$iCalYear = isset($_GET[Calendar::CAL_YEAR])?$_GET[Calendar::CAL_YEAR]:0; //cy = yyyy $iCalYear = isset($_GET[Calendar::CAL_YEAR])?$_GET[Calendar::CAL_YEAR]:0; //cy = yyyy
$iCalMonth = isset($_GET[Calendar::CAL_MONTH])?$_GET[Calendar::CAL_MONTH]:0; //cm = m $iCalMonth = isset($_GET[Calendar::CAL_MONTH])?$_GET[Calendar::CAL_MONTH]:0; //cm = m
$oMyThougths = new MyThoughts(); $oMyThougths = new MyThoughts();
$bValidPost = ($sPostToken!='' && $oMyThougths->checkPostToken($sPostToken)); $bValidPost = ($sPostToken!='' && $oMyThougths->checkPostToken($sPostToken));
if($bValidPost) if($bValidPost)
{ {
if($bRegister) if($bRegister)
{ {
$oMyThougths->register($sLogin, $sPass); $oMyThougths->register($sLogin, $sPass);
$sPage = 'r'; $sPage = 'r';
} }
elseif($sLogin!='' && $sPass!='') elseif($sLogin!='' && $sPass!='')
{ {
$oMyThougths->logMeIn($sLogin, $sPass); $oMyThougths->logMeIn($sLogin, $sPass);
} }
} }
//if loggued in //if loggued in
if(!$oMyThougths->isLogguedIn()) if(!$oMyThougths->isLogguedIn())
{ {
$oMyThougths->logonPage($sLogin); $oMyThougths->logonPage($sLogin);
} }
else else
{ {
$oMyThougths->activateMenu(); $oMyThougths->activateMenu();
$oMyThougths->setCalendarDate(); $oMyThougths->setCalendarDate();
switch($sPage) switch($sPage)
{ {
case 'w': //write a thought case 'w': //write a thought
if($bValidPost && $sThought!='' && $sThought!='Talk to me.') if($bValidPost && $sThought!='' && $sThought!='Talk to me.')
{ {
if($iThoughtId==0) if($iThoughtId==0)
{ {
$iThoughtId = $oMyThougths->addThought($sThought); $iThoughtId = $oMyThougths->addThought($sThought);
} }
else else
{ {
$oMyThougths->updateThought($iThoughtId, $sThought); $oMyThougths->updateThought($iThoughtId, $sThought);
} }
} }
if($bFinishedWriting) if($bFinishedWriting)
{ {
$oMyThougths->readingPage(); $oMyThougths->readingPage();
} }
else else
{ {
$oMyThougths->writingPage($iThoughtId); $oMyThougths->writingPage($iThoughtId);
} }
break; break;
case 'r': //read a thought (per day) case 'r': //read a thought (per day)
if($iDay<=0 || !$oMyThougths->readingPage(strtotime($iDay))) if($iDay<=0 || !$oMyThougths->readingPage(strtotime($iDay)))
{ {
$oMyThougths->writingPage(); $oMyThougths->writingPage();
} }
break; break;
case 's': // go to settings page case 's': // go to settings page
if($bValidPost) if($bValidPost)
{ {
$asSettings = array_intersect_key($_POST, array_flip($oMyThougths->getSettingsList())); $asSettings = array_intersect_key($_POST, array_flip($oMyThougths->getSettingsList()));
$oMyThougths->setSettings($asSettings); $oMyThougths->setSettings($asSettings);
$oMyThougths->writingPage(); $oMyThougths->writingPage();
} }
else else
{ {
$oMyThougths->settingsPage(); $oMyThougths->settingsPage();
} }
break; break;
case 'q': //quit case 'q': //quit
$oMyThougths->logMeOut(); $oMyThougths->logMeOut();
} }
if($iCalYear!=0 && $iCalMonth!=0) if($iCalYear!=0 && $iCalMonth!=0)
{ {
$oMyThougths->setCalendarDate($iCalYear, $iCalMonth); $oMyThougths->setCalendarDate($iCalYear, $iCalMonth);
} }
} }
echo $oMyThougths->getPage(); echo $oMyThougths->getPage();
*/ */
?> ?>

View File

@@ -1,23 +1,23 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="author" content="Franzz" /> <meta name="author" content="Franzz" />
<link href="style/mythoughts.css" rel="stylesheet" type="text/css" /> <link href="style/mythoughts.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="scripts/jquery.min.js"></script> <script type="text/javascript" src="scripts/jquery.min.js"></script>
<script type="text/javascript" src="scripts/bootstrap.bundle.min.js"></script> <script type="text/javascript" src="scripts/bootstrap.bundle.min.js"></script>
<script type="text/javascript" src="scripts/jquery.mousewheel.min.js"></script> <script type="text/javascript" src="scripts/jquery.mousewheel.min.js"></script>
<script type="text/javascript" src="scripts/quill.min.js"></script> <script type="text/javascript" src="scripts/quill.min.js"></script>
<script type="text/javascript" src="scripts/common.js"></script> <script type="text/javascript" src="scripts/common.js"></script>
<script type="text/javascript" src="scripts/mythoughts.js"></script> <script type="text/javascript" src="scripts/mythoughts.js"></script>
<link rel="shortcut icon" href="images/favicon2.ico" /> <link rel="shortcut icon" href="images/favicon2.ico" />
<title>My Thoughts</title> <title>My Thoughts</title>
<script type="text/javascript"> <script type="text/javascript">
var oMyThoughts = new MyThoughts(asGlobalVars); var oMyThoughts = new MyThoughts(asGlobalVars);
$(document).ready(oMyThoughts.init); $(document).ready(oMyThoughts.init);
</script> </script>
</head> </head>
<body> <body>
<div id="container"></div> <div id="container"></div>
</body> </body>
</html> </html>

View File

@@ -1,96 +1,96 @@
<div id="logon" class="border border-grey-400 shadow rounded"> <div id="logon" class="border border-grey-400 shadow rounded">
<form name="post_logon" id="post_logon" method="post"> <form name="post_logon" id="post_logon" method="post">
<div class="form-group align-items-center"> <div class="form-group align-items-center">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text" data-toggle="tooltip" data-placement="left" title="Username"><i class="fal fa-user"></i></span> <span class="input-group-text" data-toggle="tooltip" data-placement="left" title="Username"><i class="fal fa-user"></i></span>
</div> </div>
<input type="text" class="form-control" placeholder="Nickname" name="login" id="login" tool value="Franzz" /> <input type="text" class="form-control" placeholder="Nickname" name="login" id="login" tool value="Franzz" />
</div> </div>
</div> </div>
<div class="form-group align-items-center"> <div class="form-group align-items-center">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text" data-toggle="tooltip" data-placement="left" title="Password"><i class="fal fa-password"></i></span> <span class="input-group-text" data-toggle="tooltip" data-placement="left" title="Password"><i class="fal fa-password"></i></span>
</div> </div>
<input type="password" class="form-control" placeholder="Password" name="pass" id="pass" value="123456" /> <input type="password" class="form-control" placeholder="Password" name="pass" id="pass" value="123456" />
</div> </div>
</div> </div>
<div id="pass_conf_box" class="form-group align-items-center collapse"> <div id="pass_conf_box" class="form-group align-items-center collapse">
<div class="input-group"> <div class="input-group">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text" data-toggle="tooltip" data-placement="left" title="Repeat password"> <span class="input-group-text" data-toggle="tooltip" data-placement="left" title="Repeat password">
<i class="fal fa-password first"></i> <i class="fal fa-password first"></i>
<i class="fal fa-password second"></i> <i class="fal fa-password second"></i>
</span> </span>
</div> </div>
<input type="password" class="form-control" placeholder="Confirm password" name="pass_conf" id="pass_conf" value="123456" /> <input type="password" class="form-control" placeholder="Confirm password" name="pass_conf" id="pass_conf" value="123456" />
</div> </div>
</div> </div>
<div id="btn_box" class="btn-toolbar justify-content-between" role="toolbar"> <div id="btn_box" class="btn-toolbar justify-content-between" role="toolbar">
<button type="button" class="btn btn-secondary shadow-sm transition" id="register">Register</button> <button type="button" class="btn btn-secondary shadow-sm transition" id="register">Register</button>
<button type="button" class="btn btn-primary shadow-sm" name="signin" id="signin">Sign in</button> <button type="button" class="btn btn-primary shadow-sm" name="signin" id="signin">Sign in</button>
</div> </div>
</form> </form>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
oMyThoughts.pageInit = function(asHash, bFirstPage) oMyThoughts.pageInit = function(asHash, bFirstPage)
{ {
self.elem.$Main.addClass('no_frame'); self.elem.$Main.addClass('no_frame');
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
//$(window).keyup(function(e){if(e.which==13) logMeIn();}); //$(window).keyup(function(e){if(e.which==13) logMeIn();});
$('#signin').click(logMeIn); $('#signin').click(logMeIn);
$('#register').on('click submit', function(event){ $('#register').on('click submit', function(event){
event.preventDefault(); event.preventDefault();
$(this) $(this)
.blur() .blur()
.off('click') .off('click')
.click(register); .click(register);
$('#pass_conf_box').collapse('show'); $('#pass_conf_box').collapse('show');
$('#signin').hide('fast', function(){$('#btn_box').addClass('registering');}); $('#signin').hide('fast', function(){$('#btn_box').addClass('registering');});
}); });
}; };
function logMeIn() function logMeIn()
{ {
var oChecker = $('#post_logon').checkForm('#login, #pass'); var oChecker = $('#post_logon').checkForm('#login, #pass');
if(oChecker.success) if(oChecker.success)
{ {
var asInputs = getInputs(); var asInputs = getInputs();
Tools.ajax( Tools.ajax(
'logmein', 'logmein',
oMyThoughts.loadHome, oMyThoughts.loadHome,
{token: getToken(asInputs)}, {token: getToken(asInputs)},
); );
} }
else Tools.feedback('warning', oChecker.desc); else Tools.feedback('warning', oChecker.desc);
} }
function register() function register()
{ {
var oChecker = $('#post_logon').checkForm(); var oChecker = $('#post_logon').checkForm();
if(oChecker.success) if(oChecker.success)
{ {
var asInputs = getInputs(); var asInputs = getInputs();
Tools.ajax( Tools.ajax(
'register', 'register',
oMyThoughts.loadHome, oMyThoughts.loadHome,
{token: getToken(asInputs), nickname:asInputs.nickname} {token: getToken(asInputs), nickname:asInputs.nickname}
); );
} }
else Tools.feedback('warning', oChecker.desc); else Tools.feedback('warning', oChecker.desc);
} }
function getInputs() { function getInputs() {
var sNickname = $('#login').val(); var sNickname = $('#login').val();
var sUsername = sNickname.toLowerCase().replace(/\s+/g, ''); var sUsername = sNickname.toLowerCase().replace(/\s+/g, '');
var sPass = $.trim($('#pass').val()); var sPass = $.trim($('#pass').val());
return {username: sUsername, nickname: sNickname, pass: sPass}; return {username: sUsername, nickname: sNickname, pass: sPass};
} }
function getToken(asInputs) { function getToken(asInputs) {
return md5(asInputs.username)+oMyThoughts.consts.token_sep+getLoginToken(asInputs.pass); return md5(asInputs.username)+oMyThoughts.consts.token_sep+getLoginToken(asInputs.pass);
} }
</script> </script>

View File

@@ -1,13 +1,18 @@
<div id="read"></div> <div id="read"></div>
<script type="text/javascript"> <script type="text/javascript">
oMyThoughts.pageInit = function(asHash, bFirstPage) { oMyThoughts.pageInit = function(asHash, bFirstPage) {
oEditor = new Editor('#read', true); oEditor = new Editor('#read', true);
oEditor.open(asHash.items[0]); oEditor.open(asHash.items[0]);
}; };
oMyThoughts.onSamePageMove = function(asHash) { oMyThoughts.onSamePageMove = function(asHash) {
$('#read').empty(); $('#read').empty();
self.pageInit(self.getHash()); self.pageInit(self.getHash());
return false; return false;
}; };
oMyThoughts.onKeydown = function(oEvent) {
if(oEvent.which == 37 || oEvent.which == 38) oEditor.prevPage();
else if(oEvent.which == 39 || oEvent.which == 40) oEditor.nextPage();
}
</script> </script>

View File

@@ -1,17 +1,17 @@
<div id="feedback"></div> <div id="feedback"></div>
<div id="header"></div> <div id="header"></div>
<div id="menu"> <div id="menu">
<ul> <ul>
<li><a href="#settings" class="button fal fa-settings"></a></li> <li><a href="#settings" class="button fal fa-settings"></a></li>
<li><a href="#logoff" class="button fal fa-logoff"></a></li> <li><a href="#logoff" class="button fal fa-logoff"></a></li>
</ul> </ul>
</div> </div>
<div id="main"></div> <div id="main"></div>
<div id="side"> <div id="side">
<div class="tag write"><a href="#write" class="fal fa-write"></a></div> <div class="tag write"><a href="#write" class="fal fa-write"></a></div>
</div> </div>
<footer> <footer>
<span>Designed and powered by Franzz &amp; Clarita. </span> <span>Designed and powered by Franzz &amp; Clarita. </span>
<span>My Thoughts Project under <a href="http://www.gnu.org/licenses/gpl.html" target="_blank">GPLv3</a> License.</span> <span>My Thoughts Project under <a href="http://www.gnu.org/licenses/gpl.html" target="_blank">GPLv3</a> License.</span>
</footer> </footer>
#errors# #errors#

View File

@@ -1,111 +1,111 @@
<div id="write"> <div id="write">
<div id="write_feedback"></div> <div id="write_feedback"></div>
<div id="edi_write"></div> <div id="edi_write"></div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
oMyThoughts.pageInit = function(asHash, bFirstPage) oMyThoughts.pageInit = function(asHash, bFirstPage)
{ {
self.tmp('default_text', "\n"); self.tmp('default_text', "\n");
self.tmp('keystrokes', 0); self.tmp('keystrokes', 0);
self.tmp('saving', false); self.tmp('saving', false);
oEditor = new Editor('#edi_write'); oEditor = new Editor('#edi_write');
oEditor.onKeyStroke = (e) => { oEditor.onKeyStroke = (e) => {
if(e.which == 83 && e.ctrlKey) { if(e.which == 83 && e.ctrlKey) {
e.preventDefault(); e.preventDefault();
save(true); save(true);
} }
else save(); else save();
} }
oEditor.moveToPage('last'); oEditor.moveToPage('last');
}; };
oMyThoughts.onFeedback = function(sType, sMsg) oMyThoughts.onFeedback = function(sType, sMsg)
{ {
var $Feedback = $('#write_feedback').stop(); var $Feedback = $('#write_feedback').stop();
if(sMsg != $Feedback.find('span').text()) { if(sMsg != $Feedback.find('span').text()) {
$Feedback.fadeOut($Feedback.is(':empty')?0:'fast', function(){ $Feedback.fadeOut($Feedback.is(':empty')?0:'fast', function(){
$(this) $(this)
.empty() .empty()
.append($('<span>', {'class':sType}).text(sMsg)) .append($('<span>', {'class':sType}).text(sMsg))
.fadeIn('fast'); .fadeIn('fast');
}); });
} }
}; };
oMyThoughts.onQuitPage = function() oMyThoughts.onQuitPage = function()
{ {
return save(true); return save(true);
}; };
function save(bForce) function save(bForce)
{ {
if(typeof oSaveTimer != 'undefined') clearTimeout(oSaveTimer); if(typeof oSaveTimer != 'undefined') clearTimeout(oSaveTimer);
var bSave = (oEditor.keystrokes % 20 == 0 || bForce); var bSave = (oEditor.keystrokes % 20 == 0 || bForce);
if(bSave) { if(bSave) {
if(self.tmp('saving')) { if(self.tmp('saving')) {
oSaveTimer = setTimeout(function(){save(true);}, 500); oSaveTimer = setTimeout(function(){save(true);}, 500);
} }
else { else {
var sContent = oEditor.getContent(); var sContent = oEditor.getContent();
if(!oEditor.isEmpty() || oEditor.id != 0) { if(!oEditor.isEmpty() || oEditor.id != 0) {
self.tmp('saving', true); self.tmp('saving', true);
oMyThoughts.onFeedback('info', 'Saving...'); oMyThoughts.onFeedback('info', 'Saving...');
getInfo( getInfo(
'update', 'update',
function(sDesc, asData) { function(sDesc, asData) {
if(oEditor.id == 0) oMyThoughts.updateSideMenu(); if(oEditor.id == 0) oMyThoughts.updateSideMenu();
oEditor.id = asData.id; oEditor.id = asData.id;
oMyThoughts.feedback('notice', 'Thought saved ('+asData.led.substr(11, 5)+')'); oMyThoughts.feedback('notice', 'Thought saved ('+asData.led.substr(11, 5)+')');
self.tmp('saving', false); self.tmp('saving', false);
}, },
{ {
id: oEditor.id, id: oEditor.id,
content: sContent content: sContent
}, },
function(sError) { function(sError) {
oMyThoughts.feedback('error', 'Not saved! An error occured: '+sError); oMyThoughts.feedback('error', 'Not saved! An error occured: '+sError);
self.tmp('saving', false); self.tmp('saving', false);
oSaveTimer = setTimeout(save, 1000); oSaveTimer = setTimeout(save, 1000);
}, },
'POST' 'POST'
); );
} }
} }
} }
else { else {
oSaveTimer = setTimeout(function(){save(true);}, 1000*10); oSaveTimer = setTimeout(function(){save(true);}, 1000*10);
} }
return true; return true;
} }
/*function setLastContent(oQuill, fCallback) /*function setLastContent(oQuill, fCallback)
{ {
getInfo getInfo
( (
'load', 'load',
function(sDesc, asData) function(sDesc, asData)
{ {
if(asData.ops.length != 1 || asData.ops[0].insert != '' && false) { if(asData.ops.length != 1 || asData.ops[0].insert != '' && false) {
var $Date = $('<p>', {'class':'entry_date'}).text(asData.created_f); var $Date = $('<p>', {'class':'entry_date'}).text(asData.created_f);
var $Sep = $('<div>', {'class':'entry_sep'}) var $Sep = $('<div>', {'class':'entry_sep'})
.text('~') .text('~')
.click(function(){oQuill.focus();}); .click(function(){oQuill.focus();});
oQuill.setContents(asData.ops); oQuill.setContents(asData.ops);
$('#context') $('#context')
.append($('#editor .ql-editor').html()) .append($('#editor .ql-editor').html())
.append($Date) .append($Date)
.append($Sep); .append($Sep);
oQuill.setContents([]); oQuill.setContents([]);
} }
else oQuill.focus(); else oQuill.focus();
if(typeof fCallback == 'function') fCallback(); if(typeof fCallback == 'function') fCallback();
}, },
{id: 'last'} {id: 'last'}
); );
}*/ }*/
</script> </script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,30 @@
$fa-font-path: "fa/fonts"; $fa-font-path: "fa/fonts";
$fa-css-prefix: fa; $fa-css-prefix: fa;
@import 'fa/light'; @import 'fa/light';
@import 'fa/mixins'; @import 'fa/mixins';
@import 'fa/core'; @import 'fa/core';
@import 'fa/larger'; @import 'fa/larger';
@import 'fa/fixed-width'; @import 'fa/fixed-width';
@import 'fa/list'; @import 'fa/list';
@import 'fa/bordered-pulled'; @import 'fa/bordered-pulled';
@import 'fa/animated'; @import 'fa/animated';
@import 'fa/rotated-flipped'; @import 'fa/rotated-flipped';
@import 'fa/stacked'; @import 'fa/stacked';
.#{$fa-css-prefix}-user:before { content: fa-content($fa-var-user); } .#{$fa-css-prefix}-user:before { content: fa-content($fa-var-user); }
.#{$fa-css-prefix}-password:before { content: fa-content($fa-var-key); } .#{$fa-css-prefix}-password:before { content: fa-content($fa-var-key); }
//Menu //Menu
.#{$fa-css-prefix}-write:before { content: fa-content($fa-var-pen); } .#{$fa-css-prefix}-write:before { content: fa-content($fa-var-pen); }
.#{$fa-css-prefix}-settings:before { content: fa-content($fa-var-cog); } .#{$fa-css-prefix}-settings:before { content: fa-content($fa-var-cog); }
.#{$fa-css-prefix}-logoff:before { content: fa-content($fa-var-sign-out); } .#{$fa-css-prefix}-logoff:before { content: fa-content($fa-var-sign-out); }
//Writer //Writer
.#{$fa-css-prefix}-bold:before { content: fa-content($fa-var-bold); } .#{$fa-css-prefix}-bold:before { content: fa-content($fa-var-bold); }
.#{$fa-css-prefix}-underline:before { content: fa-content($fa-var-underline); } .#{$fa-css-prefix}-underline:before { content: fa-content($fa-var-underline); }
.#{$fa-css-prefix}-ol:before { content: fa-content($fa-var-list-ol); } .#{$fa-css-prefix}-ol:before { content: fa-content($fa-var-list-ol); }
.#{$fa-css-prefix}-ul:before { content: fa-content($fa-var-list-ul); } .#{$fa-css-prefix}-ul:before { content: fa-content($fa-var-list-ul); }
.#{$fa-css-prefix}-strike:before { content: fa-content($fa-var-strikethrough); } .#{$fa-css-prefix}-strike:before { content: fa-content($fa-var-strikethrough); }
.#{$fa-css-prefix}-prev:before { content: fa-content($fa-var-angle-left); } .#{$fa-css-prefix}-prev:before { content: fa-content($fa-var-angle-left); }
.#{$fa-css-prefix}-next:before { content: fa-content($fa-var-angle-right); } .#{$fa-css-prefix}-next:before { content: fa-content($fa-var-angle-right); }

View File

@@ -28,13 +28,36 @@
font-family: $font_para; font-family: $font_para;
font-size: 14px; font-size: 14px;
& > p + p > .edi_thought_date, & > p + p > .edi_thought_time {
padding-top: 1em;
}
div { div {
margin: 0; margin: 0;
&.edi_header { &.edi_thought_date {
width: calc(50% - 1.5em);
}
&.edi_thought_date, &.edi_thought_time {
color: $gray-500; color: $gray-500;
font-style: italic; font-style: italic;
line-height: 3em; line-height: 3em;
display: inline-block;
text-indent: 0;
& + .edi_thought_date {
text-align: right;
margin-right: 2px;
padding-top: 0;
width: calc(50% - 2px);
}
}
&.edi_thought_time {
text-align: right;
margin-right: 2px;
width: 100%;
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
sass --unix-newline -l --style=compressed --watch mythoughts.scss:mythoughts.css sass --style=compressed --watch mythoughts.scss:mythoughts.css --poll