mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-01-26 08:38:22 +08:00
Add pagenator to contact list
Move contacts from plugin back to core
This commit is contained in:
parent
daa958ed44
commit
0ec4fc89d4
44 changed files with 1250 additions and 889 deletions
|
@ -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"/>
|
||||
|
|
|
@ -10,6 +10,12 @@ Consts.DataImages = {};
|
|||
*/
|
||||
Consts.Defaults.MessagesPerPage = 20;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
Consts.Defaults.ContactsPerPage = 20;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {Array}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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.
|
|
@ -1 +0,0 @@
|
|||
Personal addressbook plugin (MySQL)
|
|
@ -1 +0,0 @@
|
|||
1.0
|
|
@ -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('')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 */;
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)'),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = '';
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>:
|
||||
|
||||
<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>:
|
||||
|
||||
<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>:
|
||||
|
||||
<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>:
|
||||
|
||||
<span class="bcc" data-bind="text: viewBcc, title: viewBcc"></span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="i18n uiLabel labelBcc" data-i18n-text="MESSAGE/LABEL_DATE"></span>:
|
||||
|
||||
<span class="date" data-bind="text: viewDate"></span>
|
||||
|
||||
(<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>:
|
||||
|
||||
<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>:
|
||||
|
||||
<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>:
|
||||
|
||||
<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>:
|
||||
|
||||
<span class="bcc" data-bind="text: viewBcc, title: viewBcc"></span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="i18n uiLabel labelBcc" data-i18n-text="MESSAGE/LABEL_DATE"></span>:
|
||||
|
||||
<span class="date" data-bind="text: viewDate"></span>
|
||||
|
||||
(<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
|
5
rainloop/v/0.0.0/app/templates/Views/Pagenator.html
Normal file
5
rainloop/v/0.0.0/app/templates/Views/Pagenator.html
Normal 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>
|
|
@ -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">
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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é"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 = "Контакты не найдены"
|
||||
|
|
|
@ -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;
|
||||
|
|
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
2
rainloop/v/0.0.0/static/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -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;
|
||||
|
|
|
@ -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/
|
||||
|
|
6
rainloop/v/0.0.0/static/js/admin.min.js
vendored
6
rainloop/v/0.0.0/static/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -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({
|
||||
|
|
14
rainloop/v/0.0.0/static/js/app.min.js
vendored
14
rainloop/v/0.0.0/static/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue