Add pagenator to contact list

Move contacts from plugin back to core
This commit is contained in:
RainLoop Team 2013-12-08 03:58:33 +04:00
parent daa958ed44
commit 0ec4fc89d4
44 changed files with 1250 additions and 889 deletions

View file

@ -67,11 +67,6 @@
<param name="plugin-name" value="ispmanager-change-password"/>
</antcall>
</target>
<target name="personal-address-book-mysql">
<antcall target="_build_plugin_">
<param name="plugin-name" value="personal-address-book-mysql"/>
</antcall>
</target>
<target name="black-list">
<antcall target="_build_plugin_">
<param name="plugin-name" value="black-list"/>

View file

@ -10,6 +10,12 @@ Consts.DataImages = {};
*/
Consts.Defaults.MessagesPerPage = 20;
/**
* @const
* @type {number}
*/
Consts.Defaults.ContactsPerPage = 20;
/**
* @const
* @type {Array}

View file

@ -1342,3 +1342,117 @@ Utils.resizeAndCrop = function (sUrl, iValue, fCallback)
oTempImg.src = sUrl;
};
Utils.computedPagenatorHelper = function (koCurrentPage, koPageCount) {
return function() {
var
iPrev = 0,
iNext = 0,
iLimit = 2,
aResult = [],
iCurrentPage = koCurrentPage(),
iPageCount = koPageCount(),
/**
* @param {number} iIndex
* @param {boolean=} bPush
* @param {string=} sCustomName
*/
fAdd = function (iIndex, bPush, sCustomName) {
var oData = {
'current': iIndex === iCurrentPage,
'name': Utils.isUnd(sCustomName) ? iIndex.toString() : sCustomName.toString(),
'custom': Utils.isUnd(sCustomName) ? false : true,
'title': Utils.isUnd(sCustomName) ? '' : iIndex.toString(),
'value': iIndex.toString()
};
if (Utils.isUnd(bPush) ? true : !!bPush)
{
aResult.push(oData);
}
else
{
aResult.unshift(oData);
}
}
;
if (1 < iPageCount || (0 < iPageCount && iPageCount < iCurrentPage))
// if (0 < iPageCount && 0 < iCurrentPage)
{
if (iPageCount < iCurrentPage)
{
fAdd(iPageCount);
iPrev = iPageCount;
iNext = iPageCount;
}
else
{
if (3 >= iCurrentPage || iPageCount - 2 <= iCurrentPage)
{
iLimit += 2;
}
fAdd(iCurrentPage);
iPrev = iCurrentPage;
iNext = iCurrentPage;
}
while (0 < iLimit) {
iPrev -= 1;
iNext += 1;
if (0 < iPrev)
{
fAdd(iPrev, false);
iLimit--;
}
if (iPageCount >= iNext)
{
fAdd(iNext, true);
iLimit--;
}
else if (0 >= iPrev)
{
break;
}
}
if (3 === iPrev)
{
fAdd(2, false);
}
else if (3 < iPrev)
{
fAdd(Math.round((iPrev - 1) / 2), false, '...');
}
if (iPageCount - 2 === iNext)
{
fAdd(iPageCount - 1, true);
}
else if (iPageCount - 2 > iNext)
{
fAdd(Math.round((iPageCount + iNext) / 2), true, '...');
}
// first and last
if (1 < iPrev)
{
fAdd(1, false);
}
if (iPageCount > iNext)
{
fAdd(iPageCount, true);
}
}
return aResult;
};
};

View file

@ -187,7 +187,7 @@ function WebMailDataStorage()
this.messageListPageCount = ko.computed(function () {
var iPage = Math.ceil(this.messageListCount() / this.messagesPerPage());
return 0 === iPage ? 1 : iPage;
return 0 >= iPage ? 1 : iPage;
}, this);
this.mainMessageListSearch = ko.computed({

View file

@ -1,5 +1,5 @@
@contacts-popup-left-width: 250px;
@contacts-popup-left-width: 270px;
.b-contacts-content {
@ -58,13 +58,28 @@
.e-search {
margin-top: 7px;
width: @contacts-popup-left-width - 25;
}
}
.b-list-footer-toopbar {
position: absolute;
left: 0;
bottom: 0;
height: 105px;
width: @contacts-popup-left-width;
background-color: #eee;
.box-shadow(inset 0 1px 0 #ccc);
.footer-pager {
padding: 8px 10px 0 0;
}
}
.b-list-content {
position: absolute;
top: 45px;
bottom: 60px;
bottom: 105px;
left: 0;
width: @contacts-popup-left-width;
overflow: hidden;
@ -81,6 +96,7 @@
font-size: 14px;
line-height: 13px;
background-color: #fff;
.box-shadow(inset 0 -1px 0 #ccc);
}
.listEmptyList, .listEmptyListLoading, .listEmptySearchList {

View file

@ -485,29 +485,6 @@ html.rl-no-preview-pane {
margin-right: 10px !important;
}
}
.pagenator {
.page {
display: inline-block;
color: #999;
text-decoration: none;
font-size: 24px;
padding: 3px;
cursor: pointer;
&:hover .pageNumber {
color: #555;
}
&.current .pageNumber {
font-size: 28px;
color: #333;
border-bottom: 2px solid #000;
}
}
}
}
.draggablePlace {

View file

@ -256,6 +256,13 @@ html.rl-no-preview-pane {
.b-text-part {
div[data-x-div-type=html] {
height: 100%;
div[data-x-div-type=html] {
height: 100%;
}
}
a {
color: blue;
text-decoration: underline;

View file

@ -80,11 +80,38 @@
}
}
.g-ui-height-100proc {
height: 100%;
}
.g-ui-resizable-delimiter-highlight {
border: none;
border-right: 6px solid #aaa;
}
.e-pagenator {
.e-page {
display: inline-block;
color: #999;
text-decoration: none;
font-size: 24px;
padding: 3px;
cursor: pointer;
&:hover .e-page-number {
color: #555;
}
&.current .e-page-number {
font-size: 28px;
color: #333;
border-bottom: 2px solid #000;
}
}
}
html.rgba .g-ui-resizable-delimiter-highlight {
border-right-color: rgba(0, 0, 0, 0.2);
}

View file

@ -56,118 +56,7 @@ function MailBoxMessageListViewModel()
return '' === sValue ? '' : Utils.i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', {'SEARCH': sValue});
});
this.messageListPagenator = ko.computed(function () {
var
iPrev = 0,
iNext = 0,
iLimit = 2,
aResult = [],
iCurrentPage = oData.messageListPage(),
iPageCount = oData.messageListPageCount(),
/**
* @param {number} iIndex
* @param {boolean=} bPush
* @param {string=} sCustomName
*/
fAdd = function (iIndex, bPush, sCustomName) {
var oData = {
'current': iIndex === iCurrentPage,
'name': Utils.isUnd(sCustomName) ? iIndex.toString() : sCustomName.toString(),
'custom': Utils.isUnd(sCustomName) ? false : true,
'title': Utils.isUnd(sCustomName) ? '' : iIndex.toString(),
'value': iIndex.toString()
};
if (Utils.isUnd(bPush) ? true : !!bPush)
{
aResult.push(oData);
}
else
{
aResult.unshift(oData);
}
}
;
if (1 < iPageCount || (0 < iPageCount && iPageCount < iCurrentPage))
// if (0 < iPageCount && 0 < iCurrentPage)
{
if (iPageCount < iCurrentPage)
{
fAdd(iPageCount);
iPrev = iPageCount;
iNext = iPageCount;
}
else
{
if (3 >= iCurrentPage || iPageCount - 2 <= iCurrentPage)
{
iLimit += 2;
}
fAdd(iCurrentPage);
iPrev = iCurrentPage;
iNext = iCurrentPage;
}
while (0 < iLimit) {
iPrev -= 1;
iNext += 1;
if (0 < iPrev)
{
fAdd(iPrev, false);
iLimit--;
}
if (iPageCount >= iNext)
{
fAdd(iNext, true);
iLimit--;
}
else if (0 >= iPrev)
{
break;
}
}
if (3 === iPrev)
{
fAdd(2, false);
}
else if (3 < iPrev)
{
fAdd(Math.round((iPrev - 1) / 2), false, '...');
}
if (iPageCount - 2 === iNext)
{
fAdd(iPageCount - 1, true);
}
else if (iPageCount - 2 > iNext)
{
fAdd(Math.round((iPageCount + iNext) / 2), true, '...');
}
// first and last
if (1 < iPrev)
{
fAdd(1, false);
}
if (iPageCount > iNext)
{
fAdd(iPageCount, true);
}
}
return aResult;
}, this);
this.messageListPagenator = ko.computed(Utils.computedPagenatorHelper(oData.messageListPage, oData.messageListPageCount));
this.checkAll = ko.computed({
'read': function () {
@ -804,7 +693,7 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
});
oDom
.on('click', '.pagenator .page', function () {
.on('click', '.e-pagenator .e-page', function () {
var oPage = ko.dataFor(this);
if (oPage)
{

View file

@ -25,10 +25,19 @@ function PopupsContactsViewModel()
;
this.search = ko.observable('');
this.contactsCount = ko.observable(0);
this.contacts = ko.observableArray([]);
this.contacts.loading = ko.observable(false).extend({'throttle': 200});
this.currentContact = ko.observable(null);
this.contactsPage = ko.observable(1);
this.contactsPageCount = ko.computed(function () {
var iPage = Math.ceil(this.contactsCount() / Consts.Defaults.ContactsPerPage);
return 0 >= iPage ? 1 : iPage;
}, this);
this.contactsPagenator = ko.computed(Utils.computedPagenatorHelper(this.contactsPage, this.contactsPageCount));
this.emptySelection = ko.observable(true);
this.viewClearSearch = ko.observable(false);
@ -225,28 +234,24 @@ function PopupsContactsViewModel()
var bV = this.viewHasNonEmptyRequaredProperties();
return !this.viewSaving() && bV;
});
this.bDropPageAfterDelete = false;
}
Utils.extendAsViewModel('PopupsContactsViewModel', PopupsContactsViewModel);
PopupsContactsViewModel.prototype.addNewEmail = function ()
{
// if (0 === this.viewPropertiesEmailsEmpty().length)
// {
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.EmailPersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
// }
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.EmailPersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
};
PopupsContactsViewModel.prototype.addNewPhone = function ()
{
// if (0 === this.viewPropertiesPhonesEmpty().length)
// {
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.PhonePersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
// }
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.PhonePersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
};
PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = function ()
@ -255,6 +260,7 @@ PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = func
self = this,
oKoContacts = this.contacts,
oCurrentContact = this.currentContact(),
iCount = this.contacts().length,
aContacts = this.contactsCheckedOrSelected()
;
@ -269,8 +275,14 @@ PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = func
}
oContact.deleted(true);
iCount--;
});
if (iCount <= 0)
{
this.bDropPageAfterDelete = true;
}
_.delay(function () {
_.each(aContacts, function (oContact) {
@ -302,13 +314,13 @@ PopupsContactsViewModel.prototype.deleteResponse = function (sResult, oData)
{
if (500 < (Enums.StorageResultType.Success === sResult && oData && oData.Time ? Utils.pInt(oData.Time) : 0))
{
this.reloadContactList();
this.reloadContactList(this.bDropPageAfterDelete);
}
else
{
_.delay((function (self) {
return function () {
self.reloadContactList();
self.reloadContactList(self.bDropPageAfterDelete);
};
}(this)), 500);
}
@ -361,12 +373,31 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
this.viewProperties(aList);
};
PopupsContactsViewModel.prototype.reloadContactList = function ()
/**
* @param {boolean=} bDropPagePosition = false
*/
PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePosition)
{
var self = this;
var
self = this,
iOffset = (this.contactsPage() - 1) * Consts.Defaults.ContactsPerPage
;
this.bDropPageAfterDelete = false;
if (Utils.isUnd(bDropPagePosition) ? false : !!bDropPagePosition)
{
this.contactsPage(1);
iOffset = 0;
}
this.contacts.loading(true);
RL.remote().contacts(function (sResult, oData) {
var aList = [];
var
iCount = 0,
aList = []
;
if (Enums.StorageResultType.Success === sResult && oData && oData.Result && oData.Result.List)
{
if (Utils.isNonEmptyArray(oData.Result.List))
@ -377,9 +408,14 @@ PopupsContactsViewModel.prototype.reloadContactList = function ()
});
aList = _.compact(aList);
iCount = Utils.pInt(oData.Result.Count);
iCount = 0 < iCount ? iCount : 0;
}
}
self.contactsCount(iCount);
self.contacts(aList);
self.viewClearSearch('' !== self.search());
self.contacts.loading(false);
@ -389,7 +425,7 @@ PopupsContactsViewModel.prototype.reloadContactList = function ()
self.contacts.setSelectedByUid('' + self.viewID());
}
}, 0, 20, this.search());
}, iOffset, Consts.Defaults.ContactsPerPage, this.search());
};
PopupsContactsViewModel.prototype.onBuild = function (oDom)
@ -399,6 +435,8 @@ PopupsContactsViewModel.prototype.onBuild = function (oDom)
this.selector.init(this.oContentVisible, this.oContentScrollable);
var self = this;
ko.computed(function () {
var
bModalVisibility = this.modalVisibility(),
@ -406,12 +444,23 @@ PopupsContactsViewModel.prototype.onBuild = function (oDom)
;
this.selector.useKeyboard(bModalVisibility && bUseKeyboardShortcuts);
}, this).extend({'notify': 'always'});
oDom
.on('click', '.e-pagenator .e-page', function () {
var oPage = ko.dataFor(this);
if (oPage)
{
self.contactsPage(Utils.pInt(oPage.value));
self.reloadContactList();
}
})
;
};
PopupsContactsViewModel.prototype.onShow = function ()
{
kn.routeOff();
this.reloadContactList();
this.reloadContactList(true);
};
PopupsContactsViewModel.prototype.onHide = function ()

