diff --git a/dev/Common/Enums.js b/dev/Common/Enums.js index fc27403a4..df28bd75a 100644 --- a/dev/Common/Enums.js +++ b/dev/Common/Enums.js @@ -296,6 +296,8 @@ Enums.ContactPropertyType = { 'Phone': 31, 'Web': 32, + 'Birthday': 40, + 'Facebook': 90, 'Skype': 91, 'GitHub': 92, diff --git a/dev/Models/ContactPropertyModel.js b/dev/Models/ContactPropertyModel.js index 4d0b98606..87c1053fb 100644 --- a/dev/Models/ContactPropertyModel.js +++ b/dev/Models/ContactPropertyModel.js @@ -22,4 +22,9 @@ function ContactPropertyModel(iType, sTypeStr, sValue, bFocused, sPlaceholder) var sPlaceholder = this.placeholder(); return sPlaceholder ? Utils.i18n(sPlaceholder) : ''; }, this); + + this.largeValue = ko.computed(function () { + return Enums.ContactPropertyType.Note === this.type(); + }, this); + } diff --git a/dev/Styles/Contacts.less b/dev/Styles/Contacts.less index 42a11de2f..98c83732d 100644 --- a/dev/Styles/Contacts.less +++ b/dev/Styles/Contacts.less @@ -255,7 +255,7 @@ -webkit-overflow-scrolling: touch; } - .contactValueStatic { + .contactValueStatic, .contactValueLargeStatic, .contactValueTextAreaStatic { height: 20px; line-height: 20px; font-size: 18px; @@ -266,11 +266,11 @@ } &.read-only { - .contactValueStatic { + .contactValueStatic, .contactValueLargeStatic, .contactValueTextAreaStatic { display: inline-block; } - .contactValueInput { + .contactValueInput, .contactValueInputLarge, .contactValueTextArea { display: none; } } @@ -303,12 +303,12 @@ color: #aaa; } - .contactValueStatic { + .contactValueStatic, .contactValueLargeStatic, .contactValueTextAreaStatic { font-size: 18px; display: none; } - .contactValueInput { + .contactValueInput, .contactValueInputLarge, .contactValueTextArea { .box-shadow(none); border-color: #fff; font-size: 18px; @@ -337,6 +337,14 @@ color: #ddd; } } + + .contactValueTextArea { + width: 300px; + } + + .contactValueInputLarge { + width: 400px; + } .hasError { .contactValueInput { @@ -376,6 +384,12 @@ } } + .button-add-prop { + position: absolute; + top: 60px; + right: 20px; + } + &.read-only { .e-read-only-sign { display: inline-block; diff --git a/dev/Styles/Layout.less b/dev/Styles/Layout.less index 3f6c507e7..849b88c23 100644 --- a/dev/Styles/Layout.less +++ b/dev/Styles/Layout.less @@ -85,7 +85,7 @@ html.ssm-state-desktop-large { } .b-contacts-content.modal { - width: 800px; + width: 900px; } } @@ -112,7 +112,7 @@ html.ssm-state-desktop { } .b-contacts-content.modal { - width: 800px; + width: 900px; } } @@ -169,7 +169,7 @@ html.ssm-state-tablet { } .b-contacts-content.modal { - width: 700px; + width: 800px; } .b-contacts-content.modal { diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index ef70bb1ea..ce47a40b8 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -175,6 +175,18 @@ html.rgba.textshadow { border-bottom-right-radius: @btnBorderRadius; } +.dropdown.colored-toggle.open .btn.dropdown-toggle { + color: #BD362F; + + .caret { + border-top-color: #BD362F; + } + + [class^="icon-"]:before { + color: #BD362F; + } +} + textarea, input[type="text"], input[type="password"], input[type="email"], input[type="search"] { border: @rlInputBorderSize solid @inputBorder; diff --git a/dev/ViewModels/PopupsContactsViewModel.js b/dev/ViewModels/PopupsContactsViewModel.js index 0ebe6f43e..a2032288f 100644 --- a/dev/ViewModels/PopupsContactsViewModel.js +++ b/dev/ViewModels/PopupsContactsViewModel.js @@ -47,8 +47,30 @@ function PopupsContactsViewModel() this.viewSaveTrigger = ko.observable(Enums.SaveSettingsStep.Idle); this.viewPropertiesNames = this.viewProperties.filter(function(oProperty) { - return -1 < Utils.inArray(oProperty.type(), [Enums.ContactPropertyType.FirstName, Enums.ContactPropertyType.LastName]); + return -1 < Utils.inArray(oProperty.type(), [ + Enums.ContactPropertyType.FirstName, Enums.ContactPropertyType.LastName + ]); }); + + this.viewPropertiesOther = this.viewProperties.filter(function(oProperty) { + return -1 < Utils.inArray(oProperty.type(), [ + Enums.ContactPropertyType.Note + ]); + }); + + this.viewPropertiesOther = ko.computed(function () { + + var aList = _.filter(this.viewProperties(), function (oProperty) { + return -1 < Utils.inArray(oProperty.type(), [ + Enums.ContactPropertyType.Nick + ]); + }); + + return _.sortBy(aList, function (oProperty) { + return oProperty.type(); + }); + + }, this); this.viewPropertiesEmails = this.viewProperties.filter(function(oProperty) { return Enums.ContactPropertyType.Email === oProperty.type(); @@ -94,6 +116,13 @@ function PopupsContactsViewModel() return '' === Utils.trim(oProperty.value()) && !bF; }); + this.viewPropertiesOtherEmptyAndOnFocused = ko.computed(function () { + return _.filter(this.viewPropertiesOther(), function (oProperty) { + var bF = oProperty.focused(); + return '' === Utils.trim(oProperty.value()) && !bF; + }); + }, this); + this.viewPropertiesEmailsEmptyAndOnFocused.subscribe(function(aList) { fFastClearEmptyListHelper(aList); }); @@ -106,6 +135,10 @@ function PopupsContactsViewModel() fFastClearEmptyListHelper(aList); }); + this.viewPropertiesOtherEmptyAndOnFocused.subscribe(function(aList) { + fFastClearEmptyListHelper(aList); + }); + this.viewSaving = ko.observable(false); this.useCheckboxesInList = RL.data().useCheckboxesInList; @@ -314,11 +347,44 @@ function PopupsContactsViewModel() Utils.extendAsViewModel('PopupsContactsViewModel', PopupsContactsViewModel); +PopupsContactsViewModel.prototype.getPropertyPlceholder = function (sType) +{ + var sResult = ''; + switch (sType) + { + case Enums.ContactPropertyType.LastName: + sResult = 'CONTACTS/PLACEHOLDER_ENTER_LAST_NAME'; + break; + case Enums.ContactPropertyType.FirstName: + sResult = 'CONTACTS/PLACEHOLDER_ENTER_FIRST_NAME'; + break; + case Enums.ContactPropertyType.Nick: + sResult = 'CONTACTS/PLACEHOLDER_ENTER_NICK_NAME'; + break; + } + + return sResult; +}; + PopupsContactsViewModel.prototype.addNewProperty = function (sType, sTypeStr) { - var oItem = new ContactPropertyModel(sType, sTypeStr || '', ''); - oItem.focused(true); - this.viewProperties.push(oItem); + this.viewProperties.push(new ContactPropertyModel(sType, sTypeStr || '', '', true, this.getPropertyPlceholder(sType))); +}; + +PopupsContactsViewModel.prototype.addNewOrFocusProperty = function (sType, sTypeStr) +{ + var oItem = _.find(this.viewProperties(), function (oItem) { + return sType === oItem.type(); + }); + + if (oItem) + { + oItem.focused(true); + } + else + { + this.addNewProperty(sType, sTypeStr); + } }; PopupsContactsViewModel.prototype.addNewEmail = function () @@ -336,6 +402,25 @@ PopupsContactsViewModel.prototype.addNewWeb = function () this.addNewProperty(Enums.ContactPropertyType.Web); }; +PopupsContactsViewModel.prototype.addNewNickname = function () +{ + this.addNewOrFocusProperty(Enums.ContactPropertyType.Nick); +}; + +PopupsContactsViewModel.prototype.addNewNotes = function () +{ + this.addNewOrFocusProperty(Enums.ContactPropertyType.Note); +}; + +PopupsContactsViewModel.prototype.addNewBirthday = function () +{ + this.addNewOrFocusProperty(Enums.ContactPropertyType.Birthday); +}; + +//PopupsContactsViewModel.prototype.addNewAddress = function () +//{ +//}; + PopupsContactsViewModel.prototype.exportVcf = function () { RL.download(RL.link().exportContactsVcf()); @@ -507,8 +592,11 @@ PopupsContactsViewModel.prototype.populateViewContact = function (oContact) this.viewReadOnly(!!oContact.readOnly); } - aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.LastName, '', sLastName, false, 'CONTACTS/PLACEHOLDER_ENTER_LAST_NAME')); - aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.FirstName, '', sFirstName, !oContact, 'CONTACTS/PLACEHOLDER_ENTER_FIRST_NAME')); + aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.LastName, '', sLastName, false, + this.getPropertyPlceholder(Enums.ContactPropertyType.LastName))); + + aList.unshift(new ContactPropertyModel(Enums.ContactPropertyType.FirstName, '', sFirstName, !oContact, + this.getPropertyPlceholder(Enums.ContactPropertyType.FirstName))); this.viewID(sId); this.viewProperties([]); diff --git a/package.json b/package.json index 05e0c69c6..17886f39f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.6.6", - "release": "911", + "release": "913", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "Gruntfile.js", diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index 7cc6e796d..438d514b8 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -2465,6 +2465,15 @@ class Actions $oImapClient->Disconnect(); $bImapResult = true; } + catch (\MailSo\Net\Exceptions\SocketCanNotConnectToHostException $oException) + { + $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); + $sImapErrorDesc = $oException->getSocketMessage(); + if (empty($sImapErrorDesc)) + { + $sImapErrorDesc = $oException->getMessage(); + } + } catch (\Exception $oException) { $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); @@ -2482,6 +2491,15 @@ class Actions $oSmtpClient->Disconnect(); $bSmtpResult = true; } + catch (\MailSo\Net\Exceptions\SocketCanNotConnectToHostException $oException) + { + $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); + $sSmtpErrorDesc = $oException->getSocketMessage(); + if (empty($sSmtpErrorDesc)) + { + $sSmtpErrorDesc = $oException->getMessage(); + } + } catch (\Exception $oException) { $this->Logger()->WriteException($oException, \MailSo\Log\Enumerations\Type::ERROR); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php index f4abd69df..ed0df1f7a 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Classes/Contact.php @@ -220,6 +220,9 @@ class Contact case PropertyType::NICK_NAME: $oVCard->NICKNAME = $oProperty->Value; break; + case PropertyType::NOTE: + $oVCard->NOTE = $oProperty->Value; + break; case PropertyType::FIRST_NAME: $sFirstName = $oProperty->Value; break; @@ -511,11 +514,11 @@ class Contact $aProperties[] = new Property(PropertyType::NICK_NAME, $sValue); } -// if (isset($oVCard->NOTE) && '' !== \trim($oVCard->NOTE)) -// { -// $sValue = $this->getPropertyValueHelper($oVCard->NOTE, $bOldVersion); -// $aProperties[] = new Property(PropertyType::NOTE, $sValue); -// } + if (isset($oVCard->NOTE) && '' !== \trim($oVCard->NOTE)) + { + $sValue = $this->getPropertyValueHelper($oVCard->NOTE, $bOldVersion); + $aProperties[] = new Property(PropertyType::NOTE, $sValue); + } if (isset($oVCard->N)) { diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php index 2957f1d81..4068e4abc 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/Enumerations/PropertyType.php @@ -21,6 +21,8 @@ class PropertyType // const MOBILE = 32; // const FAX = 33; const WEB_PAGE = 32; + + const BIRTHDAY = 40; const FACEBOOK = 90; const SKYPE = 91; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php index 53a125206..9d041ac7e 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/AddressBook/PdoAddressBook.php @@ -923,7 +923,7 @@ class PdoAddressBook $iUserID = $this->getUserId($sEmail); $sTypes = implode(',', array( - PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME + PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME, PropertyType::NICK_NAME )); $sSql = 'SELECT id_contact, id_prop, prop_type, prop_value FROM rainloop_ab_properties '. @@ -961,9 +961,14 @@ class PdoAddressBook $aIdContacts[$iIdContact] = $iIdContact; $aIdProps[$iIdProp] = $iIdProp; - if (\in_array($iType, array(PropertyType::LAST_NAME, PropertyType::FIRST_NAME))) + if (\in_array($iType, array(PropertyType::LAST_NAME, PropertyType::FIRST_NAME, PropertyType::NICK_NAME))) { - $aContactAllAccess[$iIdContact] = $iIdContact; + if (!isset($aContactAllAccess[$iIdContact])) + { + $aContactAllAccess[$iIdContact] = array(); + } + + $aContactAllAccess[$iIdContact][] = $iType; } } } @@ -977,7 +982,7 @@ class PdoAddressBook $oStmt->closeCursor(); $sTypes = \implode(',', array( - PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME + PropertyType::EMAIl, PropertyType::FIRST_NAME, PropertyType::LAST_NAME, PropertyType::NICK_NAME )); $sSql = 'SELECT id_prop, id_contact, prop_type, prop_value FROM rainloop_ab_properties '. @@ -990,6 +995,7 @@ class PdoAddressBook if (\is_array($aFetch) && 0 < \count($aFetch)) { $aNames = array(); + $aNicks = array(); $aEmails = array(); foreach ($aFetch as $aItem) @@ -1000,7 +1006,11 @@ class PdoAddressBook $iIdProp = (int) $aItem['id_prop']; $iType = (int) $aItem['prop_type']; - if (\in_array($iType, array(PropertyType::LAST_NAME, PropertyType::FIRST_NAME))) + if (PropertyType::NICK_NAME === $iType) + { + $aNicks[$iIdContact] = $aItem['prop_value']; + } + else if (\in_array($iType, array(PropertyType::LAST_NAME, PropertyType::FIRST_NAME))) { if (!isset($aNames[$iIdContact])) { @@ -1024,12 +1034,28 @@ class PdoAddressBook foreach ($aEmails as $iId => $aItems) { - $aNameItem = isset($aNames[$iId]) && \is_array($aNames[$iId]) ? $aNames[$iId] : array('', ''); - $sNameItem = \trim($aNameItem[0].' '.$aNameItem[1]); - - foreach ($aItems as $sEmail) + if (isset($aContactAllAccess[$iId])) { - $aResult[] = array($sEmail, $sNameItem); + $bName = \in_array(PropertyType::FIRST_NAME, $aContactAllAccess[$iId]) || \in_array(PropertyType::LAST_NAME, $aContactAllAccess[$iId]); + $bNick = \in_array(PropertyType::NICK_NAME, $aContactAllAccess[$iId]); + + $aNameItem = isset($aNames[$iId]) && \is_array($aNames[$iId]) ? $aNames[$iId] : array('', ''); + $sNameItem = \trim($aNameItem[0].' '.$aNameItem[1]); + + $sNickItem = isset($aNicks[$iId]) ? $aNicks[$iId] : ''; + + foreach ($aItems as $sEmail) + { + if ($bName) + { + $aResult[] = array($sEmail, $sNameItem); + } + + if ($bNick) + { + $aResult[] = array($sEmail, $sNickItem); + } + } } } } diff --git a/rainloop/v/0.0.0/app/templates/Views/MailMessageList.html b/rainloop/v/0.0.0/app/templates/Views/MailMessageList.html index 8ba93556f..4d2a25bf0 100644 --- a/rainloop/v/0.0.0/app/templates/Views/MailMessageList.html +++ b/rainloop/v/0.0.0/app/templates/Views/MailMessageList.html @@ -9,7 +9,7 @@
 
-