diff --git a/Core/Frameworks/Baikal/Model/Calendar.php b/Core/Frameworks/Baikal/Model/Calendar.php
index 1a9467148..6736c82b5 100644
--- a/Core/Frameworks/Baikal/Model/Calendar.php
+++ b/Core/Frameworks/Baikal/Model/Calendar.php
@@ -242,7 +242,7 @@ function isDefault() {
function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
- "count(*)",
+ "count(*) as count",
"calendarinstances",
"calendarid" . "='" . $this->aData["calendarid"] . "'"
);
@@ -252,7 +252,7 @@ function hasInstances() {
} else {
reset($aRs);
- return $aRs["count(*)"] > 1;
+ return $aRs["count"] > 1;
}
}
diff --git a/Core/Frameworks/Baikal/Model/Calendar/Calendar.php b/Core/Frameworks/Baikal/Model/Calendar/Calendar.php
index 4dfe46428..3031d7604 100644
--- a/Core/Frameworks/Baikal/Model/Calendar/Calendar.php
+++ b/Core/Frameworks/Baikal/Model/Calendar/Calendar.php
@@ -38,7 +38,7 @@ class Calendar extends \Flake\Core\Model\Db {
function hasInstances() {
$rSql = $GLOBALS["DB"]->exec_SELECTquery(
- "count(*)",
+ "count(*) as count",
"calendarinstances",
"calendarid" . "='" . $this->aData["id"] . "'"
);
@@ -48,7 +48,7 @@ function hasInstances() {
} else {
reset($aRs);
- return $aRs["count(*)"] > 1;
+ return $aRs["count"] > 1;
}
}
diff --git a/Core/Frameworks/Baikal/Model/Config/Database.php b/Core/Frameworks/Baikal/Model/Config/Database.php
index e0035d59b..f26301068 100644
--- a/Core/Frameworks/Baikal/Model/Config/Database.php
+++ b/Core/Frameworks/Baikal/Model/Config/Database.php
@@ -37,6 +37,11 @@ class Database extends \Baikal\Model\Config {
"mysql_username" => "",
"mysql_password" => "",
"encryption_key" => "",
+ "pgsql" => false,
+ "pgsql_host" => "",
+ "pgsql_dbname" => "",
+ "pgsql_username" => "",
+ "pgsql_password" => "",
];
function __construct() {
@@ -82,6 +87,34 @@ function formMorphologyForThisModelInstance() {
"label" => "MySQL password",
]));
+ $oMorpho->add(new \Formal\Element\Checkbox(array(
+ "prop" => "pgsql",
+ "label" => "Use PostgreSQL",
+ "help" => "If checked, Baïkal will use PostgreSQL",
+ "refreshonchange" => true,
+ )));
+
+ $oMorpho->add(new \Formal\Element\Text(array(
+ "prop" => "pgsql_host",
+ "label" => "PostgreSQL host",
+ "help" => "Host ip or name, including ':portnumber' if port is not the default one (?)"
+ )));
+
+ $oMorpho->add(new \Formal\Element\Text(array(
+ "prop" => "pgsql_dbname",
+ "label" => "PostgreSQL database name",
+ )));
+
+ $oMorpho->add(new \Formal\Element\Text(array(
+ "prop" => "pgsql_username",
+ "label" => "PostgreSQL username",
+ )));
+
+ $oMorpho->add(new \Formal\Element\Password(array(
+ "prop" => "pgsql_password",
+ "label" => "PostgreSQL password",
+ )));
+
return $oMorpho;
}
diff --git a/Core/Frameworks/BaikalAdmin/Controller/Install/Database.php b/Core/Frameworks/BaikalAdmin/Controller/Install/Database.php
index 4a57584f5..1da56b788 100644
--- a/Core/Frameworks/BaikalAdmin/Controller/Install/Database.php
+++ b/Core/Frameworks/BaikalAdmin/Controller/Install/Database.php
@@ -48,8 +48,8 @@ function execute() {
$this->oForm = $this->oModel->formForThisModelInstance([
"close" => false,
- "hook.validation" => [$this, "validateConnection"],
- "hook.morphology" => [$this, "hideMySQLFieldWhenNeeded"],
+ "hook.validation" => [$this, "validateSQLConnection"],
+ "hook.morphology" => [$this, "hideSQLFieldWhenNeeded"],
]);
if ($this->oForm->submitted()) {
@@ -99,7 +99,7 @@ function render() {
return $oView->render();
}
- function validateConnection($oForm, $oMorpho) {
+ function validateMySQLConnection($oForm, $oMorpho) {
if ($oForm->refreshed()) {
return true;
}
@@ -226,4 +226,102 @@ function hideMySQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $
$oMorpho->remove("mysql_password");
}
}
+
+ function validatePgSQLConnection($oForm, $oMorpho) {
+ $bPgSqlEnabled = $oMorpho->element("pgsql")->value();
+
+ if ($bPgSqlEnabled) {
+ $sHost = $oMorpho->element("pgsql_host")->value();
+ $sDbname = $oMorpho->element("pgsql_dbname")->value();
+ $sUsername = $oMorpho->element("pgsql_username")->value();
+ $sPassword = $oMorpho->element("pgsql_password")->value();
+
+ try {
+ $oDb = new \Flake\Core\Database\Pgsql(
+ $sHost,
+ $sDbname,
+ $sUsername,
+ $sPassword
+ );
+
+ if(($aMissingTables = \Baikal\Core\Tools::isDBStructurallyComplete($oDb)) !== true) {
+
+ # Checking if all tables are missing
+ $aRequiredTables = \Baikal\Core\Tools::getRequiredTablesList();
+ if(count($aRequiredTables) !== count($aMissingTables)) {
+ $sMessage = "
Database is not structurally complete.
";
+ $sMessage .= "Missing tables are: " . implode(", ", $aMissingTables) . "
";
+ $sMessage .= "You will find the SQL definition of Baïkal tables in this file: Core/Resources/Db/PgSQL/db.sql
";
+ $sMessage .= "
Nothing has been saved. Please, add these tables to the database before pursuing Baïkal initialization.
";
+
+ $oForm->declareError(
+ $oMorpho->element("pgsql"),
+ $sMessage
+ );
+ } else {
+ # All tables are missing
+ # We add these tables ourselves to the database, to initialize Baïkal
+ $sSqlDefinition = file_get_contents(PROJECT_PATH_CORERESOURCES . "Db/PgSQL/db.sql");
+ $oDb->getPDO()->exec($sSqlDefinition);
+ }
+ }
+ return TRUE;
+ } catch (\Exception $e) {
+ $oForm->declareError(
+ $oMorpho->element("pgsql"),
+ "Baïkal was not able to establish a connexion to the PostgreSQL database as configured.
PostgreSQL says: " . $e->getMessage()
+ );
+
+ $oForm->declareError(
+ $oMorpho->element("pgsql_host")
+ );
+
+ $oForm->declareError(
+ $oMorpho->element("pgsql_dbname")
+ );
+
+ $oForm->declareError(
+ $oMorpho->element("pgsql_username")
+ );
+
+ $oForm->declareError(
+ $oMorpho->element("pgsql_password")
+ );
+ }
+ }
+ }
+
+ public function validateSQLConnection($oForm, $oMorpho) {
+ if ($oMorpho->element("mysql")->value()) {
+ $this->validateMySQLConnection($oForm, $oMorpho);
+ } else if ($oMorpho->element("pgsql")->value()) {
+ $this->validatePgSQLConnection($oForm, $oMorpho);
+ }
+ }
+
+ public function hideSqlFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
+ if ($oMorpho->element("mysql")->value()) {
+ $this->hideMySQLFieldWhenNeeded($oForm, $oMorpho);
+ } else if ($oMorpho->element("pgsql")->value()) {
+ $this->hidePgSQLFieldWhenNeeded($oForm, $oMorpho);
+ }
+ }
+
+ public function hidePgSQLFieldWhenNeeded(\Formal\Form $oForm, \Formal\Form\Morphology $oMorpho) {
+ if($oForm->submitted()) {
+ $bPgSQL = (intval($oForm->postValue("pgsql")) === 1);
+ } else {
+ $bPgSQL = pgsql;
+ }
+
+ if($bPgSQL === true) {
+ $oMorpho->remove("sqlite_file");
+ $this->hideMySQLFieldWhenNeeded($oForm, $oMorpho);
+ } else {
+ $oMorpho->remove("pgsql_host");
+ $oMorpho->remove("pgsql_dbname");
+ $oMorpho->remove("pgsql_username");
+ $oMorpho->remove("pgsql_password");
+ }
+ }
}
diff --git a/Core/Frameworks/Flake/Core/Database/Pgsql.php b/Core/Frameworks/Flake/Core/Database/Pgsql.php
new file mode 100644
index 000000000..d84565d95
--- /dev/null
+++ b/Core/Frameworks/Flake/Core/Database/Pgsql.php
@@ -0,0 +1,67 @@
+
+# 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 Pgsql 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(
+ 'pgsql:host=' . $this->sHost . ';dbname=' . $this->sDbName,
+ $this->sUsername,
+ $this->sPassword
+ );
+ }
+
+ public function tables() {
+ $aTables = array();
+
+ $sSql = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = 'public'";
+ $oStmt = $this->query($sSql);
+
+ while(($aRs = $oStmt->fetch()) !== FALSE) {
+ $aTables[] = array_shift($aRs);
+ }
+
+ asort($aTables);
+ reset($aTables);
+ return $aTables;
+ }
+}
diff --git a/Core/Frameworks/Flake/Framework.php b/Core/Frameworks/Flake/Framework.php
index 73f6b9df4..b7780455a 100644
--- a/Core/Frameworks/Flake/Framework.php
+++ b/Core/Frameworks/Flake/Framework.php
@@ -261,6 +261,8 @@ protected static function initDb() {
}
if ($config['database']['mysql'] === true) {
self::initDbMysql($config);
+ } elseif ($config['database']['pgsql'] === true) {
+ self::initDbPgsql($config);
} else {
self::initDbSqlite($config);
}
@@ -325,6 +327,37 @@ protected static function initDbMysql(array $config) {
return true;
}
+ protected static function initDbPgsql(array $config) {
+ if (!$config['database']['pgsql_host']) {
+ die("The constant PROJECT_DB_PGSQL_HOST, containing the PostgreSQL host name, is not set.
You should set it in config/baikal.yaml
");
+ }
+
+ if (!$config['database']['pgsql_dbname']) {
+ die("The constant PROJECT_DB_PGSQL_DBNAME, containing the PostgreSQL database name, is not set.
You should set it in config/baikal.yaml
");
+ }
+
+ if (!$config['database']['pgsql_username']) {
+ die("The constant PROJECT_DB_PGSQL_USERNAME, containing the PostgreSQL database username, is not set.
You should set it in config/baikal.yaml
");
+ }
+
+ if ($config['database']['pgsql_password'] === null) {
+ die("The constant PROJECT_DB_PGSQL_PASSWORD, containing the PostgreSQL database password, is not set.
You should set it in config/baikal.yaml
");
+ }
+
+ try {
+ $GLOBALS["DB"] = new \Flake\Core\Database\Pgsql(
+ $config['database']['pgsql_host'],
+ $config['database']['pgsql_dbname'],
+ $config['database']['pgsql_username'],
+ $config['database']['pgsql_password']
+ );
+
+ $GLOBALS["DB"]->query("SET NAMES 'UTF8'");
+ } catch(\Exception $e) {
+ die("Baïkal was not able to establish a connection to the configured PostgreSQL database (as configured in config/baikal.yaml).
");
+ }
+ }
+
static function isDBInitialized() {
return isset($GLOBALS["DB"]) && \Flake\Util\Tools::is_a($GLOBALS["DB"], "\Flake\Core\Database");
}
diff --git a/Core/Resources/Db/PgSQL/db.sql b/Core/Resources/Db/PgSQL/db.sql
new file mode 100644
index 000000000..7335dbb2a
--- /dev/null
+++ b/Core/Resources/Db/PgSQL/db.sql
@@ -0,0 +1,142 @@
+
+CREATE TABLE addressbooks (
+ id SERIAL PRIMARY KEY,
+ principaluri TEXT,
+ displayname VARCHAR(255),
+ uri TEXT,
+ description TEXT,
+ synctoken INT CHECK (synctoken > 0) NOT NULL DEFAULT '1'
+);
+
+CREATE TABLE cards (
+ id SERIAL PRIMARY KEY,
+ addressbookid INT CHECK (addressbookid > 0) NOT NULL,
+ carddata TEXT,
+ uri TEXT,
+ lastmodified INT CHECK (lastmodified > 0),
+ etag TEXT,
+ size INT CHECK (size > 0) NOT NULL
+);
+
+CREATE TABLE addressbookchanges (
+ id SERIAL PRIMARY KEY,
+ uri TEXT NOT NULL,
+ synctoken INT CHECK (synctoken > 0) NOT NULL,
+ addressbookid INT CHECK (addressbookid > 0) NOT NULL,
+ operation SMALLINT NOT NULL
+);
+
+CREATE INDEX addressbookid_synctoken ON addressbookchanges (addressbookid, synctoken);
+
+CREATE TABLE calendarobjects (
+ id SERIAL PRIMARY KEY,
+ calendardata TEXT,
+ uri TEXT,
+ calendarid INTEGER CHECK (calendarid > 0) NOT NULL,
+ lastmodified INT CHECK (lastmodified > 0),
+ etag TEXT,
+ size INT CHECK (size > 0) NOT NULL,
+ componenttype TEXT,
+ firstoccurence INT CHECK (firstoccurence > 0),
+ lastoccurence INT CHECK (lastoccurence > 0),
+ uid TEXT
+);
+
+CREATE TABLE calendars (
+ id SERIAL PRIMARY KEY,
+ synctoken INTEGER CHECK (synctoken > 0) NOT NULL DEFAULT '1',
+ components TEXT
+);
+
+CREATE TABLE calendarinstances (
+ id SERIAL PRIMARY KEY,
+ calendarid INTEGER CHECK (calendarid > 0) NOT NULL,
+ principaluri TEXT,
+ access SMALLINT NOT NULL DEFAULT '1',
+ displayname VARCHAR(100),
+ uri TEXT,
+ description TEXT,
+ calendarorder INT CHECK (calendarorder >= 0) NOT NULL DEFAULT '0',
+ calendarcolor TEXT,
+ timezone TEXT,
+ transparent SMALLINT NOT NULL DEFAULT '0',
+ share_href TEXT,
+ share_displayname VARCHAR(100),
+ share_invitestatus SMALLINT NOT NULL DEFAULT '2'
+);
+
+CREATE TABLE calendarchanges (
+ id SERIAL PRIMARY KEY,
+ uri TEXT NOT NULL,
+ synctoken INT CHECK (synctoken > 0) NOT NULL,
+ calendarid INT CHECK (calendarid > 0) NOT NULL,
+ operation SMALLINT NOT NULL
+);
+
+CREATE INDEX calendarid_synctoken ON calendarchanges (calendarid, synctoken);
+
+CREATE TABLE calendarsubscriptions (
+ id SERIAL PRIMARY KEY,
+ uri TEXT NOT NULL,
+ principaluri TEXT NOT NULL,
+ source TEXT,
+ displayname VARCHAR(100),
+ refreshrate VARCHAR(10),
+ calendarorder INT CHECK (calendarorder >= 0) NOT NULL DEFAULT '0',
+ calendarcolor TEXT,
+ striptodos SMALLINT NULL,
+ stripalarms SMALLINT NULL,
+ stripattachments SMALLINT NULL,
+ lastmodified INT CHECK (lastmodified > 0)
+);
+
+CREATE TABLE schedulingobjects (
+ id SERIAL PRIMARY KEY,
+ principaluri TEXT,
+ calendardata TEXT,
+ uri TEXT,
+ lastmodified INT CHECK (lastmodified > 0),
+ etag TEXT,
+ size INT CHECK (size > 0) NOT NULL
+);
+CREATE TABLE locks (
+ id SERIAL PRIMARY KEY,
+ owner VARCHAR(100),
+ timeout INTEGER CHECK (timeout > 0),
+ created INTEGER,
+ token TEXT,
+ scope SMALLINT,
+ depth SMALLINT,
+ uri TEXT
+);
+
+CREATE INDEX ON locks (token);
+CREATE INDEX ON locks (uri);
+
+CREATE TABLE principals (
+ id SERIAL PRIMARY KEY,
+ uri TEXT NOT NULL,
+ email TEXT,
+ displayname VARCHAR(80)
+);
+
+CREATE TABLE groupmembers (
+ id SERIAL PRIMARY KEY,
+ principal_id INTEGER CHECK (principal_id > 0) NOT NULL,
+ member_id INTEGER CHECK (member_id > 0) NOT NULL
+);
+
+CREATE TABLE propertystorage (
+ id SERIAL PRIMARY KEY,
+ path TEXT NOT NULL,
+ name TEXT NOT NULL,
+ valuetype INT CHECK (valuetype > 0),
+ value TEXT
+);
+
+CREATE UNIQUE INDEX path_property ON propertystorage (path, name);
+CREATE TABLE users (
+ id SERIAL PRIMARY KEY,
+ username TEXT,
+ digesta1 TEXT
+);