This commit is contained in:
2014-12-03 20:27:47 +01:00
commit 49a500fafb
19 changed files with 2638 additions and 0 deletions

131
inc/main.php Executable file
View File

@@ -0,0 +1,131 @@
<?php
/**
* Main Class
* @author franzz
* @version 1.0
*/
class Main extends PhpObject
{
//Constants
//...
//Objects
private $oClassManagement;
private $oMySql;
private $oTranslator;
//...
//Variables
private $sLang;
private $asMasks;
//...
/**
* Main constructor [to be called from index.php]
* @param ClassManagement $oClassManagement
* @param string $sLang
*/
public function __construct($oClassManagement, $sLang='')
{
parent::__construct(__CLASS__, Settings::DEBUG);
$this->oClassManagement = $oClassManagement;
//Browser <> PHP <> MySql synchronization
date_default_timezone_set(Settings::TIMEZONE);
ini_set('default_charset', Settings::TEXT_ENC);
header('Content-Type: text/html; charset='.Settings::TEXT_ENC);
mb_internal_encoding(Settings::TEXT_ENC);
mb_http_output(Settings::TEXT_ENC);
mb_http_input(Settings::TEXT_ENC);
mb_language('uni');
mb_regex_encoding(Settings::TEXT_ENC);
//Load classes
$this->oClassManagement->incClass('mask');
$this->oClassManagement->incClass('translator');
$this->oClassManagement->incClass('mysqlmanager');
//...
//Init objects
$this->setLanguage($sLang);
$this->setMasks();
$this->oTranslator = new Translator($this->getLanguage());
$this->oMySql = new MySqlManager(Settings::DB_SERVER, Settings::DB_LOGIN, Settings::DB_PASS, Settings::DB_NAME, self::getSqlOptions() , Settings::DB_ENC);
if($this->oMySql->sDbState == MySqlManager::DB_NO_DATA) $this->install();
//...
}
/**
* DB structure. See MySqlManager::__construct
*/
private static function getSqlOptions()
{
return array
(
'tables' => array
(
self::USER_TABLE=>array( 'first_name', 'last_name', 'nickname', 'email', 'pass')
//...
),
'types' => array
(
'first_name'=>"varchar(20) NOT NULL",
'last_name'=>"varchar(20) NOT NULL",
'nickname'=>"varchar(50) NOT NULL",
'email'=>"varchar(100) NOT NULL",
'pass'=>"varchar(128) NOT NULL"
//...
),
'constraints' => array
(
self::USER_TABLE=>"UNIQUE KEY `firstname_lastname_nickname` (`first_name`, `last_name`, `nickname`)"
//...
),
'cascading_delete' => array
(
self::USER_TABLE=>array(self::DON_TABLE)
//...
)
);
}
private function getLanguage()
{
return $this->sLang;
}
private function setLanguage($sLang='')
{
if($sLang!='') $this->sLang = $sLang;
else
{
//$_SERVER['REMOTE_ADDR'] = '193.106.178.41'; //Spain
//$_SERVER['REMOTE_ADDR'] = '160.92.167.193'; //France
//$_SERVER['REMOTE_ADDR'] = '74.125.230.216'; //US
$asIpInfo = json_decode(file_get_contents('http://api.ipinfodb.com/v3/ip-country/?key='.self::LOC_API_KEY.'&format=json&ip='.$_SERVER['REMOTE_ADDR']), true);
if($asIpInfo['statusCode'] == 'OK') $this->sLang = $asIpInfo['countryCode'];
}
}
private function setMasks()
{
//List all available masks
$asMaskPaths = glob(Mask::getMaskFile('*'));
$this->asMasks = array_map('basename', $asMaskPaths, array_fill(1, count($asMaskPaths), Mask::MASK_EXT));
}
private static function jsonExport($asData)
{
header('Content-type: application/json');
//return htmlspecialchars(json_encode($asData), ENT_NOQUOTES);
return self::jsonConvert($asData);
}
private static function jsonConvert($asData)
{
return json_encode($asData);
}
}
?>

231
inc/mask.php Executable file
View File