View file

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 RainLoop Team
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1 +0,0 @@
Personal addressbook plugin (MySQL)

View file

@ -1 +0,0 @@
1.0

View file

@ -1,70 +0,0 @@
<?php
class PersonalAddressBookMysqlPlugin extends \RainLoop\Plugins\AbstractPlugin
{
/**
* @return void
*/
public function Init()
{
$this->addHook('main.fabrica', 'MainFabrica');
}
/**
* @return string
*/
public function Supported()
{
if (!extension_loaded('pdo') || !class_exists('PDO'))
{
return 'The PHP exention PDO (mysql) must be installed to use this plugin';
}
$aDrivers = \PDO::getAvailableDrivers();
if (!is_array($aDrivers) || !in_array('mysql', $aDrivers))
{
return 'The PHP exention PDO (mysql) must be installed to use this plugin';
}
return '';
}
/**
* @param string $sName
* @param mixed $oProvider
*/
public function MainFabrica($sName, &$oProvider)
{
if (!$oProvider && 'personal-address-book' === $sName &&
$this->Config()->Get('plugin', 'enabled', false))
{
$sDsn = \trim($this->Config()->Get('plugin', 'pdo_dsn', ''));
$sUser = \trim($this->Config()->Get('plugin', 'user', ''));
$sPassword = (string) $this->Config()->Get('plugin', 'password', '');
include_once __DIR__.'/MySqlPersonalAddressBookDriver.php';
$oProvider = new MySqlPersonalAddressBookDriver($sDsn, $sUser, $sPassword);
$oProvider->SetLogger($this->Manager()->Actions()->Logger());
}
}
/**
* @return array
*/
public function configMapping()
{
return array(
\RainLoop\Plugins\Property::NewInstance('enabled')->SetLabel('Enable')
->SetType(\RainLoop\Enumerations\PluginPropertyType::BOOL)
->SetDefaultValue(false),
\RainLoop\Plugins\Property::NewInstance('pdo_dsn')->SetLabel('PDO dsn')
->SetDefaultValue('mysql:host=127.0.0.1;port=3306;dbname=rainloop'),
\RainLoop\Plugins\Property::NewInstance('user')->SetLabel('DB User')
->SetDefaultValue('root'),
\RainLoop\Plugins\Property::NewInstance('password')->SetLabel('DB Password')
->SetType(\RainLoop\Enumerations\PluginPropertyType::PASSWORD)
->SetDefaultValue('')
);
}
}

View file

