Added support for PostgreSQL and SQLite in contacts (beta)

This commit is contained in:
RainLoop Team 2013-12-13 19:59:36 +04:00
parent 5877ff77dd
commit 76ce119980
11 changed files with 658 additions and 148 deletions

View file

@ -7,9 +7,86 @@ function AdminContacts()
{ {
// var oData = RL.data(); // var oData = RL.data();
this.contactsSupported = !!RL.settingsGet('ContactsIsSupported'); this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable')); this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable'));
var
aTypes = ['sqlite', 'mysql', 'pgsql'],
aSupportedTypes = [],
getTypeName = function(sName) {
switch (sName)
{
case 'sqlite':
sName = 'SQLite';
break;
case 'mysql':
sName = 'MySQL';
break;
case 'pgsql':
sName = 'PostgreSQL';
break;
}
return sName;
}
;
if (!!RL.settingsGet('SQLiteIsSupported'))
{
aSupportedTypes.push('sqlite');
}
if (!!RL.settingsGet('MySqlIsSupported'))
{
aSupportedTypes.push('mysql');
}
if (!!RL.settingsGet('PostgreSqlIsSupported'))
{
aSupportedTypes.push('pgsql');
}
this.contactsSupported = 0 < aSupportedTypes.length;
this.contactsTypes = ko.observableArray([]);
this.contactsTypesOptions = this.contactsTypes.map(function (sValue) {
var bDisabled = -1 === Utils.inArray(sValue, aSupportedTypes);
return {
'id': sValue,
'name': getTypeName(sValue) + (bDisabled ? ' (not supported)' : ''),
'disable': bDisabled
};
});
this.contactsTypes(aTypes);
this.contactsType = ko.observable('');
this.mainContactsType = ko.computed({
'owner': this,
'read': this.contactsType,
'write': function (sValue) {
if (sValue !== this.contactsType())
{
if (-1 < Utils.inArray(sValue, aSupportedTypes))
{
this.contactsType(sValue);
}
else if (0 < aSupportedTypes.length)
{
this.contactsType('');
}
}
else
{
this.contactsType.valueHasMutated();
}
}
});
this.contactsType.subscribe(function () {
this.testContactsSuccess(false);
this.testContactsError(false);
this.testContactsErrorMessage('');
}, this);
this.pdoDsn = ko.observable(RL.settingsGet('ContactsPdoDsn')); this.pdoDsn = ko.observable(RL.settingsGet('ContactsPdoDsn'));
this.pdoUser = ko.observable(RL.settingsGet('ContactsPdoUser')); this.pdoUser = ko.observable(RL.settingsGet('ContactsPdoUser'));
this.pdoPassword = ko.observable(RL.settingsGet('ContactsPdoPassword')); this.pdoPassword = ko.observable(RL.settingsGet('ContactsPdoPassword'));
@ -17,6 +94,7 @@ function AdminContacts()
this.pdoDsnTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.pdoDsnTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.pdoUserTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.pdoUserTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.pdoPasswordTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.pdoPasswordTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.contactsTypeTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.testing = ko.observable(false); this.testing = ko.observable(false);
this.testContactsSuccess = ko.observable(false); this.testContactsSuccess = ko.observable(false);
@ -31,6 +109,7 @@ function AdminContacts()
this.testing(true); this.testing(true);
RL.remote().testContacts(this.onTestContactsResponse, { RL.remote().testContacts(this.onTestContactsResponse, {
'ContactsPdoType': this.contactsType(),
'ContactsPdoDsn': this.pdoDsn(), 'ContactsPdoDsn': this.pdoDsn(),
'ContactsPdoUser': this.pdoUser(), 'ContactsPdoUser': this.pdoUser(),
'ContactsPdoPassword': this.pdoPassword() 'ContactsPdoPassword': this.pdoPassword()
@ -40,6 +119,8 @@ function AdminContacts()
return '' !== this.pdoDsn() && '' !== this.pdoUser(); return '' !== this.pdoDsn() && '' !== this.pdoUser();
}); });
this.contactsType(RL.settingsGet('ContactsPdoType'));
this.onTestContactsResponse = _.bind(this.onTestContactsResponse, this); this.onTestContactsResponse = _.bind(this.onTestContactsResponse, this);
} }
@ -58,7 +139,14 @@ AdminContacts.prototype.onTestContactsResponse = function (sResult, oData)
else else
{ {
this.testContactsError(true); this.testContactsError(true);
this.testContactsErrorMessage(oData.Result.Message || ''); if (oData && oData.Result)
{
this.testContactsErrorMessage(oData.Result.Message || '');
}
else
{
this.testContactsErrorMessage('');
}
} }
this.testing(false); this.testing(false);
@ -78,8 +166,9 @@ AdminContacts.prototype.onBuild = function ()
var var
f1 = Utils.settingsSaveHelperSimpleFunction(self.pdoDsnTrigger, self), f1 = Utils.settingsSaveHelperSimpleFunction(self.pdoDsnTrigger, self),
f2 = Utils.settingsSaveHelperSimpleFunction(self.pdoUserTrigger, self), f3 = Utils.settingsSaveHelperSimpleFunction(self.pdoUserTrigger, self),
f3 = Utils.settingsSaveHelperSimpleFunction(self.pdoPasswordTrigger, self) f4 = Utils.settingsSaveHelperSimpleFunction(self.pdoPasswordTrigger, self),
f5 = Utils.settingsSaveHelperSimpleFunction(self.contactsTypeTrigger, self)
; ;
self.enableContacts.subscribe(function (bValue) { self.enableContacts.subscribe(function (bValue) {
@ -88,23 +177,31 @@ AdminContacts.prototype.onBuild = function ()
}); });
}); });
self.contactsType.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, {
'ContactsPdoType': sValue
});
});
self.pdoDsn.subscribe(function (sValue) { self.pdoDsn.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f1, { RL.remote().saveAdminConfig(f1, {
'ContactsPdoDsn': Utils.trim(sValue) 'ContactsPdoDsn': Utils.trim(sValue)
}); });
}); });
self.pdoUser.subscribe(function (sValue) { self.pdoUser.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f2, { RL.remote().saveAdminConfig(f3, {
'ContactsPdoUser': Utils.trim(sValue) 'ContactsPdoUser': Utils.trim(sValue)
}); });
}); });
self.pdoPassword.subscribe(function (sValue) { self.pdoPassword.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f3, { RL.remote().saveAdminConfig(f4, {
'ContactsPdoPassword': Utils.trim(sValue) 'ContactsPdoPassword': Utils.trim(sValue)
}); });
}); });
self.contactsType(RL.settingsGet('ContactsPdoType'));
}, 50); }, 50);
}; };