@@ -0,0 +1,231 @@
<?php
/**
* Mask Reader
* @author franzz
* @version 1.0
*/
class Mask extends PhpObject
{
public $sMaskName;
public $sFilePath;
private $sMask;
private $asTags;
private $asPartsSource;
private $aoInstances;
const MASK_FOLDER = 'masks/';
const MASK_EXT = '.html';
const START_TAG = 'START';
const END_TAG = 'END';
const TAG_MARK = '[#]';
public function __construct($sFileName='')
{
//init
parent::__construct(__CLASS__, Settings::DEBUG);
$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 static function getMaskFile($sFileName)
{
return self::MASK_FOLDER.$sFileName.self::MASK_EXT;
}
public function initFile($sFileName)
{
$sFilePath = self::getMaskFile(basename($sFileName));
if(file_exists($sFilePath))
{
$this->sFilePath = $sFilePath;
$sSource = file_get_contents($this->sFilePath);
$this->initMask($sFileName, $sSource);
}
else
{
$this->addError('Fichier introuvable &agrave; 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<part>\S+) \[START\] --\>/u', $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 = mb_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 mb_strpos($this->sMask, $sPartStartPattern) + strlen($sPartStartPattern);
}
private function getPartEndPos($sPartName)
{
$sPartEndPattern = $this->getPartPattern($sPartName, self::END_TAG);
return mb_strpos($this->sMask, $sPartEndPattern);
}
private function getPartPattern($sPartName, $sAction)
{
return '<!-- [PART] '.$sPartName.' ['.$sAction.'] -->';
}
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 is a pointer)
$oMask = $this->findPart($this, $sPartName);
if(!$oMask) $this->addError('No part found : '.$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);
if(!$oMask) $this->addError('No part found : '.$sPartName);
$oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue);
}
private function findPart($oMask, $sPartName)
{
if(array_key_exists($sPartName, $oMask->aoInstances))
{
return $oMask;
}
else //going deeper to find the part
{
foreach($oMask->aoInstances as $sLevelPartName=>$aoInstances)
{
if(!empty($aoInstances))
{
//take last instances
$oTmpMask = $this->findPart($oMask->getCurrentInstance($sLevelPartName), $sPartName);
if($oTmpMask) return $oTmpMask;
}
}
}
return false;
}
private function getCurrentInstance($sPartName)
{
if(!empty($this->aoInstances[$sPartName]))
{
return end($this->aoInstances[$sPartName]);
}
else
{
return false;
}
}
public function getTags()
{
$sSafeTagMark = preg_quote(self::TAG_MARK);
$sPattern = '/'.$sSafeTagMark.'(?P<tag>\w+)'.$sSafeTagMark.'/u';
preg_match_all($sPattern, $this->sMask, $asMatches);
return array_unique(array_filter($asMatches['tag']));
}
public function setTag($sTagName, $sTagValue)
{
$this->asTags[$sTagName] = $sTagValue;
}
public function setTags($asTags)
{
foreach($asTags as $sTagName=>$sTagValue) $this->setTag($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 ToolBox::array_map_encapsulate($oData, self::TAG_MARK);
}
}
?>

600
inc/mysqlmanager.php Executable file
View File

@@ -0,0 +1,600 @@
<?php
/**
* MySql query manager and generator
* @author franzz
* @version 1.0
*/
class MySqlManager extends PhpObject
{
const DB_NO_CONN = 'ERR_1';
const DB_NO_DATA = 'ERR_2';
const ID_TAG = 'id_';
public $sDbState;
private $bTrace;
private $asOptions;
/**
* SQL connection Handle
* @var mysqli
*/
private $oConnection;
private $sDatabase;
/**
* Constructor MySqlManager
* @param String $sDbServer
* @param String $sLogin
* @param String $sPass
* @param String $sDatabase
* @param Array $asOptions
* array( 'tables'=>array('table_name1'=>array('table_field1', 'table_field2', ...), 'table_name2'=>array(...)),
* 'types'=>array('field1'=>'field_type1', 'field2'=>'field_type2', ...)
* 'constraints'=>array('table_name1'=>'table_contraint1', 'table_name2'=>'table_contraint2', ...),
* 'cascading_delete'=>array('table_name1'=>array('linked_table1', 'linked_table2', ...), 'table_name2'=>...))
* @param string $sEncoding
*/
public function __construct($sDbServer, $sLogin, $sPass, $sDatabase, $asOptions, $sEncoding='utf8mb4')
{
parent::__construct(__CLASS__, Settings::DEBUG);
$this->sDatabase = $sDatabase;
$this->asOptions = $asOptions;
//$this->oConnection = mysql_connect(self::DB_SERVER, self::DB_LOGIN, self::DB_PASS);
$this->oConnection = new mysqli($sDbServer, $sLogin, $sPass);
$this->syncPhpParams($sEncoding);
/*
$dsn = 'mysql:dbname='.$this->sDatabase.';host='.self::DB_SERVER;
try {$dbh = new PDO($dsn, self::DB_LOGIN, self::DB_PASS);}
catch (PDOException $e) {$this->addError('Connexion échouée : ' . $e->getMessage());}
*/
$this->setTrace(false);
//if(!$this->oConnection)
if($this->oConnection->connect_error)
{
//$this->addError('bug connection');
$this->addError('bug connection : '.$this->oConnection->connect_error);
$this->sDbState = self::DB_NO_CONN;
}
else
{
//if(!mysql_select_db($this->sDatabase, $this->oConnection))
if(!$this->oConnection->select_db($this->sDatabase))
{
$this->addError('bug selecting database. Installing...');
$this->sDbState = self::DB_NO_DATA;
}
}
}
private function syncPhpParams($sEncoding)
{
//Characters encoding
$this->oConnection->set_charset($sEncoding); //SET NAMES
//Time zone
$oNow = new DateTime();
$iMins = $oNow->getOffset() / 60;
$iSign = ($iMins < 0)?-1:1;
$iMins = abs($iMins);
$iHours = floor($iMins / 60);
$iMins -= $iHours * 60;
$sOffset = sprintf('%+d:%02d', $iHours*$iSign, $iMins);
$this->setQuery("SET time_zone='{$sOffset}';");
}
public function __destruct()
{
parent::__destruct();
//mysql_close($this->oConnection);
$this->oConnection->close();
}
public function setTrace($bAction)
{
$this->bTrace = $bAction;
}
public function getTables()
{
/*
$oReflect = new ReflectionClass(__CLASS__);
$asConstants = $oReflect->getConstants();
$sTableTag = '_TABLE';
$asTables = array();
foreach($asConstants as $sConstant=>$sConstantValue)
{
if(mb_strpos($sConstant, $sTableTag)!==false && mb_strpos($sConstant, $sTableTag)==(mb_strlen($sConstant) - mb_strlen($sTableTag)))
{
$asTables[] = $sConstantValue;
}
}
return $asTables;
*/
return array_keys($this->asOptions['tables']);
}
public function install()
{
//Create Database
$this->setQuery("DROP DATABASE IF EXISTS ".$this->sDatabase);
$this->setQuery("CREATE DATABASE ".$this->sDatabase." DEFAULT CHARACTER SET ".Settings::DB_ENC." DEFAULT COLLATE ".Settings::DB_ENC."_general_ci");
//mysql_select_db($this->sDatabase, $this->oConnection);
$this->oConnection->select_db($this->sDatabase);
//Create tables
@array_walk($this->getInstallQueries(), array($this, 'setQuery'));
}
//For debug purposes
public function getFullInstallQuery()
{
$asInstallQueries = $this->getInstallQueries();
return str_replace("\n", "<br />", implode(";\n\n", $asInstallQueries))."\n\n<!-- \n".implode(";\n\n", $asInstallQueries)."\n -->";
}
private function getInstallQueries()
{
$asTables = $this->getTables();
$asInstallQueries = array_map(array($this, 'getInstallQuery'), $asTables);
$asAlterQueries = $this->getForeignKeyQueries($asTables);
return array_merge($asInstallQueries, $asAlterQueries);
}
private function getInstallQuery($sTableName)
{
$asTableColumns = $this->getTableColumns($sTableName);
$sQuery = "\n".$this->implodeAll($asTableColumns, "` ", "\n", "`", ",")."\n".implode(", \n", $this->getTableConstraints($sTableName));
return "CREATE TABLE `{$sTableName}` ({$sQuery})";
}
private function getForeignKeyQueries($asTableNames)
{
$asForeignKeyQueries = array();
foreach($asTableNames as $sTableName)
{
$asTableColumns = array_keys($this->getTablecolumns($sTableName));
foreach($asTableColumns as $sColumnName)
{
if($this->isId($sColumnName) && $sColumnName!=self::getId($sTableName))
{
$asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD INDEX(`".$sColumnName."`)";
$asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD FOREIGN KEY (`".$sColumnName."`) REFERENCES ".self::getTable($sColumnName)."(`".$sColumnName."`)";
}
}
}
return $asForeignKeyQueries;
}
private function setQuery($sQuery, $sTypeQuery=__FUNCTION__)
{
$this->getQuery($sQuery, $sTypeQuery);
//return (mysql_affected_rows()!=0);
return ($this->oConnection->affected_rows!=0);
}
private function getQuery($sQuery, $sTypeQuery=__FUNCTION__)
{
$sQuery = str_replace(array("\n", "\t"), array(" ", ""), $sQuery);
//$oResult = mysql_query($sQuery, $this->oConnection);
if($this->bTrace)
{
$this->setDebug(true);
$this->addNotice($sQuery);
}
if(!($oResult = $this->oConnection->query($sQuery)))
{
$this->addError("\nErreur SQL : \n".str_replace("\t", "", $sQuery)."\n\n".str_replace(array("\t", "\n"), "", $this->oConnection->error));
}
return $oResult;
}
public function getLastError()
{
}
public function getArrayQuery($sQuery, $bStringOnly=false, $sGroupBy='', $sTypeQuery=__FUNCTION__)
{
$iIndex = 0;
$iColumnCount = 0;
$asResult = array();
$oResult = $this->getQuery($sQuery, true, $sTypeQuery);
if($oResult!==false)
{
//while($asCurrentRow = mysql_fetch_array($oResult))
while($asCurrentRow = $oResult->fetch_array())
{
if($bStringOnly) $asCurrentRow = $this->arrayKeyFilter($asCurrentRow, 'is_string');
//Add table reel keys
if($sGroupBy!='' && array_key_exists($sGroupBy, $asCurrentRow))
{
$iRowKey = $asCurrentRow[$sGroupBy];
unset($asCurrentRow[$sGroupBy]);
}
else $iRowKey = $iIndex;
//For first loop, check table width
if($iIndex==0) $iColumnCount = count($asCurrentRow);
//One column case : collapse a level
if($iColumnCount==1) $asCurrentRow = array_shift($asCurrentRow);
$asResult[$iRowKey] = $asCurrentRow;
$iIndex++;
}
}
return $asResult;
}
private function getMaxIncrementedValue($sTable)
{
return $this->selectValue($sTable, "MAX(".$this->getId($sTable).")");
}
public static function getId($sTableName, $bFull=false)
{
$sColumnName = self::ID_TAG.self::getText($sTableName);
return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName;
}
public static function getText($sTableName, $bFull=false)
{
$sColumnName = mb_substr(str_replace('`', '', $sTableName), 0, -1);
$sColumnName = mb_substr($sColumnName, -2)=='ie'?mb_substr($sColumnName, 0, -2).'y':$sColumnName;
return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName;
}
public static function getFullColumnName($sTableName, $sColumnName)
{
return $sTableName.".".$sColumnName;
}
private function isId($sColumnName, $sTableName='')
{
$asTables = ($sTableName=='')?$this->getTables():array($sTableName);
$asTableIds = array_map(array('self', 'getId'), $asTables);
return in_array($sColumnName, $asTableIds);
}
private function getTable($sTableId)
{
$asTables = $this->getTables();
$asTableIds = array_map(array('self', 'getId'), $asTables);
if(in_array($sTableId, $asTableIds)) return $asTables[array_search($sTableId, $asTableIds)];
else
{
$this->addError('Id '.$sTableId.' pr&eacute;sent dans aucune table');
return false;
}
}
public function getTablecolumns($sTableName)
{
if(!array_key_exists($sTableName, $this->asOptions['tables'])) return false;
$asTableColumns = array(self::getId($sTableName));
foreach($this->asOptions['tables'][$sTableName] as $sFieldName) $asTableColumns[] = $sFieldName;
$asTableColumns[] = 'led';
$asTableName = array_fill(0, count($asTableColumns), $sTableName);
return array_combine($asTableColumns, array_map(array('self', 'getColumnType'), $asTableColumns, $asTableName));
}
private function getColumnType($sColumnName, $sTableName)
{
$sColumnType = '';
switch($sColumnName)
{
case array_key_exists($sColumnName, $this->asOptions['types']):
$sColumnType = $this->asOptions['types'][$sColumnName];
break;
case 'led':
$sColumnType = "TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP";
break;
case $this->isId($sColumnName, $sTableName):
$sColumnType = "int(10) UNSIGNED auto_increment";
break;
case $this->isId($sColumnName):
$sColumnType = "int(10) UNSIGNED";
break;
}
return $sColumnType;
}
private function getTableConstraints($sTableName)
{
//Primary key
$asTableConstraints = array('PRIMARY' => "PRIMARY KEY (`".self::getId($sTableName)."`)");
//Foreign keys: applied using ALTER TABLE syntax at the end to prevent scheduling CREATE TABLE queries
//Other constraints
if(array_key_exists($sTableName, $this->asOptions['constraints'])) $asTableConstraints[] = $this->asOptions['constraints'][$sTableName];
return $asTableConstraints;
}
private function addQuotes($oData)
{
//TODO remake
$asTrustedFunc = array('CURDATE()', 'NOW()');
$sChar = "'";
if(is_array($oData))
{
$asChar = array_fill(1, count($oData), $sChar);
return array_combine(array_keys($oData), array_map(array($this, 'addQuotes'), $oData, $asChar));
}
else
{
if(in_array($oData, $asTrustedFunc)) return $oData;
else return $sChar.$oData.$sChar;
}
}
private function getLastId()
{
return $this->oConnection->insert_id;
}
public function insertRow($sTableName, $asData)
{
$this->cleanSql($sTableName);
$this->cleanSql($asData);
$asQueryValues = $this->addQuotes($asData);
$sQuery = "INSERT INTO ".$sTableName." (`".implode("`, `", array_keys($asQueryValues))."`) VALUES (".implode(", ", $asQueryValues).")";
return $this->setQuery($sQuery)?$this->getLastId():0;
}
public function updateRow($sTableName, $asConstraints, $asData)
{
return $this->updateRows($sTableName, $asConstraints, $asData, 1);
}
public function updateRows($sTableName, $asConstraints, $asData, $iLimit=0)
{
if(!is_array($asConstraints))
{
$asConstraints = array($this->getId($sTableName)=>$asConstraints);
}
$this->cleanSql($sTableName);
$this->cleanSql($iTableId);
$this->cleanSql($asData);
$this->cleanSql($asConstraints);
$asQueryValues = $this->addQuotes($asData);
$asConstraintsValues = $this->addQuotes($asConstraints);
$this->addColumnSelectors($asQueryValues);
$this->addColumnSelectors($asConstraintsValues);
$sLimit = $iLimit>0?" LIMIT $iLimit":"";
$sQuery = "UPDATE {$sTableName} ".
"SET ".$this->implodeAll($asQueryValues, " = ", ", ")." ".
"WHERE ".$this->implodeAll($asConstraintsValues, " = ", " AND ").$sLimit;
$iResult = false;
if($this->setQuery($sQuery))
{
$iResult = ($iLimit==1)?$this->selectValue($sTableName, $this->getId($sTableName), $asConstraints):true;
}
return $iResult;
}
public function insertUpdateRow($sTableName, $asData, $asKeys=array(), $bUpdate=true)
{
$sTableIdName = self::getId($sTableName);
//check for data in the db
if($asKeys==array())
{
$asKeys[] = $sTableIdName;
}
$asValues = array_intersect_key($asData, array_flip($asKeys));
$iTableId = $this->selectValue($sTableName, $sTableIdName, $asValues);
//insert
if(!$iTableId)
{
$iTableId = $this->insertRow($sTableName, $asData);
}
//Update
elseif($bUpdate)
{
if(array_key_exists($sTableIdName, $asData))
{
unset($asData[$sTableIdName]);
}
$iTableId = $this->updateRow($sTableName, $iTableId, $asData);
}
return $iTableId;
}
public function selectInsert($sTableName, $asData, $asKeys=array())
{
return $this->insertUpdateRow($sTableName, $asData, $asKeys, false);
}
public function deleteRow($sTableName, $iTableId)
{
$this->cleanSql($sTableName);
$this->cleanSql($iTableId);
//linked tables
switch($sTableName)
{
case array_key_exists($sTableName, $this->asOptions['cascading_delete']) :
$asTables = array_filter(is_array($sTableName)?$sTableName:array($sTableName) + $this->asOptions['cascading_delete'][$sTableName]);
break;
case is_string($sTableName) :
$asTables = array($sTableName);
break;
case is_array($sTableName):
$asTables = $sTableName;
break;
default:
$asTables = array();
}
foreach($asTables as $sTable)
{
$this->setQuery("DELETE FROM ".$sTable." WHERE ".$this->getId($sTableName)." = ".$iTableId);
}
}
public function selectRows($asInfo, $bStringOnly=true, $sGroupBy='')
{
$asAttributes = array('select'=>"SELECT", 'from'=>"FROM", 'join'=>"LEFT JOIN", 'joinOn'=>"LEFT JOIN", 'constraint'=>"WHERE", 'groupBy'=>"GROUP BY", 'orderBy'=>"ORDER BY", 'limit'=>'LIMIT');
$asRowSeparators = array('select'=>", ", 'from'=>"", 'join'=>" LEFT JOIN ", 'joinOn'=>" LEFT JOIN ", 'constraint'=>" AND ", 'groupBy'=>", ", 'orderBy'=>", ", 'limit'=>"");
$asOperators = array('constraint'=>" = ", 'orderBy'=>" ", 'join'=>" USING(", 'joinOn'=>" ON ");
$asEndOfStatement = array('constraint'=>"", 'orderBy'=>"", 'join'=>")", 'joinOn'=>"");
//$sQuery = "/* ".str_replace(array("\n", "\t"), '', print_r($asInfo, true))." */";
$sQuery = "";
foreach($asAttributes 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' && !array_key_exists('constVar', $asInfo))
{
$asSelection = $this->addQuotes($asSelection);
}
$this->addColumnSelectors($asSelection);
$sQuery .= " ".$sKeyWord." ";
//in case of double value input
if(array_key_exists($sStatement, $asOperators))
{
if($sStatement=='constraint' && array_key_exists('constOpe', $asInfo))
{
$asOperators[$sStatement] = $asInfo['constOpe'];
}
$sQuery .= $this->implodeAll($asSelection, $asOperators[$sStatement], $asRowSeparators[$sStatement], "", $asEndOfStatement[$sStatement]);
}
else
{
$sQuery .= implode($asRowSeparators[$sStatement], $asSelection);
}
}
//default value for select
elseif($sStatement=='select')
{
$sQuery .= " ".$sKeyWord." * ";
}
}
return $this->getArrayQuery($sQuery, $bStringOnly, $sGroupBy);
}
private function addColumnSelectors(&$asSelection)
{
//TODO get rid of this
$sSqlWord = 'option';
$sKey = array_search($sSqlWord, $asSelection);
if($sKey!==false)
{
$asSelection[$sKey] = "`".$asSelection[$sKey]."`";
}
elseif(array_key_exists($sSqlWord, $asSelection))
{
$asSelection["`".$sSqlWord."`"] = $asSelection[$sSqlWord];
unset($asSelection[$sSqlWord]);
}
}
public function selectRow($sTableName, $asConstraints=array(), $sColumnName='*')
{
if(!is_array($asConstraints))
{
$asConstraints = array($this->getId($sTableName)=>$asConstraints);
}
$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&eacute;sultats pour un selectRow() : '.$iCountNb.' lignes. Table: '.$sTableName.', contrainte: '.self::implodeAll($asConstraints, '=', ' ').', colonne: '.$sColumnName);
break;
}
return array_shift($asResult);
}
public function selectValue($sTableName, $sColumnName, $oConstraints=array())
{
if(!is_array($oConstraints))
{
$oConstraints = array($this->getId($sTableName)=>$oConstraints);
}
return $this->selectRow($sTableName, $oConstraints, $sColumnName);
}
public function pingValue($sTableName, $oConstraints)
{
return $this->selectValue($sTableName, 'COUNT(1)', $oConstraints);
}
public function cleanSql(&$oData)
{
//self::cleanData($oData, 'mysql_real_escape_string');
//$oData = self::cleanData($oData, 'mysql_real_escape_string');
$this->cleanData($oData);
$oData = $this->cleanData($oData);
}
public static function implodeAll($asText, $asKeyValueSeparator='', $sRowSeparator='', $sKeyPre='', $sValuePost=false)
{
if($sValuePost===false)
{
$sValuePost = $sKeyPre;
}
$asCombinedText = array();
//if unique value for key value separator
if(!is_array($asKeyValueSeparator) && !empty($asText))
{
$asKeyValueSeparator = array_combine(array_keys($asText), array_fill(0, count($asText), $asKeyValueSeparator));
}
foreach($asText as $sKey=>$sValue)
{
$asCombinedText[] = $sKeyPre.$sKey.$asKeyValueSeparator[$sKey].(is_array($sValue)?implode($sValue):$sValue).$sValuePost;
}
return implode($sRowSeparator, $asCombinedText);
}
public static function arrayKeyFilter($asArray, $sCallBack)
{
$asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack));
return array_intersect_key($asArray, $asValidKeys);
}
public function cleanData($oData)
{
if(!is_array($oData))
{
return $this->oConnection->real_escape_string($oData);
}
elseif(count($oData)>0)
{
$asKeys = array_map(array($this, 'cleanData'), array_keys($oData));
$asValues = array_map(array($this, 'cleanData'), $oData);
return array_combine($asKeys, $asValues);
}
}
}
?>

