diff --git a/CoreVersions/0.2.1/Frameworks/Baikal/Core/Tools.php b/CoreVersions/0.2.1/Frameworks/Baikal/Core/Tools.php index 0e328fe..b41a6c4 100755 --- a/CoreVersions/0.2.1/Frameworks/Baikal/Core/Tools.php +++ b/CoreVersions/0.2.1/Frameworks/Baikal/Core/Tools.php @@ -61,7 +61,7 @@ class Tools { # Asserting DB file exists if(!file_exists(PROJECT_SQLITE_FILE)) { - throw new \Exception("DB file does not exist. To create it, please copy 'Core/Resources/db.empty.sqlite' to '" . PROJECT_SQLITE_FILE . "'"); + throw new \Exception("DB file does not exist. To create it, please copy 'Core/Resources/Db/db.empty.sqlite' to '" . PROJECT_SQLITE_FILE . "'"); } # Asserting DB file is readable diff --git a/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config.php b/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config.php index 8d91f16..93ffee1 100755 --- a/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config.php +++ b/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config.php @@ -202,7 +202,7 @@ abstract class Config extends \Flake\Core\Model\NoDb { asort($aNewConfig); asort($aWrittenConfig); - + if($aNewConfig != $aWrittenConfig) { throw new \Exception("New config does not correspond to expected config. Aborting, nothing has been changed."); } diff --git a/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config/System.php b/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config/System.php index 7e83385..35b825c 100755 --- a/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config/System.php +++ b/CoreVersions/0.2.1/Frameworks/Baikal/Model/Config/System.php @@ -50,6 +50,21 @@ class System extends \Baikal\Model\Config { "PROJECT_SQLITE_FILE" => array( "type" => "litteral", ), + "PROJECT_DB_MYSQL" => array( + "type" => "boolean", + ), + "PROJECT_DB_MYSQL_HOST" => array( + "type" => "string", + ), + "PROJECT_DB_MYSQL_DBNAME" => array( + "type" => "string", + ), + "PROJECT_DB_MYSQL_USERNAME" => array( + "type" => "string", + ), + "PROJECT_DB_MYSQL_PASSWORD" => array( + "type" => "string", + ), ); protected $aData = array( @@ -60,6 +75,11 @@ class System extends \Baikal\Model\Config { "BAIKAL_STANDALONE_ALLOWED" => "", "BAIKAL_STANDALONE_PORT" => "", "PROJECT_SQLITE_FILE" => "", + "PROJECT_DB_MYSQL" => "", + "PROJECT_DB_MYSQL_HOST" => "", + "PROJECT_DB_MYSQL_DBNAME" => "", + "PROJECT_DB_MYSQL_USERNAME" => "", + "PROJECT_DB_MYSQL_PASSWORD" => "", ); public function formMorphologyForThisModelInstance() { @@ -130,7 +150,35 @@ class System extends \Baikal\Model\Config { "inputclass" => "input-xxlarge", "help" => "The absolute server path to the SQLite file", ))); - + + $oMorpho->add(new \Formal\Element\Checkbox(array( + "prop" => "PROJECT_DB_MYSQL", + "label" => "Use MySQL", + "help" => "If checked, Baïkal will use MySQL instead of SQLite.", + "refreshonchange" => TRUE, + ))); + + $oMorpho->add(new \Formal\Element\Text(array( + "prop" => "PROJECT_DB_MYSQL_HOST", + "label" => "MySQL host", + "help" => "Host ip or name, including ':portnumber' if port is not the default one (3306)" + ))); + + $oMorpho->add(new \Formal\Element\Text(array( + "prop" => "PROJECT_DB_MYSQL_DBNAME", + "label" => "MySQL database name", + ))); + + $oMorpho->add(new \Formal\Element\Text(array( + "prop" => "PROJECT_DB_MYSQL_USERNAME", + "label" => "MySQL username", + ))); + + $oMorpho->add(new \Formal\Element\Password(array( + "prop" => "PROJECT_DB_MYSQL_PASSWORD", + "label" => "MySQL password", + ))); + return $oMorpho; } diff --git a/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Install/Initialize.php b/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Install/Initialize.php index db45eb2..2742eec 100755 --- a/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Install/Initialize.php +++ b/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Install/Initialize.php @@ -201,6 +201,21 @@ define("BAIKAL_CAL_BASEURI", PROJECT_BASEURI . "cal.php/"); # Define path to Baïkal Database SQLite file define("PROJECT_SQLITE_FILE", PROJECT_PATH_SPECIFIC . "db/db.sqlite"); +# Mysql > Use mysql instead of SQLite ? +define("PROJECT_DB_MYSQL", FALSE); + +# MySQL > Host, including ':portnumber' if port is not the default one (3306) +define("PROJECT_DB_MYSQL_HOST", ""); + +# MySQL > Database name +define("PROJECT_DB_MYSQL_DBNAME", ""); + +# MySQL > Username +define("PROJECT_DB_MYSQL_USERNAME", ""); + +# MySQL > Password +define("PROJECT_DB_MYSQL_PASSWORD", ""); + CODE; $sCode = trim($sCode); return $sCode; diff --git a/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Settings/System.php b/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Settings/System.php index 5bc42f8..358855e 100755 --- a/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Settings/System.php +++ b/CoreVersions/0.2.1/Frameworks/BaikalAdmin/Controller/Settings/System.php @@ -37,7 +37,9 @@ class System extends \Flake\Core\Controller { } $this->oForm = $this->oModel->formForThisModelInstance(array( - "close" => FALSE + "close" => FALSE, + "hook.morphology" => array($this, "morphologyHook"), + "hook.validation" => array($this, "validationHook"), )); if($this->oForm->submitted()) { @@ -58,4 +60,52 @@ class System extends \Flake\Core\Controller { return $oView->render(); } + + public function morphologyHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) { + if($oForm->submitted()) { + $bMySQL = (intval($oForm->postValue("PROJECT_DB_MYSQL")) === 1); + } else { + $bMySQL = PROJECT_DB_MYSQL; + } + + if($bMySQL === TRUE) { + $oMorpho->remove("PROJECT_SQLITE_FILE"); + } else { + + $oMorpho->remove("PROJECT_DB_MYSQL_HOST"); + $oMorpho->remove("PROJECT_DB_MYSQL_DBNAME"); + $oMorpho->remove("PROJECT_DB_MYSQL_USERNAME"); + $oMorpho->remove("PROJECT_DB_MYSQL_PASSWORD"); + } + } + + public function validationHook(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) { + if(intval($oForm->modelInstance()->get("PROJECT_DB_MYSQL")) === 1) { + + # We have to check the MySQL connection + $sHost = $oForm->modelInstance()->get("PROJECT_DB_MYSQL_HOST"); + $sDbName = $oForm->modelInstance()->get("PROJECT_DB_MYSQL_DBNAME"); + $sUsername = $oForm->modelInstance()->get("PROJECT_DB_MYSQL_USERNAME"); + $sPassword = $oForm->modelInstance()->get("PROJECT_DB_MYSQL_PASSWORD"); + + try { + $oDB = new \Flake\Core\Database\Mysql( + $sHost, + $sDbName, + $sUsername, + $sPassword + ); + + unset($oDB); + } catch(\Exception $e) { + $sMessage = "MySQL error: " . $e->getMessage(); + $sMessage .= "
Nothing has been saved"; + $oForm->declareError($oMorpho->element("PROJECT_DB_MYSQL_HOST"), $sMessage); + $oForm->declareError($oMorpho->element("PROJECT_DB_MYSQL_DBNAME")); + $oForm->declareError($oMorpho->element("PROJECT_DB_MYSQL_USERNAME")); + $oForm->declareError($oMorpho->element("PROJECT_DB_MYSQL_PASSWORD")); + } + + } + } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Core/Collection.php b/CoreVersions/0.2.1/Frameworks/Flake/Core/Collection.php index 43cfb24..679968b 100755 --- a/CoreVersions/0.2.1/Frameworks/Flake/Core/Collection.php +++ b/CoreVersions/0.2.1/Frameworks/Flake/Core/Collection.php @@ -51,13 +51,14 @@ class Collection extends \Flake\Core\FLObject implements \Iterator { return ($key !== NULL && $key !== FALSE); } - public function getForKey($sKey) { + public function &getForKey($sKey) { $aKeys = $this->keys(); if(!in_array($sKey, $aKeys)) { throw new \Exception("\Flake\Core\Collection->getForKey(): key '" . $sKey . "' not found in Collection"); } - return $this->aCollection[$sKey]; + $oRes = $this->aCollection[$sKey]; + return $oRes; } public function &each() { @@ -155,6 +156,16 @@ class Collection extends \Flake\Core\FLObject implements \Iterator { return $oNewColl; } + public function remove($sKey) { + $aKeys = $this->keys(); + if(!in_array($sKey, $aKeys)) { + throw new \Exception("\Flake\Core\Collection->remove(): key '" . $sKey . "' not found in Collection"); + } + + unset($this->aCollection[$sKey]); + $this->aCollection = array_values($this->aCollection); + } + public function &__call($sName, $aArguments) { if( strlen($sName) > 7 && diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Core/Database.php b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database.php index 2df57a1..8a1b3d8 100755 --- a/CoreVersions/0.2.1/Frameworks/Flake/Core/Database.php +++ b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database.php @@ -30,16 +30,16 @@ abstract class Database extends \Flake\Core\FLObject { /* common stuff */ - function messageAndDie($sMessage) { + protected function messageAndDie($sMessage) { $sError = "

" . get_class($this) . ": " . $sMessage . "

"; die($sError); } - function exec_INSERTquery($table,$fields_values,$no_quote_fields=FALSE) { + public function exec_INSERTquery($table,$fields_values,$no_quote_fields=FALSE) { return $this->query($this->INSERTquery($table,$fields_values,$no_quote_fields)); } - function INSERTquery($table,$fields_values,$no_quote_fields=FALSE) { + public function INSERTquery($table,$fields_values,$no_quote_fields=FALSE) { // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure). if (is_array($fields_values) && count($fields_values)) { @@ -63,11 +63,11 @@ abstract class Database extends \Flake\Core\FLObject { } } - function exec_UPDATEquery($table,$where,$fields_values,$no_quote_fields=FALSE) { + public function exec_UPDATEquery($table,$where,$fields_values,$no_quote_fields=FALSE) { return $this->query($this->UPDATEquery($table,$where,$fields_values,$no_quote_fields)); } - function UPDATEquery($table,$where,$fields_values,$no_quote_fields=FALSE) { + public function UPDATEquery($table,$where,$fields_values,$no_quote_fields=FALSE) { // Table and fieldnames should be "SQL-injection-safe" when supplied to this function (contrary to values in the arrays which may be insecure). if (is_string($where)) { @@ -99,11 +99,11 @@ abstract class Database extends \Flake\Core\FLObject { } } - function exec_DELETEquery($table,$where) { + public function exec_DELETEquery($table,$where) { return $this->query($this->DELETEquery($table,$where)); } - function DELETEquery($table,$where) { + public function DELETEquery($table,$where) { if (is_string($where)) { // Table and fieldnames should be "SQL-injection-safe" when supplied to this function @@ -119,11 +119,11 @@ abstract class Database extends \Flake\Core\FLObject { } } - function exec_SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='') { + public function exec_SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='') { return $this->query($this->SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy,$limit)); } - function SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='') { + public function SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='') { // Table and fieldnames should be "SQL-injection-safe" when supplied to this function // Build basic query: @@ -154,11 +154,11 @@ abstract class Database extends \Flake\Core\FLObject { return $query; } - function fullQuote($str, $table) { + public function fullQuote($str, $table) { return '\''.$this->quote($str, $table).'\''; } - function fullQuoteArray($arr, $table, $noQuote=FALSE) { + public function fullQuoteArray($arr, $table, $noQuote=FALSE) { if (is_string($noQuote)) { $noQuote = explode(',',$noQuote); } elseif (!is_array($noQuote)) { // sanity check @@ -173,11 +173,34 @@ abstract class Database extends \Flake\Core\FLObject { return $arr; } - /* fonctions abstraites */ + /* Should be abstract, but we provide a body anyway as PDO abstracts these methods for us */ - abstract function query($sSql); + public function query($sSql) { + if(($stmt = $this->oDb->query($sSql)) === FALSE) { + $sMessage = print_r($this->oDb->errorInfo(), TRUE); + throw new \Exception("SQL ERROR in: '" . $sSql . "'; Message: " . $sMessage); + } + + return new \Flake\Core\Database\Statement($stmt); + } - abstract function lastInsertId(); + public function lastInsertId() { + return $this->oDb->lastInsertId(); + } - abstract function quote($str); + public function quote($str) { + return substr($this->oDb->quote($str), 1, -1); # stripping first and last quote + } + + public function getPDO() { + return $this->oDb; + } + + public function close() { + $this->oDb = null; + } + + public function __destruct() { + $this->close(); + } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Mysql.php b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Mysql.php new file mode 100644 index 0000000..b3a376d --- /dev/null +++ b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Mysql.php @@ -0,0 +1,52 @@ + +# All rights reserved +# +# http://flake.codr.fr +# +# This script is part of the Flake project. The Flake +# project is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# This copyright notice MUST APPEAR in all copies of the script! +################################################################# + +namespace Flake\Core\Database; + +class Mysql extends \Flake\Core\Database { + + protected $oDb = FALSE; // current DB link + protected $debugOutput = FALSE; + protected $store_lastBuiltQuery = TRUE; + protected $debug_lastBuiltQuery = ""; + protected $sHost = ""; + protected $sDbName = ""; + protected $sUsername = ""; + protected $sPassword = ""; + + public function __construct($sHost, $sDbName, $sUsername, $sPassword) { + $this->sHost = $sHost; + $this->sDbName = $sDbName; + $this->sUsername = $sUsername; + $this->sPassword = $sPassword; + + $this->oDb = new \PDO( + 'mysql:host=' . $this->sHost . ';dbname=' . $this->sDbName, + $this->sUsername, + $this->sPassword + ); + } +} \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Sqlite.php b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Sqlite.php index 8228764..173f2cd 100755 --- a/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Sqlite.php +++ b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Sqlite.php @@ -37,44 +37,5 @@ class Sqlite extends \Flake\Core\Database { public function __construct($sDbPath) { $this->sDbPath = $sDbPath; $this->oDb = new \PDO('sqlite:' . $this->sDbPath); -# $this->oDb->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - } - - public function query($sSql) { - if(($stmt = $this->oDb->query($sSql)) === FALSE) { - $sMessage = print_r($this->oDb->errorInfo(), TRUE); - throw new \Exception("SQL ERROR in: '" . $sSql . "'; Message: " . $sMessage); - } - - return new \Flake\Core\Database\SqliteStatement($stmt); - } - - public function lastInsertId() { - return $this->oDb->lastInsertId(); - } - - public function quote($str) { - return substr($this->oDb->quote($str), 1, -1); # stripping first and last quote - } - - public function getPDO() { - return $this->oDb; - } -} - -Class SqliteStatement { - - protected $stmt = null; - - public function __construct($stmt) { - $this->stmt = $stmt; - } - - public function fetch() { - if($this->stmt !== FALSE) { - return $this->stmt->fetch(\PDO::FETCH_ASSOC, \PDO::FETCH_ORI_FIRST); - } - - return FALSE; } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Statement.php b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Statement.php new file mode 100644 index 0000000..71aec56 --- /dev/null +++ b/CoreVersions/0.2.1/Frameworks/Flake/Core/Database/Statement.php @@ -0,0 +1,43 @@ + +# All rights reserved +# +# http://flake.codr.fr +# +# This script is part of the Flake project. The Flake +# project is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# This copyright notice MUST APPEAR in all copies of the script! +################################################################# + +namespace Flake\Core\Database; + +class Statement extends \Flake\Core\FLObject { + protected $stmt = null; + + public function __construct($stmt) { + $this->stmt = $stmt; + } + + public function fetch() { + if($this->stmt !== FALSE) { + return $this->stmt->fetch(\PDO::FETCH_ASSOC, \PDO::FETCH_ORI_FIRST); + } + + return FALSE; + } +} \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Framework.php b/CoreVersions/0.2.1/Frameworks/Flake/Framework.php index 91bed41..8b73863 100755 --- a/CoreVersions/0.2.1/Frameworks/Flake/Framework.php +++ b/CoreVersions/0.2.1/Frameworks/Flake/Framework.php @@ -166,6 +166,14 @@ class Framework extends \Flake\Core\Framework { protected static function initDb() { + if(defined("PROJECT_DB_MYSQL") && PROJECT_DB_MYSQL === TRUE) { + self::initDbMysql(); + } else { + self::initDbSqlite(); + } + } + + protected static function initDbSqlite() { # Asserting DB filepath is set if(!defined("PROJECT_SQLITE_FILE")) { return; @@ -173,7 +181,7 @@ class Framework extends \Flake\Core\Framework { # Asserting DB file exists if(!file_exists(PROJECT_SQLITE_FILE)) { - die("

DB file does not exist. To create it, please copy 'Core/Resources/db.empty.sqlite' to '" . PROJECT_SQLITE_FILE . "'

"); + die("

DB file does not exist. To create it, please copy 'Core/Resources/Db/db.empty.sqlite' to '" . PROJECT_SQLITE_FILE . "'

"); } # Asserting DB file is readable @@ -190,4 +198,37 @@ class Framework extends \Flake\Core\Framework { $GLOBALS["DB"] = new \Flake\Core\Database\Sqlite(PROJECT_SQLITE_FILE); } } + + protected static function initDbMysql() { + + if(!defined("PROJECT_DB_MYSQL_HOST")) { + die("

The constant PROJECT_DB_MYSQL_HOST, containing the MySQL host name, is not set.
You should set it in Specific/config.system.php

"); + } + + if(!defined("PROJECT_DB_MYSQL_DBNAME")) { + die("

The constant PROJECT_DB_MYSQL_DBNAME, containing the MySQL database name, is not set.
You should set it in Specific/config.system.php

"); + } + + if(!defined("PROJECT_DB_MYSQL_USERNAME")) { + die("

The constant PROJECT_DB_MYSQL_USERNAME, containing the MySQL database username, is not set.
You should set it in Specific/config.system.php

"); + } + + if(!defined("PROJECT_DB_MYSQL_PASSWORD")) { + die("

The constant PROJECT_DB_MYSQL_PASSWORD, containing the MySQL database password, is not set.
You should set it in Specific/config.system.php

"); + } + + try { + $GLOBALS["DB"] = new \Flake\Core\Database\Mysql( + PROJECT_DB_MYSQL_HOST, + PROJECT_DB_MYSQL_DBNAME, + PROJECT_DB_MYSQL_USERNAME, + PROJECT_DB_MYSQL_PASSWORD + ); + } catch(\Exception $e) { + die("

Baïkal was not able to establish a connexion to the configured MySQL database (as configured in Specific/config.system.php).

"); + } + + # We now setup the connexion to use UTF8 + $GLOBALS["DB"]->query("SET NAMES UTF8"); + } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Flake/Util/Tools.php b/CoreVersions/0.2.1/Frameworks/Flake/Util/Tools.php index 7895047..81e4a1e 100755 --- a/CoreVersions/0.2.1/Frameworks/Flake/Util/Tools.php +++ b/CoreVersions/0.2.1/Frameworks/Flake/Util/Tools.php @@ -105,12 +105,29 @@ class Tools extends \Flake\Core\FLObject { public static function debug($var="",$brOrHeader=0) { if($brOrHeader === 0) { - $trail = debug_backtrace(); - $trail = array_reverse($trail); - array_pop($trail); // la ligne d'appel à debug - array_pop($trail); // la ligne d'appel à debug - $aLastNode = array_pop($trail); // l'appel qui nous intéresse - $brOrHeader = @strval($aLastNode['class']).@strval($aLastNode['type']).@strval($aLastNode['function']); + try { + $trail = debug_backtrace(); + $trail = array_reverse($trail); + array_pop($trail); // la ligne d'appel à debug + array_pop($trail); // la ligne d'appel à debug + $aLastNode = array_pop($trail); // l'appel qui nous intéresse + + if(array_key_exists("class", $aLastNode)) { + $sClass = @strval($aLastNode["class"]); + } else { + $sClass = ""; + } + + if(array_key_exists("type", $aLastNode)) { + $sType = @strval($aLastNode["type"]); + } else { + $sType = ""; + } + + $brOrHeader = $sClass.$sType.@strval($aLastNode['function']); + } catch(\Exception $e) { + $brOrHeader = "Undetermined context"; + } } if ($brOrHeader) { diff --git a/CoreVersions/0.2.1/Frameworks/Formal/Element.php b/CoreVersions/0.2.1/Frameworks/Formal/Element.php index 5c28adc..e12fd40 100755 --- a/CoreVersions/0.2.1/Frameworks/Formal/Element.php +++ b/CoreVersions/0.2.1/Frameworks/Formal/Element.php @@ -37,6 +37,7 @@ abstract class Element { "placeholder" => "", "help" => "", "popover" => "", + "refreshonchange" => FALSE, ); protected $sValue = ""; @@ -81,5 +82,19 @@ abstract class Element { return get_class($this) . "<" . $this->option("label") . ">"; } + public function renderWitness() { + return ''; + } + + public function posted() { + $aPost = \Flake\Util\Tools::POST("witness"); + if(is_array($aPost)) { + $sProp = $this->option("prop"); + return (array_key_exists($sProp, $aPost)) && (intval($aPost[$sProp]) === 1); + } + + return FALSE; + } + public abstract function render(); } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Formal/Element/Checkbox.php b/CoreVersions/0.2.1/Frameworks/Formal/Element/Checkbox.php index 2be7a71..9061d48 100755 --- a/CoreVersions/0.2.1/Frameworks/Formal/Element/Checkbox.php +++ b/CoreVersions/0.2.1/Frameworks/Formal/Element/Checkbox.php @@ -37,14 +37,15 @@ class Checkbox extends \Formal\Element { $disabled = ""; $inputclass = ""; $groupclass = ""; + $onchange = ""; + $helpblock = ""; + $popover = ""; $value = $this->value(); $checked = ($value === TRUE ? " checked=\"checked\" " : ""); $label = $this->option("label"); $prop = $this->option("prop"); - $helpblock = ""; - $popover = ""; if($this->option("readonly") === TRUE) { $inputclass .= " disabled"; @@ -65,15 +66,19 @@ class Checkbox extends \Formal\Element { $popover .= " data-content=\"" . htmlspecialchars($aPopover["content"]) . "\" "; } + if($this->option("refreshonchange") === TRUE) { + $onchange = " onchange=\"document.getElementsByTagName('form')[0].submit();\" "; + } + $sHtml =<<
- + {$helpblock}
HTML; - return $sHtml; + return $sHtml . $this->renderWitness(); } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Formal/Element/Listbox.php b/CoreVersions/0.2.1/Frameworks/Formal/Element/Listbox.php index 29aa718..27cf2c6 100755 --- a/CoreVersions/0.2.1/Frameworks/Formal/Element/Listbox.php +++ b/CoreVersions/0.2.1/Frameworks/Formal/Element/Listbox.php @@ -92,13 +92,13 @@ class Listbox extends \Formal\Element {
- {$sRenderedOptions} {$helpblock}
HTML; - return $sHtml; + return $sHtml . $this->renderWitness(); } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Formal/Element/Text.php b/CoreVersions/0.2.1/Frameworks/Formal/Element/Text.php index 7e5ea86..2c1b19c 100755 --- a/CoreVersions/0.2.1/Frameworks/Formal/Element/Text.php +++ b/CoreVersions/0.2.1/Frameworks/Formal/Element/Text.php @@ -91,11 +91,11 @@ class Text extends \Formal\Element {
- + {$helpblock}
HTML; - return $sHtml; + return $sHtml . $this->renderWitness(); } } \ No newline at end of file diff --git a/CoreVersions/0.2.1/Frameworks/Formal/Form.php b/CoreVersions/0.2.1/Frameworks/Formal/Form.php index a1790ee..24296c1 100755 --- a/CoreVersions/0.2.1/Frameworks/Formal/Form.php +++ b/CoreVersions/0.2.1/Frameworks/Formal/Form.php @@ -33,6 +33,8 @@ class Form { "action" => "", "close" => TRUE, "closeurl" => "", + "hook.validation" => FALSE, + "hook.morphology" => FALSE, ); protected $oModelInstance = null; protected $oElements = null; @@ -42,6 +44,8 @@ class Form { protected $sDisplayTitle = ""; # Displayed form title; generated in setModelInstance() protected $sDisplayMessage = ""; # Displayed confirm message; generated in execute() + protected $oMorpho = null; + public function __construct($sModelClass, $aOptions = array()) { $this->sModelClass = $sModelClass; $this->aOptions = array_merge($this->aOptions, $aOptions); @@ -66,6 +70,21 @@ class Form { return $aOptions; } + public function getMorpho() { + if(!is_null($this->oMorpho)) { + return $this->oMorpho; + } + + $this->oMorpho = $this->modelInstance()->formMorphologyForThisModelInstance(); + + # Calling validation hook if defined + if(($aHook = $this->option("hook.morphology")) !== FALSE) { + call_user_func($aHook, $this, $this->oMorpho); + } + + return $this->oMorpho; + } + public function setModelInstance($oModelInstance) { if(!\Flake\Util\Tools::is_a($oModelInstance, $this->sModelClass)) { throw new \Exception("\Formal\Core->setModelInstance(): Given instance is not of class '" . $this->sModelClass . "'"); @@ -104,7 +123,7 @@ class Form { public function execute() { # Obtaining morphology from model object - $oMorpho = $this->modelInstance()->formMorphologyForThisModelInstance(); + $oMorpho = $this->getMorpho(); $this->aErrors = array(); $oMorpho->elements()->reset(); @@ -117,15 +136,24 @@ class Form { $sPropName = $oElement->option("prop"); # posted value is fetched, then passes to element before persistance - $sPostValue = $this->postValue($sPropName); - $oElement->setValue($sPostValue); - - $sValue = $oElement->value(); - - $this->modelInstance()->set( - $sPropName, - $sValue - ); + if($oElement->posted()) { + + $sPostValue = $this->postValue($sPropName); + $oElement->setValue($sPostValue); + + $sValue = $oElement->value(); + + $this->modelInstance()->set( + $sPropName, + $sValue + ); + } else { + $oElement->setValue( + $this->modelInstance()->get( + $sPropName + ) + ); + } } $oMorpho->elements()->reset(); @@ -162,18 +190,17 @@ class Form { } if($mValid !== TRUE) { - $this->aErrors[] = array( - "element" => $oElement, - "message" => $mValid, - ); - - $oElement->setOption("error", TRUE); - + $this->declareError($oElement, $mValid); break; # one error per element per submit } } } + # Calling validation hook if defined + if(($aHook = $this->option("hook.validation")) !== FALSE) { + call_user_func($aHook, $this, $oMorpho); + } + if(empty($this->aErrors)) { # Model object is persisted @@ -198,7 +225,7 @@ class Form { $this->modelInstance()->persist(); if($bWasFloating === FALSE) { # Title is generated now, as submitted data might have changed the model instance label - $this->sDisplayTitle = "Editing " . $this->modelInstance()->humanName() . "" . $this->modelInstance()->label() . ""; + $this->sDisplayTitle = "Editing " . $this->modelInstance()->humanName() . "" . $this->modelInstance()->label() . ""; } $this->bPersisted = TRUE; } else { @@ -206,6 +233,16 @@ class Form { } } + # public, as it may be called from a hook + public function declareError(\Formal\Element $oElement, $sMessage = "") { + $this->aErrors[] = array( + "element" => $oElement, + "message" => $sMessage, + ); + + $oElement->setOption("error", TRUE); + } + public function persisted() { if($this->submitted()) { if(is_null($this->bPersisted)) { @@ -279,13 +316,19 @@ class Form { } public function postValue($sPropName) { - return \Flake\Util\Tools::POST($sPropName); + $aData = \Flake\Util\Tools::POST("data"); + + if(is_array($aData) && array_key_exists($sPropName, $aData)) { + return $aData[$sPropName]; + } + + return ""; } public function render() { $aHtml = array(); - $oMorpho = $this->modelInstance()->formMorphologyForThisModelInstance(); + $oMorpho = $this->getMorpho(); $oMorpho->elements()->reset(); foreach($oMorpho->elements() as $oElement) { @@ -320,6 +363,10 @@ class Form { $aMessages = array(); reset($this->aErrors); foreach($this->aErrors as $aError) { + if(trim($aError["message"]) === "") { + continue; + } + $aMessages[] = $aError["message"]; } diff --git a/CoreVersions/0.2.1/Frameworks/Formal/Form/Morphology.php b/CoreVersions/0.2.1/Frameworks/Formal/Form/Morphology.php index e3eef57..b16dc06 100755 --- a/CoreVersions/0.2.1/Frameworks/Formal/Form/Morphology.php +++ b/CoreVersions/0.2.1/Frameworks/Formal/Form/Morphology.php @@ -38,18 +38,35 @@ class Morphology { $this->oElements->push($oElement); } - public function element($sPropName) { + protected function keyForPropName($sPropName) { $aKeys = $this->oElements->keys(); reset($aKeys); foreach($aKeys as $sKey) { $oElement = $this->oElements->getForKey($sKey); if($oElement->option("prop") === $sPropName) { - return $oElement; + return $sKey; } } - throw new \Exception("\Formal\Form\Morphology->element(): Element prop='" . $sPropName . "' not found"); + return FALSE; + } + + public function &element($sPropName) { + if(($sKey = $this->keyForPropName($sPropName)) === FALSE) { + throw new \Exception("\Formal\Form\Morphology->element(): Element prop='" . $sPropName . "' not found"); + } + + $oElement = $this->oElements->getForKey($sKey); + return $oElement; + } + + public function remove($sPropName) { + if(($sKey = $this->keyForPropName($sPropName)) === FALSE) { + throw new \Exception("\Formal\Form\Morphology->element(): Element prop='" . $sPropName . "' not found"); + } + + $this->oElements->remove($sKey); } public function elements() { diff --git a/CoreVersions/0.2.1/Resources/Db/db.empty.mysql.sql b/CoreVersions/0.2.1/Resources/Db/db.empty.mysql.sql new file mode 100644 index 0000000..3dc2323 --- /dev/null +++ b/CoreVersions/0.2.1/Resources/Db/db.empty.mysql.sql @@ -0,0 +1,80 @@ +# +# This is the empty database schema for Baïkal +# Corresponds to the MySQL Schema definition of project SabreDAV 1.6.4 +# http://code.google.com/p/sabredav/ +# + +CREATE TABLE users ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(50), + digesta1 VARCHAR(32), + UNIQUE(username) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE principals ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + uri VARCHAR(200) NOT NULL, + email VARCHAR(80), + displayname VARCHAR(80), + vcardurl VARCHAR(80), + UNIQUE(uri) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE groupmembers ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principal_id INTEGER UNSIGNED NOT NULL, + member_id INTEGER UNSIGNED NOT NULL, + UNIQUE(principal_id, member_id) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE locks ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + owner VARCHAR(100), + timeout INTEGER UNSIGNED, + created INTEGER, + token VARCHAR(100), + scope TINYINT, + depth TINYINT, + uri text +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE calendarobjects ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + calendardata MEDIUMBLOB, + uri VARCHAR(200), + calendarid INTEGER UNSIGNED NOT NULL, + lastmodified INT(11), + UNIQUE(calendarid, uri) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE calendars ( + id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(100), + displayname VARCHAR(100), + uri VARCHAR(200), + ctag INTEGER UNSIGNED NOT NULL DEFAULT '0', + description TEXT, + calendarorder INTEGER UNSIGNED NOT NULL DEFAULT '0', + calendarcolor VARCHAR(10), + timezone TEXT, + components VARCHAR(20), + UNIQUE(principaluri, uri) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE addressbooks ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + principaluri VARCHAR(255), + displayname VARCHAR(255), + uri VARCHAR(200), + description TEXT, + ctag INT(11) UNSIGNED NOT NULL DEFAULT '1', + UNIQUE(principaluri, uri) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE cards ( + id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + addressbookid INT(11) UNSIGNED NOT NULL, + carddata MEDIUMBLOB, + uri VARCHAR(200), + lastmodified INT(11) UNSIGNED +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; \ No newline at end of file diff --git a/CoreVersions/0.2.1/Resources/db.empty.sqlite b/CoreVersions/0.2.1/Resources/Db/db.empty.sqlite old mode 100755 new mode 100644 similarity index 100% rename from CoreVersions/0.2.1/Resources/db.empty.sqlite rename to CoreVersions/0.2.1/Resources/Db/db.empty.sqlite