View file

@ -402,7 +402,7 @@ ko.bindingHandlers.saveTrigger = {
var $oEl = $(oElement); var $oEl = $(oElement);
$oEl.data('save-trigger-type', $oEl.is('input[type=text],select,textarea') ? 'input' : 'custom'); $oEl.data('save-trigger-type', $oEl.is('input[type=text],input[type=email],input[type=password],select,textarea') ? 'input' : 'custom');
if ('custom' === $oEl.data('save-trigger-type')) if ('custom' === $oEl.data('save-trigger-type'))
{ {

View file

@ -233,7 +233,17 @@ class Actions
$sUser = \trim($this->Config()->Get('contacts', 'pdo_user', '')); $sUser = \trim($this->Config()->Get('contacts', 'pdo_user', ''));
$sPassword = (string) $this->Config()->Get('contacts', 'pdo_password', ''); $sPassword = (string) $this->Config()->Get('contacts', 'pdo_password', '');
$oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook($sDsn, $sUser, $sPassword); $sDsnType = $this->ValidateContactPdoType(\trim($this->Config()->Get('contacts', 'type', 'sqlite')));
if ('sqlite' === $sDsnType)
{
$oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook(
'sqlite:'.APP_PRIVATE_DATA.'PersonalAddressBook.sqlite', '', '', 'sqlite');
}
else
{
$oResult = new \RainLoop\Providers\PersonalAddressBook\PdoPersonalAddressBook($sDsn, $sUser, $sPassword, $sDsnType);
}
$oResult->SetLogger($this->Logger()); $oResult->SetLogger($this->Logger());
break; break;
case 'suggestions': case 'suggestions':
@ -974,10 +984,15 @@ class Actions
$aResult['UseTokenProtection'] = (bool) $oConfig->Get('security', 'csrf_protection', true); $aResult['UseTokenProtection'] = (bool) $oConfig->Get('security', 'csrf_protection', true);
$aResult['EnabledPlugins'] = (bool) $oConfig->Get('plugins', 'enable', false); $aResult['EnabledPlugins'] = (bool) $oConfig->Get('plugins', 'enable', false);
$aResult['ContactsIsSupported'] = (bool) $this->PersonalAddressBookProvider(null, true)->IsSupported(); $aDrivers = \class_exists('PDO') ? \PDO::getAvailableDrivers() : array();
$aResult['MySqlIsSupported'] = \is_array($aDrivers) ? \in_array('mysql', $aDrivers) : false;
$aResult['SQLiteIsSupported'] = \is_array($aDrivers) ? \in_array('sqlite', $aDrivers) : false;
$aResult['PostgreSqlIsSupported'] = \is_array($aDrivers) ? \in_array('pgsql', $aDrivers) : false;
$aResult['ContactsEnable'] = (bool) $oConfig->Get('contacts', 'enable', false); $aResult['ContactsEnable'] = (bool) $oConfig->Get('contacts', 'enable', false);
$aResult['ContactsPdoType'] = $this->ValidateContactPdoType(\trim($this->Config()->Get('contacts', 'type', 'sqlite')));
$aResult['ContactsPdoDsn'] = (string) $oConfig->Get('contacts', 'pdo_dsn', ''); $aResult['ContactsPdoDsn'] = (string) $oConfig->Get('contacts', 'pdo_dsn', '');
$aResult['ContactsPdoType'] = (string) $oConfig->Get('contacts', 'type', '');
$aResult['ContactsPdoUser'] = (string) $oConfig->Get('contacts', 'pdo_user', ''); $aResult['ContactsPdoUser'] = (string) $oConfig->Get('contacts', 'pdo_user', '');
$aResult['ContactsPdoPassword'] = APP_DUMMY; $aResult['ContactsPdoPassword'] = APP_DUMMY;
@ -1854,6 +1869,10 @@ class Actions
$this->setConfigFromParams($oConfig, 'ContactsPdoUser', 'contacts', 'pdo_user', 'string'); $this->setConfigFromParams($oConfig, 'ContactsPdoUser', 'contacts', 'pdo_user', 'string');
$this->setConfigFromParams($oConfig, 'ContactsPdoPassword', 'contacts', 'pdo_password', 'dummy'); $this->setConfigFromParams($oConfig, 'ContactsPdoPassword', 'contacts', 'pdo_password', 'dummy');
$this->setConfigFromParams($oConfig, 'ContactsPdoType', 'contacts', 'type', 'string', function ($sType) use ($self) {
return $self->ValidateContactPdoType($sType);
});
$this->setConfigFromParams($oConfig, 'AllowAdditionalAccounts', 'webmail', 'allow_additional_accounts', 'bool'); $this->setConfigFromParams($oConfig, 'AllowAdditionalAccounts', 'webmail', 'allow_additional_accounts', 'bool');
$this->setConfigFromParams($oConfig, 'AllowIdentities', 'webmail', 'allow_identities', 'bool'); $this->setConfigFromParams($oConfig, 'AllowIdentities', 'webmail', 'allow_identities', 'bool');
@ -1941,6 +1960,11 @@ class Actions
$this->setConfigFromParams($oConfig, 'ContactsPdoUser', 'contacts', 'pdo_user', 'string'); $this->setConfigFromParams($oConfig, 'ContactsPdoUser', 'contacts', 'pdo_user', 'string');
$this->setConfigFromParams($oConfig, 'ContactsPdoPassword', 'contacts', 'pdo_password', 'dummy'); $this->setConfigFromParams($oConfig, 'ContactsPdoPassword', 'contacts', 'pdo_password', 'dummy');
$self = $this;
$this->setConfigFromParams($oConfig, 'ContactsPdoType', 'contacts', 'type', 'string', function ($sType) use ($self) {
return $self->ValidateContactPdoType($sType);
});
$sTestMessage = $this->PersonalAddressBookProvider(null, true)->Test(); $sTestMessage = $this->PersonalAddressBookProvider(null, true)->Test();
return $this->DefaultResponse(__FUNCTION__, array( return $this->DefaultResponse(__FUNCTION__, array(
'Result' => '' === $sTestMessage, 'Result' => '' === $sTestMessage,
@ -5097,21 +5121,31 @@ class Actions
*/ */
public function ValidateTheme($sTheme) public function ValidateTheme($sTheme)
{ {
return in_array($sTheme, $this->GetThemes()) ? return \in_array($sTheme, $this->GetThemes()) ?
$sTheme : $this->Config()->Get('themes', 'default', 'Default'); $sTheme : $this->Config()->Get('themes', 'default', 'Default');
} }
/** /**
* @param $sLanguage $sLanguage * @param string $sLanguage
* *
* @return $sLanguage * @return string
*/ */
public function ValidateLanguage($sLanguage) public function ValidateLanguage($sLanguage)
{ {
return in_array($sLanguage, $this->GetLanguages()) ? return \in_array($sLanguage, $this->GetLanguages()) ?
$sLanguage : $this->Config()->Get('i18n', 'default', 'en'); $sLanguage : $this->Config()->Get('i18n', 'default', 'en');
} }
/**
* @param string $sType
*
* @return string
*/
public function ValidateContactPdoType($sType)
{
return \in_array($sType, array('mysql', 'pgsql', 'sqlite')) ? $sType : 'sqlite';
}
/** /**
* @staticvar array $aCache * @staticvar array $aCache
* @param bool $bAdmin = false * @param bool $bAdmin = false

View file

@ -48,6 +48,14 @@ abstract class PdoAbstract
return array('', '', '', ''); return array('', '', '', '');
} }
/**
* @return bool
*/
protected function isTransactionSupported()
{
return \in_array($this->sDbType, array('mysql'));
}
/** /**
* @return \PDO * @return \PDO
* *
@ -67,6 +75,11 @@ abstract class PdoAbstract
$sType = $sDsn = $sDbLogin = $sDbPassword = ''; $sType = $sDsn = $sDbLogin = $sDbPassword = '';
list($sType, $sDsn, $sDbLogin, $sDbPassword) = $this->getPdoAccessData(); list($sType, $sDsn, $sDbLogin, $sDbPassword) = $this->getPdoAccessData();
if (!\in_array($sType, array('mysql', 'sqlite', 'pgsql')))
{
throw new \Exception('Unknown PDO SQL connection type');
}
$this->sDbType = $sType; $this->sDbType = $sType;
$oPdo = false; $oPdo = false;
@ -76,7 +89,7 @@ abstract class PdoAbstract
if ($oPdo) if ($oPdo)
{ {
$oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
if ('mysql' === $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) if ('mysql' === $sType && 'mysql' === $oPdo->getAttribute(\PDO::ATTR_DRIVER_NAME))
{ {
$oPdo->exec('SET NAMES utf8 COLLATE utf8_general_ci'); $oPdo->exec('SET NAMES utf8 COLLATE utf8_general_ci');
// $oPdo->exec('SET NAMES utf8'); // $oPdo->exec('SET NAMES utf8');
@ -111,7 +124,7 @@ abstract class PdoAbstract
} }
/** /**
* @return nool * @return bool
*/ */
protected function beginTransaction() protected function beginTransaction()
{ {
@ -119,7 +132,7 @@ abstract class PdoAbstract
} }
/** /**
* @return nool * @return bool
*/ */
protected function commit() protected function commit()
{ {
@ -127,7 +140,7 @@ abstract class PdoAbstract
} }
/** /**
* @return nool * @return bool
*/ */
protected function rollBack() protected function rollBack()
{ {
@ -322,7 +335,10 @@ abstract class PdoAbstract
$oPdo = $this->getPDO(); $oPdo = $this->getPDO();
if ($oPdo) if ($oPdo)
{ {
$oPdo->beginTransaction(); if ($this->isTransactionSupported())
{
$oPdo->beginTransaction();
}
$sQuery = 'DELETE FROM rainloop_system WHERE sys_name = ? AND value_int <= ?;'; $sQuery = 'DELETE FROM rainloop_system WHERE sys_name = ? AND value_int <= ?;';
$this->writeLog($sQuery); $this->writeLog($sQuery);
@ -341,13 +357,16 @@ abstract class PdoAbstract
} }
} }
if ($bResult) if ($this->isTransactionSupported())
{ {
$oPdo->commit(); if ($bResult)
} {
else $oPdo->commit();
{ }
$oPdo->rollBack(); else
{
$oPdo->rollBack();
}
} }
} }
@ -371,17 +390,17 @@ abstract class PdoAbstract
sys_name varchar(50) NOT NULL, sys_name varchar(50) NOT NULL,
value_int int UNSIGNED NOT NULL DEFAULT 0, value_int int UNSIGNED NOT NULL DEFAULT 0,
value_str varchar(128) NOT NULL DEFAULT \'\', value_str varchar(128) NOT NULL DEFAULT \'\',
INDEX `sys_name_index` (`sys_name`) INDEX sys_name_rainloop_system_index (sys_name)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;'; ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;';
$aQ[] = 'CREATE TABLE IF NOT EXISTS rainloop_users ( $aQ[] = 'CREATE TABLE IF NOT EXISTS rainloop_users (
id_user int UNSIGNED NOT NULL AUTO_INCREMENT, id_user int UNSIGNED NOT NULL AUTO_INCREMENT,
rl_email varchar(128) NOT NULL DEFAULT \'\', rl_email varchar(128) NOT NULL DEFAULT \'\',
PRIMARY KEY(`id_user`), PRIMARY KEY(id_user),
INDEX `rl_email_index` (`rl_email`) INDEX rl_email_rainloop_users_index (rl_email)
) /*!40000 ENGINE=INNODB */;'; ) /*!40000 ENGINE=INNODB */;';
} }
else if ('postgres' === $this->sDbType) else if ('pgsql' === $this->sDbType)
{ {
$aQ[] = 'CREATE TABLE rainloop_system ( $aQ[] = 'CREATE TABLE rainloop_system (
sys_name varchar(50) NOT NULL, sys_name varchar(50) NOT NULL,
@ -389,22 +408,41 @@ abstract class PdoAbstract
value_str varchar(128) NOT NULL DEFAULT \'\' value_str varchar(128) NOT NULL DEFAULT \'\'
);'; );';
$aQ[] = 'CREATE INDEX sys_name_index ON rainloop_system (sys_name);'; $aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);';
$aQ[] = 'CREATE SEQUENCE rainloop_users_seq START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;'; $aQ[] = 'CREATE SEQUENCE id_user START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;';
$aQ[] = 'CREATE TABLE rainloop_users ( $aQ[] = 'CREATE TABLE rainloop_users (
id_user integer DEFAULT nextval(\'rainloop_users_seq\'::text) PRIMARY KEY, id_user integer DEFAULT nextval(\'id_user\'::text) PRIMARY KEY,
rl_email varchar(128) NOT NULL DEFAULT \'\' rl_email varchar(128) NOT NULL DEFAULT \'\'
);'; );';
$aQ[] = 'CREATE INDEX rl_email_index ON rainloop_users (rl_email);'; $aQ[] = 'CREATE INDEX rl_email_rainloop_users_index ON rainloop_users (rl_email);';
}
else if ('sqlite' === $this->sDbType)
{
$aQ[] = 'CREATE TABLE rainloop_system (
sys_name text NOT NULL,
value_int integer NOT NULL default 0,
value_str text NOT NULL default \'\'
);';
$aQ[] = 'CREATE INDEX sys_name_rainloop_system_index ON rainloop_system (sys_name);';
$aQ[] = 'CREATE TABLE rainloop_users (
id_user integer NOT NULL PRIMARY KEY,
rl_email text NOT NULL default \'\'
);';
$aQ[] = 'CREATE INDEX rl_email_rainloop_users_index ON rainloop_users (rl_email);';
} }
if (0 < \count($aQ)) if (0 < \count($aQ))
{ {
try try
{ {
$oPdo->beginTransaction(); if ($this->isTransactionSupported())
{
$oPdo->beginTransaction();
}
foreach ($aQ as $sQuery) foreach ($aQ as $sQuery)
{ {
@ -412,23 +450,37 @@ abstract class PdoAbstract
{ {
$this->writeLog($sQuery); $this->writeLog($sQuery);
$bResult = false !== $oPdo->exec($sQuery); $bResult = false !== $oPdo->exec($sQuery);
if (!$bResult)
{
$this->writeLog('Result=false');
}
else
{
$this->writeLog('Result=true');
}
} }
} }
if ($bResult) if ($this->isTransactionSupported())
{ {
$oPdo->rollBack(); if ($bResult)
} {
else $oPdo->rollBack();
{ }
$oPdo->commit(); else
{
$oPdo->commit();
}
} }
} }
catch (\Exception $oException) catch (\Exception $oException)
{ {
$oPdo->rollBack();
$this->writeLog($oException); $this->writeLog($oException);
if ($this->isTransactionSupported())
{
$oPdo->rollBack();
}
throw $oException; throw $oException;
} }
} }
@ -453,18 +505,32 @@ abstract class PdoAbstract
catch (\PDOException $oException) catch (\PDOException $oException)
{ {
$this->writeLog($oException); $this->writeLog($oException);
$this->initSystemTables(); try
{
$iFromVersion = $this->getVersion($sName); $this->initSystemTables();
$iFromVersion = $this->getVersion($sName);
}
catch (\PDOException $oSubException)
{
$this->writeLog($oSubException);
throw $oSubException;
}
} }
if (0 <= $iFromVersion) if (\is_int($iFromVersion) && 0 <= $iFromVersion)
{ {
$oPdo = false; $oPdo = false;
$bResult = false; $bResult = false;
foreach ($aData as $iVersion => $aQuery) foreach ($aData as $iVersion => $aQuery)
{ {
if (0 === \count($aQuery))
{
continue;
}
if (!$oPdo) if (!$oPdo)
{ {
$oPdo = $this->getPDO(); $oPdo = $this->getPDO();
@ -475,30 +541,44 @@ abstract class PdoAbstract
{ {
try try
{ {
$oPdo->beginTransaction(); if ($this->isTransactionSupported())
{
$oPdo->beginTransaction();
}
foreach ($aQuery as $sQuery) foreach ($aQuery as $sQuery)
{ {
$this->writeLog($sQuery); $this->writeLog($sQuery);
if (false === $oPdo->exec($sQuery)) $bExec = $oPdo->exec($sQuery);
if (false === $bExec)
{ {
$this->writeLog('Result: false');
$bResult = false; $bResult = false;
break; break;
} }
} }
if ($bResult) if ($this->isTransactionSupported())
{ {
$oPdo->commit(); if ($bResult)
} {
else $oPdo->commit();
{ }
$oPdo->rollBack(); else
{
$oPdo->rollBack();
}
} }
} }
catch (\Exception $oException) catch (\Exception $oException)
{ {
$oPdo->rollBack(); $this->writeLog($oException);
if ($this->isTransactionSupported())
{
$oPdo->rollBack();
}
throw $oException; throw $oException;
} }

View file

@ -84,7 +84,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'contacts' => array( 'contacts' => array(
'enable' => array(false, 'Enable contacts'), 'enable' => array(false, 'Enable contacts'),
'suggestions_limit' => array(30), 'suggestions_limit' => array(30),
'type' => array('mysql', ''), 'type' => array('sqlite', ''),
'pdo_dsn' => array('mysql:host=127.0.0.1;port=3306;dbname=rainloop', ''), 'pdo_dsn' => array('mysql:host=127.0.0.1;port=3306;dbname=rainloop', ''),
'pdo_user' => array('root', ''), 'pdo_user' => array('root', ''),
'pdo_password' => array('', ''), 'pdo_password' => array('', ''),

View file

@ -12,6 +12,11 @@ class PdoPersonalAddressBook
* @var string * @var string
*/ */
private $sDsn; private $sDsn;
/**
* @var string
*/
private $sDsnType;
/** /**
* @var string * @var string
@ -23,11 +28,12 @@ class PdoPersonalAddressBook
*/ */
private $sPassword; private $sPassword;
public function __construct($sDsn, $sUser, $sPassword) public function __construct($sDsn, $sUser = '', $sPassword = '', $sDsnType = 'mysql')
{ {
$this->sDsn = $sDsn; $this->sDsn = $sDsn;
$this->sUser = $sUser; $this->sUser = $sUser;
$this->sPassword = $sPassword; $this->sPassword = $sPassword;
$this->sDsnType = $sDsnType;
$this->bExplain = false; $this->bExplain = false;
} }
@ -38,7 +44,7 @@ class PdoPersonalAddressBook
public function IsSupported() public function IsSupported()
{ {
$aDrivers = \class_exists('PDO') ? \PDO::getAvailableDrivers() : array(); $aDrivers = \class_exists('PDO') ? \PDO::getAvailableDrivers() : array();
return \is_array($aDrivers) ? \in_array('mysql', $aDrivers) : false; return \is_array($aDrivers) ? \in_array($this->sDsnType, $aDrivers) : false;
} }
/** /**
@ -94,7 +100,10 @@ class PdoPersonalAddressBook
try try
{ {
$this->beginTransaction(); if ($this->isTransactionSupported())
{
$this->beginTransaction();
}
$aFreq = array(); $aFreq = array();
if ($bUpdate) if ($bUpdate)
@ -182,11 +191,18 @@ class PdoPersonalAddressBook
} }
catch (\Exception $oException) catch (\Exception $oException)
{ {
$this->rollBack(); if ($this->isTransactionSupported())
{
$this->rollBack();
}
throw $oException; throw $oException;
} }
$this->commit(); if ($this->isTransactionSupported())
{
$this->commit();
}
return 0 < $iIdContact; return 0 < $iIdContact;
} }
@ -199,7 +215,6 @@ class PdoPersonalAddressBook
*/ */
public function DeleteContacts($oAccount, $aContactIds) public function DeleteContacts($oAccount, $aContactIds)
{ {
$this->Sync();
$iUserID = $this->getUserId($oAccount->ParentEmailHelper()); $iUserID = $this->getUserId($oAccount->ParentEmailHelper());
$aContactIds = \array_filter($aContactIds, function (&$mItem) { $aContactIds = \array_filter($aContactIds, function (&$mItem) {
@ -279,7 +294,9 @@ class PdoPersonalAddressBook
if (0 < \strlen($sSearch)) if (0 < \strlen($sSearch))
{ {
$sSql = 'SELECT id_prop, id_contact FROM rainloop_pab_properties WHERE id_user = :id_user AND scope_type = :scope_type AND prop_value LIKE :search ESCAPE \'=\' GROUP BY id_contact'; $sSql = 'SELECT id_prop, id_contact FROM rainloop_pab_properties WHERE id_user = :id_user'.
' AND scope_type = :scope_type AND prop_value LIKE :search ESCAPE \'=\' GROUP BY id_contact, id_prop';
$aParams = array( $aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT), ':id_user' => array($iUserID, \PDO::PARAM_INT),
':scope_type' => array($iScopeType, \PDO::PARAM_INT), ':scope_type' => array($iScopeType, \PDO::PARAM_INT),
@ -756,8 +773,7 @@ class PdoPersonalAddressBook
try try
{ {
$this->Sync(); $this->Sync();
if (0 >= $this->getVersion($this->sDsnType.'-pab-version'))
if (0 >= $this->getVersion('mysql-pab-version'))
{ {
$sResult = 'Unknown database error'; $sResult = 'Unknown database error';
} }
@ -779,73 +795,214 @@ class PdoPersonalAddressBook
return $sResult; return $sResult;
} }
/** private function getInitialTablesArray($sDbType)
* @return bool
*/
public function Sync()
{ {
return $this->dataBaseUpgrade('mysql-pab-version', array( $sInitial = '';
1 => array( $aResult = array();
switch ($sDbType)
{
case 'mysql':
$sInitial = <<<MYSQLINITIAL
CREATE TABLE IF NOT EXISTS rainloop_pab_contacts (
// -- rainloop_pab_contacts --
'CREATE TABLE IF NOT EXISTS rainloop_pab_contacts (
id_contact bigint UNSIGNED NOT NULL AUTO_INCREMENT, id_contact bigint UNSIGNED NOT NULL AUTO_INCREMENT,
id_user int UNSIGNED NOT NULL, id_user int UNSIGNED NOT NULL,
scope_type tinyint UNSIGNED NOT NULL DEFAULT 0, scope_type tinyint UNSIGNED NOT NULL DEFAULT 0,
display_name varchar(255) NOT NULL DEFAULT \'\', display_name varchar(255) NOT NULL DEFAULT '',
display_email varchar(255) NOT NULL DEFAULT \'\', display_email varchar(255) NOT NULL DEFAULT '',
display varchar(255) NOT NULL DEFAULT \'\', display varchar(255) NOT NULL DEFAULT '',
changed int UNSIGNED NOT NULL DEFAULT 0, changed int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY(id_contact), PRIMARY KEY(id_contact),
INDEX id_user_scope_type_index (id_user, scope_type) INDEX id_user_scope_type_rainloop_pab_contacts_index (id_user, scope_type)
)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;', )/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS rainloop_pab_properties (
// -- rainloop_pab_properties --
'CREATE TABLE IF NOT EXISTS rainloop_pab_properties (
id_prop bigint UNSIGNED NOT NULL AUTO_INCREMENT, id_prop bigint UNSIGNED NOT NULL AUTO_INCREMENT,
id_contact bigint UNSIGNED NOT NULL, id_contact bigint UNSIGNED NOT NULL,
id_user int UNSIGNED NOT NULL, id_user int UNSIGNED NOT NULL,
scope_type tinyint UNSIGNED NOT NULL DEFAULT 0, scope_type tinyint UNSIGNED NOT NULL DEFAULT 0,
prop_type tinyint UNSIGNED NOT NULL, prop_type tinyint UNSIGNED NOT NULL,
prop_type_custom varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT \'\', prop_type_custom varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '',
prop_value varchar(255) NOT NULL DEFAULT \'\', prop_value varchar(255) NOT NULL DEFAULT '',
prop_value_custom varchar(255) NOT NULL DEFAULT \'\', prop_value_custom varchar(255) NOT NULL DEFAULT '',
prop_frec int UNSIGNED NOT NULL DEFAULT 0, prop_frec int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY(id_prop), PRIMARY KEY(id_prop),
INDEX id_user_index (id_user), INDEX id_user_rainloop_pab_properties_index (id_user),
INDEX id_user_id_contact_scope_type_index (id_user, id_contact, scope_type) INDEX id_user_id_contact_scope_type_rainloop_pab_properties_index (id_user, id_contact, scope_type)
)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;', )/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
CREATE TABLE IF NOT EXISTS rainloop_pab_tags (
// -- rainloop_pab_tags --
'CREATE TABLE IF NOT EXISTS rainloop_pab_tags (
id_tag int UNSIGNED NOT NULL AUTO_INCREMENT, id_tag int UNSIGNED NOT NULL AUTO_INCREMENT,
id_user int UNSIGNED NOT NULL, id_user int UNSIGNED NOT NULL,
tag_name varchar(255) NOT NULL, tag_name varchar(255) NOT NULL,
PRIMARY KEY(id_tag), PRIMARY KEY(id_tag),
INDEX id_user_index (id_user), INDEX id_user_rainloop_pab_tags_index (id_user),
INDEX id_user_name_index (id_user, tag_name) INDEX id_user_name_rainloop_pab_tags_index (id_user, tag_name)
)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;', )/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
// -- rainloop_pab_tags_contacts --
'CREATE TABLE IF NOT EXISTS rainloop_pab_tags_contacts ( 'CREATE TABLE IF NOT EXISTS rainloop_pab_tags_contacts (
id_tag int UNSIGNED NOT NULL, id_tag int UNSIGNED NOT NULL,
id_contact bigint UNSIGNED NOT NULL, id_contact bigint UNSIGNED NOT NULL,
INDEX id_tag_index (id_tag), INDEX id_tag_rainloop_pab_tags_contacts_index (id_tag),
INDEX id_contact_index (id_contact) INDEX id_contact_rainloop_pab_tags_contacts_index (id_contact)
)/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;' )/*!40000 ENGINE=INNODB *//*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
))); MYSQLINITIAL;
break;
case 'pgsql':
$sInitial = <<<POSTGRESINITIAL
CREATE SEQUENCE id_contact START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;
CREATE TABLE rainloop_pab_contacts (
id_contact integer DEFAULT nextval('id_contact'::text) PRIMARY KEY,
id_user integer NOT NULL,
scope_type integer NOT NULL DEFAULT 0,
display_name varchar(128) NOT NULL DEFAULT '',
display_email varchar(128) NOT NULL DEFAULT '',
display varchar(128) NOT NULL DEFAULT '',
changed integer NOT NULL default 0
);
CREATE INDEX id_user_scope_type_rainloop_pab_contacts_index ON rainloop_pab_contacts (id_user, scope_type);
CREATE SEQUENCE id_prop START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;
CREATE TABLE rainloop_pab_properties (
id_prop integer DEFAULT nextval('id_prop'::text) PRIMARY KEY,
id_contact integer NOT NULL,
id_user integer NOT NULL,
scope_type integer NOT NULL DEFAULT 0,
prop_type integer NOT NULL,
prop_type_custom varchar(50) NOT NULL DEFAULT '',
prop_value varchar(128) NOT NULL DEFAULT '',
prop_value_custom varchar(128) NOT NULL DEFAULT '',
prop_frec integer NOT NULL default 0
);
CREATE INDEX id_user_rainloop_pab_properties_index ON rainloop_pab_properties (id_user);
CREATE INDEX id_user_id_contact_scope_type_rainloop_pab_properties_index ON rainloop_pab_properties (id_user, id_contact, scope_type);
CREATE SEQUENCE id_tag START WITH 1 INCREMENT BY 1 NO MAXVALUE NO MINVALUE CACHE 1;
CREATE TABLE rainloop_pab_tags (
id_tag integer DEFAULT nextval('id_tag'::text) PRIMARY KEY,
id_user integer NOT NULL,
tag_name varchar(128) NOT NULL
);
CREATE INDEX id_user_rainloop_pab_tags_index ON rainloop_pab_tags (id_user);
CREATE INDEX id_user_name_rainloop_pab_tags_index ON rainloop_pab_tags (id_user, tag_name);
CREATE TABLE rainloop_pab_tags_contacts (
id_tag integer NOT NULL,
id_contact integer NOT NULL
);
CREATE INDEX id_tag_rainloop_pab_tags_index ON rainloop_pab_tags_contacts (id_tag);
CREATE INDEX id_contact_rainloop_pab_tags_index ON rainloop_pab_tags_contacts (id_contact);
POSTGRESINITIAL;
break;
case 'sqlite':
$sInitial = <<<SQLITEINITIAL
CREATE TABLE rainloop_pab_contacts (
id_contact integer NOT NULL PRIMARY KEY,
id_user integer NOT NULL,
scope_type integer NOT NULL default 0,
display_name text NOT NULL default '',
display_email text NOT NULL default '',
display text NOT NULL default '',
changed integer NOT NULL default 0
);
CREATE INDEX id_user_scope_type_rainloop_pab_contacts_index ON rainloop_pab_contacts (id_user, scope_type);
CREATE TABLE rainloop_pab_properties (
id_prop integer NOT NULL PRIMARY KEY,
id_contact integer NOT NULL,
id_user integer NOT NULL,
scope_type integer NOT NULL default 0,
prop_type integer NOT NULL,
prop_type_custom text NOT NULL default '',
prop_value text NOT NULL default '',
prop_value_custom text NOT NULL default '',
prop_frec integer NOT NULL default 0
);
CREATE INDEX id_user_rainloop_pab_properties_index ON rainloop_pab_properties (id_user);
CREATE INDEX id_user_id_contact_scope_type_rainloop_pab_properties_index ON rainloop_pab_properties (id_user, id_contact, scope_type);
CREATE TABLE rainloop_pab_tags (
id_tag integer NOT NULL PRIMARY KEY,
id_user integer NOT NULL,
tag_name text NOT NULL
);
CREATE INDEX id_user_rainloop_pab_tags_index ON rainloop_pab_tags (id_user);
CREATE INDEX id_user_name_rainloop_pab_tags_index ON rainloop_pab_tags (id_user, tag_name);
CREATE TABLE rainloop_pab_tags_contacts (
id_tag integer NOT NULL,
id_contact integer NOT NULL
);
CREATE INDEX id_tag_rainloop_pab_tags_index ON rainloop_pab_tags_contacts (id_tag);
CREATE INDEX id_contact_rainloop_pab_tags_index ON rainloop_pab_tags_contacts (id_contact);
SQLITEINITIAL;
break;
}
if (0 < strlen($sInitial))
{
$aList = \explode(';', \trim($sInitial));
foreach ($aList as $sV)
{
$sV = \trim($sV);
if (0 < \strlen($sV))
{
$aResult[] = $sV;
}
}
}
return $aResult;
}
/**
* @return bool
*/
public function Sync()
{
switch ($this->sDsnType)
{
case 'mysql':
return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
));
case 'pgsql':
return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
));
case 'sqlite':
return $this->dataBaseUpgrade($this->sDsnType.'-pab-version', array(
1 => $this->getInitialTablesArray($this->sDsnType)
));
}
return false;
} }
/** /**
@ -902,6 +1059,6 @@ class PdoPersonalAddressBook
*/ */
protected function getPdoAccessData() protected function getPdoAccessData()
{ {
return array('mysql', $this->sDsn, $this->sUser, $this->sPassword); return array($this->sDsnType, $this->sDsn, $this->sUser, $this->sPassword);
} }
} }

View file

@ -5,10 +5,11 @@
<br /> <br />
Your system doesn't support contacts. Your system doesn't support contacts.
<br /> <br />
You need to install or enable <strong>PDO (MySQL)</strong> exstenstion on your web server. You need to install or enable <strong>PDO (SQLite / MySQL / PostgreSQL)</strong> exstenstion on your server.
</div> </div>
</div> </div>
<div class="form-horizontal" data-bind="visible: contactsSupported"> <div class="form-horizontal" data-bind="visible: contactsSupported">
<div class="form-horizontal">
<div class="legend"> <div class="legend">
Contacts Contacts
</div> </div>
@ -20,43 +21,87 @@
</label> </label>
</div> </div>
</div> </div>
<div class="legend">
PDO (MySQL)
</div>
<div class="control-group"> <div class="control-group">
<label class="control-label"> <label class="control-label">
Dsn Type
</label> </label>
<div class="controls"> <div class="controls">
<input type="text" class="span6" data-bind="value: pdoDsn, saveTrigger: pdoDsnTrigger" /> <select data-bind="options: contactsTypesOptions, value: mainContactsType,
<div data-bind="saveTrigger: pdoDsnTrigger"></div> optionsText: 'name', optionsValue: 'id', optionsAfterRender: defautOptionsAfterRender, saveTrigger: contactsTypeTrigger"></select>
<div data-bind="saveTrigger: contactsTypeTrigger"></div>
</div> </div>
</div> </div>
<div class="control-group"> <div data-bind="visible: 'sqlite' !== contactsType()">
<label class="control-label"> <div class="legend">
User PDO (MySQL / PostgreSQL / ...)
</label> </div>
<div class="controls"> <div class="control-group">
<input type="text" data-bind="value: pdoUser, saveTrigger: pdoUserTrigger" /> <label class="control-label">
<div data-bind="saveTrigger: pdoUserTrigger"></div> Dsn
</label>
<div class="controls">
<input type="text" class="span6" data-bind="value: pdoDsn, saveTrigger: pdoDsnTrigger" />
<div data-bind="saveTrigger: pdoDsnTrigger"></div>
<blockquote style="margin-top: 10px">
<p class="muted">
<strong>Examples:</strong>
<br />
mysql:host=127.0.0.1;port=3306;dbname=rainloop
<br />
pgsql:host=127.0.0.1;port=5432;dbname=rainloop
</p>
</blockquote>
</div>
</div>
<div class="control-group">
<label class="control-label">
User
</label>
<div class="controls">
<input type="text" data-bind="value: pdoUser, saveTrigger: pdoUserTrigger" />
<div data-bind="saveTrigger: pdoUserTrigger"></div>
</div>
</div>
<div class="control-group">
<label class="control-label">
Password
</label>
<div class="controls">
<input type="password" data-bind="value: pdoPassword, saveTrigger: pdoPasswordTrigger" />
<div data-bind="saveTrigger: pdoPasswordTrigger"></div>
</div>
</div>
<div class="control-group">
<div class="controls">
<a class="btn" data-bind="command: testContactsCommand, css: { 'btn-success': testContactsSuccess, 'btn-danger': testContactsError }">
<i data-bind="css: {'icon-info': !testing(), 'icon-spinner-2 animated': testing(), 'icon-white': testContactsSuccess() || testContactsError() }"></i>
&nbsp;&nbsp;
Test
</a>
</div>
</div> </div>
</div> </div>
<div class="control-group"> <div data-bind="visible: 'sqlite' === contactsType()">
<label class="control-label"> <div class="legend">
Password PDO (SQLite)
</label>
<div class="controls">
<input type="password" data-bind="value: pdoPassword, saveTrigger: pdoPasswordTrigger" />
<div data-bind="saveTrigger: pdoPasswordTrigger"></div>
</div> </div>
</div> <div class="control-group">
<div class="control-group"> <div class="controls">
<div class="controls"> <div class="alert alert-null-left-margin span8">
<a class="btn" data-bind="command: testContactsCommand, css: { 'btn-success': testContactsSuccess, 'btn-danger': testContactsError }"> <h4>Notice!</h4>
<i data-bind="css: {'icon-info': !testing(), 'icon-spinner-2 animated': testing(), 'icon-white': testContactsSuccess() || testContactsError() }"></i> <br />
&nbsp;&nbsp; Don't use this type of database with a large number of active users.
Test </div>
</a> </div>
</div>
<div class="control-group">
<div class="controls">
<a class="btn" data-bind="command: testContactsCommand, css: { 'btn-success': testContactsSuccess, 'btn-danger': testContactsError }">
<i data-bind="css: {'icon-info': !testing(), 'icon-spinner-2 animated': testing(), 'icon-white': testContactsSuccess() || testContactsError() }"></i>
&nbsp;&nbsp;
Test
</a>
</div>
</div> </div>
</div> </div>
<div class="control-group" data-bind="visible: '' !== testContactsErrorMessage()"> <div class="control-group" data-bind="visible: '' !== testContactsErrorMessage()">

View file

@ -2655,7 +2655,7 @@ ko.bindingHandlers.saveTrigger = {
var $oEl = $(oElement); var $oEl = $(oElement);
$oEl.data('save-trigger-type', $oEl.is('input[type=text],select,textarea') ? 'input' : 'custom'); $oEl.data('save-trigger-type', $oEl.is('input[type=text],input[type=email],input[type=password],select,textarea') ? 'input' : 'custom');
if ('custom' === $oEl.data('save-trigger-type')) if ('custom' === $oEl.data('save-trigger-type'))
{ {
@ -5252,9 +5252,86 @@ function AdminContacts()
{ {
// var oData = RL.data(); // var oData = RL.data();
this.contactsSupported = !!RL.settingsGet('ContactsIsSupported'); this.defautOptionsAfterRender = Utils.defautOptionsAfterRender;
this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable')); this.enableContacts = ko.observable(!!RL.settingsGet('ContactsEnable'));
var
aTypes = ['sqlite', 'mysql', 'pgsql'],
aSupportedTypes = [],
getTypeName = function(sName) {
switch (sName)
{
case 'sqlite':
sName = 'SQLite';
break;
case 'mysql':
sName = 'MySQL';
break;
case 'pgsql':
sName = 'PostgreSQL';
break;
}
return sName;
}
;
if (!!RL.settingsGet('SQLiteIsSupported'))
{
aSupportedTypes.push('sqlite');
}
if (!!RL.settingsGet('MySqlIsSupported'))
{
aSupportedTypes.push('mysql');
}
if (!!RL.settingsGet('PostgreSqlIsSupported'))
{
aSupportedTypes.push('pgsql');
}
this.contactsSupported = 0 < aSupportedTypes.length;
this.contactsTypes = ko.observableArray([]);
this.contactsTypesOptions = this.contactsTypes.map(function (sValue) {
var bDisabled = -1 === Utils.inArray(sValue, aSupportedTypes);
return {
'id': sValue,
'name': getTypeName(sValue) + (bDisabled ? ' (not supported)' : ''),
'disable': bDisabled
};
});
this.contactsTypes(aTypes);
this.contactsType = ko.observable('');
this.mainContactsType = ko.computed({
'owner': this,
'read': this.contactsType,
'write': function (sValue) {
if (sValue !== this.contactsType())
{
if (-1 < Utils.inArray(sValue, aSupportedTypes))
{
this.contactsType(sValue);
}
else if (0 < aSupportedTypes.length)
{
this.contactsType('');
}
}
else
{
this.contactsType.valueHasMutated();
}
}
});
this.contactsType.subscribe(function () {
this.testContactsSuccess(false);
this.testContactsError(false);
this.testContactsErrorMessage('');
}, this);
this.pdoDsn = ko.observable(RL.settingsGet('ContactsPdoDsn')); this.pdoDsn = ko.observable(RL.settingsGet('ContactsPdoDsn'));
this.pdoUser = ko.observable(RL.settingsGet('ContactsPdoUser')); this.pdoUser = ko.observable(RL.settingsGet('ContactsPdoUser'));
this.pdoPassword = ko.observable(RL.settingsGet('ContactsPdoPassword')); this.pdoPassword = ko.observable(RL.settingsGet('ContactsPdoPassword'));
@ -5262,6 +5339,7 @@ function AdminContacts()
this.pdoDsnTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.pdoDsnTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.pdoUserTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.pdoUserTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.pdoPasswordTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.pdoPasswordTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.contactsTypeTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.testing = ko.observable(false); this.testing = ko.observable(false);
this.testContactsSuccess = ko.observable(false); this.testContactsSuccess = ko.observable(false);
@ -5276,6 +5354,7 @@ function AdminContacts()
this.testing(true); this.testing(true);
RL.remote().testContacts(this.onTestContactsResponse, { RL.remote().testContacts(this.onTestContactsResponse, {
'ContactsPdoType': this.contactsType(),
'ContactsPdoDsn': this.pdoDsn(), 'ContactsPdoDsn': this.pdoDsn(),
'ContactsPdoUser': this.pdoUser(), 'ContactsPdoUser': this.pdoUser(),
'ContactsPdoPassword': this.pdoPassword() 'ContactsPdoPassword': this.pdoPassword()
@ -5285,6 +5364,8 @@ function AdminContacts()
return '' !== this.pdoDsn() && '' !== this.pdoUser(); return '' !== this.pdoDsn() && '' !== this.pdoUser();
}); });
this.contactsType(RL.settingsGet('ContactsPdoType'));
this.onTestContactsResponse = _.bind(this.onTestContactsResponse, this); this.onTestContactsResponse = _.bind(this.onTestContactsResponse, this);
} }
@ -5303,7 +5384,14 @@ AdminContacts.prototype.onTestContactsResponse = function (sResult, oData)
else else
{ {
this.testContactsError(true); this.testContactsError(true);
this.testContactsErrorMessage(oData.Result.Message || ''); if (oData && oData.Result)
{
this.testContactsErrorMessage(oData.Result.Message || '');
}
else
{
this.testContactsErrorMessage('');
}
} }
this.testing(false); this.testing(false);
@ -5323,8 +5411,9 @@ AdminContacts.prototype.onBuild = function ()
var var
f1 = Utils.settingsSaveHelperSimpleFunction(self.pdoDsnTrigger, self), f1 = Utils.settingsSaveHelperSimpleFunction(self.pdoDsnTrigger, self),
f2 = Utils.settingsSaveHelperSimpleFunction(self.pdoUserTrigger, self), f3 = Utils.settingsSaveHelperSimpleFunction(self.pdoUserTrigger, self),
f3 = Utils.settingsSaveHelperSimpleFunction(self.pdoPasswordTrigger, self) f4 = Utils.settingsSaveHelperSimpleFunction(self.pdoPasswordTrigger, self),
f5 = Utils.settingsSaveHelperSimpleFunction(self.contactsTypeTrigger, self)
; ;
self.enableContacts.subscribe(function (bValue) { self.enableContacts.subscribe(function (bValue) {
@ -5333,24 +5422,32 @@ AdminContacts.prototype.onBuild = function ()
}); });
}); });
self.contactsType.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, {
'ContactsPdoType': sValue
});
});
self.pdoDsn.subscribe(function (sValue) { self.pdoDsn.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f1, { RL.remote().saveAdminConfig(f1, {
'ContactsPdoDsn': Utils.trim(sValue) 'ContactsPdoDsn': Utils.trim(sValue)
}); });
}); });
self.pdoUser.subscribe(function (sValue) { self.pdoUser.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f2, { RL.remote().saveAdminConfig(f3, {
'ContactsPdoUser': Utils.trim(sValue) 'ContactsPdoUser': Utils.trim(sValue)
}); });
}); });
self.pdoPassword.subscribe(function (sValue) { self.pdoPassword.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f3, { RL.remote().saveAdminConfig(f4, {
'ContactsPdoPassword': Utils.trim(sValue) 'ContactsPdoPassword': Utils.trim(sValue)
}); });
}); });
self.contactsType(RL.settingsGet('ContactsPdoType'));
}, 50); }, 50);
}; };

File diff suppressed because one or more lines are too long

View file

@ -2655,7 +2655,7 @@ ko.bindingHandlers.saveTrigger = {
var $oEl = $(oElement); var $oEl = $(oElement);
$oEl.data('save-trigger-type', $oEl.is('input[type=text],select,textarea') ? 'input' : 'custom'); $oEl.data('save-trigger-type', $oEl.is('input[type=text],input[type=email],input[type=password],select,textarea') ? 'input' : 'custom');
if ('custom' === $oEl.data('save-trigger-type')) if ('custom' === $oEl.data('save-trigger-type'))
{ {

File diff suppressed because one or more lines are too long