169
inc/rss.php Executable file
View File

@@ -0,0 +1,169 @@
<?php
/**
* RSS Feed Class
* @author franzz
* @version 1.1
*
* Input:
* - $asDesc: Array. Description of the feed: fields 'title', 'link' (optional), 'copyright', 'description', 'language', 'webmaster_mail'
* - $asItems: Array. Feed item data: fields 'title', 'description', 'author', 'link', 'pub_date', 'guid'
*/
class Feed extends PhpObject {
private $asDesc;
private $asItems;
public function __construct($asDesc, $asItems=array())
{
parent::__construct(__CLASS__, Settings::DEBUG);
if(!array_key_exists('link', $asDesc))
{
$asDesc['link'] = 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME'].'/rss';
}
$this->asDesc = $asDesc;
array_walk($asItems, array($this, 'addItem'));
}
public function addItem($asItem)
{
$this->asItems[] = $asItem;
return count($this->asItems) - 1;
}
public function removeItem($iItemId)
{
$bExist = array_key_exists($iItemId, $this->asItems);
if($bExist) unset($this->asItems[$iItemId]);
return $bExist;
}
private function getGlobalPubDate()
{
$iGlobalPubDate = 0;
foreach($this->asItems as $asItem)
{
$iItemPubDate = strtotime($asItem['pub_date']);
if($iItemPubDate>$iGlobalPubDate)
{
$iGlobalPubDate = $iItemPubDate;
}
}
return self::cleanRss(self::getDate($iGlobalPubDate));
}
public function getFeed()
{
//feed header
$sRssFeedHeader = self::getHtml($this->asDesc['title'], 'title');
$sRssFeedHeader .= self::getHtml($this->asDesc['link'], 'link');
$sRssFeedHeader .= self::getHtml($this->asDesc['copyright'], 'copyright');
$sRssFeedHeader .= self::getHtml($this->asDesc['description'], 'description');
$sRssFeedHeader .= self::getHtml('', 'atom:link', '', '', array('href'=>$this->asDesc['link'], 'rel'=>'self', 'type'=>'application/atom+xml'), true);
$sRssFeedHeader .= self::getHtml($this->asDesc['language'], 'language');
$sRssFeedHeader .= self::getHtml($this->getGlobalPubDate(), 'pubDate');
$sRssFeedHeader .= self::getHtml('Lutran.fr RSS Feed Generator', 'generator');
$sRssFeedHeader .= self::getHtml($this->asDesc['webmaster_mail'].' (Webmaster)', 'webMaster');
//feed items
$asSortedItems = $this->rSortTimeMatrix($this->asItems, 'pub_date');
$sItems = implode("\n", array_map(array($this, 'buildItem'), $asSortedItems));
//Global Feed
$sFeed = '<?xml version="1.0" encoding="'.Settings::TEXT_ENC.'" ?>';
$sFeed .= self::getHtml(self::getHtml($sRssFeedHeader.$sItems, 'channel'), 'rss', '', '', array('version'=>'2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
return $sFeed;
}
private static function getDate($sDate)
{
if(!is_numeric($sDate))
{
$sDate = strtotime($sDate);
}
return date('r', $sDate);
}
private function buildItem($asItem)
{
$sRssItem = self::getHtml(self::cleanRss($asItem['title']), 'title');
$sRssItem .= self::getHtml(self::cleanRss($asItem['author']), 'author');
$sRssItem .= self::getHtml($asItem['link'], 'link');
$sRssItem .= self::getHtml($asItem['category'], 'category');
$sRssItem .= self::getHtml(self::cleanRss($asItem['description'].'.'), 'description');
$sRssItem .= self::getHtml(self::getDate($asItem['pub_date']), 'pubDate');
$sRssItem .= self::getHtml($asItem['guid'], 'guid', '', '', array('isPermaLink'=>'true'));
return self::getHtml($sRssItem, 'item');
}
private static function getHtml($oText, $sTag, $sClass='', $sStyle='', $asExtraAttr=array(), $bAutoClose=false, $sInter='')
{
$sHtmlAttr = '';
if($sClass!='')
{
$asExtraAttr['class'] = $sClass;
}
if($sStyle!='')
{
$asExtraAttr['style'] = $sStyle;
}
foreach($asExtraAttr as $sAttrName=>$sAttrValue)
{
$sHtmlAttr .= ' '.$sAttrName.'="'.$sAttrValue.'"';
}
if($bAutoClose)
{
$sHtml = self::encapsulate('', "\n".'<'.$sTag.$sHtmlAttr, ' />');
}
else
{
$sHtml = self::encapsulate($oText, "\n".'<'.$sTag.$sHtmlAttr.'>', '</'.$sTag.'>', $sInter);
}
return $sHtml;
}
private static function cleanRss($oText)
{
$asForbiddenChars = array('&', '<', '>', '"', "'");
$asReplacementCode = array('&amp;', '&lt;', '&gt;', '&quot;', '&apos;');
if(!is_array($oText))
{
return str_replace($asForbiddenChars, $asReplacementCode, $oText);
}
elseif(count($oText)>0)
{
$oTextKeys = array_map(array($this, 'cleanRss'), array_keys($oText));
$oTextValues = array_map(array($this, 'cleanRss'), array_values($oText));
return array_combine($oTextKeys, $oTextValues);
}
else
{
return $oText;
}
}
private static function encapsulate($oText, $sPre='', $sPost=false, $sInter='')
{
if($sPost===false)
{
$sPost = $sPre;
}
if(is_array($oText))
{
$oText = implode($sPost.$sInter.$sPre, $oText);
}
return $sPre.$oText.$sPost;
}
private static function rSortTimeMatrix($asMatrix, $sTimeCol)
{
$asResult = array();
foreach($asMatrix as $iRowId=>$asLine) $asKeys[$iRowId] = strtotime($asLine[$sTimeCol]);
arsort($asKeys);
foreach($asKeys as $iRowId=>$iTimeStamp) $asResult[$iRowId] = $asMatrix[$iRowId];
return $asResult;
}
}
?>

235
inc/toolbox.php Executable file
View File

@@ -0,0 +1,235 @@
<?php
/**
* ToolBox - Only static functions missing from php librairy
* @author franzz
* @version 1.1
*/
class ToolBox
{
const MAIL_SUCCESS = 2;
public static function cleanPost(&$asData)
{
//get rid of magic quotes
if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
{
$asData = self::cleanData($asData, 'stripslashes');
}
}
public static function cleanData($oData, $sCleaningFunc)
{
if(!is_array($oData))
{
return call_user_func($sCleaningFunc, $oData);
}
elseif(count($oData)>0)
{
$asCleaningFunc = array_fill(1, count($oData), $sCleaningFunc);
$asKeys = array_map(array('self', 'cleanData'), array_keys($oData), $asCleaningFunc);
$asValues = array_map(array('self', 'cleanData'), $oData, $asCleaningFunc);
return array_combine($asKeys, $asValues);
}
}
public static function fixGlobalVars($argv)
{
//Add CLI arguments
if(defined('STDIN')) mb_parse_str(implode('&', array_slice($argv, 1)), $_GET);
//Add Server Name
$sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD'];
$sAppPath = 'http://'.str_replace('http://', '', $sServerName.dirname($_SERVER['SCRIPT_NAME']));
$_GET['serv_name'] = $sAppPath.(mb_substr($sAppPath, -1)!='/'?'/':'');
}
public static 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('self', 'array_map_encapsulate'), $oData, $asChar));
}
else
{
return $sChar.$oData.$sChar;
}
}
public static function capitalizeWords($acText, $sCharList = '')
{
//Use ucwords if no delimiters are given
if($sCharList=='')
{
return Toolbox::mb_ucwords($acText);
}
// Go through all characters
$capitalizeNext = true;
$max = mb_strlen($acText);
for ($i = 0; $i < $max; $i++)
{
if(mb_strpos($sCharList, $acText[$i]) !== false)
{
$capitalizeNext = true;
}
elseif($capitalizeNext)
{
$capitalizeNext = false;
$acText[$i] = mb_strtoupper($acText[$i]);
}
}
return $acText;
}
/**
*
* @param String $sFromName
* @param String $sSubject
* @param String $sMessage
* @param String $sTo
* @param Array $asCc array(name => email)
* @param Boolean $bSelfMail
* @return mixed
*/
public function sendMail($sFromName, $sSubject, $sMessage, $sTo, $asCc=array(), $bSelfMail=true)
{
$asForm = array('api_key'=>Settings::MAIL_API_KEY,
'app'=>'Wedding',
'from_name'=>$sFromName,
'subject'=>$sSubject,
'msg'=>$sMessage,
'to_email'=>$sTo,
'cc_email'=>self::jsonConvert($asCc),
'self'=>$bSelfMail);
$oCurl = curl_init();
curl_setopt($oCurl, CURLOPT_URL, Settings::MAIL_SCRIPT);
curl_setopt($oCurl, CURLOPT_POST, true);
curl_setopt($oCurl, CURLOPT_HEADER, false);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($oCurl, CURLOPT_POSTFIELDS, $asForm);
$iResult = curl_exec($oCurl);
curl_close($oCurl);
return $iResult;
}
public static function jsonExport($asData)
{
header('Content-type: application/json');
//return htmlspecialchars(json_encode($asData), ENT_NOQUOTES);
return self::jsonConvert($asData);
}
public static function jsonConvert($asData)
{
return json_encode($asData);
}
public static function createThumbnail($sInPath, $iMaxWidth, $iMaxHeight, $sOutPath='', $bDeleteIn=false, $asImageExts=array('jpg', 'jpeg', 'gif', 'png'))
{
$asResult = array('error'=>'');
//Look up the extension to choose the image creator
//TODO use MIME types
$sInInfo = pathinfo($sInPath);
$sInName = mb_strtolower($sInInfo['basename']);
$sImageExt = mb_strtolower($sInInfo['extension']);
$sImageExt = ($sImageExt=='jpg')?'jpeg':$sImageExt;
//New Destination folder
if($sOutPath=='') $sOutPath = $sInPath;
elseif(mb_substr($sOutPath, -1)=='/') $sOutPath .= $sInName;
//New sizes
if(in_array($sImageExt, $asImageExts))
{
list($iWidth, $iHeight) = getimagesize($sInPath);
if($iWidth > $iMaxWidth || $iHeight > $iMaxHeight)
{
$dResizeDeltaWidth = $iWidth - $iMaxWidth;
$dResizeDeltaHeight = $iHeight - $iMaxHeight;
if($dResizeDeltaWidth > $dResizeDeltaHeight)
{
$iResizedWidth = $iMaxWidth;
$iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight;
}
else
{
$iResizedHeight = $iMaxHeight;
$iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth;
}
//create image from source
$oSource = call_user_func('imagecreatefrom'.$sImageExt, $sInPath);
//Resize
$oThumb = imagecreatetruecolor($iResizedWidth, $iResizedHeight);
imagecopyresized($oThumb, $oSource, 0, 0, 0, 0, $iResizedWidth, $iResizedHeight, $iWidth, $iHeight);
//Save
if(file_exists($sOutPath)) unlink($sOutPath);
if(!call_user_func_array('image'.$sImageExt, array($oThumb, $sOutPath)))
{
$asResult['error'] = 'Unable to create thumbnail : '.$sOutPath;
}
}
elseif($sInPath != $sOutPath)
{
$iResizedWidth = $iWidth;
$iResizedHeight = $iHeight;
if(!copy($sInPath, $sOutPath)) $asResult['error'] = 'Copy failed from '.$sInPath.' to '.$sOutPath;
}
$asResult['width'] = $iResizedWidth;
$asResult['height'] = $iResizedHeight;
$asResult['out'] = $sOutPath;
}
else $asResult['error'] = 'Wrong file type';
if($bDeleteIn && $asResult['error']=='' && $sInPath != $sOutPath) unlink($sInPath);
return $asResult;
}
public static function utf8_compliant($sText)
{
if(strlen($sText) == 0) return true;
return (preg_match('/^.{1}/us', $sText, $ar) == 1);
}
public static function mb_ucwords($sText)
{
return mb_convert_case($sText, MB_CASE_TITLE, "UTF-8");
}
public static function file_get_contents_utf8($oFile)
{
$sContent = file_get_contents($oFile);
return mb_convert_encoding($sContent, 'UTF-8', mb_detect_encoding($sContent, 'UTF-8, ISO-8859-1', true));
}
public static function rgbToHex($R, $G, $B)
{
$R = dechex($R);
if(strlen($R)<2) $R='0'.$R;
$G = dechex($G);
if(strlen($G)<2) $G='0'.$G;
$B = dechex($B);
if(strlen($B)<2) $B='0'.$B;
return $R.$G.$B;
}
public static function setCookie($sCookieName, $sCookieValue, $iDays)
{
$iTimeLimit = time()+60*60*24*$iDays;
setcookie($sCookieName, $sCookieValue, $iTimeLimit);
}
}
?>