@ -1,82 +0,0 @@
/*!40014 SET FOREIGN_KEY_CHECKS=0 */;
-- rainloop_system --
CREATE TABLE IF NOT EXISTS `rainloop_system` (
`sys_name` varchar(50) NOT NULL,
`value_int` int(11) UNSIGNED NOT NULL DEFAULT '0',
`value_str` varchar(255) NOT NULL DEFAULT ''
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- rainloop_users --
CREATE TABLE IF NOT EXISTS `rainloop_users` (
`id_user` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`rl_email` varchar(255) NOT NULL,
UNIQUE `email_unique` (`rl_email`),
PRIMARY KEY(`id_user`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */;
-- rainloop_pab_contacts --
CREATE TABLE IF NOT EXISTS `rainloop_pab_contacts` (
`id_contact` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_user` int(11) UNSIGNED NOT NULL,
`display` varchar(255) NOT NULL DEFAULT '',
`display_name` varchar(255) NOT NULL DEFAULT '',
`display_email` varchar(255) NOT NULL DEFAULT '',
`auto` int(1) UNSIGNED NOT NULL DEFAULT '0',
`changed` int(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY(`id_contact`),
CONSTRAINT `id_user_fk_rainloop_pab_contacts` FOREIGN KEY (`id_user`)
REFERENCES `rainloop_users` (`id_user`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- rainloop_pab_prop --
CREATE TABLE IF NOT EXISTS `rainloop_pab_prop` (
`id_prop` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_contact` int(11) UNSIGNED NOT NULL,
`id_user` int(11) UNSIGNED NOT NULL,
`type` int(11) UNSIGNED NOT NULL,
`type_custom` varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT '',
`value` varchar(255) NOT NULL DEFAULT '',
`value_custom` varchar(255) NOT NULL DEFAULT '',
`frec` int(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY(`id_prop`),
INDEX `id_user_id_contact_index` (`id_user`, `id_contact`),
INDEX `id_user_value_index` (`id_user`, `value`),
CONSTRAINT `id_contact_fk_rainloop_pab_prop` FOREIGN KEY (`id_contact`)
REFERENCES `rainloop_pab_contacts` (`id_contact`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- rainloop_pab_tags --
CREATE TABLE IF NOT EXISTS `rainloop_pab_tags` (
`id_tag` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_user` int(11) UNSIGNED NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY(`id_tag`),
UNIQUE `id_user_name_unique` (`id_user`, `name`),
CONSTRAINT `id_user_fk_rainloop_pab_tags` FOREIGN KEY (`id_user`)
REFERENCES `rainloop_users` (`id_user`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-- rainloop_pab_tags_contacts --
CREATE TABLE IF NOT EXISTS `rainloop_pab_tags_contacts` (
`id_tag` int(11) UNSIGNED NOT NULL,
`id_contact` int(11) UNSIGNED NOT NULL,
CONSTRAINT `id_contact_fk_rainloop_tags_contacts` FOREIGN KEY (`id_contact`)
REFERENCES `rainloop_pab_contacts` (`id_contact`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `id_tag_fk_rainloop_tags_contacts` FOREIGN KEY (`id_tag`)
REFERENCES `rainloop_pab_tags` (`id_tag`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
/*!40014 SET FOREIGN_KEY_CHECKS=1 */;

View file

@ -196,7 +196,10 @@ class Actions
private function fabrica($sName, $oAccount = null)
{
$oResult = null;
$this->Plugins()->RunHook('main.fabrica', array($sName, &$oResult), false);
$this->Plugins()
->RunHook('main.fabrica', array($sName, &$oResult), false)
->RunHook('main.fabrica[v2]', array($sName, &$oResult, &$oAccount), false)
;
if (null === $oResult)
{
@ -225,6 +228,15 @@ class Actions
break;
case 'personal-address-book':
// \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface
if ($this->Config()->Get('contacts', 'enable', false))
{
$sDsn = \trim($this->Config()->Get('contacts', 'pdo_dsn', ''));
$sUser = \trim($this->Config()->Get('contacts', 'pdo_user', ''));
$sPassword = (string) $this->Config()->Get('contacts', 'pdo_password', '');
$oResult = new \RainLoop\Providers\PersonalAddressBook\MySqlPersonalAddressBook($sDsn, $sUser, $sPassword);
$oResult->SetLogger($this->Logger());
}
break;
case 'suggestions':
// \RainLoop\Providers\Suggestions\SuggestionsInterface
@ -3973,13 +3985,15 @@ class Actions
if ($this->PersonalAddressBookProvider($oAccount)->IsActive())
{
$iCount = 0;
$mResult = $this->PersonalAddressBookProvider($oAccount)->GetContacts($oAccount,
$iOffset, $iLimit, $sSearch, false);
$iOffset, $iLimit, $sSearch, false, $iCount);
}
return $this->DefaultResponse(__FUNCTION__, array(
'Offset' => $iOffset,
'Limit' => $iLimit,
'Count' => $iCount,
'Search' => $sSearch,
'List' => $mResult
));
@ -4012,6 +4026,8 @@ class Actions
*/
public function DoContactSave()
{
$iStart = \time();
$oAccount = $this->getAccountFromToken();
$bResult = false;
@ -4048,6 +4064,11 @@ class Actions
$bResult = $oPab->ContactSave($oAccount, $oContact);
}
if (\time() === $iStart)
{
sleep(1);
}
return $this->DefaultResponse(__FUNCTION__, array(
'RequestUid' => $sRequestUid,
'ResultID' => $bResult ? $oContact->IdContact : '',
@ -5608,6 +5629,7 @@ class Actions
/* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Contact */
'IdContact' => $mResponse->IdContact,
'Display' => \MailSo\Base\Utils::Utf8Clear($mResponse->Display),
'IdPropertyFromSearch' => $mResponse->IdPropertyFromSearch,
'Properties' => $this->responseObject($mResponse->Properties, $sParent, $aParameters)
));
}
@ -5615,6 +5637,7 @@ class Actions
{
$mResult = \array_merge($this->objectData($mResponse, $sParent, $aParameters), array(
/* @var $mResponse \RainLoop\Providers\PersonalAddressBook\Classes\Property */
'IdProperty' => $mResponse->IdProperty,
'Type' => $mResponse->Type,
'TypeCustom' => $mResponse->TypeCustom,
'Value' => \MailSo\Base\Utils::Utf8Clear($mResponse->Value),

View file

@ -9,6 +9,11 @@ abstract class PdoAbstract
*/
protected $oPDO = null;
/**
* @var bool
*/
protected $bExplain = false;
/**
* @var \MailSo\Log\Logger
*/
@ -130,22 +135,26 @@ abstract class PdoAbstract
}
/**
* @param \RainLoop\Account $oAccount
* @param string $sSql
* @param array $aParams
* @param bool $bMultParams = false
* @param bool $bMultiplyParams = false
*
* @return \PDOStatement|null
*/
protected function prepareAndExecute($sSql, $aParams = array(), $bMultParams = false)
protected function prepareAndExecute($sSql, $aParams = array(), $bMultiplyParams = false)
{
if ($this->bExplain && !$bMultiplyParams)
{
$this->prepareAndExplain($sSql, $aParams);
}
$mResult = null;
$this->writeLog($sSql);
$oStmt = $this->getPDO()->prepare($sSql);
if ($oStmt)
{
$aRootParams = $bMultParams ? $aParams : array($aParams);
$aRootParams = $bMultiplyParams ? $aParams : array($aParams);
foreach ($aRootParams as $aSubParams)
{
foreach ($aSubParams as $sName => $aValue)
@ -153,12 +162,45 @@ abstract class PdoAbstract
$oStmt->bindValue($sName, $aValue[0], $aValue[1]);
}
$mResult = $oStmt->execute() && !$bMultParams ? $oStmt : null;
$mResult = $oStmt->execute() && !$bMultiplyParams ? $oStmt : null;
}
}
return $mResult;
}
/**
* @param string $sSql
* @param array $aParams
*/
protected function prepareAndExplain($sSql, $aParams = array())
{
$mResult = null;
if (0 === strpos($sSql, 'SELECT '))
{
$sSql = 'EXPLAIN '.$sSql;
$this->writeLog($sSql);
$oStmt = $this->getPDO()->prepare($sSql);
if ($oStmt)
{
foreach ($aParams as $sName => $aValue)
{
$oStmt->bindValue($sName, $aValue[0], $aValue[1]);
}
$mResult = $oStmt->execute() ? $oStmt : null;
}
}
if ($mResult)
{
$aFetch = $mResult->fetchAll(\PDO::FETCH_ASSOC);
$this->oLogger->WriteDump($aFetch);
unset($aFetch);
$mResult->closeCursor();
}
}
/**
* @param string $sSql

View file

@ -81,6 +81,14 @@ class Application extends \RainLoop\Config\AbstractConfig
0 for unlimited.')
),
'contacts' => array(
'enable' => array(false, 'Enable contacts'),
'type' => array('mysql', ''),
'pdo_dsn' => array('mysql:host=127.0.0.1;port=3306;dbname=rainloop', ''),
'pdo_user' => array('root', ''),
'pdo_password' => array('', ''),
),
'security' => array(
'csrf_protection' => array(true,
'Enable CSRF protection (http://en.wikipedia.org/wiki/Cross-site_request_forgery)'),

View file

@ -69,14 +69,15 @@ class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider
* @param type $iLimit = 20
* @param string $sSearch = ''
* @param bool $bAutoOnly = false
* @param int $iResultCount = 0
*
* @return array
*/
public function GetContacts($oAccount,
$iOffset = 0, $iLimit = 20, $sSearch = '', $bAutoOnly = false)
$iOffset = 0, $iLimit = 20, $sSearch = '', $bAutoOnly = false, &$iResultCount = 0)
{
return $this->IsActive() ? $this->oDriver->GetContacts($oAccount,
$iOffset, $iLimit, $sSearch, $bAutoOnly) : array();
$iOffset, $iLimit, $sSearch, $bAutoOnly, $iResultCount) : array();
}
/**
@ -96,12 +97,13 @@ class PersonalAddressBook extends \RainLoop\Providers\AbstractProvider
/**
* @param \RainLoop\Account $oAccount
* @param array $aEmails
* @param bool $bCreateAuto = true
*
* @return bool
*/
public function IncFrec($oAccount, $aEmails)
public function IncFrec($oAccount, $aEmails, $bCreateAuto = true)
{
return $this->IsActive() ? $this->oDriver->IncFrec($oAccount, $aEmails) : false;
return $this->IsActive() ? $this->oDriver->IncFrec($oAccount, $aEmails, $bCreateAuto) : false;
}
/**

View file

@ -29,6 +29,11 @@ class Contact
*/
public $Auto;
/**
* @var bool
*/
public $Shared;
/**
* @var int
*/
@ -39,6 +44,11 @@ class Contact
*/
public $Tags;
/**
* @var int
*/
public $IdPropertyFromSearch;
/**
* @var array
*/
@ -57,8 +67,10 @@ class Contact
$this->DisplayName = '';
$this->DisplayEmail = '';
$this->Auto = false;
$this->Shared = false;
$this->Changed = \time();
$this->Tags = array();
$this->IdPropertyFromSearch = 0;
$this->Properties = array();
}

View file

@ -6,6 +6,11 @@ use RainLoop\Providers\PersonalAddressBook\Enumerations\PropertyType;
class Property
{
/**
* @var int
*/
public $IdProperty;
/**
* @var int
*/
@ -38,6 +43,8 @@ class Property
public function Clear()
{
$this->IdProperty = 0;
$this->Type = PropertyType::UNKNOWN;
$this->TypeCustom = '';

View file

@ -1,8 +1,10 @@
<?php
namespace RainLoop\Providers\PersonalAddressBook;
use \RainLoop\Providers\PersonalAddressBook\Enumerations\PropertyType;
class MySqlPersonalAddressBookDriver
class MySqlPersonalAddressBook
extends \RainLoop\Common\PdoAbstract
implements \RainLoop\Providers\PersonalAddressBook\PersonalAddressBookInterface
{
@ -26,14 +28,8 @@ class MySqlPersonalAddressBookDriver
$this->sDsn = $sDsn;
$this->sUser = $sUser;
$this->sPassword = $sPassword;
}
/**
* @return string
*/
public function Version()
{
return 'MySqlPersonalAddressBookDriver-v1';
$this->bExplain = false;
}
/**
@ -103,7 +99,7 @@ class MySqlPersonalAddressBookDriver
$aFreq = $this->getContactFreq($iUserID, $iIdContact);
$sSql = 'UPDATE `rainloop_pab_contacts` SET `display` = :display, `display_name` = :display_name, `display_email` = :display_email, '.
'`auto` = :auto, `changed` = :changed WHERE id_user = :id_user AND `id_contact` = :id_contact';
'`auto` = :auto, `shared` = :shared, `changed` = :changed WHERE id_user = :id_user AND `id_contact` = :id_contact';
$this->prepareAndExecute($sSql,
array(
@ -113,13 +109,14 @@ class MySqlPersonalAddressBookDriver
':display_name' => array($oContact->DisplayName, \PDO::PARAM_STR),
':display_email' => array($oContact->DisplayEmail, \PDO::PARAM_STR),
':auto' => array($oContact->Auto, \PDO::PARAM_INT),
':shared' => array($oContact->Shared, \PDO::PARAM_INT),
':changed' => array($oContact->Changed, \PDO::PARAM_INT),
)
);
// clear previos props
$this->prepareAndExecute(
'DELETE FROM `rainloop_pab_prop` WHERE `id_user` = :id_user AND `id_contact` = :id_contact',
'DELETE FROM `rainloop_pab_properties` WHERE `id_user` = :id_user AND `id_contact` = :id_contact',
array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':id_contact' => array($iIdContact, \PDO::PARAM_INT)
@ -129,8 +126,8 @@ class MySqlPersonalAddressBookDriver
else
{
$sSql = 'INSERT INTO `rainloop_pab_contacts` '.
'(`id_user`, `display`, `display_name`, `display_email`, `auto`, `changed`) VALUES '.
'(:id_user, :display, :display_name, :display_email, :auto, :changed)';
'(`id_user`, `display`, `display_name`, `display_email`, `auto`, `shared`, `changed`) VALUES '.
'(:id_user, :display, :display_name, :display_email, :auto, :shared, :changed)';
$this->prepareAndExecute($sSql,
array(
@ -139,6 +136,7 @@ class MySqlPersonalAddressBookDriver
':display_name' => array($oContact->DisplayName, \PDO::PARAM_STR),
':display_email' => array($oContact->DisplayEmail, \PDO::PARAM_STR),
':auto' => array($oContact->Auto, \PDO::PARAM_INT),
':shared' => array($oContact->Shared, \PDO::PARAM_INT),
':changed' => array($oContact->Changed, \PDO::PARAM_INT)
)
);
@ -169,13 +167,15 @@ class MySqlPersonalAddressBookDriver
':type_custom' => array($oProp->TypeCustom, \PDO::PARAM_STR),
':value' => array($oProp->Value, \PDO::PARAM_STR),
':value_custom' => array($oProp->ValueClear, \PDO::PARAM_STR),
':auto' => array($oContact->Auto, \PDO::PARAM_INT),
':shared' => array($oContact->Shared, \PDO::PARAM_INT),
':frec' => array($iFreq, \PDO::PARAM_INT),
);
}
$sSql = 'INSERT INTO `rainloop_pab_prop` '.
'(`id_contact`, `id_user`, `type`, `type_custom`, `value`, `value_custom`, `frec`) VALUES '.
'(:id_contact, :id_user, :type, :type_custom, :value, :value_custom, :frec)';
$sSql = 'INSERT INTO `rainloop_pab_properties` '.
'(`id_contact`, `id_user`, `type`, `type_custom`, `value`, `value_custom`, `auto`, `shared`, `frec`) VALUES '.
'(:id_contact, :id_user, :type, :type_custom, :value, :value_custom, :auto, :shared, :frec)';
$this->prepareAndExecute($sSql, $aParams, true);
}
@ -211,9 +211,43 @@ class MySqlPersonalAddressBookDriver
return false;
}
return !!$this->prepareAndExecute(
'DELETE FROM `rainloop_pab_contacts` WHERE `id_user` = :id_user AND `id_contact` IN ('.\implode(',', $aContactIds).')',
array(':id_user' => array($iUserID, \PDO::PARAM_INT)));
$sIDs = \implode(',', $aContactIds);
$aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT));
$this->prepareAndExecute('DELETE FROM `rainloop_pab_tags_contacts` WHERE `id_contact` IN ('.$sIDs.')');
$this->prepareAndExecute('DELETE FROM `rainloop_pab_properties` WHERE `id_user` = :id_user AND `id_contact` IN ('.$sIDs.')', $aParams);
$this->prepareAndExecute('DELETE FROM `rainloop_pab_contacts` WHERE `id_user` = :id_user AND `id_contact` IN ('.$sIDs.')', $aParams);
return true;
}
/**
* @param \RainLoop\Account $oAccount
* @param array $aTagsIds
*
* @return bool
*/
public function DeleteTags($oAccount, $aTagsIds)
{
$iUserID = $this->getUserId($oAccount->ParentEmailHelper());
$aTagsIds = \array_filter($aTagsIds, function (&$mItem) {
$mItem = (int) \trim($mItem);
return 0 < $mItem;
});
if (0 === \count($aTagsIds))
{
return false;
}
$sIDs = \implode(',', $aTagsIds);
$aParams = array(':id_user' => array($iUserID, \PDO::PARAM_INT));
$this->prepareAndExecute('DELETE FROM `rainloop_pab_tags_contacts` WHERE `id_tag` IN ('.$sIDs.')');
$this->prepareAndExecute('DELETE FROM `rainloop_pab_tags` WHERE `id_user` = :id_user AND `id_tag` IN ('.$sIDs.')', $aParams);
return true;
}
/**
@ -222,109 +256,166 @@ class MySqlPersonalAddressBookDriver
* @param int $iLimit = 20
* @param string $sSearch = ''
* @param bool $bAutoOnly = false
* @param int $iResultCount = 0
*
* @return array
*/
public function GetContacts($oAccount, $iOffset = 0, $iLimit = 20, $sSearch = '', $bAutoOnly = false)
public function GetContacts($oAccount, $iOffset = 0, $iLimit = 20, $sSearch = '', $bAutoOnly = false, &$iResultCount = 0)
{
$iOffset = 0 <= $iOffset ? $iOffset : 0;
$iLimit = 0 < $iLimit ? (int) $iLimit : 20;
$sSearch = \trim($sSearch);
$iUserID = $this->getUserId($oAccount->ParentEmailHelper());
$sSql = 'SELECT * FROM `rainloop_pab_contacts` WHERE id_user = :id_user AND `auto` = :auto';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':auto' => array($bAutoOnly ? 1 : 0, \PDO::PARAM_INT)
);
$iCount = 0;
$aSearchIds = array();
$aPropertyFromSearchIds = array();
if (0 < \strlen($sSearch))
{
$sSql .= ' AND `id_contact` IN ('.
'SELECT DISTINCT `id_contact` FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `value` LIKE :search ESCAPE \'=\''.
')';
$sSql = 'SELECT `id_prop`, `id_contact` FROM `rainloop_pab_properties` WHERE `id_user` = :id_user AND `auto` = :auto AND `value` LIKE :search ESCAPE \'=\' GROUP BY `id_contact`';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':auto' => array($bAutoOnly ? 1 : 0, \PDO::PARAM_INT),
':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR)
);
$aParams[':search'] = array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR);
}
$sSql .= ' ORDER BY `display` ASC LIMIT :limit OFFSET :offset';
$aParams[':limit'] = array($iLimit, \PDO::PARAM_INT);
$aParams[':offset'] = array($iOffset, \PDO::PARAM_INT);
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
$aContacts = array();
$aIdContacts = array();
if (\is_array($aFetch) && 0 < \count($aFetch))
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
foreach ($aFetch as $aItem)
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
$iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0;
if (0 < $iIdContact)
foreach ($aFetch as $aItem)
{
$aIdContacts[] = $iIdContact;
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$oContact->IdContact = (string) $iIdContact;
$oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : '';
$oContact->DisplayName = isset($aItem['display_name']) ? (string) $aItem['display_name'] : '';
$oContact->DisplayEmail = isset($aItem['display_email']) ? (string) $aItem['display_email'] : '';
$oContact->Auto = isset($aItem['auto']) ? (bool) $aItem['auto'] : false;
$oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0;
$aContacts[$iIdContact] = $oContact;
$iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0;
if (0 < $iIdContact)
{
$aSearchIds[] = $iIdContact;
$aPropertyFromSearchIds[$iIdContact] = isset($aItem['id_prop']) ? (int) $aItem['id_prop'] : 0;
}
}
}
$iCount = \count($aSearchIds);
}
}
else
{
$sSql = 'SELECT COUNT(DISTINCT `id_contact`) as `contact_count` FROM `rainloop_pab_properties` WHERE `id_user` = :id_user AND `auto` = :auto';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':auto' => array($bAutoOnly ? 1 : 0, \PDO::PARAM_INT)
);
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if ($aFetch && isset($aFetch[0]['contact_count']) && is_numeric($aFetch[0]['contact_count']) && 0 < (int) $aFetch[0]['contact_count'])
{
$iCount = (int) $aFetch[0]['contact_count'];
}
}
}
$iResultCount = $iCount;
if (0 < $iCount)
{
$sSql = 'SELECT * FROM `rainloop_pab_contacts` WHERE id_user = :id_user AND `auto` = :auto';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':auto' => array($bAutoOnly ? 1 : 0, \PDO::PARAM_INT)
);
if (0 < \count($aSearchIds))
{
$sSql .= ' AND `id_contact` IN ('.implode(',', $aSearchIds).')';
}
unset($aFetch);
$sSql .= ' ORDER BY `display` ASC LIMIT :limit OFFSET :offset';
$aParams[':limit'] = array($iLimit, \PDO::PARAM_INT);
$aParams[':offset'] = array($iOffset, \PDO::PARAM_INT);
if (0 < count($aIdContacts))
$oStmt = $this->prepareAndExecute($sSql, $aParams);
if ($oStmt)
{
$oStmt->closeCursor();
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
$sSql = 'SELECT * FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `id_contact` IN ('.\implode(',', $aIdContacts).')';
$oStmt = $this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
if ($oStmt)
$aContacts = array();
$aIdContacts = array();
if (\is_array($aFetch) && 0 < \count($aFetch))
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
foreach ($aFetch as $aItem)
{
foreach ($aFetch as $aItem)
$iIdContact = $aItem && isset($aItem['id_contact']) ? (int) $aItem['id_contact'] : 0;
if (0 < $iIdContact)
{
if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['type'], $aItem['value']))
{
$iId = (int) $aItem['id_contact'];
if (0 < $iId && isset($aContacts[$iId]))
{
$oProperty = new \RainLoop\Providers\PersonalAddressBook\Classes\Property();
$oProperty->Type = (int) $aItem['type'];
$oProperty->TypeCustom = isset($aItem['type_custom']) ? (string) $aItem['type_custom'] : '';
$oProperty->Value = (string) $aItem['value'];
$oProperty->ValueClear = isset($aItem['value_clear']) ? (string) $aItem['value_clear'] : '';
$oProperty->Frec = isset($aItem['frec']) ? (int) $aItem['frec'] : 0;
$aIdContacts[] = $iIdContact;
$oContact = new \RainLoop\Providers\PersonalAddressBook\Classes\Contact();
$aContacts[$iId]->Properties[] = $oProperty;
$oContact->IdContact = (string) $iIdContact;
$oContact->Display = isset($aItem['display']) ? (string) $aItem['display'] : '';
$oContact->DisplayName = isset($aItem['display_name']) ? (string) $aItem['display_name'] : '';
$oContact->DisplayEmail = isset($aItem['display_email']) ? (string) $aItem['display_email'] : '';
$oContact->Auto = isset($aItem['auto']) ? (bool) $aItem['auto'] : false;
$oContact->Changed = isset($aItem['changed']) ? (int) $aItem['changed'] : 0;
$oContact->IdPropertyFromSearch = isset($aPropertyFromSearchIds[$iIdContact]) &&
0 < $aPropertyFromSearchIds[$iIdContact] ? $aPropertyFromSearchIds[$iIdContact] : 0;
$aContacts[$iIdContact] = $oContact;
}
}
}
unset($aFetch);
if (0 < count($aIdContacts))
{
$oStmt->closeCursor();
$sSql = 'SELECT * FROM `rainloop_pab_properties` WHERE id_user = :id_user AND `id_contact` IN ('.\implode(',', $aIdContacts).')';
$oStmt = $this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
foreach ($aFetch as $aItem)
{
if ($aItem && isset($aItem['id_prop'], $aItem['id_contact'], $aItem['type'], $aItem['value']))
{
$iId = (int) $aItem['id_contact'];
if (0 < $iId && isset($aContacts[$iId]))
{
$oProperty = new \RainLoop\Providers\PersonalAddressBook\Classes\Property();
$oProperty->IdProperty = (int) $aItem['id_prop'];
$oProperty->Type = (int) $aItem['type'];
$oProperty->TypeCustom = isset($aItem['type_custom']) ? (string) $aItem['type_custom'] : '';
$oProperty->Value = (string) $aItem['value'];
$oProperty->ValueClear = isset($aItem['value_clear']) ? (string) $aItem['value_clear'] : '';
$oProperty->Frec = isset($aItem['frec']) ? (int) $aItem['frec'] : 0;
$aContacts[$iId]->Properties[] = $oProperty;
}
}
}
}
unset($aFetch);
foreach ($aContacts as &$oItem)
{
$oItem->UpdateDependentValues();
}
return \array_values($aContacts);
}
unset($aFetch);
foreach ($aContacts as &$oItem)
{
$oItem->UpdateDependentValues();
}
return \array_values($aContacts);
}
}
}
@ -355,7 +446,7 @@ class MySqlPersonalAddressBookDriver
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FULLNAME
));
$sSql = 'SELECT `id_contact`, `id_prop`, `type`, `value` FROM `rainloop_pab_prop` '.
$sSql = 'SELECT `id_contact`, `id_prop`, `type`, `value` FROM `rainloop_pab_properties` '.
'WHERE id_user = :id_user AND `type` IN ('.$sTypes.') AND `value` LIKE :search ESCAPE \'=\'';
$aParams = array(
@ -364,7 +455,8 @@ class MySqlPersonalAddressBookDriver
':search' => array($this->specialConvertSearchValue($sSearch, '='), \PDO::PARAM_STR)
);
$sSql .= ' ORDER BY `frec` DESC LIMIT :limit';
$sSql .= ' ORDER BY `frec` DESC';
$sSql .= ' LIMIT :limit';
$aResult = array();
$aFirstResult = array();
@ -417,13 +509,12 @@ class MySqlPersonalAddressBookDriver
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER, PropertyType::FULLNAME
));
$sSql = 'SELECT `id_prop`, `id_contact`, `type`, `value` FROM `rainloop_pab_prop` '.
$sSql = 'SELECT `id_prop`, `id_contact`, `type`, `value` FROM `rainloop_pab_properties` '.
'WHERE id_user = :id_user AND `type` IN ('.$sTypes.') AND `id_contact` IN ('.\implode(',', $aIdContacts).')';
$oStmt = $this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
if ($oStmt)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
@ -504,10 +595,11 @@ class MySqlPersonalAddressBookDriver
/**
* @param \RainLoop\Account $oAccount
* @param array $aEmails
* @param bool $bCreateAuto = true
*
* @return bool
*/
public function IncFrec($oAccount, $aEmails)
public function IncFrec($oAccount, $aEmails, $bCreateAuto = true)
{
$iUserID = $this->getUserId($oAccount->ParentEmailHelper());
@ -531,41 +623,60 @@ class MySqlPersonalAddressBookDriver
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
));
$sSql = 'SELECT `value` FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `type` IN ('.$sTypes.')';
$oStmt = $this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
$aExists = array();
if ($oStmt)
$aEmailsToCreate = array();
$aEmailsToUpdate = array();
if ($bCreateAuto)
{
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
$sSql = 'SELECT `value` FROM `rainloop_pab_properties` WHERE id_user = :id_user AND `type` IN ('.$sTypes.')';
$oStmt = $this->prepareAndExecute($sSql, array(
':id_user' => array($iUserID, \PDO::PARAM_INT)
));
if ($oStmt)
{
foreach ($aFetch as $aItem)
$aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
if (\is_array($aFetch) && 0 < \count($aFetch))
{
if ($aItem && !empty($aItem['value']))
foreach ($aFetch as $aItem)
{
$aExists[] = \strtolower(\trim($aItem['value']));
if ($aItem && !empty($aItem['value']))
{
$aExists[] = \strtolower(\trim($aItem['value']));
}
}
}
}
$aEmailsToCreate = \array_filter($aEmailsObjects, function ($oItem) use ($aExists, &$aEmailsToUpdate) {
if ($oItem)
{
$sEmail = \strtolower(\trim($oItem->GetEmail()));
if (0 < \strlen($sEmail))
{
$aEmailsToUpdate[] = $sEmail;
return !\in_array($sEmail, $aExists);
}
}
return false;
});
}
else
{
foreach ($aEmailsObjects as $oItem)
{
if ($oItem)
{
$sEmail = \strtolower(\trim($oItem->GetEmail()));
if (0 < \strlen($sEmail))
{
$aEmailsToUpdate[] = $sEmail;
}
}
}
}
$aEmailsToUpdate = array();
$aEmailsToCreate = \array_filter($aEmailsObjects, function ($oItem) use ($aExists, &$aEmailsToUpdate) {
if ($oItem)
{
$sEmail = \strtolower(\trim($oItem->GetEmail()));
if (0 < \strlen($sEmail))
{
$aEmailsToUpdate[] = $sEmail;
return !\in_array($sEmail, $aExists);
}
}
return false;
});
unset($aEmails, $aEmailsObjects);
@ -603,7 +714,7 @@ class MySqlPersonalAddressBookDriver
}
}
$sSql = 'UPDATE `rainloop_pab_prop` SET `frec` = `frec` + 1 WHERE id_user = :id_user AND `type` IN ('.$sTypes;
$sSql = 'UPDATE `rainloop_pab_properties` SET `frec` = `frec` + 1 WHERE id_user = :id_user AND `type` IN ('.$sTypes;
$aEmailsQuoted = \array_map(function ($mItem) use ($self) {
return $self->quoteValue($mItem);
@ -622,6 +733,14 @@ class MySqlPersonalAddressBookDriver
':id_user' => array($iUserID, \PDO::PARAM_INT),
));
}
/**
* @return string
*/
public function Version()
{
return 'MySqlPersonalAddressBookDriver-v14';
}
/**
* @return bool
@ -640,16 +759,16 @@ class MySqlPersonalAddressBookDriver
`display_name` varchar(255) NOT NULL DEFAULT \'\',
`display_email` varchar(255) NOT NULL DEFAULT \'\',
`auto` int(1) UNSIGNED NOT NULL DEFAULT \'0\',
`shared` int(1) UNSIGNED NOT NULL DEFAULT \'0\',
`changed` int(11) UNSIGNED NOT NULL DEFAULT \'0\',
PRIMARY KEY(`id_contact`),
CONSTRAINT `id_user_fk_rainloop_pab_contacts` FOREIGN KEY (`id_user`)
REFERENCES `rainloop_users` (`id_user`) ON DELETE CASCADE ON UPDATE CASCADE
INDEX `id_user_index` (`id_user`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;',
// -- rainloop_pab_prop --
'CREATE TABLE IF NOT EXISTS `rainloop_pab_prop` (
// -- rainloop_pab_properties --
'CREATE TABLE IF NOT EXISTS `rainloop_pab_properties` (
`id_prop` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_contact` int(11) UNSIGNED NOT NULL,
@ -658,13 +777,13 @@ class MySqlPersonalAddressBookDriver
`type_custom` varchar(50) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL DEFAULT \'\',
`value` varchar(255) NOT NULL DEFAULT \'\',
`value_custom` varchar(255) NOT NULL DEFAULT \'\',
`auto` int(1) UNSIGNED NOT NULL DEFAULT \'0\',
`shared` int(1) UNSIGNED NOT NULL DEFAULT \'0\',
`frec` int(11) UNSIGNED NOT NULL DEFAULT \'0\',
PRIMARY KEY(`id_prop`),
INDEX `id_user_id_contact_index` (`id_user`, `id_contact`),
INDEX `id_user_value_index` (`id_user`, `value`),
CONSTRAINT `id_contact_fk_rainloop_pab_prop` FOREIGN KEY (`id_contact`)
REFERENCES `rainloop_pab_contacts` (`id_contact`) ON DELETE CASCADE ON UPDATE CASCADE
INDEX `id_user_index` (`id_user`),
INDEX `id_user_id_contact_index` (`id_user`, `id_contact`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;',
@ -676,9 +795,7 @@ class MySqlPersonalAddressBookDriver
`name` varchar(255) NOT NULL,
PRIMARY KEY(`id_tag`),
UNIQUE `id_user_name_unique` (`id_user`, `name`),
CONSTRAINT `id_user_fk_rainloop_pab_tags` FOREIGN KEY (`id_user`)
REFERENCES `rainloop_users` (`id_user`) ON DELETE CASCADE ON UPDATE CASCADE
INDEX `id_user_index` (`id_user`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;',
@ -688,10 +805,7 @@ class MySqlPersonalAddressBookDriver
`id_tag` int(11) UNSIGNED NOT NULL,
`id_contact` int(11) UNSIGNED NOT NULL,
CONSTRAINT `id_contact_fk_rainloop_tags_contacts` FOREIGN KEY (`id_contact`)
REFERENCES `rainloop_pab_contacts` (`id_contact`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `id_tag_fk_rainloop_tags_contacts` FOREIGN KEY (`id_tag`)
REFERENCES `rainloop_pab_tags` (`id_tag`) ON DELETE CASCADE ON UPDATE CASCADE
INDEX `id_contact_id_tag_index` (`id_contact`, `id_tag`)
) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;'
)));
@ -710,7 +824,7 @@ class MySqlPersonalAddressBookDriver
PropertyType::EMAIl_PERSONAL, PropertyType::EMAIl_BUSSINES, PropertyType::EMAIl_OTHER
));
$sSql = 'SELECT `value`, `frec` FROM `rainloop_pab_prop` WHERE id_user = :id_user AND `id_contact` = :id_contact AND `type` IN ('.$sTypes.')';
$sSql = 'SELECT `value`, `frec` FROM `rainloop_pab_properties` WHERE id_user = :id_user AND `id_contact` = :id_contact AND `type` IN ('.$sTypes.')';
$aParams = array(
':id_user' => array($iUserID, \PDO::PARAM_INT),
':id_contact' => array($iIdContact, \PDO::PARAM_INT)

View file

@ -30,17 +30,26 @@ interface PersonalAddressBookInterface
*/
public function DeleteContacts($oAccount, $aContactIds);
/**
* @param \RainLoop\Account $oAccount
* @param array $aTagsIds
*
* @return bool
*/
public function DeleteTags($oAccount, $aTagsIds);
/**
* @param \RainLoop\Account $oAccount
* @param int $iOffset = 0
* @param type $iLimit = 20
* @param string $sSearch = ''
* @param bool $bAutoOnly = false
* @param int $iResultCount = 0
*
* @return array
*/
public function GetContacts($oAccount,
$iOffset = 0, $iLimit = 20, $sSearch = '', $bAutoOnly = false);
$iOffset = 0, $iLimit = 20, $sSearch = '', $bAutoOnly = false, &$iResultCount = 0);
/**
* @param \RainLoop\Account $oAccount

View file

@ -151,11 +151,7 @@
<span data-bind="text: userUsageProc"></span>%
</span>
<div class="pull-right">
<div class="pagenator g-ui-user-select-none" data-bind="foreach: messageListPagenator">
<a class="page" data-bind="css: { 'current': current, 'custom': custom }, title: title">
<span class="pageNumber" data-bind="text: name"></span>
</a>
</div>
<!-- ko template: { name: 'Pagenator', data: messageListPagenator } --><!-- /ko -->
</div>
</div>
</div>

View file

@ -1,192 +1,192 @@
<div id="rl-resizer-right">
<div class="messageView" data-bind="css: {'message-selected': isMessageSelected}">
<div class="toolbar g-ui-user-select-none">
<div class="messageButtons btn-toolbar">
<div class="btn-group" data-placement="bottom" data-bind="visible: !$root.usePreviewPane(), tooltip: 'MESSAGE/BUTTON_CLOSE'">
<a class="btn buttonClose" data-bind="command: $root.closeMessage">
<i class="icon-remove"></i>
</a>
</div>
<div class="btn-group" data-placement="bottom" data-bind="visible: $root.isDraftFolder(), tooltip: 'MESSAGE/BUTTON_EDIT'">
<a class="btn btn-success buttonEdit" data-bind="command: $root.messageEditCommand">
<i class="icon-edit2 icon-white"></i>
</a>
</div>
<div class="btn-group" data-bind="visible: !$root.isDraftFolder()">
<a class="btn buttonReply" data-placement="bottom" data-bind="command: $root.replyCommand, tooltip: 'MESSAGE/BUTTON_REPLY'">
<i class="icon-reply"></i>
</a>
<a class="btn buttonReplyAll" data-placement="bottom" data-bind="command: $root.replyAllCommand, tooltip: 'MESSAGE/BUTTON_REPLY_ALL'">
<i class="icon-reply-all"></i>
</a>
<a class="btn buttonForward" data-placement="bottom" data-bind=" command: $root.forwardCommand, tooltip: 'MESSAGE/BUTTON_FORWARD'">
<i class="icon-forward"></i>
</a>
</div>
<div class="btn-group">
<a class="btn dropdown-toggle buttonMore" data-placement="right" data-toggle="dropdown" data-bind="command: $root.messageVisibilityCommand, tooltip: 'MESSAGE/BUTTON_MORE'">
<i class="icon-reorder"></i>
</a>
<ul class="dropdown-menu g-ui-menu">
<li class="e-item" data-bind="visible: !$root.isDraftFolder()">
<a class="e-link" data-bind="command: $root.forwardAsAttachmentCommand">
<i class="icon-reply"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
</a>
</li>
<li class="divider" data-bind="visible: !$root.isDraftFolder()"></li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="click: function () { if (message()) { message().viewPopupMessage(); }}">
<i class="icon-opennewwindow"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
</a>
</li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="click: function () { if (message()) { message().printMessage(); }} ">
<i class="icon-print"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/MENU_PRINT"></span>
</a>
</li>
<li class="divider"></li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="link: viewViewLink()">
<i class="icon-file-css"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/MENU_VIEW_ORIGINAL"></span>
</a>
</li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="link: viewDownloadLink()">
<i class="icon-download"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="b-content thm-message-view-background-color">
<div>
<div class="b-message-view-desc" data-bind="visible: !message() && '' === $root.messageError()">
<span class="i18n" data-i18n-text="MESSAGE/MESSAGE_VIEW_DESC"></span>
</div>
<div class="b-message-view-desc error" data-bind="visible: !message() && '' !== $root.messageError()">
<span class="text" data-bind="text: $root.messageError()"></span>
</div>
<div data-bind="visible: message">
<div class="messageItem" data-bind="css: viewLineAsCcc(), nano: true">
<div class="content g-scrollbox">
<div class="content-wrapper">
<div>
<span class="buttonUp" data-bind="click: scrollToTop">
<i class="icon-arrow-up--upload"></i>
</span>
<span class="buttonFull" data-bind="click: fullScreen">
<i class="icon-scaleup"></i>
</span>
<span class="buttonUnFull" data-bind="click: unFullScreen">
<i class="icon-scaledown"></i>
</span>
<div class="messageItemHeader">
<div>
<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }">
<div style="overflow: hidden;">
<div class="subjectParent" data-bind="event: { 'dblclick': toggleFullScreen }">
<span class="subject" data-bind="text: viewSubject, title: viewSubject"></span>
<span class="i18n emptySubjectText" data-i18n-text="MESSAGE/EMPTY_SUBJECT_TEXT"></span>
</div>
<div class="senderParent">
<div class="g-ui-user-select-none" style="float: left; cursor: pointer;" data-bind="click: function() { $root.showFullInfo(!$root.showFullInfo()); }">
<i class="icon-arrow-right" data-bind="css: $root.showFullInfo() ? 'icon-arrow-down' : 'icon-arrow-right'"></i>
</div>
<div class="informationShort" data-bind="event: { 'dblclick': toggleFullScreen }">
<span data-bind="visible: !$root.isDraftOrSentFolder()">
<span class="from" data-bind="html: viewFromShort, title: viewFrom"></span>
</span>
<span data-bind="visible: $root.isDraftOrSentFolder()">
<span class="i18n uiLabel labelTo" data-i18n-text="MESSAGE/LABEL_TO"></span>:
<span class="to" data-bind="html: viewToShort, title: viewTo"></span>
</span>
(<span class="date" data-bind="text: viewDate"></span>)
</div>
<div class="clearfix"></div>
</div>
</div>
<div class="informationFull" data-bind="visible: $root.showFullInfo()">
<div data-bind="visible: '' !== viewFrom()">
<span class="i18n uiLabel labelFrom" data-i18n-text="MESSAGE/LABEL_FROM"></span>:
&nbsp;
<span class="from" data-bind="text: viewFrom, title: viewFrom"></span>
</div>
<div data-bind="visible: '' !== viewTo()">
<span class="i18n uiLabel labelTo" data-i18n-text="MESSAGE/LABEL_TO"></span>:
&nbsp;
<span class="to" data-bind="text: viewTo, title: viewTo"></span>
</div>
<div data-bind="visible: '' !== viewCc()">
<span class="i18n uiLabel labelCc" data-i18n-text="MESSAGE/LABEL_CC"></span>:
&nbsp;
<span class="cc" data-bind="text: viewCc, title: viewCc"></span>
</div>
<div data-bind="visible: '' !== viewBcc()">
<span class="i18n uiLabel labelBcc" data-i18n-text="MESSAGE/LABEL_BCC"></span>:
&nbsp;
<span class="bcc" data-bind="text: viewBcc, title: viewBcc"></span>
</div>
<div>
<span class="i18n uiLabel labelBcc" data-i18n-text="MESSAGE/LABEL_DATE"></span>:
&nbsp;
<span class="date" data-bind="text: viewDate"></span>
&nbsp;
(<span class="date" data-bind="text: viewMoment"></span>)
</div>
</div>
</div>
</div>
<div class="line-loading e-strip-animation" data-bind="visible: $root.messageLoadingThrottle()"></div>
<div class="loading g-ui-min-height-300" data-bind="visible: $root.messageLoadingThrottle()">
<span class="i18n text" data-i18n-text="MESSAGE/MESSAGE_LOADING"></span><span class="textLoadingAnimationD1">.</span><span class="textLoadingAnimationD2">.</span><span class="textLoadingAnimationD3">.</span>
</div>
</div>
<div class="g-ui-min-height-300" data-bind="visible: !$root.messageLoadingThrottle()">
<div class="showImages" data-bind="visible: message() && message().hasImages(), click: function() { $root.showImages(message()); }">
<i class="icon-image"></i>
&nbsp;&nbsp;
<span class="i18n text" data-i18n-text="MESSAGE/BUTTON_SHOW_IMAGES"></span>
</div>
<div class="attachmentsPlace" data-bind="visible: message() && message().hasVisibleAttachments()">
<ul class="attachmentList" data-bind="foreach: message() ? message().attachments() : []">
<li class="attachmentItem" draggable="true" data-bind="visible: !isLinked, title: fileName, event: { 'dragstart': eventDragStart }">
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
<i class="attachmentIcon" data-bind="css: iconClass()"></i>
&nbsp;&nbsp;
<span class="attachmentName" data-bind="text: fileName"></span>
</div>
<div>
<a class="attachmentPreview magnificPopupImage pull-left"
data-bind="visible: isImage(), attr: {href: linkPreview(), title: fileName}" target="_blank">
<i class="icon-eye"></i>
</a>
<span class="attachmentSize pull-right" data-bind="text: friendlySize"></span>
</div>
</li>
</ul>
<hr />
</div>
<div class="bodyText g-ui-min-height-300" data-bind="initDom: messagesBodiesDom"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="rl-resizer-right">
<div class="messageView" data-bind="css: {'message-selected': isMessageSelected}">
<div class="toolbar g-ui-user-select-none">
<div class="messageButtons btn-toolbar">
<div class="btn-group" data-placement="bottom" data-bind="visible: !$root.usePreviewPane(), tooltip: 'MESSAGE/BUTTON_CLOSE'">
<a class="btn buttonClose" data-bind="command: $root.closeMessage">
<i class="icon-remove"></i>
</a>
</div>
<div class="btn-group" data-placement="bottom" data-bind="visible: $root.isDraftFolder(), tooltip: 'MESSAGE/BUTTON_EDIT'">
<a class="btn btn-success buttonEdit" data-bind="command: $root.messageEditCommand">
<i class="icon-edit2 icon-white"></i>
</a>
</div>
<div class="btn-group" data-bind="visible: !$root.isDraftFolder()">
<a class="btn buttonReply" data-placement="bottom" data-bind="command: $root.replyCommand, tooltip: 'MESSAGE/BUTTON_REPLY'">
<i class="icon-reply"></i>
</a>
<a class="btn buttonReplyAll" data-placement="bottom" data-bind="command: $root.replyAllCommand, tooltip: 'MESSAGE/BUTTON_REPLY_ALL'">
<i class="icon-reply-all"></i>
</a>
<a class="btn buttonForward" data-placement="bottom" data-bind=" command: $root.forwardCommand, tooltip: 'MESSAGE/BUTTON_FORWARD'">
<i class="icon-forward"></i>
</a>
</div>
<div class="btn-group">
<a class="btn dropdown-toggle buttonMore" data-placement="right" data-toggle="dropdown" data-bind="command: $root.messageVisibilityCommand, tooltip: 'MESSAGE/BUTTON_MORE'">
<i class="icon-reorder"></i>
</a>
<ul class="dropdown-menu g-ui-menu">
<li class="e-item" data-bind="visible: !$root.isDraftFolder()">
<a class="e-link" data-bind="command: $root.forwardAsAttachmentCommand">
<i class="icon-reply"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_FORWARD_AS_ATTACHMENT"></span>
</a>
</li>
<li class="divider" data-bind="visible: !$root.isDraftFolder()"></li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="click: function () { if (message()) { message().viewPopupMessage(); }}">
<i class="icon-opennewwindow"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/BUTTON_IN_NEW_WINDOW"></span>
</a>
</li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="click: function () { if (message()) { message().printMessage(); }} ">
<i class="icon-print"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/MENU_PRINT"></span>
</a>
</li>
<li class="divider"></li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="link: viewViewLink()">
<i class="icon-file-css"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/MENU_VIEW_ORIGINAL"></span>
</a>
</li>
<li class="e-item">
<a target="_blank" class="e-link" data-bind="link: viewDownloadLink()">
<i class="icon-download"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="MESSAGE/MENU_DOWNLOAD_ORIGINAL"></span>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="b-content thm-message-view-background-color">
<div>
<div class="b-message-view-desc" data-bind="visible: !message() && '' === $root.messageError()">
<span class="i18n" data-i18n-text="MESSAGE/MESSAGE_VIEW_DESC"></span>
</div>
<div class="b-message-view-desc error" data-bind="visible: !message() && '' !== $root.messageError()">
<span class="text" data-bind="text: $root.messageError()"></span>
</div>
<div data-bind="visible: message">
<div class="messageItem" data-bind="css: viewLineAsCcc(), nano: true">
<div class="content g-scrollbox">
<div class="content-wrapper">
<div>
<span class="buttonUp" data-bind="click: scrollToTop">
<i class="icon-arrow-up--upload"></i>
</span>
<span class="buttonFull" data-bind="click: fullScreen">
<i class="icon-scaleup"></i>
</span>
<span class="buttonUnFull" data-bind="click: unFullScreen">
<i class="icon-scaledown"></i>
</span>
<div class="messageItemHeader">
<div>
<img class="fromPic" data-bind="visible: viewUserPicVisible, attr: {'src': viewUserPic() }">
<div style="overflow: hidden;">
<div class="subjectParent" data-bind="event: { 'dblclick': toggleFullScreen }">
<span class="subject" data-bind="text: viewSubject, title: viewSubject"></span>
<span class="i18n emptySubjectText" data-i18n-text="MESSAGE/EMPTY_SUBJECT_TEXT"></span>
</div>
<div class="senderParent">
<div class="g-ui-user-select-none" style="float: left; cursor: pointer;" data-bind="click: function() { $root.showFullInfo(!$root.showFullInfo()); }">
<i class="icon-arrow-right" data-bind="css: $root.showFullInfo() ? 'icon-arrow-down' : 'icon-arrow-right'"></i>
</div>
<div class="informationShort" data-bind="event: { 'dblclick': toggleFullScreen }">
<span data-bind="visible: !$root.isDraftOrSentFolder()">
<span class="from" data-bind="html: viewFromShort, title: viewFrom"></span>
</span>
<span data-bind="visible: $root.isDraftOrSentFolder()">
<span class="i18n uiLabel labelTo" data-i18n-text="MESSAGE/LABEL_TO"></span>:
<span class="to" data-bind="html: viewToShort, title: viewTo"></span>
</span>
(<span class="date" data-bind="text: viewDate"></span>)
</div>
<div class="clearfix"></div>
</div>
</div>
<div class="informationFull" data-bind="visible: $root.showFullInfo()">
<div data-bind="visible: '' !== viewFrom()">
<span class="i18n uiLabel labelFrom" data-i18n-text="MESSAGE/LABEL_FROM"></span>:
&nbsp;
<span class="from" data-bind="text: viewFrom, title: viewFrom"></span>
</div>
<div data-bind="visible: '' !== viewTo()">
<span class="i18n uiLabel labelTo" data-i18n-text="MESSAGE/LABEL_TO"></span>:
&nbsp;
<span class="to" data-bind="text: viewTo, title: viewTo"></span>
</div>
<div data-bind="visible: '' !== viewCc()">
<span class="i18n uiLabel labelCc" data-i18n-text="MESSAGE/LABEL_CC"></span>:
&nbsp;
<span class="cc" data-bind="text: viewCc, title: viewCc"></span>
</div>
<div data-bind="visible: '' !== viewBcc()">
<span class="i18n uiLabel labelBcc" data-i18n-text="MESSAGE/LABEL_BCC"></span>:
&nbsp;
<span class="bcc" data-bind="text: viewBcc, title: viewBcc"></span>
</div>
<div>
<span class="i18n uiLabel labelBcc" data-i18n-text="MESSAGE/LABEL_DATE"></span>:
&nbsp;
<span class="date" data-bind="text: viewDate"></span>
&nbsp;
(<span class="date" data-bind="text: viewMoment"></span>)
</div>
</div>
</div>
</div>
<div class="line-loading e-strip-animation" data-bind="visible: $root.messageLoadingThrottle()"></div>
<div class="loading g-ui-min-height-300" data-bind="visible: $root.messageLoadingThrottle()">
<span class="i18n text" data-i18n-text="MESSAGE/MESSAGE_LOADING"></span><span class="textLoadingAnimationD1">.</span><span class="textLoadingAnimationD2">.</span><span class="textLoadingAnimationD3">.</span>
</div>
</div>
<div class="g-ui-min-height-300" data-bind="visible: !$root.messageLoadingThrottle()">
<div class="showImages" data-bind="visible: message() && message().hasImages(), click: function() { $root.showImages(message()); }">
<i class="icon-image"></i>
&nbsp;&nbsp;
<span class="i18n text" data-i18n-text="MESSAGE/BUTTON_SHOW_IMAGES"></span>
</div>
<div class="attachmentsPlace" data-bind="visible: message() && message().hasVisibleAttachments()">
<ul class="attachmentList" data-bind="foreach: message() ? message().attachments() : []">
<li class="attachmentItem" draggable="true" data-bind="visible: !isLinked, title: fileName, event: { 'dragstart': eventDragStart }">
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
<i class="attachmentIcon" data-bind="css: iconClass()"></i>
&nbsp;&nbsp;
<span class="attachmentName" data-bind="text: fileName"></span>
</div>
<div>
<a class="attachmentPreview magnificPopupImage pull-left"
data-bind="visible: isImage(), attr: {href: linkPreview(), title: fileName}" target="_blank">
<i class="icon-eye"></i>
</a>
<span class="attachmentSize pull-right" data-bind="text: friendlySize"></span>
</div>
</li>
</ul>
<hr />
</div>
<div class="bodyText g-ui-min-height-300" data-bind="initDom: messagesBodiesDom"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,5 @@
<div class="e-pagenator g-ui-user-select-none" data-bind="foreach: $data">
<a class="e-page" data-bind="css: { 'current': current, 'custom': custom }, title: title">
<span class="e-page-number" data-bind="text: name"></span>
</a>
</div>

View file

@ -54,6 +54,11 @@
</div>
</div>
</div>
<div class="b-list-footer-toopbar">
<div class="pull-right footer-pager">
<!-- ko template: { name: 'Pagenator', data: contactsPagenator } --><!-- /ko -->
</div>
</div>
<div class="b-view-content" data-bind="nano: true">
<div class="content g-scrollbox">
<div class="content-wrapper">

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Searching..."
LEGEND_CONTACTS = "Contacts"
SEARCH_INPUT_PLACEHOLDER = "Search"
BUTTON_ADD_CONTACT = "Add Contact"
BUTTON_CREATE_CONTACT = "Create Contact"
BUTTON_UPDATE_CONTACT = "Update Contact"
BUTTON_CREATE_CONTACT = "Create"
BUTTON_UPDATE_CONTACT = "Update"
LIST_LOADING = "Loading"
EMPTY_LIST = "No contacts here"
EMPTY_SEARCH = "No contacts found"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Buscando..."
LEGEND_CONTACTS = "Contactos"
SEARCH_INPUT_PLACEHOLDER = "Buscar"
BUTTON_ADD_CONTACT = "Añadir Contacto"
BUTTON_CREATE_CONTACT = "Crear Contacto"
BUTTON_UPDATE_CONTACT = "Actualizar Contacto"
BUTTON_CREATE_CONTACT = "Crear"
BUTTON_UPDATE_CONTACT = "Actualizar"
LIST_LOADING = "Cargando"
EMPTY_LIST = "No hay contactos aquí"
EMPTY_SEARCH = "No se encontraron contactos"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Recherche..."
LEGEND_CONTACTS = "Contacts"
SEARCH_INPUT_PLACEHOLDER = "Recherche"
BUTTON_ADD_CONTACT = "Ajouter un contact"
BUTTON_CREATE_CONTACT = "Créer un contact"
BUTTON_UPDATE_CONTACT = "Modifier un contact"
BUTTON_CREATE_CONTACT = "Créer"
BUTTON_UPDATE_CONTACT = "Modifier"
LIST_LOADING = "Chargement"
EMPTY_LIST = "Aucun contact"
EMPTY_SEARCH = "Aucun contact trouvé"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Leita..."
LEGEND_CONTACTS = "Tengiliðir"
SEARCH_INPUT_PLACEHOLDER = "Leita"
BUTTON_ADD_CONTACT = "Bæta við tengilið"
BUTTON_CREATE_CONTACT = "Búa til tengilið"
BUTTON_UPDATE_CONTACT = "Uppfæra tengilið"
BUTTON_CREATE_CONTACT = "Búa til"
BUTTON_UPDATE_CONTACT = "Uppfæra"
LIST_LOADING = "Hleð"
EMPTY_LIST = "Engir tengiliðir hér"
EMPTY_SEARCH = "Engir tengiliðir fundust"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Meklē..."
LEGEND_CONTACTS = "Kontakti"
SEARCH_INPUT_PLACEHOLDER = "Meklēt"
BUTTON_ADD_CONTACT = "Pievienot kontaktu"
BUTTON_CREATE_CONTACT = "Izveidot kontaktu"
BUTTON_UPDATE_CONTACT = "Atjaunot kontaktu"
BUTTON_CREATE_CONTACT = "Izveidot"
BUTTON_UPDATE_CONTACT = "Atjaunot"
LIST_LOADING = "Lādējās"
EMPTY_LIST = "Nav kontaktu"
EMPTY_SEARCH = "Kontakti nav atrasti"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Zoeken..."
LEGEND_CONTACTS = "Contacten"
SEARCH_INPUT_PLACEHOLDER = "Zoeken"
BUTTON_ADD_CONTACT = "Contact Toevoegen"
BUTTON_CREATE_CONTACT = "Nieuw Contact"
BUTTON_UPDATE_CONTACT = "Update Contact"
BUTTON_CREATE_CONTACT = "Nieuw"
BUTTON_UPDATE_CONTACT = "Update"
LIST_LOADING = "Laden"
EMPTY_LIST = "Geen contacten hier"
EMPTY_SEARCH = "Geen contacten gevonden"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Wyszukiwanie..."
LEGEND_CONTACTS = "Kontakty"
SEARCH_INPUT_PLACEHOLDER = "Szukaj"
BUTTON_ADD_CONTACT = "Dodaj Kontakt"
BUTTON_CREATE_CONTACT = "Utwórz Kontakt"
BUTTON_UPDATE_CONTACT = "Aktualizuj Kontakt"
BUTTON_CREATE_CONTACT = "Utwórz"
BUTTON_UPDATE_CONTACT = "Aktualizuj"
LIST_LOADING = "Ładowanie"
EMPTY_LIST = "Brak kontaktów"
EMPTY_SEARCH = "Nie znaleziono żadnych kontaktów"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Procurando..."
LEGEND_CONTACTS = "Contatos"
SEARCH_INPUT_PLACEHOLDER = "Procurar"
BUTTON_ADD_CONTACT = "Adicionar Contatos"
BUTTON_CREATE_CONTACT = "Criar Contatos"
BUTTON_UPDATE_CONTACT = "Atualizar Contatos"
BUTTON_CREATE_CONTACT = "Criar"
BUTTON_UPDATE_CONTACT = "Atualizar"
LIST_LOADING = "Carregando"
EMPTY_LIST = "Nenhum contato aqui"
EMPTY_SEARCH = "Nenhum contato encontrado"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Procurando..."
LEGEND_CONTACTS = "Contatos"
SEARCH_INPUT_PLACEHOLDER = "Procurar"
BUTTON_ADD_CONTACT = "Adicionar Contatos"
BUTTON_CREATE_CONTACT = "Criar Contatos"
BUTTON_UPDATE_CONTACT = "Atualizar Contatos"
BUTTON_CREATE_CONTACT = "Criar"
BUTTON_UPDATE_CONTACT = "Atualizar"
LIST_LOADING = "Carregando"
EMPTY_LIST = "Nenhum contato aqui"
EMPTY_SEARCH = "Nenhum contato encontrado"

View file

@ -129,8 +129,8 @@ SEARCHING_DESC = "Поиск..."
LEGEND_CONTACTS = "Контакты"
SEARCH_INPUT_PLACEHOLDER = "Поиск контактов"
BUTTON_ADD_CONTACT = "Добавить контакт"
BUTTON_CREATE_CONTACT = "Сохранить контакт"
BUTTON_UPDATE_CONTACT = "Обновить контакт"
BUTTON_CREATE_CONTACT = "Сохранить"
BUTTON_UPDATE_CONTACT = "Обновить"
LIST_LOADING = "Загрузка"
EMPTY_LIST = "Нет контактов"
EMPTY_SEARCH = "Контакты не найдены"

View file

@ -5736,10 +5736,29 @@ html.no-rgba .modal {
vertical-align: top;
text-align: left;
}
.g-ui-height-100proc {
height: 100%;
}
.g-ui-resizable-delimiter-highlight {
border: none;
border-right: 6px solid #aaa;
}
.e-pagenator .e-page {
display: inline-block;
color: #999;
text-decoration: none;
font-size: 24px;
padding: 3px;
cursor: pointer;
}
.e-pagenator .e-page:hover .e-page-number {
color: #555;
}
.e-pagenator .e-page.current .e-page-number {
font-size: 28px;
color: #333;
border-bottom: 2px solid #000;
}
html.rgba .g-ui-resizable-delimiter-highlight {
border-right-color: rgba(0, 0, 0, 0.2);
}
@ -6707,22 +6726,6 @@ html.rl-no-preview-pane .messageList.message-selected {
.messageList.hideMessageListCheckbox .sidebarParent {
margin-right: 10px !important;
}
.messageList .pagenator .page {
display: inline-block;
color: #999;
text-decoration: none;
font-size: 24px;
padding: 3px;
cursor: pointer;
}
.messageList .pagenator .page:hover .pageNumber {
color: #555;
}
.messageList .pagenator .page.current .pageNumber {
font-size: 28px;
color: #333;
border-bottom: 2px solid #000;
}
.draggablePlace {
z-index: 10002;
color: #fff;
@ -6966,6 +6969,12 @@ html.rl-no-preview-pane .messageView.message-selected {
color: #000;
font-family: arial, sans-serif;
}
.messageView .b-content .messageItem .bodyText .b-text-part div[data-x-div-type=html] {
height: 100%;
}
.messageView .b-content .messageItem .bodyText .b-text-part div[data-x-div-type=html] div[data-x-div-type=html] {
height: 100%;
}
.messageView .b-content .messageItem .bodyText .b-text-part a {
color: blue;
text-decoration: underline;
@ -7108,20 +7117,35 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
padding: 0;
height: 45px;
text-align: center;
width: 250px;
width: 270px;
-webkit-box-shadow: inset 0 -1px 0 #cccccc;
-moz-box-shadow: inset 0 -1px 0 #cccccc;
box-shadow: inset 0 -1px 0 #cccccc;
}
.b-contacts-content.modal .b-list-toopbar .e-search {
margin-top: 7px;
width: 245px;
}
.b-contacts-content.modal .b-list-footer-toopbar {
position: absolute;
left: 0;
bottom: 0;
height: 105px;
width: 270px;
background-color: #eee;
-webkit-box-shadow: inset 0 1px 0 #cccccc;
-moz-box-shadow: inset 0 1px 0 #cccccc;
box-shadow: inset 0 1px 0 #cccccc;
}
.b-contacts-content.modal .b-list-footer-toopbar .footer-pager {
padding: 8px 10px 0 0;
}
.b-contacts-content.modal .b-list-content {
position: absolute;
top: 45px;
bottom: 60px;
bottom: 105px;
left: 0;
width: 250px;
width: 270px;
overflow: hidden;
overflow-y: auto;
}
@ -7135,6 +7159,9 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
font-size: 14px;
line-height: 13px;
background-color: #fff;
-webkit-box-shadow: inset 0 -1px 0 #cccccc;
-moz-box-shadow: inset 0 -1px 0 #cccccc;
box-shadow: inset 0 -1px 0 #cccccc;
}
.b-contacts-content.modal .b-list-content .listEmptyList,
.b-contacts-content.modal .b-list-content .listEmptyListLoading,
@ -7232,7 +7259,7 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
position: absolute;
top: 0;
bottom: 60px;
left: 250px;
left: 270px;
right: 0;
overflow: hidden;
overflow-y: auto;

File diff suppressed because one or more lines are too long

View file

@ -3581,10 +3581,29 @@ html.no-rgba .modal {
vertical-align: top;
text-align: left;
}
.g-ui-height-100proc {
height: 100%;
}
.g-ui-resizable-delimiter-highlight {
border: none;
border-right: 6px solid #aaa;
}
.e-pagenator .e-page {
display: inline-block;
color: #999;
text-decoration: none;
font-size: 24px;
padding: 3px;
cursor: pointer;
}
.e-pagenator .e-page:hover .e-page-number {
color: #555;
}
.e-pagenator .e-page.current .e-page-number {
font-size: 28px;
color: #333;
border-bottom: 2px solid #000;
}
html.rgba .g-ui-resizable-delimiter-highlight {
border-right-color: rgba(0, 0, 0, 0.2);
}
@ -4552,22 +4571,6 @@ html.rl-no-preview-pane .messageList.message-selected {
.messageList.hideMessageListCheckbox .sidebarParent {
margin-right: 10px !important;
}
.messageList .pagenator .page {
display: inline-block;
color: #999;
text-decoration: none;
font-size: 24px;
padding: 3px;
cursor: pointer;
}
.messageList .pagenator .page:hover .pageNumber {
color: #555;
}
.messageList .pagenator .page.current .pageNumber {
font-size: 28px;
color: #333;
border-bottom: 2px solid #000;
}
.draggablePlace {
z-index: 10002;
color: #fff;
@ -4811,6 +4814,12 @@ html.rl-no-preview-pane .messageView.message-selected {
color: #000;
font-family: arial, sans-serif;
}
.messageView .b-content .messageItem .bodyText .b-text-part div[data-x-div-type=html] {
height: 100%;
}
.messageView .b-content .messageItem .bodyText .b-text-part div[data-x-div-type=html] div[data-x-div-type=html] {
height: 100%;
}
.messageView .b-content .messageItem .bodyText .b-text-part a {
color: blue;
text-decoration: underline;
@ -4953,20 +4962,35 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
padding: 0;
height: 45px;
text-align: center;
width: 250px;
width: 270px;
-webkit-box-shadow: inset 0 -1px 0 #cccccc;
-moz-box-shadow: inset 0 -1px 0 #cccccc;
box-shadow: inset 0 -1px 0 #cccccc;
}
.b-contacts-content.modal .b-list-toopbar .e-search {
margin-top: 7px;
width: 245px;
}
.b-contacts-content.modal .b-list-footer-toopbar {
position: absolute;
left: 0;
bottom: 0;
height: 105px;
width: 270px;
background-color: #eee;
-webkit-box-shadow: inset 0 1px 0 #cccccc;
-moz-box-shadow: inset 0 1px 0 #cccccc;
box-shadow: inset 0 1px 0 #cccccc;
}
.b-contacts-content.modal .b-list-footer-toopbar .footer-pager {
padding: 8px 10px 0 0;
}
.b-contacts-content.modal .b-list-content {
position: absolute;
top: 45px;
bottom: 60px;
bottom: 105px;
left: 0;
width: 250px;
width: 270px;
overflow: hidden;
overflow-y: auto;
}
@ -4980,6 +5004,9 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
font-size: 14px;
line-height: 13px;
background-color: #fff;
-webkit-box-shadow: inset 0 -1px 0 #cccccc;
-moz-box-shadow: inset 0 -1px 0 #cccccc;
box-shadow: inset 0 -1px 0 #cccccc;
}
.b-contacts-content.modal .b-list-content .listEmptyList,
.b-contacts-content.modal .b-list-content .listEmptyListLoading,
@ -5077,7 +5104,7 @@ html.rl-message-fullscreen .messageView .b-content .buttonFull {
position: absolute;
top: 0;
bottom: 60px;
left: 250px;
left: 270px;
right: 0;
overflow: hidden;
overflow-y: auto;

View file

@ -154,6 +154,12 @@ Consts.DataImages = {};
*/
Consts.Defaults.MessagesPerPage = 20;
/**
* @const
* @type {number}
*/
Consts.Defaults.ContactsPerPage = 20;
/**
* @const
* @type {Array}
@ -1922,6 +1928,120 @@ Utils.resizeAndCrop = function (sUrl, iValue, fCallback)
oTempImg.src = sUrl;
};
Utils.computedPagenatorHelper = function (koCurrentPage, koPageCount) {
return function() {
var
iPrev = 0,
iNext = 0,
iLimit = 2,
aResult = [],
iCurrentPage = koCurrentPage(),
iPageCount = koPageCount(),
/**
* @param {number} iIndex
* @param {boolean=} bPush
* @param {string=} sCustomName
*/
fAdd = function (iIndex, bPush, sCustomName) {
var oData = {
'current': iIndex === iCurrentPage,
'name': Utils.isUnd(sCustomName) ? iIndex.toString() : sCustomName.toString(),
'custom': Utils.isUnd(sCustomName) ? false : true,
'title': Utils.isUnd(sCustomName) ? '' : iIndex.toString(),
'value': iIndex.toString()
};
if (Utils.isUnd(bPush) ? true : !!bPush)
{
aResult.push(oData);
}
else
{
aResult.unshift(oData);
}
}
;
if (1 < iPageCount || (0 < iPageCount && iPageCount < iCurrentPage))
// if (0 < iPageCount && 0 < iCurrentPage)
{
if (iPageCount < iCurrentPage)
{
fAdd(iPageCount);
iPrev = iPageCount;
iNext = iPageCount;
}
else
{
if (3 >= iCurrentPage || iPageCount - 2 <= iCurrentPage)
{
iLimit += 2;
}
fAdd(iCurrentPage);
iPrev = iCurrentPage;
iNext = iCurrentPage;
}
while (0 < iLimit) {
iPrev -= 1;
iNext += 1;
if (0 < iPrev)
{
fAdd(iPrev, false);
iLimit--;
}
if (iPageCount >= iNext)
{
fAdd(iNext, true);
iLimit--;
}
else if (0 >= iPrev)
{
break;
}
}
if (3 === iPrev)
{
fAdd(2, false);
}
else if (3 < iPrev)
{
fAdd(Math.round((iPrev - 1) / 2), false, '...');
}
if (iPageCount - 2 === iNext)
{
fAdd(iPageCount - 1, true);
}
else if (iPageCount - 2 > iNext)
{
fAdd(Math.round((iPageCount + iNext) / 2), true, '...');
}
// first and last
if (1 < iPrev)
{
fAdd(1, false);
}
if (iPageCount > iNext)
{
fAdd(iPageCount, true);
}
}
return aResult;
};
};
// Base64 encode / decode
// http://www.webtoolkit.info/

File diff suppressed because one or more lines are too long

View file

@ -154,6 +154,12 @@ Consts.DataImages = {};
*/
Consts.Defaults.MessagesPerPage = 20;
/**
* @const
* @type {number}
*/
Consts.Defaults.ContactsPerPage = 20;
/**
* @const
* @type {Array}
@ -1922,6 +1928,120 @@ Utils.resizeAndCrop = function (sUrl, iValue, fCallback)
oTempImg.src = sUrl;
};
Utils.computedPagenatorHelper = function (koCurrentPage, koPageCount) {
return function() {
var
iPrev = 0,
iNext = 0,
iLimit = 2,
aResult = [],
iCurrentPage = koCurrentPage(),
iPageCount = koPageCount(),
/**
* @param {number} iIndex
* @param {boolean=} bPush
* @param {string=} sCustomName
*/
fAdd = function (iIndex, bPush, sCustomName) {
var oData = {
'current': iIndex === iCurrentPage,
'name': Utils.isUnd(sCustomName) ? iIndex.toString() : sCustomName.toString(),
'custom': Utils.isUnd(sCustomName) ? false : true,
'title': Utils.isUnd(sCustomName) ? '' : iIndex.toString(),
'value': iIndex.toString()
};
if (Utils.isUnd(bPush) ? true : !!bPush)
{
aResult.push(oData);
}
else
{
aResult.unshift(oData);
}
}
;
if (1 < iPageCount || (0 < iPageCount && iPageCount < iCurrentPage))
// if (0 < iPageCount && 0 < iCurrentPage)
{
if (iPageCount < iCurrentPage)
{
fAdd(iPageCount);
iPrev = iPageCount;
iNext = iPageCount;
}
else
{
if (3 >= iCurrentPage || iPageCount - 2 <= iCurrentPage)
{
iLimit += 2;
}
fAdd(iCurrentPage);
iPrev = iCurrentPage;
iNext = iCurrentPage;
}
while (0 < iLimit) {
iPrev -= 1;
iNext += 1;
if (0 < iPrev)
{
fAdd(iPrev, false);
iLimit--;
}
if (iPageCount >= iNext)
{
fAdd(iNext, true);
iLimit--;
}
else if (0 >= iPrev)
{
break;
}
}
if (3 === iPrev)
{
fAdd(2, false);
}
else if (3 < iPrev)
{
fAdd(Math.round((iPrev - 1) / 2), false, '...');
}
if (iPageCount - 2 === iNext)
{
fAdd(iPageCount - 1, true);
}
else if (iPageCount - 2 > iNext)
{
fAdd(Math.round((iPageCount + iNext) / 2), true, '...');
}
// first and last
if (1 < iPrev)
{
fAdd(1, false);
}
if (iPageCount > iNext)
{
fAdd(iPageCount, true);
}
}
return aResult;
};
};
// Base64 encode / decode
// http://www.webtoolkit.info/
@ -9060,10 +9180,19 @@ function PopupsContactsViewModel()
;
this.search = ko.observable('');
this.contactsCount = ko.observable(0);
this.contacts = ko.observableArray([]);
this.contacts.loading = ko.observable(false).extend({'throttle': 200});
this.currentContact = ko.observable(null);
this.contactsPage = ko.observable(1);
this.contactsPageCount = ko.computed(function () {
var iPage = Math.ceil(this.contactsCount() / Consts.Defaults.ContactsPerPage);
return 0 >= iPage ? 1 : iPage;
}, this);
this.contactsPagenator = ko.computed(Utils.computedPagenatorHelper(this.contactsPage, this.contactsPageCount));
this.emptySelection = ko.observable(true);
this.viewClearSearch = ko.observable(false);
@ -9260,28 +9389,24 @@ function PopupsContactsViewModel()
var bV = this.viewHasNonEmptyRequaredProperties();
return !this.viewSaving() && bV;
});
this.bDropPageAfterDelete = false;
}
Utils.extendAsViewModel('PopupsContactsViewModel', PopupsContactsViewModel);
PopupsContactsViewModel.prototype.addNewEmail = function ()
{
// if (0 === this.viewPropertiesEmailsEmpty().length)
// {
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.EmailPersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
// }
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.EmailPersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
};
PopupsContactsViewModel.prototype.addNewPhone = function ()
{
// if (0 === this.viewPropertiesPhonesEmpty().length)
// {
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.PhonePersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
// }
var oItem = new ContactPropertyModel(Enums.ContactPropertyType.PhonePersonal, '');
oItem.focused(true);
this.viewProperties.push(oItem);
};
PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = function ()
@ -9290,6 +9415,7 @@ PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = func
self = this,
oKoContacts = this.contacts,
oCurrentContact = this.currentContact(),
iCount = this.contacts().length,
aContacts = this.contactsCheckedOrSelected()
;
@ -9304,8 +9430,14 @@ PopupsContactsViewModel.prototype.removeCheckedOrSelectedContactsFromList = func
}
oContact.deleted(true);
iCount--;
});
if (iCount <= 0)
{
this.bDropPageAfterDelete = true;
}
_.delay(function () {
_.each(aContacts, function (oContact) {
@ -9337,13 +9469,13 @@ PopupsContactsViewModel.prototype.deleteResponse = function (sResult, oData)
{
if (500 < (Enums.StorageResultType.Success === sResult && oData && oData.Time ? Utils.pInt(oData.Time) : 0))
{
this.reloadContactList();
this.reloadContactList(this.bDropPageAfterDelete);
}
else
{
_.delay((function (self) {
return function () {
self.reloadContactList();
self.reloadContactList(self.bDropPageAfterDelete);
};
}(this)), 500);
}
@ -9396,12 +9528,31 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact)
this.viewProperties(aList);
};
PopupsContactsViewModel.prototype.reloadContactList = function ()
/**
* @param {boolean=} bDropPagePosition = false
*/
PopupsContactsViewModel.prototype.reloadContactList = function (bDropPagePosition)
{
var self = this;
var
self = this,
iOffset = (this.contactsPage() - 1) * Consts.Defaults.ContactsPerPage
;
this.bDropPageAfterDelete = false;
if (Utils.isUnd(bDropPagePosition) ? false : !!bDropPagePosition)
{
this.contactsPage(1);
iOffset = 0;
}
this.contacts.loading(true);
RL.remote().contacts(function (sResult, oData) {
var aList = [];
var
iCount = 0,
aList = []
;
if (Enums.StorageResultType.Success === sResult && oData && oData.Result && oData.Result.List)
{
if (Utils.isNonEmptyArray(oData.Result.List))
@ -9412,9 +9563,14 @@ PopupsContactsViewModel.prototype.reloadContactList = function ()
});
aList = _.compact(aList);
iCount = Utils.pInt(oData.Result.Count);
iCount = 0 < iCount ? iCount : 0;
}
}
self.contactsCount(iCount);
self.contacts(aList);
self.viewClearSearch('' !== self.search());
self.contacts.loading(false);
@ -9424,7 +9580,7 @@ PopupsContactsViewModel.prototype.reloadContactList = function ()
self.contacts.setSelectedByUid('' + self.viewID());
}
}, 0, 20, this.search());
}, iOffset, Consts.Defaults.ContactsPerPage, this.search());
};
PopupsContactsViewModel.prototype.onBuild = function (oDom)
@ -9434,6 +9590,8 @@ PopupsContactsViewModel.prototype.onBuild = function (oDom)
this.selector.init(this.oContentVisible, this.oContentScrollable);
var self = this;
ko.computed(function () {
var
bModalVisibility = this.modalVisibility(),
@ -9441,12 +9599,23 @@ PopupsContactsViewModel.prototype.onBuild = function (oDom)
;
this.selector.useKeyboard(bModalVisibility && bUseKeyboardShortcuts);
}, this).extend({'notify': 'always'});
oDom
.on('click', '.e-pagenator .e-page', function () {
var oPage = ko.dataFor(this);
if (oPage)
{
self.contactsPage(Utils.pInt(oPage.value));
self.reloadContactList();
}
})
;
};
PopupsContactsViewModel.prototype.onShow = function ()
{
kn.routeOff();
this.reloadContactList();
this.reloadContactList(true);
};
PopupsContactsViewModel.prototype.onHide = function ()
@ -10404,118 +10573,7 @@ function MailBoxMessageListViewModel()
return '' === sValue ? '' : Utils.i18n('MESSAGE_LIST/SEARCH_RESULT_FOR', {'SEARCH': sValue});
});
this.messageListPagenator = ko.computed(function () {
var
iPrev = 0,
iNext = 0,
iLimit = 2,
aResult = [],
iCurrentPage = oData.messageListPage(),
iPageCount = oData.messageListPageCount(),
/**
* @param {number} iIndex
* @param {boolean=} bPush
* @param {string=} sCustomName
*/
fAdd = function (iIndex, bPush, sCustomName) {
var oData = {
'current': iIndex === iCurrentPage,
'name': Utils.isUnd(sCustomName) ? iIndex.toString() : sCustomName.toString(),
'custom': Utils.isUnd(sCustomName) ? false : true,
'title': Utils.isUnd(sCustomName) ? '' : iIndex.toString(),
'value': iIndex.toString()
};
if (Utils.isUnd(bPush) ? true : !!bPush)
{
aResult.push(oData);
}
else
{
aResult.unshift(oData);
}
}
;
if (1 < iPageCount || (0 < iPageCount && iPageCount < iCurrentPage))
// if (0 < iPageCount && 0 < iCurrentPage)
{
if (iPageCount < iCurrentPage)
{
fAdd(iPageCount);
iPrev = iPageCount;
iNext = iPageCount;
}
else
{
if (3 >= iCurrentPage || iPageCount - 2 <= iCurrentPage)
{
iLimit += 2;
}
fAdd(iCurrentPage);
iPrev = iCurrentPage;
iNext = iCurrentPage;
}
while (0 < iLimit) {
iPrev -= 1;
iNext += 1;
if (0 < iPrev)
{
fAdd(iPrev, false);
iLimit--;
}
if (iPageCount >= iNext)
{
fAdd(iNext, true);
iLimit--;
}
else if (0 >= iPrev)
{
break;
}
}
if (3 === iPrev)
{
fAdd(2, false);
}
else if (3 < iPrev)
{
fAdd(Math.round((iPrev - 1) / 2), false, '...');
}
if (iPageCount - 2 === iNext)
{
fAdd(iPageCount - 1, true);
}
else if (iPageCount - 2 > iNext)
{
fAdd(Math.round((iPageCount + iNext) / 2), true, '...');
}
// first and last
if (1 < iPrev)
{
fAdd(1, false);
}
if (iPageCount > iNext)
{
fAdd(iPageCount, true);
}
}
return aResult;
}, this);
this.messageListPagenator = ko.computed(Utils.computedPagenatorHelper(oData.messageListPage, oData.messageListPageCount));
this.checkAll = ko.computed({
'read': function () {
@ -11152,7 +11210,7 @@ MailBoxMessageListViewModel.prototype.onBuild = function (oDom)
});
oDom
.on('click', '.pagenator .page', function () {
.on('click', '.e-pagenator .e-page', function () {
var oPage = ko.dataFor(this);
if (oPage)
{
@ -12834,7 +12892,7 @@ function WebMailDataStorage()
this.messageListPageCount = ko.computed(function () {
var iPage = Math.ceil(this.messageListCount() / this.messagesPerPage());
return 0 === iPage ? 1 : iPage;
return 0 >= iPage ? 1 : iPage;
}, this);
this.mainMessageListSearch = ko.computed({

File diff suppressed because one or more lines are too long