117
inc/translator.php Executable file
View File

@@ -0,0 +1,117 @@
<?php
/**
* Translator Class
* @author franzz
* @version 1.0
*/
class Translator extends PhpObject
{
private $sLang;
private $asLanguages;
private $asTranslations; // [lang][key_word] = translation
const LANG_FOLDER = 'languages/';
const LANG_EXT = '.lang';
const LANG_SEP = '=';
const DEFAULT_LANG = 'FR';
public function __construct($sLang='')
{
parent::__construct(__CLASS__, Settings::DEBUG);
$this->asLanguages = array();
$this->asTranslations = array();
$this->loadLanguages();
$this->setLanguage($sLang);
}
public function setLanguage($sLang)
{
$this->sLang = in_array($sLang, $this->asLanguages)?$sLang:self::DEFAULT_LANG;
}
public function getTranslation($sTransKey, $sLang='')
{
$sTransText = false;
//Select language
if($sLang=='')
{
$sLang = $this->sLang;
}
//Look up in the selected language dictionary
if(in_array($sLang, $this->asLanguages) && array_key_exists($sTransKey, $this->asTranslations[$sLang]))
{
$sTransText = $this->asTranslations[$sLang][$sTransKey];
}
//Look up in the default language dictionary
elseif(array_key_exists($sTransKey, $this->asTranslations[self::DEFAULT_LANG]))
{
$this->addWarning('Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'", falling back to "'.self::DEFAULT_LANG.'"');
$sTransText = $this->asTranslations[self::DEFAULT_LANG][$sTransKey];
}
else $this->addWarning('Missing translation in "'.$sLang.'" for the key "'.$sTransKey.'"');
return $sTransText;
}
public function getHashToPage($asMenuPages)
{
$asHashToPage = array();
foreach($asMenuPages as $sHash)
{
foreach($this->asLanguages as $sLang)
{
$asHashToPage[$this->getTranslation('menu_'.$sHash.'_key', $sLang)] = $sHash;
}
}
return $asHashToPage;
}
public function getPageToHash($asMenuPages)
{
$asPageToHash = array();
foreach($asMenuPages as $sHash)
{
$asPageToHash[$sHash] = $this->getTranslation('menu_'.$sHash.'_key');
}
return $asPageToHash;
}
private function loadLanguages()
{
//List all available languages
$asLangPaths = glob(self::getLangPath('*'));
$this->asLanguages = array_map('basename', $asLangPaths, array_fill(1, count($asLangPaths), self::LANG_EXT));
//Load languages
array_walk($this->asLanguages, array($this, 'loadLanguageFile'));
}
private function loadLanguageFile($sLang)
{
if(!in_array($sLang, $this->asTranslations))
{
$sData = file_get_contents(self::getLangPath($sLang));
$asData = explode("\n", $sData);
foreach($asData as $sTranslation)
{
$iSepPos = stripos($sTranslation, self::LANG_SEP);
if($iSepPos!==false)
{
$sTransKey = trim(substr($sTranslation, 0, $iSepPos));
$sTransText = /*htmlspecialchars(*/trim(substr($sTranslation, $iSepPos+1))/*, ENT_QUOTES)*/; //TODO when all entities have been removed
$this->asTranslations[$sLang][$sTransKey] = $sTransText;
}
}
}
}
private static function getLangPath($sLang)
{
return self::LANG_FOLDER.$sLang.self::LANG_EXT;
}
}
?>