diff --git a/config.performance.php b/config.performance.php index db6125cdc0ff2af6a0bc7943f02cdda4fcfa92ab..303c0dcf5006bb1737ab28658ccf554a2ca414cf 100644 --- a/config.performance.php +++ b/config.performance.php @@ -10,7 +10,7 @@ /* Performance paramters can be configured to fine tune vtiger CRM runtime */ $PERFORMANCE_CONFIG = Array( // Enable Vtiger Log Level for debugging only if requried - 'LOGLEVEl_DEBUG' => false, + 'LOGLEVEL_DEBUG' => false, // Should the caller information be captured in SQL Logging? // It adds little overhead for performance but will be useful to debug diff --git a/include/utils/EditViewUtils.php b/include/utils/EditViewUtils.php index 0bc464190e7d27e0e7c82f126358e5010b2b575a..2694b1360d93cb85dc279346c908f890b0b3822f 100755 --- a/include/utils/EditViewUtils.php +++ b/include/utils/EditViewUtils.php @@ -539,7 +539,7 @@ function getAssociatedProducts($module, $focus, $seid = '', $refModuleName = fal $shtax_percent = 0; //if condition is added to call this function when we create PO/SO/Quotes/Invoice from Product module if (in_array($module, $inventoryModules)) { - $shtax_percent = getInventorySHTaxPercent($focus->id,$shtax_name); + $shtax_percent = getInventorySHTaxPercent($focus->id,$shtax_name,$shtax_count); } $shtaxamount = $shCharge*$shtax_percent/100; $shtaxtotal = $shtaxtotal + $shtaxamount; diff --git a/include/utils/InventoryUtils.php b/include/utils/InventoryUtils.php index baa17172be770ec75f7ffec23b407984dd121372..223bce80a16dacb34084327d793646265c846fcc 100644 --- a/include/utils/InventoryUtils.php +++ b/include/utils/InventoryUtils.php @@ -969,7 +969,7 @@ function getInventoryProductTaxValue($id, $productId, $taxName, $lineItemId = 0) * @param string $taxname - shipping and handling taxname * @return float $taxpercentage - shipping and handling taxpercentage which is associated with the given entity */ -function getInventorySHTaxPercent($id, $taxname) +function getInventorySHTaxPercent($id, $taxname, $taxnum=null) { global $log, $adb; $log->debug("Entering into function getInventorySHTaxPercent($id, $taxname)"); @@ -978,6 +978,22 @@ function getInventorySHTaxPercent($id, $taxname) $res = $adb->pquery("select $taxname from vtiger_inventoryshippingrel where id= ?", array($id)); $taxpercentage = $adb->query_result($res,0,$taxname); + // If shipping details is not found then try to get the values from the vtiger_inventorychargesrel + // where the actual shipping and handling tax info of particular record stored. + if($adb->num_rows($res) < 1){ + $j=$taxnum+1; + // parse through the json detail and extract the value of specific tax. + $charges_result = $adb->pquery( + "SELECT JSON_UNQUOTE(JSON_EXTRACT(charges, CONCAT('$.\"1\".taxes.\"', ? ,'\"'))) as charges + FROM vtiger_inventorychargesrel + WHERE recordid = ?", + array($taxnum + 1, $id) + ); + $rowData = $adb->fetch_array($charges_result); + $charges = Zend_Json::decode(html_entity_decode($rowData['charges'])); + $taxpercentage = $charges; + } + if($taxpercentage == '') $taxpercentage = 0; @@ -1730,4 +1746,4 @@ function getCompoundTaxesInfoForInventoryRecord($recordId, $moduleName) { return $compoundTaxesInfo; } -?> \ No newline at end of file +?> diff --git a/layouts/v7/modules/Documents/resources/Documents.js b/layouts/v7/modules/Documents/resources/Documents.js index ac8e4123aaec00a9f564f5cdff56cf0684be4a63..492273eb7d6cb405d82639addcc989a94bddda98 100644 --- a/layouts/v7/modules/Documents/resources/Documents.js +++ b/layouts/v7/modules/Documents/resources/Documents.js @@ -471,7 +471,16 @@ Vtiger.Class('Documents_Index_Js', { vtigerInstance.referenceModulePopupRegisterEvent(container); vtigerInstance.registerClearReferenceSelectionEvent(container); vtigerInstance.registerAutoCompleteFields(container); - app.helper.registerModalDismissWithoutSubmit(container.find('form')); + + // Avoid duplicate registrations + // container could remain in DOM where as form in it could get replaced next invoke after cancel + // to avoid duplicate registration on form close/cancel within same transaction we are handling state in form DOM element. + var containerForm = container.find("form"); + if (!containerForm.data("isDismissWithoutSubmitRegistered")) { + app.helper.registerModalDismissWithoutSubmit(container.find('form')); + containerForm.data("isDismissWithoutSubmitRegistered", true); + } + var moduleInstance = Vtiger_Edit_Js.getInstanceByModuleName('Documents'); moduleInstance.registerEventForPicklistDependencySetup(container); diff --git a/layouts/v7/modules/Emails/resources/MassEdit.js b/layouts/v7/modules/Emails/resources/MassEdit.js index 830481f6ddb567d1d789f6ecb93a2c9efa6eb95d..e4b53740daa86557ac39437bd5a88e5e9a33132a 100644 --- a/layouts/v7/modules/Emails/resources/MassEdit.js +++ b/layouts/v7/modules/Emails/resources/MassEdit.js @@ -472,7 +472,7 @@ jQuery.Class("Emails_MassEdit_Js",{},{ addToEmails : function(mailInfo){ var toEmails = this.getMassEmailForm().find('[name="to"]'); var value = JSON.parse(toEmails.val()); - if(value == ""){ + if(!value || value == ""){ value = new Array(); } value.push(mailInfo.emailid); diff --git a/layouts/v7/modules/Products/resources/Detail.js b/layouts/v7/modules/Products/resources/Detail.js index 696144f0fb5c8237f22d840a9670657e5c663695..d48aa4cb88cb967196df33ec01423183d45aabd7 100644 --- a/layouts/v7/modules/Products/resources/Detail.js +++ b/layouts/v7/modules/Products/resources/Detail.js @@ -102,7 +102,7 @@ PriceBooks_Detail_Js("Products_Detail_Js", { 'html': true, 'container':'body', 'placement': 'top', - }).data('bs.popover').tip().addClass('productBundlePopover'); + }).data('bs.popover').tip().addClass('productBundlePopover').css({'left':'0px', 'margin-left': '60px'}); /* 60px to overcome sidebar-offset + container-padding */ element.one('shown.bs.popover',function(){ app.helper.showVerticalScroll(jQuery('.productBundlePopover .popover-content')); }); diff --git a/layouts/v7/modules/Settings/LayoutEditor/FieldCreate.tpl b/layouts/v7/modules/Settings/LayoutEditor/FieldCreate.tpl index db9c43a4e8630bfd386a164c8a998f9b1dfa9351..af3e377fa43988bde4aaf523e938994d03bc64ed 100644 --- a/layouts/v7/modules/Settings/LayoutEditor/FieldCreate.tpl +++ b/layouts/v7/modules/Settings/LayoutEditor/FieldCreate.tpl @@ -141,7 +141,7 @@ <input type="hidden" name="presence" value="1"/> <label class="checkbox"> <input type="checkbox" class ='cursorPointer bootstrap-switch' id="fieldPresence" name="presence" {if $FIELD_MODEL->isViewable()} checked {/if} - {if $FIELD_MODEL->isActiveOptionDisabled()} readonly="readonly" {/if} {if $FIELD_MODEL->isMandatory()} readonly="readonly" {/if} + {if $FIELD_MODEL->isActiveOptionDisabled()} optionDisabled = "true" readonly="readonly" {/if} {if $FIELD_MODEL->isMandatory()} readonly="readonly" {/if} data-on-text="Yes" data-off-text="No" value="{$FIELD_MODEL->get('presence')}"/> </label> </div> diff --git a/layouts/v7/modules/Settings/LayoutEditor/resources/LayoutEditor.js b/layouts/v7/modules/Settings/LayoutEditor/resources/LayoutEditor.js index 603295ad483fb289926f00a407a999790458ae88..b8c46da53ff8d0d5382aa5d164dc864492fe26c3 100644 --- a/layouts/v7/modules/Settings/LayoutEditor/resources/LayoutEditor.js +++ b/layouts/v7/modules/Settings/LayoutEditor/resources/LayoutEditor.js @@ -504,15 +504,19 @@ Vtiger.Class('Settings_LayoutEditor_Js', { data.find('input[name="quickcreate"]').prop('checked', true).attr('readonly', 'readonly'); data.find('input[name="quickcreate"]').removeClass('cursorPointer').addClass('cursorPointerNotAllowed'); } - data.find('input[name="presence"]').attr('checked', true).attr('readonly', 'readonly'); - data.find('#fieldPresence').bootstrapSwitch('toggleReadonly', true); + if(data.find('#fieldPresence').attr('optionDisabled') != 'true'){ + data.find('input[name="presence"]').attr('checked', true).attr('readonly', 'readonly'); + data.find('#fieldPresence').bootstrapSwitch('toggleReadonly', true); + } } else { if (data.find('input[name="isquickcreatesupported"]').val()) { data.find('input[name="quickcreate"]').removeAttr('readonly'); data.find('input[name="quickcreate"]').removeClass('cursorPointerNotAllowed').addClass('cursorPointer'); } - data.find('input[name="presence"]').removeAttr('readonly'); - data.find('#fieldPresence').bootstrapSwitch('toggleReadonly'); + if(data.find('#fieldPresence').attr('optionDisabled') != 'true'){ + data.find('input[name="presence"]').removeAttr('readonly'); + data.find('#fieldPresence').bootstrapSwitch('toggleReadonly'); + } } }) data.find('input[type="checkbox"]').on('click', function (e) { diff --git a/layouts/v7/modules/Settings/Picklist/resources/Picklist.js b/layouts/v7/modules/Settings/Picklist/resources/Picklist.js index 3243ddd4d5c022feb6a20b85e332f562f8e05d03..5905fd955802d9ed99f551e537626151a68677f7 100644 --- a/layouts/v7/modules/Settings/Picklist/resources/Picklist.js +++ b/layouts/v7/modules/Settings/Picklist/resources/Picklist.js @@ -306,7 +306,8 @@ var Settings_Picklist_Js = { var form = container.find('[name="addItemForm"]'); var params = { submitHandler: function(form) { - var specialChars = /[<\>\"\,\[\]\{\}]/; + // detect characters that would break convention for css-class names + var specialChars = /[\<\>\"\,\[\]\{\}\'\!\@\#\$\%\^\&\*\(\)\+\=\?\|\\\;\:\/]/; var newValueEle = jQuery('[name="newValue"]', container); var newValues = newValueEle.val(); var newValueArray = newValues.split(','); @@ -323,7 +324,7 @@ var Settings_Picklist_Js = { return false; } if (specialChars.test(newValueArray[i])) { - var errorMessage = app.vtranslate('JS_SPECIAL_CHARACTERS') + " < > \" , [ ] { } " + app.vtranslate('JS_NOT_ALLOWED'); + var errorMessage = app.vtranslate('JS_SPECIAL_CHARACTERS') + " <>\",[]{}\'!@#$%^&*()+=?|\\;:/ " + app.vtranslate('JS_NOT_ALLOWED'); vtUtils.showValidationMessage(newValueEle, errorMessage, showValidationParams); return false; } @@ -372,7 +373,7 @@ var Settings_Picklist_Js = { var params = { submitHandler: function(form) { var form = jQuery(form); - var specialChars = /[<\>\"\,\[\]\{\}]/; + var specialChars = /[\<\>\"\,\[\]\{\}\'\!\@\#\$\%\^\&\*\(\)\+\=\?\|\\\;\:\/]/; var newValueEle = jQuery('[name="renamedValue"]',form); var newValue = jQuery.trim(newValueEle.val()); if(Settings_Picklist_Js.duplicateItemNameCheck(form)) { @@ -397,7 +398,7 @@ var Settings_Picklist_Js = { at: 'top left', container : form }}; - var errorMessage = app.vtranslate('JS_SPECIAL_CHARACTERS') + " < > \" , [ ] { } " + app.vtranslate('JS_NOT_ALLOWED'); + var errorMessage = app.vtranslate('JS_SPECIAL_CHARACTERS') + " <>\",[]{}\'!@#$%^&*()+=?|\\;:/ " + app.vtranslate('JS_NOT_ALLOWED'); vtUtils.showValidationMessage(newValueEle, errorMessage, showValidationParams); return false; } @@ -620,4 +621,4 @@ var Settings_Picklist_Js = { jQuery(document).ready(function(){ Settings_Picklist_Js.registerEvents(); -}); \ No newline at end of file +}); diff --git a/layouts/v7/modules/Settings/Tags/resources/List.js b/layouts/v7/modules/Settings/Tags/resources/List.js index 0a945a6cb2b726e5d7942b5b06094ba6f5d70c0b..2a084ad13de1b6273d148755138d8a3a1cf70226 100644 --- a/layouts/v7/modules/Settings/Tags/resources/List.js +++ b/layouts/v7/modules/Settings/Tags/resources/List.js @@ -82,7 +82,7 @@ Settings_Vtiger_List_Js('Settings_Tags_List_Js',{ var editTagContainer = this.getEditTagContainer(); editTagContainer.find('[name="id"]').val(tagInfo.id); - editTagContainer.find('[name="tagName"]').val(tagInfo.tag); + editTagContainer.find('[name="tagName"]').val(app.helper.getDecodedValue(tagInfo.tag)); if(tagInfo.visibility == "public") { editTagContainer.find('[type="checkbox"]').prop('checked',true); }else{ @@ -199,4 +199,4 @@ Settings_Vtiger_List_Js('Settings_Tags_List_Js',{ self.registerEditTagSaveEvent(); }) } -}); \ No newline at end of file +}); diff --git a/layouts/v7/modules/Settings/Workflows/resources/AdvanceFilter.js b/layouts/v7/modules/Settings/Workflows/resources/AdvanceFilter.js index d1559a86a1dbf47ccce49d3606ac791d6c4cd16f..cebdc7b24397e2ea47fbd47cb1ea711cb302d427 100644 --- a/layouts/v7/modules/Settings/Workflows/resources/AdvanceFilter.js +++ b/layouts/v7/modules/Settings/Workflows/resources/AdvanceFilter.js @@ -296,7 +296,7 @@ Vtiger_Date_Field_Js('Workflows_Date_Field_Js',{},{ var dateSpecificConditions = this.get('dateSpecificConditions'); if(comparatorSelectedOptionVal.length > 0) { if(comparatorSelectedOptionVal == 'between' || comparatorSelectedOptionVal == 'custom'){ - var html = '<div class="date"><input class="dateField inputElement" style="width:auto;" data-calendar-type="range" name="'+ this.getName() +'" data-date-format="'+ this.getDateFormat() +'" type="text" ReadOnly="true" value="'+ this.getValue() + '"></div>'; + var html = '<div class="date"><input class="dateField inputElement" style="width:auto;" data-calendar-type="range" name="'+ this.getName() +'" data-date-format="'+ this.getDateFormat() +'" type="text" value="'+ this.getValue() + '"></div>'; var element = jQuery(html); return this.addValidationToElement(element); } else if(this._specialDateComparator(comparatorSelectedOptionVal)) { @@ -384,12 +384,19 @@ Vtiger_Currency_Field_Js('Workflows_Currency_Field_Js',{},{ Vtiger_Time_Field_Js('Workflows_Time_Field_Js',{},{ + /** + * Function to get the user time format + */ + getTimeFormat : function(){ + return this.get('time-format'); + }, + /** * Function to get the ui * @return - input text field */ getUi : function() { - var html = '<input type="text" class="getPopupUi time inputElement" name="'+ this.getName() +'" value="'+ this.getValue() + '" />'+ + var html = '<input type="text" class="getPopupUi time inputElement" name="'+ this.getName() +'" data-time-format="'+ this.getTimeFormat() + '" value="'+ this.getValue() + '" />'+ '<input type="hidden" name="valuetype" value="'+this.get('workflow_valuetype')+'" />'; var element = jQuery(html); return this.addValidationToElement(element); diff --git a/layouts/v7/modules/Settings/Workflows/resources/Edit.js b/layouts/v7/modules/Settings/Workflows/resources/Edit.js index fa768106f00f1c639d86c75f9ceab54b72d4c662..7f313207ad1fcb8645b342dcd6640da85ddb9260 100644 --- a/layouts/v7/modules/Settings/Workflows/resources/Edit.js +++ b/layouts/v7/modules/Settings/Workflows/resources/Edit.js @@ -147,12 +147,13 @@ Settings_Vtiger_Edit_Js("Settings_Workflows_Edit_Js", { clonedPopupUi.find('.fieldValueContainer div').prepend(clonedDateElement); } else if (fieldValueElement.hasClass('time')) { clonedPopupUi.find('.textType').find('option[value="rawtext"]').attr('data-ui', 'input'); + var timeFormat = fieldValueElement.data('time-format'); if (valueType == 'rawtext') { var value = fieldValueElement.val(); } else { value = ''; } - var clonedTimeElement = '<input type="text" style="width: 30%;" class="timepicker-default fieldValue inputElement" value="' + value + '" data-input="true" >' + var clonedTimeElement = '<input type="text" style="width: 30%;" class="timepicker-default fieldValue inputElement" value="' + value + '" data-format="' + timeFormat + '" data-input="true" >' clonedPopupUi.find('.fieldValueContainer div').prepend(clonedTimeElement); } else if (fieldValueElement.hasClass('boolean')) { clonedPopupUi.find('.textType').find('option[value="rawtext"]').attr('data-ui', 'input'); @@ -603,7 +604,7 @@ Settings_Vtiger_Edit_Js("Settings_Workflows_Edit_Js", { isEmptyFieldSelected: function (fieldSelect) { var selectedOption = fieldSelect.find('option:selected'); //assumption that empty field will be having value none - if (selectedOption.val() == 'none') { + if (selectedOption.val() == 'none' || selectedOption.val() === '') { return true; } return false; diff --git a/layouts/v7/modules/Vtiger/RecentComments.tpl b/layouts/v7/modules/Vtiger/RecentComments.tpl index 5bbec85ea00036bd438f7ac81c91a6586c12b36b..a9de04159e12716a4bf45044506aadfcadd50eb7 100644 --- a/layouts/v7/modules/Vtiger/RecentComments.tpl +++ b/layouts/v7/modules/Vtiger/RecentComments.tpl @@ -232,8 +232,8 @@ </div> </div> - <div class="hide basicEditCommentBlock container-fluid" style="min-height: 150px;"> - <div class="row commentArea" > + <div class="hide basicEditCommentBlock container-fluid"> + <div class="row commentArea" style="padding-bottom: 10px" > <input style="width:100%;height:30px;" type="text" name="reasonToEdit" placeholder="{vtranslate('LBL_REASON_FOR_CHANGING_COMMENT', $MODULE_NAME)}" class="input-block-level"/> </div> <div class="row" style="padding-bottom: 10px;"> @@ -242,9 +242,11 @@ </div> </div> <input type="hidden" name="is_private"> - <div class="pull-right row"> - <button class="btn btn-success btn-sm detailViewSaveComment" type="button" data-mode="edit">{vtranslate('LBL_POST', $MODULE_NAME)}</button> - <a href="javascript:void(0);" class="cursorPointer closeCommentBlock cancelLink" type="reset">{vtranslate('LBL_CANCEL', $MODULE_NAME)}</a> + <div class="row" style="padding-bottom: 10px;"> + <div class="pull-right"> + <button class="btn btn-success btn-sm detailViewSaveComment" type="button" data-mode="edit">{vtranslate('LBL_POST', $MODULE_NAME)}</button> + <a href="javascript:void(0);" class="cursorPointer closeCommentBlock cancelLink" type="reset">{vtranslate('LBL_CANCEL', $MODULE_NAME)}</a> + </div> </div> </div> </div> diff --git a/layouts/v7/modules/Vtiger/resources/DashBoard.js b/layouts/v7/modules/Vtiger/resources/DashBoard.js index b611e7877c49a9708638a64b9fc4ae236de674ec..3350b5a5c8507bedf31003be9676935196401e58 100644 --- a/layouts/v7/modules/Vtiger/resources/DashBoard.js +++ b/layouts/v7/modules/Vtiger/resources/DashBoard.js @@ -724,8 +724,8 @@ Vtiger.Class("Vtiger_DashBoard_Js",{ var tabEle = '<li class="dashboardTab" data-tabid="'+tabid+'" data-tabname="'+tabname+'">'; tabEle += '<a data-toggle="tab" href="#tab_'+tabid+'">\n\ <div>\n\ - <span class="name textOverflowEllipsis" value="'+tabname+'" style="width:10%">\n\ - <strong>'+tabname+'</strong>\n\ + <span class="name textOverflowEllipsis" style="width:10%">\n\ + <strong></strong>\n\ </span>\n\ <span class="editTabName hide"><input type="text" name="tabName"></span>\n\ <i class="fa fa-close deleteTab"></i>\n\ diff --git a/layouts/v7/modules/Vtiger/resources/Field.js b/layouts/v7/modules/Vtiger/resources/Field.js index 3c5bda8be6983ab252530d10dbdf1ddda6be0493..40b778a71cec572428edac1dcaf2669af413db04 100644 --- a/layouts/v7/modules/Vtiger/resources/Field.js +++ b/layouts/v7/modules/Vtiger/resources/Field.js @@ -420,6 +420,9 @@ Vtiger_Field_Js('Vtiger_Multipicklist_Field_Js',{},{ if (picklistColor) { className = '.picklistColor_'+fieldName+'_'+option.replace(' ', '_'); html += className+'{background-color: '+picklistColor+' !important;}'; + + className = className + '.select2-highlighted'; + html += className+'{white: #ffffff !important; background-color: #337ab7 !important;}'; } } html +='<\style>'; diff --git a/layouts/v7/modules/Vtiger/resources/Vtiger.js b/layouts/v7/modules/Vtiger/resources/Vtiger.js index 2056278a69126f995b647bc308cf8399f24f4eb5..3d13692268e5d2c9990f9cc479101d5a5c4f96ce 100644 --- a/layouts/v7/modules/Vtiger/resources/Vtiger.js +++ b/layouts/v7/modules/Vtiger/resources/Vtiger.js @@ -1230,7 +1230,7 @@ Vtiger.Class('Vtiger_Index_Js', { var quickCreateNode = jQuery('#quickCreateModules').find('[data-name="'+ referenceModuleName +'"]'); if(quickCreateNode.length <= 0) { var notificationOptions = { - 'title' : app.vtranslate('JS_NO_CREATE_OR_NOT_QUICK_CREATE_ENABLED') + 'message' : app.vtranslate('JS_NO_CREATE_OR_NOT_QUICK_CREATE_ENABLED') } app.helper.showAlertNotification(notificationOptions); } diff --git a/layouts/v7/modules/Vtiger/resources/validation.js b/layouts/v7/modules/Vtiger/resources/validation.js index 240ea286cda1ef3f7c9eca1cd2b50571aea4ee09..16d59a3bb71da07477c3f4570ae6c07eeb4a1e3b 100644 --- a/layouts/v7/modules/Vtiger/resources/validation.js +++ b/layouts/v7/modules/Vtiger/resources/validation.js @@ -700,7 +700,7 @@ jQuery.validator.addMethod("PositiveNumber",function(value,element,params){ jQuery.validator.addMethod("percentage", function(value, element, params){ var decimalSeparator = app.getDecimalSeparator(); - var strippedValue = value.replace(decimalSeparator, ''); + var strippedValue = value.replace(/[^\d,]/g, ''); var spacePattern = /\s/; if(spacePattern.test(decimalSeparator)) { strippedValue = strippedValue.replace(/ /g, ''); diff --git a/layouts/v7/modules/Vtiger/uitypes/FileLocationType.tpl b/layouts/v7/modules/Vtiger/uitypes/FileLocationType.tpl index 74f61bb808f8ccbf6e7693011577b90060e82a5e..2324ed618f6f5b647f68793c22b29ad58cdf64f6 100644 --- a/layouts/v7/modules/Vtiger/uitypes/FileLocationType.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/FileLocationType.tpl @@ -11,9 +11,17 @@ -->*} {strip} {assign var=FIELD_VALUES value=$FIELD_MODEL->getFileLocationType()} -<select class="select2" name="{$FIELD_MODEL->getFieldName()}"> +{* The options displayed based on the file location type received on request *} +<select class="select2" name="{$FIELD_MODEL->getFieldName()}" {if $FILE_LOCATION_TYPE eq 'I' OR $FILE_LOCATION_TYPE eq 'E'} disabled {/if}> {foreach item=TYPE key=KEY from=$FIELD_VALUES} - <option value="{$KEY}" {if $FIELD_MODEL->get('fieldvalue') eq $KEY} selected {/if}>{vtranslate($TYPE, $MODULE)}</option> + {if $FILE_LOCATION_TYPE eq 'I'} + {assign var=SELECTED value='I'} + {elseif $FILE_LOCATION_TYPE eq 'E'} + {assign var=SELECTED value='E'} + {else} + {assign var=SELECTED value=$FIELD_MODEL->get('fieldvalue')} + {/if} + <option value="{$KEY}" {if $SELECTED eq $KEY} selected {/if}>{vtranslate($TYPE, $MODULE)}</option> {/foreach} </select> {/strip} \ No newline at end of file diff --git a/layouts/v7/modules/Vtiger/uitypes/MultiPicklist.tpl b/layouts/v7/modules/Vtiger/uitypes/MultiPicklist.tpl index 593d83b42d6f3ca66ff1462f0208d80dd3251345..9867ae7045a740a19612279b49a5b4c01288c4a4 100644 --- a/layouts/v7/modules/Vtiger/uitypes/MultiPicklist.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/MultiPicklist.tpl @@ -33,7 +33,17 @@ {assign var=CLASS_NAME value="{$FIELD_MODEL->getFieldName()}_{$PICKLIST_NAME|replace:' ':'_'}"} .picklistColor_{$CLASS_NAME} { background-color: {$PICKLIST_COLORS[$PICKLIST_NAME]} !important; + {if isset($PICKLIST_COLORS[$PICKLIST_NAME])} + background-color: {$PICKLIST_COLORS[$PICKLIST_NAME]} !important; + {if $PICKLIST_COLORS[$PICKLIST_NAME] eq '#ffffff'} + color: #000000 !important; + {/if} + {/if} } + .picklistColor_{$CLASS_NAME}.select2-highlighted { + white: #ffffff !important; + background-color: #337ab7 !important; + } {/foreach} </style> {/if} diff --git a/layouts/v7/modules/Vtiger/uitypes/TimeFieldSearchView.tpl b/layouts/v7/modules/Vtiger/uitypes/TimeFieldSearchView.tpl index 82975a17eedb363de65460bf7b03c6f07baf7e7f..23553209bdb28c8d63b0e9082e501f53c487d668 100644 --- a/layouts/v7/modules/Vtiger/uitypes/TimeFieldSearchView.tpl +++ b/layouts/v7/modules/Vtiger/uitypes/TimeFieldSearchView.tpl @@ -12,7 +12,8 @@ {strip} {assign var="FIELD_INFO" value=Vtiger_Util_Helper::toSafeHTML(Zend_Json::encode($FIELD_MODEL->getFieldInfo()))} {assign var="SEARCH_VALUE" value=$SEARCH_INFO['searchValue']} +{assign var="TIME_FORMAT" value=$USER_MODEL->get('hour_format')} <div class=""> -<input type="text" class="timepicker-default listSearchContributor" value="{$SEARCH_VALUE}" name="{$FIELD_MODEL->getFieldName()}" data-field-type="{$FIELD_MODEL->getFieldDataType()}" data-fieldinfo='{$FIELD_INFO}'/> +<input type="text" data-format="{$TIME_FORMAT}" class="timepicker-default listSearchContributor" value="{$SEARCH_VALUE}" name="{$FIELD_MODEL->getFieldName()}" data-field-type="{$FIELD_MODEL->getFieldDataType()}" data-fieldinfo='{$FIELD_INFO}'/> </div> {/strip} \ No newline at end of file diff --git a/layouts/v7/resources/application.js b/layouts/v7/resources/application.js index 834d54ae8456456f644e41e835d054d0b669427c..a7ace97698f33bd66d43256f9eacfcb3219b0d2c 100644 --- a/layouts/v7/resources/application.js +++ b/layouts/v7/resources/application.js @@ -399,7 +399,16 @@ window.app = (function () { }, convertTojQueryDatePickerFormat: function (dateFormat) { var i = 0; - var splitDateFormat = dateFormat.split('-'); + if (dateFormat.includes('.')) { + separator = '.'; + splitDateFormat = dateFormat.split('.'); + } else if (dateFormat.includes('/')) { + separator = '/'; + splitDateFormat = dateFormat.split('/'); + } else if (dateFormat.includes('-')) { + separator = '-'; + splitDateFormat = dateFormat.split('-'); + } for (var i in splitDateFormat) { var sectionDate = splitDateFormat[i]; var sectionCount = sectionDate.length; @@ -408,7 +417,7 @@ window.app = (function () { splitDateFormat[i] = strippedString; } } - var joinedDateFormat = splitDateFormat.join('-'); + var joinedDateFormat = splitDateFormat.join(separator); return joinedDateFormat; }, getDateInVtigerFormat: function (dateFormat, dateObject) { @@ -537,4 +546,20 @@ jQuery(function () { modal_this.$element.focus() } })}; -}); \ No newline at end of file +}); + +/** + * Pre-filter Ajax requests to guard against XSS attacks. + * + * See https://github.com/jquery/jquery/issues/2432 + */ +if (jQuery.ajaxPrefilter) { + // For newer versions of jQuery, use an Ajax prefilter to prevent + // auto-executing script tags from untrusted domains. This is similar to the + // fix that is built in to jQuery 3.0 and higher. + jQuery.ajaxPrefilter(function (s) { + if (s.crossDomain) { + s.contents.script = false; + } + }); +} diff --git a/layouts/v7/skins/contact/style.css b/layouts/v7/skins/contact/style.css index 279cca60508b9ab6f511cab92038ee99ba8d3549..a28b9641094ba832f21d772723342bde62937280 100644 --- a/layouts/v7/skins/contact/style.css +++ b/layouts/v7/skins/contact/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,29 +7740,32 @@ body .fc { padding: 30px; } } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; } - .detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; + max-width: none; + width: 90%; +} +#vt-mask { + position: relative; + display: inline-flex; } -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/inventory/style.css b/layouts/v7/skins/inventory/style.css index be86719bdafb686cbded313d54463e3baeaeaad4..cb673dd683a46bddbf658ce5d0371ff0c88cd666 100644 --- a/layouts/v7/skins/inventory/style.css +++ b/layouts/v7/skins/inventory/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,33 +7740,36 @@ body .fc { padding: 30px; } } +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; +} +.detailview-table textarea.inputElement.form-control { + max-width: none; + width: 90%; +} .modules-menu ul li.active a { border-left: 3px solid #F1C40F; opacity: 1; } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; -} - -.detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; +#vt-mask { + position: relative; + display: inline-flex; } -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/marketing/style.css b/layouts/v7/skins/marketing/style.css index 083922ddb4a05f3069bce07b9c2999f5e5a1269f..6ea9bf97958843f33ac6e0ec69c3a9ab130ea3ce 100644 --- a/layouts/v7/skins/marketing/style.css +++ b/layouts/v7/skins/marketing/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,10 +7740,39 @@ body .fc { padding: 30px; } } +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; +} +.detailview-table textarea.inputElement.form-control { + max-width: none; + width: 90%; +} .modules-menu ul li.active a { border-left: 3px solid #EF5E29; opacity: 1; } +#vt-mask { + position: relative; + display: inline-flex; +} .nav .nav-stacks .cp-nav-header-wrapper > li.disabled > a:hover { background-color: #fff; } @@ -8004,29 +8035,3 @@ a.btnReport:hover { height: 450px; overflow: auto; } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; -} - -.detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; -} -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/marketing_and_sales/style.css b/layouts/v7/skins/marketing_and_sales/style.css index 39d80cf2d575bb1ae7b0c32670c5a9e012cd987d..0271494f1de6d0f1795a048c32de350363f2184a 100644 --- a/layouts/v7/skins/marketing_and_sales/style.css +++ b/layouts/v7/skins/marketing_and_sales/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2512,6 +2512,12 @@ th { -o-transition: all 1s ease; transition: all 1s ease; } +.advanceFilterContainer .modal-content { + height: 100vh; +} +.p-xy-8 { + padding: 8px !important; +} #advanceSearchHolder { max-height: 0px; overflow: hidden; @@ -5589,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6123,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6145,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6164,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7732,33 +7740,36 @@ body .fc { padding: 30px; } } +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; +} +.detailview-table textarea.inputElement.form-control { + max-width: none; + width: 90%; +} .modules-menu ul li.active a { border-left: 3px solid #3CB878; opacity: 1; } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; -} - -.detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; +#vt-mask { + position: relative; + display: inline-flex; } -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/project/style.css b/layouts/v7/skins/project/style.css index 7bf6f834fc179226b79e973f808cd6c22a907896..c837291e6e79aaa5f6a46cf9057d45ba1b9a98b9 100644 --- a/layouts/v7/skins/project/style.css +++ b/layouts/v7/skins/project/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,33 +7740,36 @@ body .fc { padding: 30px; } } +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; +} +.detailview-table textarea.inputElement.form-control { + max-width: none; + width: 90%; +} .modules-menu ul li.active a { border-left: 3px solid #8E44AD; opacity: 1; } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; -} - -.detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; +#vt-mask { + position: relative; + display: inline-flex; } -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/sales/style.css b/layouts/v7/skins/sales/style.css index a9802c5d0e798bdea14a7daa3f40e08dac8bdd06..0271494f1de6d0f1795a048c32de350363f2184a 100644 --- a/layouts/v7/skins/sales/style.css +++ b/layouts/v7/skins/sales/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,33 +7740,36 @@ body .fc { padding: 30px; } } +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; +} +.detailview-table textarea.inputElement.form-control { + max-width: none; + width: 90%; +} .modules-menu ul li.active a { border-left: 3px solid #3CB878; opacity: 1; } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; -} - -.detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; +#vt-mask { + position: relative; + display: inline-flex; } -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/support/style.css b/layouts/v7/skins/support/style.css index aaa810782f789aa846f8a6d5241b1e3f6a2059c3..aa735ce15a26dcd1bb6cf9a33a467123377c629c 100644 --- a/layouts/v7/skins/support/style.css +++ b/layouts/v7/skins/support/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,10 +7740,39 @@ body .fc { padding: 30px; } } +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; +} +.detailview-table textarea.inputElement.form-control { + max-width: none; + width: 90%; +} .modules-menu ul li.active a { border-left: 3px solid #6297C3; opacity: 1; } +#vt-mask { + position: relative; + display: inline-flex; +} .publicCommentHeader, div.flip { background: none repeat scroll 0 0 #fbfbfb; @@ -7876,29 +7907,3 @@ div.flip { .caseRelatedRecords { margin-bottom: 0px !important; } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; -} - -.detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; -} -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/tools/style.css b/layouts/v7/skins/tools/style.css index 2474bef36686ccc42cd7f07075012664cb24d1ff..a5a3b7ba7fe33545016c358dbc74fd5191a790c1 100644 --- a/layouts/v7/skins/tools/style.css +++ b/layouts/v7/skins/tools/style.css @@ -2422,10 +2422,10 @@ th { border: none; border-left: 1px solid #ddd; } -.input-group.inputElement .input-group-addon-right{ - border:none; - border-left:0px; - border-right:1px solid #ddd; +.input-group.inputElement .input-group-addon-right { + border: none; + border-left: 0px; + border-right: 1px solid #ddd; } .fieldBlockContainer textarea.inputElement { height: auto; @@ -2516,7 +2516,7 @@ th { height: 100vh; } .p-xy-8 { - padding: 8px !important; + padding: 8px !important; } #advanceSearchHolder { max-height: 0px; @@ -5595,8 +5595,8 @@ TN-34230 .row .nav > li > a:hover { .width75per { width: 75%; } -.width100per{ - width: 100% !important; +.width100per { + width: 100% !important; } #_mbox_pwd { padding: 3px 8px; @@ -6129,8 +6129,8 @@ div.tooltip-inner { } #detailView .detailview-table .input-group.editElement, .calendar-timezone .input-group.editElement { - width: auto; - display: inline-flex; + width: auto; + display: inline-flex; } .summaryViewEntries .input-group.editElement { width: auto; @@ -6151,8 +6151,8 @@ div.tooltip-inner { .editViewContents .fieldValue .referencefield-wrapper { display: inline-block; } -.fieldValueWidth80{ - width:80% !important; +.fieldValueWidth80 { + width: 80% !important; } .input-group-addon { line-height: inherit; @@ -6170,6 +6170,8 @@ div.tooltip-inner { .referencefield-wrapper .input-group { width: 100%; max-width: 325px; + min-width: unset; + /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control { @@ -7738,29 +7740,32 @@ body .fc { padding: 30px; } } -#vt-mask{ - border: 0; - margin: 0; - padding: 0; - position: relative; - display: inline-flex; - left: 0; - top: 0; - min-height: 100%; - min-width: 100%; - height: auto; - width: auto; - opacity: 0; - z-index: 10001; - background-color: #fff; - } - .fieldBlockContainer .inputElement.textAreaElement { - max-width:none; - width: 100%; +#vt-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 10001; + background-color: #fff; + position: relative; + display: inline-flex; +} +.fieldBlockContainer .inputElement.textAreaElement { + max-width: none; + width: 100%; } - .detailview-table textarea.inputElement.form-control { - max-width:none; - width: 90%; + max-width: none; + width: 90%; +} +#vt-mask { + position: relative; + display: inline-flex; } -/*# sourceMappingURL=style.css.map */ \ No newline at end of file diff --git a/layouts/v7/skins/vtiger/style.less b/layouts/v7/skins/vtiger/style.less index ab00ace1d69f2a9ca9284569c23aa00ed28af275..66ee042487c7c024d55bf769362b149dcad00545 100644 --- a/layouts/v7/skins/vtiger/style.less +++ b/layouts/v7/skins/vtiger/style.less @@ -6918,6 +6918,7 @@ div.tooltip-inner{ .referencefield-wrapper .input-group{ width: 100%; max-width: 325px; + min-width: unset; /* of .input-group above */ } .summaryViewEntries .inputElement.form-control, .detailview-table .inputElement.form-control{ @@ -8699,4 +8700,4 @@ body .fc { .detailview-table textarea.inputElement.form-control { max-width:none; width: 90%; -} \ No newline at end of file +} diff --git a/modules/Calendar/models/Field.php b/modules/Calendar/models/Field.php index 05d260d74cec283c667dbae97de0f1ac0084c79d..cb81df21944800b02ad1010760b1f81a211bc105 100644 --- a/modules/Calendar/models/Field.php +++ b/modules/Calendar/models/Field.php @@ -55,6 +55,8 @@ class Calendar_Field_Model extends Vtiger_Field_Model { return 'reminder'; } else if($this->getName() == 'recurringtype') { return 'recurrence'; + } else if($this->get('uitype') == '9'){ + return 'percentage'; } $webserviceField = $this->getWebserviceFieldObject(); return $webserviceField->getFieldDataType(); diff --git a/modules/Documents/models/Field.php b/modules/Documents/models/Field.php index dec3a612e5377d453728654a61bff4a2dff7433a..37542d6d935feb3ed616e280fa5dfc60498fc025 100644 --- a/modules/Documents/models/Field.php +++ b/modules/Documents/models/Field.php @@ -44,5 +44,17 @@ class Documents_Field_Model extends Vtiger_Field_Model { } return false; } + + //The AjaxEditable for the RTE field is not allowed + public function isAjaxEditable() { + $result = parent::isAjaxEditable(); + if($result && $this->get('uitype') == 19) { + return false; + } else if(!$result) { + return false; + } else { + return true; + } + } } \ No newline at end of file diff --git a/modules/Emails/models/Record.php b/modules/Emails/models/Record.php index 44d3e41e78df7235de375f3b7c5cc75c76738474..7f5e1d99ec23cdc7cc82cc8deb6a321772502065 100644 --- a/modules/Emails/models/Record.php +++ b/modules/Emails/models/Record.php @@ -220,9 +220,10 @@ class Emails_Record_Model extends Vtiger_Record_Model { $status = $mailer->Send(true); } if(!$status) { - $status = $mailer->getError(); - //If mailer error, then update emailflag as saved - if($status){ + // Before inspecting for mailer error do a explict check on its configuration. + $err = $mailer->_serverConfigured ? $mailer->getError() : vtranslate("LBL_MAIL_SERVER_DESCRIPTION", "Settings:Vtiger"); + // If mailer error, then update emailflag as saved + if($err){ $this->updateEmailFlag(); } } else { diff --git a/modules/Migration/schema/811_to_812.php b/modules/Migration/schema/811_to_812.php new file mode 100644 index 0000000000000000000000000000000000000000..9fcc3412d7c54fcbd54e3f5c81d08297b6d5456d --- /dev/null +++ b/modules/Migration/schema/811_to_812.php @@ -0,0 +1,28 @@ +<?php +if (defined('VTIGER_UPGRADE')) { + global $adb, $current_user; + $db = PearDatabase::getInstance(); + + // Increase column length to hold longer JSONified value. + $db->pquery('ALTER TABLE com_vtiger_workflows MODIFY COLUMN schannualdates VARCHAR(500)', array()); + + // Trim the space in value. + $db->pquery('UPDATE vtiger_projecttaskstatus set projecttaskstatus = "Canceled" where projecttaskstatus = "Canceled "', array()); + + // Ensure related-tab for ModComments on Inventory modules (if missed in previous migration) + $modCommentsInstance = Vtiger_Module_Model::getInstance('ModComments'); + $modCommentFieldInstance = Vtiger_Field_Model::getInstance('related_to', $modCommentsInstance); + foreach(getInventoryModules() as $refModuleName) { + $refModuleModel = Vtiger_Module_Model::getInstance($refModuleName); + $rs = $db->pquery("SELECT 1 FROM vtiger_relatedlists WHERE tabid=? and related_tabid=? and relationfieldid=? limit 1", array( + $refModuleModel->id, $modCommentsInstance->id, $modCommentFieldInstance->id + )); + if (!$db->num_rows($rs)) { + $refModuleModel->setRelatedList($modCommentsInstance, "ModComments", '', 'get_comments', $modCommentFieldInstance->id); + } + } + + // Resize column width to text (instead of varchar) + $db->pquery("ALTER TABLE vtiger_shorturls MODIFY COLUMN handler_data text"); + +} diff --git a/modules/Settings/CronTasks/models/Record.php b/modules/Settings/CronTasks/models/Record.php index bdae29289fd19b81e562dcc7ffb79bcfc835e9ed..3de3374b8029bc217a77728d97b5ba04dbfd0492 100644 --- a/modules/Settings/CronTasks/models/Record.php +++ b/modules/Settings/CronTasks/models/Record.php @@ -97,7 +97,8 @@ class Settings_CronTasks_Record_Model extends Settings_Vtiger_Record_Model { return $lastScannedTime; } else { $dateTimeList = explode(" ", $lastScannedTime); - return $dateTimeList[0]." ".date('g:i:sa', strtotime($dateTimeList[1])); + // hour:min AM/PM in 12 hour format (there is no seconds) + return $dateTimeList[0]." ".date('g:i A', strtotime($dateTimeList[1])); } } else { return ''; @@ -232,4 +233,4 @@ class Settings_CronTasks_Record_Model extends Settings_Vtiger_Record_Model { public function getMinimumFrequency() { return getMinimumCronFrequency()*60; } -} \ No newline at end of file +} diff --git a/modules/Settings/LayoutEditor/actions/Field.php b/modules/Settings/LayoutEditor/actions/Field.php index d3d85189626718f2a4bd07907b6e33a132dbb3cf..1dc5bd7f22fa6b9f2ea9a7058012dea4de63f916 100644 --- a/modules/Settings/LayoutEditor/actions/Field.php +++ b/modules/Settings/LayoutEditor/actions/Field.php @@ -97,7 +97,7 @@ class Settings_LayoutEditor_Field_Action extends Settings_Vtiger_Index_Action { $fieldInstance->set('masseditable', $massEditable); } - $defaultValue = $fieldInstance->get('defaultvalue'); + $defaultValue = decode_html($fieldInstance->get('defaultvalue')); if(!is_null($request->get('fieldDefaultValue', null))) { if(is_array($request->get('fieldDefaultValue'))) { @@ -109,6 +109,10 @@ class Settings_LayoutEditor_Field_Action extends Settings_Vtiger_Index_Action { { $defaultValue=Vtiger_Time_UIType::getTimeValueWithSeconds($defaultValue); } + // Converting the date value to DB format (yyyy-mm-dd) + if ($defaultValue && $fieldInstance->get('uitype')=='5') { + $defaultValue = Vtiger_Date_UIType::getDBInsertedValue($defaultValue); + } $fieldInstance->set('defaultvalue', $defaultValue); } @@ -125,13 +129,13 @@ class Settings_LayoutEditor_Field_Action extends Settings_Vtiger_Index_Action { if (isset($defaultValue)) { if ($defaultValue && $fieldInfo['type'] == 'date') { $defaultValue = DateTimeField::convertToUserFormat($defaultValue); - } else if (!$defaultValue) { - $defaultValue = $fieldInstance->getDisplayValue($defaultValue); } else if (is_array($defaultValue)) { foreach ($defaultValue as $key => $value) { $defaultValue[$key] = $fieldInstance->getDisplayValue($value); } $defaultValue = Zend_Json::encode($defaultValue); + } else if ($defaultValue) { + $defaultValue = $fieldInstance->getDisplayValue($defaultValue); } } $fieldInfo['fieldDefaultValue'] = $defaultValue; @@ -200,6 +204,21 @@ class Settings_LayoutEditor_Field_Action extends Settings_Vtiger_Index_Action { foreach($fieldIds as $fieldId) { $fieldModel = Settings_LayoutEditor_Field_Model::getInstance($fieldId); $fieldInfo = $fieldModel->getFieldInfo(); + //The default value is set to response after reactivating the field. + $defaultValue = $fieldModel->getDefaultFieldValue(); + if (isset($defaultValue)) { + if ($defaultValue && $fieldInfo['type'] == 'date') { + $defaultValue = DateTimeField::convertToUserFormat($defaultValue); + } else if (!$defaultValue) { + $defaultValue = $fieldInstance->getDisplayValue($defaultValue); + } else if (is_array($defaultValue)) { + foreach ($defaultValue as $key => $value) { + $defaultValue[$key] = $fieldInstance->getDisplayValue($value); + } + $defaultValue = Zend_Json::encode($defaultValue); + } + } + $fieldInfo['fieldDefaultValue'] = $defaultValue; $responseData[] = array_merge(array('id'=>$fieldModel->getId(), 'blockid'=>$fieldModel->get('block')->id, 'customField'=>$fieldModel->isCustomField()),$fieldInfo); } $response->setResult($responseData); diff --git a/modules/Settings/LayoutEditor/models/Field.php b/modules/Settings/LayoutEditor/models/Field.php index f6b099546dab12bd703e3708be59158869d75273..f0ef76255d3ce30dd4d20d424a4cdbe495281296 100644 --- a/modules/Settings/LayoutEditor/models/Field.php +++ b/modules/Settings/LayoutEditor/models/Field.php @@ -439,7 +439,8 @@ class Settings_LayoutEditor_Field_Model extends Vtiger_Field_Model { if ($defaultValue) { if ($this->getFieldDataType() == 'currency') { - $defaultValue = $this->getCurrencyDisplayValue($defaultValue, true); + //The argument for $skipformatting parameter is passed false to get value with user preference. + $defaultValue = $this->getCurrencyDisplayValue($defaultValue, false); } else { $defaultValue = $this->getDisplayValue($defaultValue); } diff --git a/modules/Settings/LayoutEditor/models/Module.php b/modules/Settings/LayoutEditor/models/Module.php index 8de5303bbf8a00f908e5b97a49894be59d80879f..58e68b9ed1f6afb0eddbb394533903d61466660d 100644 --- a/modules/Settings/LayoutEditor/models/Module.php +++ b/modules/Settings/LayoutEditor/models/Module.php @@ -183,7 +183,8 @@ class Settings_LayoutEditor_Module_Model extends Vtiger_Module_Model { } else if (strtolower($fieldType) == 'time') { $defaultValue = Vtiger_Time_UIType::getTimeValueWithSeconds($defaultValue); } else if (strtolower($fieldType) == 'currency') { - $defaultValue = CurrencyField::convertToDBFormat($defaultValue, null, true); + //The argument for $skipformatting parameter is passed false to get the value in DB format($). + $defaultValue = CurrencyField::convertToDBFormat($defaultValue, null, false); } else if (strtolower($fieldType) == 'decimal') { $defaultValue = CurrencyField::convertToDBFormat($defaultValue, null, true); } diff --git a/modules/Settings/MailConverter/handlers/MailRecord.php b/modules/Settings/MailConverter/handlers/MailRecord.php index ca86736b0cc48c5a30376c882dbf243bc9bf50de..c708905f23018d3fe7f5eaf73baefd05ceb56031 100644 --- a/modules/Settings/MailConverter/handlers/MailRecord.php +++ b/modules/Settings/MailConverter/handlers/MailRecord.php @@ -143,7 +143,8 @@ class Vtiger_MailRecord { if ($iconv_function === NULL) $iconv_function = function_exists('iconv'); if($mb_function) { - if(!$from) $from = mb_detect_encoding($input); + // if source charset is not determined or not-encoded as per imap_mime_decode + if(!$from || $from == 'default') $from = mb_detect_encoding($input); if(strtolower(trim($to)) == strtolower(trim($from))) { return $input; diff --git a/modules/Settings/Picklist/models/Module.php b/modules/Settings/Picklist/models/Module.php index e5c4274cf7c3beb37160eb43abff491031f27e06..a46aebd0d53535bc5e6cbbdc364ab5a563e09f5c 100644 --- a/modules/Settings/Picklist/models/Module.php +++ b/modules/Settings/Picklist/models/Module.php @@ -499,7 +499,7 @@ class Settings_Picklist_Module_Model extends Vtiger_Module_Model { $db = PearDatabase::getInstance(); $primaryKey = Vtiger_Util_Helper::getPickListId($fieldName); $colums = $db->getColumnNames("vtiger_$fieldName"); - if(in_array('color',$colums)) { + if(is_array($columns) && in_array('color',$colums)) { $query = 'SELECT '.$primaryKey.',color,'.$fieldName.' FROM vtiger_'.$fieldName; $result = $db->pquery($query, array()); $pickListColorMap = array(); diff --git a/modules/Settings/Roles/actions/Save.php b/modules/Settings/Roles/actions/Save.php index 62124df5dd03a3815b90262f311bfb90b1e17aa9..5b2587ea4455a2eae77a656d59cef4de1de57f96 100644 --- a/modules/Settings/Roles/actions/Save.php +++ b/modules/Settings/Roles/actions/Save.php @@ -25,6 +25,10 @@ class Settings_Roles_Save_Action extends Vtiger_Action_Controller { $recordId = $request->get('record'); $roleName = $request->get('rolename'); $allowassignedrecordsto = $request->get('allowassignedrecordsto'); + $duplicate = Settings_Roles_Record_Model::getInstanceByName($roleName,array($recordId)); + if($duplicate) { + throw new Exception(vtranslate('LBL_DUPLICATES_EXIST',$request->getModule(false))); + } $moduleModel = Settings_Vtiger_Module_Model::getInstance($qualifiedModuleName); if(!empty($recordId)) { diff --git a/modules/Settings/Workflows/models/Record.php b/modules/Settings/Workflows/models/Record.php index fa99605f44e46cfd014f9d943a38e6373e0b2b19..5f3fb4530b58b4fe7642e652c5ef5d505a95cbe0 100644 --- a/modules/Settings/Workflows/models/Record.php +++ b/modules/Settings/Workflows/models/Record.php @@ -245,8 +245,18 @@ class Settings_Workflows_Record_Model extends Settings_Vtiger_Record_Model { $isDateValue = true; $valueArray[$i] = DateTimeField::convertToUserFormat($valueArray[$i]); } + if(Vtiger_Functions::isTimeValue($valueArray[$i])){ + $isTimeValue = true; + $userModel = Users_Record_Model::getCurrentUserModel(); + $hourFormat = $userModel->get('hour_format'); + if($hourFormat == '24') { + $valueArray[$i] = date('H:i', strtotime($valueArray[$i])); + } else { + $valueArray[$i] = date('g:i A', strtotime($valueArray[$i])); + } + } } - if($isDateValue) { + if($isDateValue || $isTimeValue ) { $value = implode(',', $valueArray); } // End diff --git a/modules/Users/DefaultDataPopulator.php b/modules/Users/DefaultDataPopulator.php index 6b97a414c4b372cf6527162a90a703a5e0c6a45c..227cd821535f1ab4cc32dcf81170b83035f36c4b 100644 --- a/modules/Users/DefaultDataPopulator.php +++ b/modules/Users/DefaultDataPopulator.php @@ -1820,6 +1820,7 @@ Should any need arise,please do give us a call.'; $this->db->query("insert into vtiger_actionmapping values(5,'Import',0)"); $this->db->query("insert into vtiger_actionmapping values(6,'Export',0)"); //$this->db->query("insert into vtiger_actionmapping values(7,'AddBusinessCard',0)"); + $this->db->query("insert into vtiger_actionmapping values(7,'CreateView',0)"); $this->db->query("insert into vtiger_actionmapping values(8,'Merge',0)"); $this->db->query("insert into vtiger_actionmapping values(1,'VendorEditView',1)"); $this->db->query("insert into vtiger_actionmapping values(4,'VendorDetailView',1)"); diff --git a/modules/Vtiger/helpers/Logger.php b/modules/Vtiger/helpers/Logger.php index 9a8ac5e00f0f155eb14a41a1dde36773fb8a1d6d..f9a61ef77bbd1fcc9f1efb6f511392474c7a60d7 100644 --- a/modules/Vtiger/helpers/Logger.php +++ b/modules/Vtiger/helpers/Logger.php @@ -32,7 +32,7 @@ class Logger { if (!self::$initialized) { global $PERFORMANCE_CONFIG; // Check if the performance config is set and debug logging is enabled - if (isset($PERFORMANCE_CONFIG) && isset($PERFORMANCE_CONFIG['LOGLEVEl_DEBUG']) && $PERFORMANCE_CONFIG['LOGLEVEl_DEBUG']) { + if (isset($PERFORMANCE_CONFIG) && isset($PERFORMANCE_CONFIG['LOGLEVEL_DEBUG']) && $PERFORMANCE_CONFIG['LOGLEVEL_DEBUG']) { // Set the default log level to 100 and the log file path self::$logLevel = 100; self::$filePath = "logs/vtigercrm.log"; @@ -50,7 +50,7 @@ class Logger { // Check if log level is set (logger is initialized) if (self::$logLevel) { - $log = new MonologLogger($channel); + $log = new MonologLoggerEx($channel); $handler = new StreamHandler(self::$filePath, self::$logLevel); // Set a custom formatter if customFormatter is true @@ -78,6 +78,13 @@ class Logger { } +// Define extended version of Monolog Logger to support functions +class MonologLoggerEx extends MonologLogger { + function fatal($message, $context = array()) { + $this->error($message, $context); + } +} + // Define a custom log formatter use Monolog\Formatter\FormatterInterface; diff --git a/modules/Vtiger/helpers/Util.php b/modules/Vtiger/helpers/Util.php index ee94c7792e56776804a92ca6debbb92007fbe75c..e55da2bc90bcdf08668f1c0357c6daa050f18967 100644 --- a/modules/Vtiger/helpers/Util.php +++ b/modules/Vtiger/helpers/Util.php @@ -182,7 +182,14 @@ class Vtiger_Util_Helper { */ if ($currentUser->get('date_format') === 'mm-dd-yyyy') { $dateInUserFormat = str_replace('-', '/', $dateInUserFormat); - } + } else if ($currentUser->get('date_format') === 'dd/mm/yyyy'){ + // strtotime expects m/d/y format - adjusting the format to make it friendly to its convention + $dateArray = explode('/', $dateInUserFormat); + $temp = $dateArray[0]; + $dateArray[0] = $dateArray[1]; + $dateArray[1] = $temp; + $dateInUserFormat = implode('/', $dateArray); + } $date = strtotime($dateInUserFormat); $formatedDate = vtranslate('LBL_'.date('D', $date)) . ' ' . date('d', $date) . ' ' . vtranslate('LBL_'.date('M', $date)); @@ -262,13 +269,16 @@ class Vtiger_Util_Helper { $dateTimeInUserFormat = Vtiger_Datetime_UIType::getDisplayDateTimeValue($dateTime); } - list($dateInUserFormat, $timeInUserFormat) = explode(' ', $dateTimeInUserFormat); - list($hours, $minutes, $seconds) = explode(':', $timeInUserFormat); - - $displayTime = $hours .':'. $minutes; - if ($currentUser->get('hour_format') === '12') { - $displayTime = Vtiger_Time_UIType::getTimeValueInAMorPM($displayTime); + list($dateInUserFormat, $timeInUserFormat, $meridiem) = explode(' ', $dateTimeInUserFormat); + if($meridiem && $currentUser->get('hour_format') === '12' ){ + $displayTime = $timeInUserFormat.' '.$meridiem; + } else { + list($hours, $minutes, $seconds) = explode(':', $timeInUserFormat); + $displayTime = $hours .':'. $minutes; + if ($currentUser->get('hour_format') === '12') { + $displayTime = Vtiger_Time_UIType::getTimeValueInAMorPM($displayTime); } + } /** * To support strtotime() for 'mm-dd-yyyy' format the separator should be '/' @@ -277,6 +287,13 @@ class Vtiger_Util_Helper { */ if ($currentUser->get('date_format') === 'mm-dd-yyyy') { $dateInUserFormat = str_replace('-', '/', $dateInUserFormat); + } else if ($currentUser->get('date_format') === 'dd/mm/yyyy'){ + // strtotime expects the format m/d/y making changes to its convenient + $dateArray = explode('/', $dateInUserFormat); + $temp = $dateArray[0]; + $dateArray[0] = $dateArray[1]; + $dateArray[1] = $temp; + $dateInUserFormat = implode('/', $dateArray); } $date = strtotime($dateInUserFormat); @@ -1279,4 +1296,4 @@ class Vtiger_Util_Helper { } return $fieldValue; } -} \ No newline at end of file +} diff --git a/packages/vtiger/optional/Google.zip b/packages/vtiger/optional/Google.zip index d6b3a6e9e172401dababc4d5668f93afc4ee0e29..1caf84d624c4337655d246d32b8a412766691f65 100644 Binary files a/packages/vtiger/optional/Google.zip and b/packages/vtiger/optional/Google.zip differ diff --git a/pkg/vtiger/modules/EmailTemplates/layouts/v7/modules/EmailTemplates/resources/List.js b/pkg/vtiger/modules/EmailTemplates/layouts/v7/modules/EmailTemplates/resources/List.js index 9507f3ba063f6e88ed4103a55f812519ab6a8483..28bbc8a14ef9b0a460a3ae862be5be2923f7a9ce 100644 --- a/pkg/vtiger/modules/EmailTemplates/layouts/v7/modules/EmailTemplates/resources/List.js +++ b/pkg/vtiger/modules/EmailTemplates/layouts/v7/modules/EmailTemplates/resources/List.js @@ -45,6 +45,8 @@ Vtiger_List_Js("EmailTemplates_List_Js", { } listInstance.clearList(); listInstance.loadListViewRecords(); + // to force reload of getInstance next attempt. + Vtiger_List_Js.listInstance=false; } ); }) diff --git a/pkg/vtiger/modules/Google/modules/Google/connectors/Contacts.php b/pkg/vtiger/modules/Google/modules/Google/connectors/Contacts.php index 91ea5dfaf3b7091242aa6ebb16e7b007ae6abbc0..373a0c27fcf96a532dad23d589f72282d749ea4b 100644 --- a/pkg/vtiger/modules/Google/modules/Google/connectors/Contacts.php +++ b/pkg/vtiger/modules/Google/modules/Google/connectors/Contacts.php @@ -18,20 +18,20 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { protected $createdRecords; protected $maxResults = 100; - const CONTACTS_URI = 'https://www.google.com/m8/feeds/contacts/default/full'; + const CONTACTS_URI = 'https://people.googleapis.com/v1/people/me/connections'; - const CONTACTS_GROUP_URI = 'https://www.google.com/m8/feeds/groups/default/full'; + const PEOPLE_URI = 'https://people.googleapis.com/v1/'; - const CONTACTS_BATCH_URI = 'https://www.google.com/m8/feeds/contacts/default/full/batch'; + const CONTACTS_GROUP_URI = 'https://people.googleapis.com/v1/contactGroups'; - const USER_PROFILE_INFO = 'https://www.googleapis.com/oauth2/v1/userinfo'; + const CONTACTS_BATCH_CREATE_URI = 'https://people.googleapis.com/v1/people:batchCreateContacts'; - protected $NS = array( - 'gd' => 'http://schemas.google.com/g/2005', - 'gContact' => 'http://schemas.google.com/contact/2008', - 'batch' => 'http://schemas.google.com/gdata/batch' - ); + const CONTACTS_BATCH_UPDATE_URI = 'https://people.googleapis.com/v1/people:batchUpdateContacts'; + const CONTACTS_BATCH_DELETE_URI = 'https://people.googleapis.com/v1/people:batchDeleteContacts'; + + const USER_PROFILE_INFO = 'https://www.googleapis.com/oauth2/v1/userinfo'; + protected $apiVersion = '3.0'; private $groups = null; @@ -77,16 +77,11 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { 'name' => 'gContact:event', 'types' => array('anniversary','custom') ), - 'description' => array( - 'name' => 'content' - ), + 'custom' => array( 'name' => 'gContact:userDefinedField' ), - 'url' => array( - 'name' => 'gContact:website', - 'types' => array('profile','blog','home-page','work','custom') - ) + ); public function __construct($oauth2Connection) { @@ -161,17 +156,12 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { $addresses = $googleRecord->getAddresses(); $googleFieldValue = $this->getMappedValue($addresses, $googleFieldDetails); break; - case 'content' : - $googleFieldValue = $googleRecord->getDescription(); - break; + case 'gContact:userDefinedField' : $userDefinedFields = $googleRecord->getUserDefineFieldsValues(); $googleFieldValue = $this->getMappedValue($userDefinedFields, $googleFieldDetails); break; - case 'gContact:website' : - $websites = $googleRecord->getUrlFields(); - $googleFieldValue = $this->getMappedValue($websites, $googleFieldDetails); - break; + } return $googleFieldValue; } @@ -278,10 +268,8 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { } function fetchContactsFeed($query) { - $query['alt'] = 'json'; if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken(); $headers = array( - 'GData-Version' => $this->apiVersion, 'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' . $this->apiConnection->token['access_token']['access_token'], ); @@ -292,7 +280,7 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { function getContactListFeed($query) { $feed = $this->fetchContactsFeed($query); $decoded_feed = json_decode($feed,true); - return $decoded_feed['feed']; + return $decoded_feed; } function googleFormat($date) { @@ -307,77 +295,88 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { public function getContacts($SyncState, $user = false) { if(!$user) $user = Users_Record_Model::getCurrentUserModel(); $query = array( - 'max-results' => $this->maxResults, - 'start-index' => 1, - 'orderby' => 'lastmodified', - 'sortorder' => 'ascending', + 'pageSize' => $this->maxResults, + 'requestSyncToken' => true, + 'sortOrder' => 'LAST_MODIFIED_ASCENDING', + 'personFields' => 'addresses,birthdays,emailAddresses,memberships,names,organizations,phoneNumbers,userDefined,metadata' ); - if(!isset($this->selectedGroup)) - $this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user); - - if($this->selectedGroup != '' && $this->selectedGroup != 'all') { - if($this->selectedGroup == 'none') return array(); - if(!isset($this->groups)) { - $this->groups = $this->pullGroups(TRUE); - } - if(in_array($this->selectedGroup, $this->groups['entry'])) - $query['group'] = $this->selectedGroup; - else - return array(); - } - - if (Google_Utils_Helper::getSyncTime('Contacts', $user)) { - $query['updated-min'] = $this->googleFormat(Google_Utils_Helper::getSyncTime('Contacts', $user)); - $query['showdeleted'] = 'true'; - } - - $feed = $this->getContactListFeed($query); - - $this->totalRecords = $feed['openSearch$totalResults']['$t']; - $contactRecords = array(); - if (php7_count($feed['entry']) > 0) { - $lastEntry = end($feed['entry']); - $maxModifiedTime = date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat($lastEntry['updated']['$t'])) + 1); - if ($this->totalRecords > $this->maxResults) { - if (!Google_Utils_Helper::getSyncTime('Contacts', $user)) { - $query['updated-min'] = $this->googleFormat(date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat($lastEntry['updated']['$t'])))); - $query['start-index'] = $this->maxResults; + $contactRecords = $feed = array(); + + do { + if($feed['nextPageToken']) { + $query['pageToken'] = $feed['nextPageToken']; + } + + if ($SyncState->getSyncToken()) { + $query['syncToken'] = $SyncState->getSyncToken(); + } + + $feed = $this->getContactListFeed($query); + + if($feed['error']) { + $SyncState->setSyncToken(''); + $this->updateSyncState($SyncState); + unset($query['syncToken']); + $feed = $this->getContactListFeed($query); + } + + if($feed['connections']) + $contactRecords = array_merge($contactRecords, $feed['connections']); + + if($feed['nextSyncToken']) { + $SyncState->setSyncToken($feed['nextSyncToken']); + $this->updateSyncState($SyncState); + } + + } while ($feed['nextPageToken']); + + if(count($contactRecords)) { + if (Google_Utils_Helper::getSyncTime('Contacts', $user)) { + $preModifiedTime = Google_Utils_Helper::getSyncTime('Contacts', $user); + } + + if(!isset($this->selectedGroup)) + $this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user); + + if($this->selectedGroup != '' && $this->selectedGroup != 'all') { + if($this->selectedGroup == 'none') return array(); + if(!isset($this->groups)) { + $this->groups = $this->pullGroups(TRUE); } - if($this->selectedGroup != '' && $this->selectedGroup != 'all') { - $query['group'] = $this->selectedGroup; - } - $query['max-results'] = (5000); - $query['updated-max'] = $this->googleFormat($maxModifiedTime); - $extendedFeed = $this->getContactListFeed($query); - if(is_array($extendedFeed['entry'])) { - $contactRecords = array_merge($feed['entry'], $extendedFeed['entry']); - } else { - $contactRecords = $feed['entry']; - } - } else { - $contactRecords = $feed['entry']; + if(in_array($this->selectedGroup, $this->groups['entry'])) + $group = 'contactGroups/'.$this->selectedGroup; } - } - - $googleRecords = array(); - foreach ($contactRecords as $i => $contact) { - $recordModel = Google_Contacts_Model::getInstanceFromValues(array('entity' => $contact)); - $deleted = false; - if(array_key_exists('gd$deleted', $contact)) { - $deleted = true; + $lastEntry = end($contactRecords); + $maxModifiedTime = date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat($lastEntry['metadata']['sources'][0]['updateTime'])) + 1); + + $googleRecords = array(); + foreach ($contactRecords as $i => $contact) { + $updateTime = date('Y-m-d H:i:s', strtotime(Google_Contacts_Model::vtigerFormat($contact['metadata']['sources'][0]['updateTime'])) + 1); + if(strtotime($updateTime) >= strtotime($preModifiedTime) || $contact['metadata']['deleted']) { + if($group && $contact['memberships'] && $group != $contact['memberships'][0]['contactGroupMembership']['contactGroupResourceName']) { + continue; + } + + $recordModel = Google_Contacts_Model::getInstanceFromValues(array('entity' => $contact)); + $deleted = false; + if($contact['metadata']['deleted']) { + $deleted = true; + } + if (!$deleted) { + $recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode(WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE); + } else { + $recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode(WSAPP_SyncRecordModel::WSAPP_DELETE_MODE); + } + $googleRecords[$contact['resourceName']] = $recordModel; + } } - if (!$deleted) { - $recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode(WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE); + $this->createdRecords = count($googleRecords); + if (isset($maxModifiedTime)) { + Google_Utils_Helper::updateSyncTime('Contacts', $maxModifiedTime, $user); } else { - $recordModel->setType($this->getSynchronizeController()->getSourceType())->setMode(WSAPP_SyncRecordModel::WSAPP_DELETE_MODE); + Google_Utils_Helper::updateSyncTime('Contacts', false, $user); } - $googleRecords[$contact['id']['$t']] = $recordModel; - } - $this->createdRecords = php7_count($googleRecords); - if (isset($maxModifiedTime)) { - Google_Utils_Helper::updateSyncTime('Contacts', $maxModifiedTime, $user); - } else { - Google_Utils_Helper::updateSyncTime('Contacts', false, $user); + } return $googleRecords; } @@ -387,16 +386,14 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <String> <Xml> $batchFeed * @return <Mixed> */ - protected function sendBatchRequest($batchFeed) { + protected function sendBatchRequest($batchFeed, $url) { if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken(); $headers = array( - 'GData-Version' => $this->apiVersion, 'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' . $this->apiConnection->token['access_token']['access_token'], - 'If-Match' => '*', - 'Content-Type' => 'application/atom+xml', + 'Content-Type' => 'application/json' ); - $response = $this->fireRequest(self::CONTACTS_BATCH_URI, $headers, $batchFeed); + $response = $this->fireRequest($url, $headers, json_encode($batchFeed)); return $response; } @@ -412,113 +409,19 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <Google_Contacts_Model> $entity * @param <Users_Record_Model> $user */ - protected function addEntityDetailsToAtomEntry(&$entry,$entity,$user) { - $gdNS = $this->NS['gd']; - $gdName = $entry->addChild("name",'',$gdNS); - if($entity->get('salutationtype')) $gdName->addChild("namePrefix", Google_Utils_Helper::toGoogleXml($entity->get('salutationtype')),$gdNS); - if($entity->get('firstname')) $gdName->addChild("givenName", Google_Utils_Helper::toGoogleXml($entity->get('firstname')),$gdNS); - if($entity->get('lastname')) $gdName->addChild("familyName", Google_Utils_Helper::toGoogleXml($entity->get('lastname')),$gdNS); - $gdRel = $gdNS . '#'; - - if($entity->get('account_id') || $entity->get('title')) { - $gdOrganization = $entry->addChild("organization",null,$gdNS); - $gdOrganization->addAttribute("rel","http://schemas.google.com/g/2005#other"); - if($entity->get('account_id')) $gdOrganization->addChild("orgName", Google_Utils_Helper::toGoogleXml($entity->get('account_id')),$gdNS); - if($entity->get('title')) $gdOrganization->addChild("orgTitle", Google_Utils_Helper::toGoogleXml($entity->get('title')),$gdNS); - } - - if(!isset($this->fieldMapping)) { - $this->fieldMapping = Google_Utils_Helper::getFieldMappingForUser($user); - } - - foreach($this->fieldMapping as $vtFieldName => $googleFieldDetails) { - if(in_array($googleFieldDetails['google_field_name'],array('gd:givenName','gd:familyName','gd:orgTitle','gd:orgName','gd:namePrefix'))) - continue; - - switch ($googleFieldDetails['google_field_name']) { - case 'gd:email' : - if($entity->get($vtFieldName)) { - $gdEmail = $entry->addChild("email",'',$gdNS); - if($googleFieldDetails['google_field_type'] == 'custom') - $gdEmail->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label']))); - else - $gdEmail->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']); - $gdEmail->addAttribute("address", Google_Utils_Helper::toGoogleXml($entity->get($vtFieldName))); - if($vtFieldName == 'email') - $gdEmail->addAttribute("primary",'true'); - } - break; - case 'gContact:birthday' : - if($entity->get('birthday')) { - $gContactNS = $this->NS['gContact']; - $gContactBirthday = $entry->addChild("birthday",'',$gContactNS); - $gContactBirthday->addAttribute("when",$entity->get('birthday')); - } - break; - case 'gd:phoneNumber' : - if($entity->get($vtFieldName)) { - $gdPhoneMobile = $entry->addChild("phoneNumber",Google_Utils_Helper::toGoogleXml($entity->get($vtFieldName)),$gdNS); - if($googleFieldDetails['google_field_type'] == 'custom') - $gdPhoneMobile->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label']))); - else - $gdPhoneMobile->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']); - } - break; - case 'gd:structuredPostalAddress' : - if($vtFieldName == 'mailingaddress') { - if($entity->get('mailingstreet') || $entity->get('mailingpobox') || $entity->get('mailingzip') || - $entity->get('mailingcity') || $entity->get('mailingstate') || $entity->get('mailingcountry')) { - $gdAddressHome = $entry->addChild("structuredPostalAddress",null,$gdNS); - if($googleFieldDetails['google_field_type'] == 'custom') - $gdAddressHome->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label']))); - else - $gdAddressHome->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']); - if($entity->get('mailingstreet')) $gdAddressHome->addChild("street", Google_Utils_Helper::toGoogleXml($entity->get('mailingstreet')),$gdNS); - if($entity->get('mailingpobox')) $gdAddressHome->addChild("pobox", Google_Utils_Helper::toGoogleXml($entity->get('mailingpobox')),$gdNS); - if($entity->get('mailingzip')) $gdAddressHome->addChild("postcode", Google_Utils_Helper::toGoogleXml($entity->get('mailingzip')),$gdNS); - if($entity->get('mailingcity')) $gdAddressHome->addChild("city", Google_Utils_Helper::toGoogleXml($entity->get('mailingcity')),$gdNS); - if($entity->get('mailingstate')) $gdAddressHome->addChild("region", Google_Utils_Helper::toGoogleXml($entity->get('mailingstate')),$gdNS); - if($entity->get('mailingcountry')) $gdAddressHome->addChild("country", Google_Utils_Helper::toGoogleXml($entity->get('mailingcountry')),$gdNS); - } - } else { - if($entity->get('otherstreet') || $entity->get('otherpobox') || $entity->get('otherzip') || - $entity->get('othercity') || $entity->get('otherstate') || $entity->get('othercountry')) { - $gdAddressWork = $entry->addChild("structuredPostalAddress",null,$gdNS); - if($googleFieldDetails['google_field_type'] == 'custom') - $gdAddressWork->addAttribute("label",$this->mbEncode(decode_html($googleFieldDetails['google_custom_label']))); - else - $gdAddressWork->addAttribute("rel",$gdRel . $googleFieldDetails['google_field_type']); - if($entity->get('otherstreet')) $gdAddressWork->addChild("street", Google_Utils_Helper::toGoogleXml($entity->get('otherstreet')),$gdNS); - if($entity->get('otherpobox')) $gdAddressWork->addChild("pobox", Google_Utils_Helper::toGoogleXml($entity->get('otherpobox')),$gdNS); - if($entity->get('otherzip')) $gdAddressWork->addChild("postcode", Google_Utils_Helper::toGoogleXml($entity->get('otherzip')),$gdNS); - if($entity->get('othercity')) $gdAddressWork->addChild("city", Google_Utils_Helper::toGoogleXml($entity->get('othercity')),$gdNS); - if($entity->get('otherstate')) $gdAddressWork->addChild("region", Google_Utils_Helper::toGoogleXml($entity->get('otherstate')),$gdNS); - if($entity->get('othercountry')) $gdAddressWork->addChild("country", Google_Utils_Helper::toGoogleXml($entity->get('othercountry')),$gdNS); - } - } - break; - case 'content' : - if($entity->get($vtFieldName)) $entry->addChild('content', Google_Utils_Helper::toGoogleXml($entity->get($vtFieldName))); - break; - case 'gContact:userDefinedField' : - if($entity->get($vtFieldName) && $googleFieldDetails['google_custom_label']) { - $userDefinedField = $entry->addChild('userDefinedField','',$this->NS['gContact']); - $userDefinedField->addAttribute('key', $this->mbEncode(decode_html($googleFieldDetails['google_custom_label']))); - $userDefinedField->addAttribute('value', Google_Utils_Helper::toGoogleXml($this->mbEncode($entity->get($vtFieldName)))); - } - break; - case 'gContact:website' : - if($entity->get($vtFieldName)) { - $websiteField = $entry->addChild('website','',$this->NS['gContact']); - if($googleFieldDetails['google_field_type'] == 'custom') - $websiteField->addAttribute('label',$this->mbEncode(decode_html($googleFieldDetails['google_custom_label']))); - else - $websiteField->addAttribute('rel',$googleFieldDetails['google_field_type']); - $websiteField->addAttribute('href', Google_Utils_Helper::toGoogleXml($this->mbEncode($entity->get($vtFieldName)))); - } - break; - } + protected function getPersonDetails($resourceName) { + if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken(); + $headers = array( + 'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' . + $this->apiConnection->token['access_token']['access_token'], + ); + + $query = array('personFields'=>'metadata,memberships'); + $response = $this->fireRequest(self::PEOPLE_URI.$resourceName, $headers, $query, 'GET'); + if($response) { + $response = json_decode($response, true); } + return $response; } @@ -528,34 +431,40 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <Google_Contacts_Model> $entity * @param <Users_Record_Model> $user */ - protected function addUpdateContactEntry(&$feed,$entity,$user,$contactsGroupMap=array()) { - $batchNS = $this->NS['batch']; + protected function addUpdateContactEntry($entity, $user) { $baseEntryId = $entryId = $entity->get('_id'); - $entryId = str_replace('/base/','/full/',$entryId); - //fix for issue https://code.google.com/p/gdata-issues/issues/detail?id=2129 - $entry = $feed->addChild('entry'); - $entry->addChild('id','update',$batchNS); - $batchOperation = $entry->addChild('operation','',$batchNS); - $batchOperation->addAttribute('type','update'); - $category = $entry->addChild("category"); - $category->addAttribute("scheme","http://schemas.google.com/g/2005#kind"); - $category->addAttribute("term","http://schemas.google.com/g/2008#contact"); - $entry->addChild('id',$entryId); + if(strpos($entryId, '/base/') !== false) { + $entryId = explode('/base/', $entryId); + $entryId = 'people/'.$entryId[1]; + } else { + $entryId = $baseEntryId; + } + + $personData = $this->getPersonDetails($entryId); if(!$user) $user = Users_Record_Model::getCurrentUserModel (); if(!isset($this->selectedGroup)) $this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user); - - if(array_key_exists($baseEntryId, $contactsGroupMap)) { - $groupMemberShip = $entry->addChild('groupMembershipInfo','',$this->NS['gContact']); - $groupMemberShip->addAttribute('href',$contactsGroupMap[$baseEntryId]); - } elseif($this->selectedGroup != '' && $this->selectedGroup != 'all') { - $groupMemberShip = $entry->addChild('groupMembershipInfo','',$this->NS['gContact']); - $groupMemberShip->addAttribute('href',$this->selectedGroup); +if($personData["memberships"][0]["contactGroupMembership"]["contactGroupResourceName"]) { + $groupId = $personData["memberships"][0]["contactGroupMembership"]["contactGroupResourceName"]; + } else if($this->selectedGroup != '' && $this->selectedGroup != 'all') { + $groupId = 'contactGroups/'.$this->selectedGroup; } - $this->addEntityDetailsToAtomEntry($entry, $entity, $user); + $data["contactPerson"] = $this->getContactEntry($entity, $user); + + if($groupId) { + $data["contactPerson"]["memberships"][0]["contactGroupMembership"] = array("contactGroupResourceName"=> $groupId); + } else { + $data["contactPerson"]["memberships"][0]["contactGroupMembership"] = $personData["memberships"][0]["contactGroupMembership"]; + } + $data["contactPerson"]["resourceName"] = $entryId; + + $data["contactPerson"]["etag"] = $personData["etag"]; + + + return $data; } /** @@ -563,16 +472,17 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <SimpleXMLElement> $feed * @param <Google_Contacts_Model> $entity */ - protected function addDeleteContactEntry(&$feed,$entity) { - $batchNS = $this->NS['batch']; - $entryId = $entity->get('_id'); - $entryId = str_replace('/base/','/full/',$entryId); - //fix for issue https://code.google.com/p/gdata-issues/issues/detail?id=2129 - $entry = $feed->addChild('entry'); - $entry->addChild('id','delete',$batchNS); - $batchOperation = $entry->addChild('operation','',$batchNS); - $batchOperation->addAttribute('type','delete'); - $entry->addChild('id',$entryId); + protected function addDeleteContactEntry($entity) { + $baseEntryId = $entryId = $entity->get('_id'); + + if(strpos($entryId, '/base/') !== false) { + $entryId = explode('/base/', $entryId); + $entryId = 'people/'.$entryId[1]; + } else { + $entryId = $baseEntryId; + } + + return $entryId; } /** @@ -581,15 +491,7 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <Google_Contacts_Model> $entity * @param <Users_Record_Model> $user */ - protected function addCreateContactEntry(&$feed,$entity,$user) { - $entry = $feed->addChild("entry"); - $batchNS = $this->NS['batch']; - $entry->addChild("id","create",$batchNS); - $batchOperation = $entry->addChild("operation",'',$batchNS); - $batchOperation->addAttribute("type","insert"); - $category = $entry->addChild("category"); - $category->addAttribute("scheme","http://schemas.google.com/g/2005#kind"); - $category->addAttribute("term","http://schemas.google.com/g/2008#contact"); + protected function addCreateContactEntry($entity, $user) { if(!$user) $user = Users_Record_Model::getCurrentUserModel (); @@ -597,12 +499,9 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { $this->selectedGroup = Google_Utils_Helper::getSelectedContactGroupForUser($user); if($this->selectedGroup != '' && $this->selectedGroup != 'all') { - $groupMemberShip = $entry->addChild('groupMembershipInfo','',$this->NS['gContact']); - $groupMemberShip->addAttribute('href',$this->selectedGroup); + $groupId = 'contactGroups/'.$this->selectedGroup; } - - $this->addEntityDetailsToAtomEntry($entry, $entity, $user); - } + $data["contactPerson"] = $this->getContactEntry($entity, $user); /** * Function to add Retreive entry to atomfeed @@ -610,15 +509,10 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <Google_Contacts_Model> $entity * @param <Users_Record_Model> $user */ - protected function addRetrieveContactEntry(&$feed, $entity, $user) { - $entryId = $entity->get('_id'); - $entryId = str_replace('/base/','/full/',$entryId); - $entry = $feed->addChild("entry"); - $batchNS = $this->NS['batch']; - $entry->addChild("id","retrieve",$batchNS); - $batchOperation = $entry->addChild("operation",'',$batchNS); - $batchOperation->addAttribute("type","query"); - $entry->addChild('id',$entryId); + if($groupId) { + $data["contactPerson"]["memberships"][0]["contactGroupMembership"] = array("contactGroupResourceName"=>$groupId); + } + return $data; } /** @@ -628,62 +522,109 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { * @param <Users_Record_Model> $user * @return <Array> */ - protected function googleContactsGroupMap($records,$user) { - global $default_charset; - $contactsGroupMap = array(); + protected function getContactEntry($entity, $user) { + if($entity->get('salutationtype')) $data["names"][0]["honorificPrefix"] = $entity->get('salutationtype'); + if($entity->get('firstname')) $data["names"][0]["givenName"] = $entity->get('firstname'); + if($entity->get('lastname')) $data["names"][0]["familyName"] = $entity->get('lastname'); - $atom = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?> - <feed xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' - xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' />"); + if($entity->get('account_id') || $entity->get('title')) { + if($entity->get('account_id')) $data["organizations"][0]["name"] = $entity->get('account_id'); + if($entity->get('title')) $data["organizations"][0]["title"] = $entity->get('title'); + } - foreach($records as $record) { - $entity = $record->get('entity'); - $this->addRetrieveContactEntry($atom, $entity, $user); + if(!isset($this->fieldMapping)) { + $this->fieldMapping = Google_Utils_Helper::getFieldMappingForUser($user); } - $payLoad = html_entity_decode($atom->asXML(), ENT_QUOTES, $default_charset); - $response = $this->sendBatchRequest($payLoad); - - - $main1 = new SimpleXMLElement($payLoad); - $main1->entry->addAttribute('gd:etag', '*'); - $payLoad = $main1->asXML(); - - - if($response) { - $responseXml = simplexml_load_string($response); - $responseXml->registerXPathNamespace('gd', $this->NS['gd']); - $responseXml->registerXPathNamespace('gContact', $this->NS['gContact']); - $responseXml->registerXPathNamespace('batch', $this->NS['batch']); - - foreach($responseXml->entry as $entry) { - $entryXML = $entry->asXML(); - $p = xml_parser_create(); - xml_parse_into_struct($p, $entryXML, $xmlList, $index); - xml_parser_free($p); - - if(php7_count($xmlList)) { - foreach($xmlList as $tagDetails) { - - if($tagDetails['tag'] == 'ID') { - $googleContactId = $tagDetails['value']; + $contacts_module = Vtiger_Module_Model::getInstance('Contacts'); + foreach($this->fieldMapping as $vtFieldName => $googleFieldDetails) { + if(in_array($googleFieldDetails['google_field_name'],array('gd:givenName','gd:familyName','gd:orgTitle','gd:orgName','gd:namePrefix'))) + continue; + + switch ($googleFieldDetails['google_field_name']) { + case 'gd:email' : + if($entity->get($vtFieldName)) { + if($googleFieldDetails['google_field_type'] == 'custom') + $type = $this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])); + else + $type = $googleFieldDetails['google_field_type']; + $data["emailAddresses"][] = array("value"=>$entity->get($vtFieldName), "type"=>$type); + } + break; + case 'gContact:birthday' : + if($entity->get('birthday')) { + $date = $entity->get('birthday'); + $date = explode('-', $date); + $data["birthdays"][] = array("date"=> array('year'=>$date[0], 'month'=>$date[1], 'day'=>$date[2])); + } + break; + case 'gd:phoneNumber' : + if($entity->get($vtFieldName)) { + if($googleFieldDetails['google_field_type'] == 'custom') + $type = $this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])); + else + $type = $googleFieldDetails['google_field_type']; + + $data["phoneNumbers"][] = array("type"=>$type, "value"=>$entity->get($vtFieldName)); + } + break; + case 'gd:structuredPostalAddress' : + if($vtFieldName == 'mailingaddress') { + if($entity->get('mailingstreet') || $entity->get('mailingpobox') || $entity->get('mailingzip') || + $entity->get('mailingcity') || $entity->get('mailingstate') || $entity->get('mailingcountry')) { + if($googleFieldDetails['google_field_type'] == 'custom') + $type = $this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])); + else + $type = $googleFieldDetails['google_field_type']; + + $address = array(); + $address["type"] = $type; + + if($entity->get('mailingstreet')) $address["streetAddress"] = $entity->get('mailingstreet'); + if($entity->get('mailingpobox')) $address["poBox"]= $entity->get('mailingpobox'); + if($entity->get('mailingzip')) $address["postalCode"] = $entity->get('mailingzip'); + if($entity->get('mailingcity')) $address["city"] = $entity->get('mailingcity'); + if($entity->get('mailingstate')) $address["region"] = $entity->get('mailingstate'); + if($entity->get('mailingcountry')) $address["country"] = $entity->get('mailingcountry'); + + $data["addresses"][] = $address; } - - if($tagDetails['tag'] == 'GCONTACT:GROUPMEMBERSHIPINFO') { - $attribs = $tagDetails['attributes']; - $googleContactGroupId = $attribs['HREF']; - } - - if(isset($googleContactId) && isset($googleContactGroupId)) { - $contactsGroupMap[$googleContactId] = $googleContactGroupId; - unset($googleContactId);unset($googleContactGroupId); + } else { + if($entity->get('otherstreet') || $entity->get('otherpobox') || $entity->get('otherzip') || + $entity->get('othercity') || $entity->get('otherstate') || $entity->get('othercountry')) { + + if($googleFieldDetails['google_field_type'] == 'custom') + $type = $this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])); + else + $type = $googleFieldDetails['google_field_type']; + + $address = array(); + $address["type"] = $type; + + if($entity->get('otherstreet')) $address["streetAddress"] = $entity->get('otherstreet'); + if($entity->get('otherpobox')) $address["poBox"]= $entity->get('otherpobox'); + if($entity->get('otherzip')) $address["postalCode"] = $entity->get('otherzip'); + if($entity->get('othercity')) $address["city"] = $entity->get('othercity'); + if($entity->get('otherstate')) $address["region"] = $entity->get('otherstate'); + if($entity->get('othercountry')) $address["country"] = $entity->get('othercountry'); + + $data["addresses"][] = $address; + } } - } + break; + case 'gContact:userDefinedField' : + if($entity->get($vtFieldName) && $googleFieldDetails['google_custom_label']) { + $fieldModel = Vtiger_Field_Model::getInstance($vtFieldName,$contacts_module); + $data["userDefined"][] = array('key' => $this->mbEncode(decode_html($googleFieldDetails['google_custom_label'])), + 'value'=>$this->mbEncode($entity->get($vtFieldName))); + } + break; + } } - return $contactsGroupMap; + return $data; } /** @@ -696,49 +637,83 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { */ protected function pushChunk($records,$user) { global $default_charset; - $atom = new SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?> - <feed xmlns='http://www.w3.org/2005/Atom' xmlns:gContact='http://schemas.google.com/contact/2008' - xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' />"); - - if($records[0]->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) { - $contactsGroupMap = $this->googleContactsGroupMap($records,$user); - } + $createdContacts = $updatedContacts = $deletedContacts = array(); foreach ($records as $record) { $entity = $record->get('entity'); try { if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_UPDATE_MODE) { - $this->addUpdateContactEntry($atom,$entity,$user, $contactsGroupMap); + $personData = $this->addUpdateContactEntry($entity, $user); + $resourceName = $personData["contactPerson"]["resourceName"]; + $updatedContacts[$resourceName] = $personData["contactPerson"]; } else if ($record->getMode() == WSAPP_SyncRecordModel::WSAPP_DELETE_MODE) { - $this->addDeleteContactEntry($atom,$entity); + $deletedContacts[] = $this->addDeleteContactEntry($entity); } else { - $this->addCreateContactEntry($atom,$entity,$user); + $createdContacts[] = $this->addCreateContactEntry($entity, $user); } } catch (Exception $e) { continue; } } - $payLoad = html_entity_decode($atom->asXML(), ENT_QUOTES, $default_charset); - - $main1 = new SimpleXMLElement($payLoad); - $main1->entry->addAttribute('gd:etag', '*'); - $payLoad = $main1->asXML(); - - - $response = $this->sendBatchRequest($payLoad); - $responseXml = simplexml_load_string($response); - if($responseXml) { - $responseXml->registerXPathNamespace('gd', $this->NS['gd']); - $responseXml->registerXPathNamespace('gContact', $this->NS['gContact']); - $responseXml->registerXPathNamespace('batch', $this->NS['batch']); + + if(count($createdContacts)) { + $url = self::CONTACTS_BATCH_CREATE_URI; + $payload = array('contacts' => $createdContacts, 'readMask'=> 'metadata'); + $response = $this->sendBatchRequest($payload, $url); + $response = json_decode($response, true); + + if($response["createdPeople"]) { + foreach ($records as $index => $record) { + $newEntity = array(); + $entry = $response["createdPeople"][$index]; + $newEntityId = $entry["person"]["resourceName"]; + $newEntity['id']['$t'] = $newEntityId; + $newEntity['updated']['$t'] = (string)$entry["person"]["metadata"]["sources"][0]["updateTime"]; + $record->set('entity', $newEntity); + } + } else { + foreach ($records as $index => $record) { + $record->set('entity', array()); + } + } + } + + if(count($updatedContacts)) { + $url = self::CONTACTS_BATCH_UPDATE_URI; + $payload = array('contacts' => $updatedContacts, 'updateMask'=> 'addresses,birthdays,emailAddresses,memberships,names,organizations,phoneNumbers,userDefined', 'readMask'=> 'metadata'); + + $response = $this->sendBatchRequest($payload, $url); + $response = json_decode($response, true); + + if($response["updateResult"]) { + $response["updateResult"] = array_values($response["updateResult"]); foreach ($records as $index => $record) { $newEntity = array(); - $entry = $responseXml->entry[$index]; - $newEntityId = (string)$entry->id; - $newEntity['id']['$t'] = str_replace('/full/', '/base/', $newEntityId); - $newEntity['updated']['$t'] = (string)$entry->updated[0]; + $entry = $response["updateResult"][$index]; + $newEntityId = $entry["person"]["resourceName"]; + $newEntity['id']['$t'] = $newEntityId; + $newEntity['updated']['$t'] = (string)$entry["person"]["metadata"]["sources"][0]["updateTime"]; $record->set('entity', $newEntity); } + } else { + foreach ($records as $index => $record) { + $record->set('entity', array()); + } } + } + + if(count($deletedContacts)) { + $url = self::CONTACTS_BATCH_DELETE_URI; + $payload = array('resourceNames' => $deletedContacts); + $response = $this->sendBatchRequest($payload, $url); + foreach ($records as $index => $record) { + $record->set('entity', array()); + $newEntity = array(); + $newEntityId = $deletedContacts[$index]; + $newEntity['id']['$t'] = $newEntityId; + $newEntity['updated']['$t'] = $this->googleFormat(date('Y-m-d H:i:s')); + $record->set('entity', $newEntity); + } + } return $records; } @@ -852,28 +827,20 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { */ public function pullGroups($onlyIds = FALSE) { //max-results: If you want to receive all of the groups, rather than only the default maximum. - $query = array( - 'alt' => 'json', - 'max-results' => 1000, - ); - if($this->apiConnection->isTokenExpired()) $this->apiConnection->refreshToken(); + $query = array('pageSize'=>1000); $headers = array( - 'GData-Version' => $this->apiVersion, 'Authorization' => $this->apiConnection->token['access_token']['token_type'] . ' ' . $this->apiConnection->token['access_token']['access_token'] ); - $response = $this->fireRequest(self::CONTACTS_GROUP_URI, $headers,$query,'GET'); + $response = $this->fireRequest(self::CONTACTS_GROUP_URI, $headers, $query,'GET'); $decoded_resp = json_decode($response,true); - $feed = $decoded_resp['feed']; - $entries = $feed['entry']; - $groups = array( - 'title' => $feed['title']['$t'] - ); + $entries = $decoded_resp['contactGroups']; if(is_array($entries)) { foreach($entries as $entry) { + $resourceName = explode("/",$entry['resourceName']); $group = array( - 'id' => $entry['id']['$t'], - 'title' => $entry['title']['$t'] + 'id' => $resourceName[1], + 'title' => $entry['formattedName'] ); if($onlyIds) $group = $group['id']; $groups['entry'][] = $group; @@ -898,4 +865,54 @@ Class Google_Contacts_Connector extends WSAPP_TargetConnector { $response = $this->fireRequest(self::USER_PROFILE_INFO, $headers, array(), 'GET'); return $response; } + public function getSyncState() { + $result = null; + $db = PearDatabase::getInstance(); + if($this->getSynchronizeController()->getSyncType() == "app"){ + $result = $db->pquery("SELECT * FROM vtiger_wsapp_sync_state WHERE name=?", array($this->getName())); + } else { + $result = $db->pquery("SELECT * FROM vtiger_wsapp_sync_state WHERE name=? and userid=?", array($this->getName(), $this->getSynchronizeController()->user->id));//$this->getSYnchronizeController()->getSyncType(); + } + if ($db->num_rows($result) <= 0) { + return parent::getSyncState(); + } + $rowData = $db->raw_query_result_rowdata($result); + $stateValues = Zend_Json::decode($rowData['stateencodedvalues']); + $model = WSAPP_SyncStateModel::getInstanceFromQueryResult($stateValues); + return $model; + } + + function isSyncStateExists() { + $db = PearDatabase::getInstance(); + $result = null; + if($this->getSynchronizeController()->getSyncType() == "app"){ + $result = $db->pquery('SELECT 1 FROM vtiger_wsapp_sync_state where name=?', array($this->getName())); + } else { + $result = $db->pquery('SELECT 1 FROM vtiger_wsapp_sync_state where name=? and userid=?', array($this->getName(), $this->getSynchronizeController()->user->id)); + } + return ($db->num_rows($result) > 0) ? true : false; + } + + function updateSyncState(WSAPP_SyncStateModel $syncStateModel) { + $db = PearDatabase::getInstance(); + $encodedValues = Zend_Json::encode(array('synctrackerid' => $syncStateModel->getSyncTrackerId(), 'synctoken' => $syncStateModel->getSyncToken(), 'more' => $syncStateModel->get('more'))); + $query = 'INSERT INTO vtiger_wsapp_sync_state(stateencodedvalues,name,userid) VALUES (?,?,?)'; + $parameters = array($encodedValues, $this->getName(), $this->getSynchronizeController()->user->id); + if ($this->isSyncStateExists()) { + $query = ''; + $parameters = array(); + if($this->getSynchronizeController()->getSyncType() == "app"){ + $query = 'UPDATE vtiger_wsapp_sync_state SET stateencodedvalues=? where name=?'; + $parameters = array($encodedValues, $this->getName()); + }else { + $query = 'UPDATE vtiger_wsapp_sync_state SET stateencodedvalues=? where name=? and userid=?'; + $parameters = array($encodedValues, $this->getName(), $this->getSynchronizeController()->user->id); + } + } + $result = $db->pquery($query, $parameters); + if ($result) { + return true; + } + return false; + } } diff --git a/pkg/vtiger/modules/Google/modules/Google/helpers/Utils.php b/pkg/vtiger/modules/Google/modules/Google/helpers/Utils.php index 68695c71ee587ec8cd007e75301822aba80bb47e..564b9abeb5a2aea8eef280c1403c9e1831264c9d 100644 --- a/pkg/vtiger/modules/Google/modules/Google/helpers/Utils.php +++ b/pkg/vtiger/modules/Google/modules/Google/helpers/Utils.php @@ -274,6 +274,9 @@ class Google_Utils_Helper { $result = $db->pquery($sql,array($user->getId())); for($i=0;$i<$db->num_rows($result);$i++) { $row = $db->fetch_row($result); + if(in_array($row['google_field'], array('gd:website', 'content'))) { + continue; + } $fieldmapping[$row['vtiger_field']] = array( 'google_field_name' => $row['google_field'], 'google_field_type' => $row['google_field_type'], diff --git a/pkg/vtiger/modules/Google/modules/Google/models/Contacts.php b/pkg/vtiger/modules/Google/modules/Google/models/Contacts.php index bb04fa6a8a73e9383806f64264a87e30f528d570..4efa1d94661a794f40af65d367d386ea36c1573c 100644 --- a/pkg/vtiger/modules/Google/modules/Google/models/Contacts.php +++ b/pkg/vtiger/modules/Google/modules/Google/models/Contacts.php @@ -16,7 +16,7 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <string> id */ public function getId() { - return $this->data['entity']['id']['$t']; + return $this->data['entity']['resourceName'] ? $this->data['entity']['resourceName'] : $this->data['entity']['id']['$t']; } /** @@ -24,11 +24,13 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <date> modified time */ public function getModifiedTime() { - return $this->vtigerFormat($this->data['entity']['updated']['$t']); + $updateTime = $this->data['entity']['metadata']['sources'][0]['updateTime'] ? $this->data['entity']['metadata']['sources'][0]['updateTime'] : $this->data['entity']['updated']['$t']; + $updateTime = $updateTime? $updateTime : str_replace(' ', 'T', date('Y-m-d H:i:s')); + return $this->vtigerFormat($updateTime); } function getNamePrefix() { - $namePrefix = $this->data['entity']['gd$name']['gd$namePrefix']['$t']; + $namePrefix = $this->data['entity']['names'][0]['honorificPrefix']; return $namePrefix; } @@ -37,7 +39,7 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <string> $first name */ function getFirstName() { - $fname = $this->data['entity']['gd$name']['gd$givenName']['$t']; + $fname = $this->data['entity']['names'][0]['givenName']; return $fname; } @@ -46,7 +48,7 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <string> Last name */ function getLastName() { - $lname = $this->data['entity']['gd$name']['gd$familyName']['$t']; + $lname = $this->data['entity']['names'][0]['familyName']; return $lname; } @@ -55,15 +57,12 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <array> emails */ function getEmails() { - $arr = $this->data['entity']['gd$email']; + $arr = $this->data['entity']['emailAddresses']; $emails = array(); if (is_array($arr)) { foreach ($arr as $email) { - if(isset($email['rel'])) - $labelEmail = parse_url($email['rel'], PHP_URL_FRAGMENT); - else - $labelEmail = $email['label']; - $emails[$labelEmail] = $email['address']; + $labelEmail = $email['type']; + $emails[$labelEmail] = $email['value']; } } return $emails; @@ -74,15 +73,12 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <array> phone numbers */ function getPhones() { - $arr = $this->data['entity']['gd$phoneNumber']; + $arr = $this->data['entity']['phoneNumbers']; $phones = array(); if(is_array($arr)) { foreach ($arr as $phone) { - $phoneNo = $phone['$t']; - if(isset($phone['rel'])) - $labelPhone = parse_url($phone['rel'], PHP_URL_FRAGMENT); - else - $labelPhone = $phone['label']; + $phoneNo = $phone['value']; + $labelPhone = $phone['type']; $phones[$labelPhone] = $phoneNo; } } @@ -94,23 +90,21 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { * @return <array> Addresses */ function getAddresses() { - $arr = $this->data['entity']['gd$structuredPostalAddress']; + $arr = $this->data['entity']['addresses']; $addresses = array(); if(is_array($arr)) { foreach ($arr as $address) { $structuredAddress = array( - 'street' => $address['gd$street']['$t'], - 'pobox' => $address['gd$pobox']['$t'], - 'postcode' => $address['gd$postcode']['$t'], - 'city' => $address['gd$city']['$t'], - 'region' => $address['gd$region']['$t'], - 'country' => $address['gd$country']['$t'], - 'formattedAddress' => $address['gd$formattedAddress']['$t'] + 'street' => $address['streetAddress'], + 'pobox' => $address['poBox'], + 'postcode' => $address['postalCode'], + 'city' => $address['city'], + 'region' => $address['region'], + 'country' => $address['country'], + 'formattedAddress' => $address['formattedValue'] ); - if(isset($address['rel'])) - $labelAddress = parse_url($address['rel'], PHP_URL_FRAGMENT); - else - $labelAddress = $address['label']; + $labelAddress = $address['type']; + $addresses[$labelAddress] = $structuredAddress; $addresses[$labelAddress] = $structuredAddress; } } @@ -119,7 +113,7 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { function getUserDefineFieldsValues() { $fieldValues = array(); - $userDefinedFields = $this->data['entity']['gContact$userDefinedField']; + $userDefinedFields = $this->data['entity']['userDefined']; if(is_array($userDefinedFields) && php7_count($userDefinedFields)) { foreach($userDefinedFields as $userDefinedField) { $fieldName = $userDefinedField['key']; @@ -129,33 +123,26 @@ class Google_Contacts_Model extends WSAPP_SyncRecordModel { return $fieldValues; } - function getUrlFields() { - $websiteFields = $this->data['entity']['gContact$website']; - $urls = array(); - if(is_array($websiteFields)) { - foreach($websiteFields as $website) { - $url = $website['href']; - if(isset($website['rel'])) - $fieldName = $website['rel']; - else - $fieldName = $website['label']; - $urls[$fieldName] = $url; - } - } - return $urls; - } + function getBirthday() { - return $this->data['entity']['gContact$birthday']['when']; + if($this->data['entity']['birthdays'][0]['date']) { + $birthDate = $this->data['entity']['birthdays'][0]['date']['year'].'-'.$this->data['entity']['birthdays'][0]['date']['month'].'-'.$this->data['entity']['birthdays'][0]['date']['day']; + } else { + $date = $this->data['entity']['birthdays'][0]['text']; + $date = explode('/', $date); + $birthDate = $date[2].'-'.$date[0].'-'.$date[1]; + } + return $birthDate; } function getTitle() { - return $this->data['entity']['gd$organization'][0]['gd$orgTitle']['$t']; + return $this->data['entity']['organizations'][0]['title']; } function getAccountName($userId) { $description = false; - $orgName = $this->data['entity']['gd$organization'][0]['gd$orgName']['$t']; + $orgName = $this->data['entity']['organizations'][0]['name']; if(empty($orgName)) { $contactsModel = Vtiger_Module_Model::getInstance('Contacts'); $accountFieldInstance = Vtiger_Field_Model::getInstance('account_id', $contactsModel); diff --git a/pkg/vtiger/modules/Import/modules/Import/actions/Data.php b/pkg/vtiger/modules/Import/modules/Import/actions/Data.php index eef394e0a99ae04a2406add4efd22060ae46783c..b1c64ce239fdae4bfeadc6ca54a1f9abfd6e799a 100644 --- a/pkg/vtiger/modules/Import/modules/Import/actions/Data.php +++ b/pkg/vtiger/modules/Import/modules/Import/actions/Data.php @@ -765,9 +765,11 @@ class Import_Data_Action extends Vtiger_Action_Controller { $_REQUEST['cur_'.$this->lineitem_currency_id.'_check'] = 1; } $fieldData['currency_id'] = $this->lineitem_currency_id; - // to save Source of Record while Creating - $fieldData['source'] = $this->recordSource; + + } + // to save Source of Record while Creating + $fieldData['source'] = $this->recordSource; if ($fieldData != null && $checkMandatoryFieldValues) { foreach ($moduleFields as $fieldName => $fieldInstance) { if ((($fieldData[$fieldName] == '') || ($fieldData[$fieldName] == null)) && $fieldInstance->isMandatory()) { diff --git a/pkg/vtiger/modules/MailManager/layouts/v7/modules/MailManager/resources/List.js b/pkg/vtiger/modules/MailManager/layouts/v7/modules/MailManager/resources/List.js index 92dc028d998934cdd62bf73581889e7a914efcce..09290bd42a9a8c2cd478b7914cc95623b8b23f32 100644 --- a/pkg/vtiger/modules/MailManager/layouts/v7/modules/MailManager/resources/List.js +++ b/pkg/vtiger/modules/MailManager/layouts/v7/modules/MailManager/resources/List.js @@ -354,7 +354,7 @@ Vtiger_List_Js("MailManager_List_Js", {}, { '_msgno' : msgNos.join(',') }; app.request.post({data : params}).then(function(err,data) { - app.helper.hideProgress(); + self.openFolder(folder); if(data.status) { app.helper.showSuccessNotification({'message': app.vtranslate('JSLBL_MAILS_DELETED')}); self.updateUnreadCount("-"+self.getUnreadCountByMsgNos(msgNos), folder); @@ -418,7 +418,7 @@ Vtiger_List_Js("MailManager_List_Js", {}, { '_msgno' : msgNos.join(',') }; app.request.post({data : params}).then(function(err,data) { - app.helper.hideProgress(); + self.openFolder(folder); if(data.status) { app.helper.showSuccessNotification({'message': app.vtranslate('JSLBL_MAIL_MOVED')}); var unreadCount = self.getUnreadCountByMsgNos(msgNos); diff --git a/pkg/vtiger/modules/MailManager/modules/MailManager/connectors/Connector.php b/pkg/vtiger/modules/MailManager/modules/MailManager/connectors/Connector.php index a3790ec7d91807b3a38f75fe6d412fbdfb802772..5bcb44ac00ce3876a0cc146d9b4e476b3d907c73 100644 --- a/pkg/vtiger/modules/MailManager/modules/MailManager/connectors/Connector.php +++ b/pkg/vtiger/modules/MailManager/modules/MailManager/connectors/Connector.php @@ -390,10 +390,12 @@ class MailManager_Connector_Connector { } foreach($records as $result) { - array_unshift($mails, MailManager_Message_Model::parseOverview($result,$mbox)); + $message = MailManager_Message_Model::parseOverview($result,$mbox); + array_unshift($mails, $message); + array_unshift($mailnos, $message->msgNo()); } $folder->setMails($mails); - $folder->setMailIds($nos); + $folder->setMailIds($mailnos); $folder->setPaging($reverse_end, $reverse_start, $maxLimit, $nmsgs, $start); //-1 as it starts from 0 } } diff --git a/pkg/vtiger/modules/MailManager/modules/MailManager/views/Folder.php b/pkg/vtiger/modules/MailManager/modules/MailManager/views/Folder.php index 9e59d3d8e4de431967ac634a6116aef6edd63af8..2da77d47b01813b4c525011004226e3f0f19889d 100644 --- a/pkg/vtiger/modules/MailManager/modules/MailManager/views/Folder.php +++ b/pkg/vtiger/modules/MailManager/modules/MailManager/views/Folder.php @@ -44,6 +44,13 @@ class MailManager_Folder_View extends MailManager_Abstract_View { $dateArray[0] = $dateArray[1]; $dateArray[1] = $temp; $q = implode('-', $dateArray); + } else if ($dateFormat == 'dd/mm/yyyy'){ + // re-align to m/d/y format for strtotime + $dateArray = explode('/', $q); + $temp = $dateArray[0]; + $dateArray[0] = $dateArray[1]; + $dateArray[1] = $temp; + $q = implode('/', $dateArray); } $query = date('d M Y',strtotime($q)); $q = ''.$type.' "'.vtlib_purify($query).'"'; diff --git a/pkg/vtiger/modules/Projects/ProjectTask/languages/en_us/ProjectTask.php b/pkg/vtiger/modules/Projects/ProjectTask/languages/en_us/ProjectTask.php index 989af3a651b796a1f9d48e604accf258876ccbe1..b76a25758479fab4525ad59640801418741e64c0 100644 --- a/pkg/vtiger/modules/Projects/ProjectTask/languages/en_us/ProjectTask.php +++ b/pkg/vtiger/modules/Projects/ProjectTask/languages/en_us/ProjectTask.php @@ -45,6 +45,6 @@ $languageStrings = array( 'In Progress' => 'In Progress', 'Completed' => 'Completed', 'Deferred' => 'Deferred', - 'Canceled ' => 'Canceled', + 'Canceled' => 'Canceled', 'LBL_NO_DATE_VALUE_MSG' => 'or Project Tasks do not have Start and/or End Date', ); \ No newline at end of file diff --git a/pkg/vtiger/modules/Webforms/layouts/v7/modules/Settings/Webforms/ShowForm.tpl b/pkg/vtiger/modules/Webforms/layouts/v7/modules/Settings/Webforms/ShowForm.tpl index 970e74787d9a13719439e413858b3562b0acd4e5..5536e6bc5f5a96107fa5ffd2c20ab7fc1a90e9c7 100644 --- a/pkg/vtiger/modules/Webforms/layouts/v7/modules/Settings/Webforms/ShowForm.tpl +++ b/pkg/vtiger/modules/Webforms/layouts/v7/modules/Settings/Webforms/ShowForm.tpl @@ -55,7 +55,7 @@ {else} {assign var=PICKLIST_DATA_LABEL value=$FIELD_MODEL->get('name')} {/if} - {else if ($DATA_TYPE eq "salutation") or ($DATA_TYPE eq "string") or ($DATA_TYPE eq "time") or ($DATA_TYPE eq "currency") or ($DATA_TYPE eq "url") or ($DATA_TYPE eq "phone")} + {else if ($DATA_TYPE eq "salutation") or ($DATA_TYPE eq "string") or ($DATA_TYPE eq "currency") or ($DATA_TYPE eq "url") or ($DATA_TYPE eq "phone")} {assign var=TYPE value="text"} {else if ($DATA_TYPE eq "text")} {assign var=TYPE value="text"} @@ -71,6 +71,8 @@ {assign var=TYPE value="checkbox"} {else if ($DATA_TYPE eq "date")} {assign var=TYPE value="date"} + {else if ($DATA_TYPE eq "time")} + {assign var=TYPE value="time"} {/if} {if $HIDDEN_STATUS eq 1} {assign var=TYPE value=hidden} @@ -105,7 +107,7 @@ {/if} {elseif ($DATA_TYPE neq "text") and ($DATA_TYPE neq "boolean")} <input type="{$TYPE}" name="{urlencode($FIELD_MODEL->getFieldName())}" data-label="{$FIELD_MODEL->get('neutralizedFieldName')}" value="{$FIELD_MODEL->get('fieldvalue')}" {if ($FIELD_MODEL->get('required') eq 1) || ($FIELD_MODEL->isMandatory(true))} required{/if} {if ($DATA_TYPE eq "double")} datatype="{$DATA_TYPE}" step="any" {/if}/> - {if ($DATA_TYPE eq "date") and ($FIELD_MODEL->get('hidden') neq 1)}(yyyy-mm-dd){/if} + {if ($DATA_TYPE eq "date") and ($FIELD_MODEL->get('hidden') neq 1)}{/if} {/if} </td> </tr> diff --git a/pkg/vtiger/modules/Webforms/settings/actions/Save.php b/pkg/vtiger/modules/Webforms/settings/actions/Save.php index 4b0e0019787ef0c04491047f8cdf69153434a3a9..7ae864e1c61a60e25c5c03b8d88afa3e65d4ee2e 100644 --- a/pkg/vtiger/modules/Webforms/settings/actions/Save.php +++ b/pkg/vtiger/modules/Webforms/settings/actions/Save.php @@ -35,11 +35,17 @@ class Settings_Webforms_Save_Action extends Settings_Vtiger_Index_Action { } $fieldsList = $recordModel->getModule()->getFields(); + $supportedModules = Settings_Webforms_Module_Model::getSupportedModulesList(); foreach ($fieldsList as $fieldName => $fieldModel) { $fieldValue = $request->get($fieldName); if (!$fieldValue) { $fieldValue = $fieldModel->get('defaultvalue'); } + if($fieldModel->isMandatory() && empty(trim($fieldValue))) { + throw new AppException(vtranslate('LBL_MANDATORY_FIELD_MISSING')); + }else if($fieldName == 'targetmodule' && !array_key_exists($fieldValue,$supportedModules)){ + throw new Exception('Target module is not supported to create webform'); + } $recordModel->set($fieldName, $fieldValue); } diff --git a/pkg/vtiger/translations/Arabic_ar_ae/modules/ProjectTask.php b/pkg/vtiger/translations/Arabic_ar_ae/modules/ProjectTask.php index 405d965ad61c1b0287ee019384aa2d92aa7d08c5..94d86ad632f797407ba1baadfced7cfd45780492 100644 --- a/pkg/vtiger/translations/Arabic_ar_ae/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Arabic_ar_ae/modules/ProjectTask.php @@ -45,6 +45,6 @@ $languageStrings = array( 'In Progress' => 'ÙÙŠ التقدم', 'Completed' => 'الانتهاء', 'Deferred' => 'تأجيل', - 'Canceled ' => 'ألغيت', + 'Canceled' => 'ألغيت', 'LBL_NO_DATE_VALUE_MSG' => 'أو مهام المشروع لا يكون البدء Ùˆ / أو تاريخ الانتهاء', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/BrazilianLanguagePack_bz_bz/modules/ProjectTask.php b/pkg/vtiger/translations/BrazilianLanguagePack_bz_bz/modules/ProjectTask.php index 9abc86377187543621fa11c8d6658d026c6dd6f1..eddfd7583372d81ba96bfd4e8173f2b5f82e053f 100644 --- a/pkg/vtiger/translations/BrazilianLanguagePack_bz_bz/modules/ProjectTask.php +++ b/pkg/vtiger/translations/BrazilianLanguagePack_bz_bz/modules/ProjectTask.php @@ -45,6 +45,6 @@ $languageStrings = array( 'In Progress' => 'Em Andamento', 'Completed' => 'ConcluÃdo', 'Deferred' => 'Adiado', - 'Canceled ' => 'Cancelado', + 'Canceled' => 'Cancelado', 'LBL_NO_DATE_VALUE_MSG' => 'ou a Tarefa do Projeto não tem Data InÃcio e/ou Data Fim', ); diff --git a/pkg/vtiger/translations/BritishLanguagePack_br_br/modules/ProjectTask.php b/pkg/vtiger/translations/BritishLanguagePack_br_br/modules/ProjectTask.php index 4515429b7698af5f0eb31f39b51b17318fb4ccc8..47d25164a83a1b208122586855b9fddc9dfe4489 100644 --- a/pkg/vtiger/translations/BritishLanguagePack_br_br/modules/ProjectTask.php +++ b/pkg/vtiger/translations/BritishLanguagePack_br_br/modules/ProjectTask.php @@ -42,6 +42,6 @@ $languageStrings = array( 'In Progress' => 'In Progress', 'Completed' => 'Completed', 'Deferred' => 'Deferred', - 'Canceled ' => 'Canceled', + 'Canceled' => 'Canceled', 'LBL_NO_DATE_VALUE_MSG' => 'or Project Tasks do not have Start and/or End Date', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/Deutsch/modules/ProjectTask.php b/pkg/vtiger/translations/Deutsch/modules/ProjectTask.php index 266e7c7afea9d8919fa54f8e1f156f63166c43a7..aff8893924ef31d59efce0dade767fea93d86750 100644 --- a/pkg/vtiger/translations/Deutsch/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Deutsch/modules/ProjectTask.php @@ -43,6 +43,6 @@ $languageStrings = array( 'Canceled' => "Gestrichen", 'LBL_NO_DATE_VALUE_MSG' => 'oder Projektaufgaben nicht starten und / oder Enddatum', - 'Canceled ' => 'Abgebrochen', + 'Canceled' => 'Abgebrochen', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/Dutch/modules/ProjectTask.php b/pkg/vtiger/translations/Dutch/modules/ProjectTask.php index 1304f88ef4ccb6bccc3a88f508f862e0934de810..a4516cca7b278dfb5812c044e758bd108befcc74 100644 --- a/pkg/vtiger/translations/Dutch/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Dutch/modules/ProjectTask.php @@ -42,6 +42,6 @@ $languageStrings = array( 'In Progress' => 'In Progress', 'Completed' => 'Completed', 'Deferred' => 'Deferred', - 'Canceled ' => 'Canceled', + 'Canceled' => 'Canceled', 'LBL_NO_DATE_VALUE_MSG' => 'of Projecttaken heb geen Start en / of Einddatum', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/French/modules/ProjectTask.php b/pkg/vtiger/translations/French/modules/ProjectTask.php index fea77870cdb6f911c01e4f5d41044103ed584b60..5162a4896b77d0ff5add504780106d8cf17a877e 100644 --- a/pkg/vtiger/translations/French/modules/ProjectTask.php +++ b/pkg/vtiger/translations/French/modules/ProjectTask.php @@ -43,6 +43,6 @@ $languageStrings = array( 'Canceled' => "Annulé", 'LBL_NO_DATE_VALUE_MSG' => 'ou tâches de projet ne ont pas de début et / ou Date de fin', - 'Canceled ' => 'Annulé', + 'Canceled' => 'Annulé', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/French/modules/Vtiger.php b/pkg/vtiger/translations/French/modules/Vtiger.php index 4cfa20675d46e627b1609525067d537d5a1f4981..c8f8b3c9de90250f3c99009d89512067d1083224 100644 --- a/pkg/vtiger/translations/French/modules/Vtiger.php +++ b/pkg/vtiger/translations/French/modules/Vtiger.php @@ -29,7 +29,7 @@ $languageStrings = array( 'LBL_DELETE' => 'Supprimer' , 'LBL_SETTINGS' => 'Configuration' , 'LBL_ADD_COMMENT' => 'Ajouter un Commentaire' , - 'LBL_EDIT_FIELDS' => 'Champs & Layout%' , + 'LBL_EDIT_FIELDS' => '%s Champs & Layout' , 'LBL_EDIT_WORKFLOWS' => '%s Workflows' , 'LBL_EDIT_PICKLIST_VALUES' => 'Picklists Valeurs' , 'LBL_EDIT_MAILSCANNER' => 'Convertir les Emails' , diff --git a/pkg/vtiger/translations/Hungarian/modules/ProjectTask.php b/pkg/vtiger/translations/Hungarian/modules/ProjectTask.php index 9b0f75a814b8ac7ce7763f5a88937ffa75598fa3..9aa910bb0973e44ad7f355f127d3811725dceb88 100644 --- a/pkg/vtiger/translations/Hungarian/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Hungarian/modules/ProjectTask.php @@ -42,6 +42,6 @@ $languageStrings = array( 'In Progress' => 'In Progress', 'Completed' => 'Completed', 'Deferred' => 'Deferred', - 'Canceled ' => 'Canceled', + 'Canceled' => 'Canceled', 'LBL_NO_DATE_VALUE_MSG' => 'vagy Projektfeladatok nincs kezdete és / vagy vége dátum', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/ItalianLanguagePack_it_it/modules/ProjectTask.php b/pkg/vtiger/translations/ItalianLanguagePack_it_it/modules/ProjectTask.php index d98e6039fdc352e6f59a1dd55ae613e540c850ea..5485b64e2725260ff8386d40559296cc11332d02 100644 --- a/pkg/vtiger/translations/ItalianLanguagePack_it_it/modules/ProjectTask.php +++ b/pkg/vtiger/translations/ItalianLanguagePack_it_it/modules/ProjectTask.php @@ -43,6 +43,6 @@ $languageStrings = array( 'Canceled' => "Cancellato", 'LBL_NO_DATE_VALUE_MSG' => 'o Attività del Progetto non hanno Data di Inizio e/o Fine', - 'Canceled ' => 'Annullata', + 'Canceled' => 'Annullata', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/ProjectTask.php b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/ProjectTask.php index 255295f1c0ac4cdffbabe3202a1dab0d9f4a82b1..5070a77ad0439cebad59bbbf1a7fc2174884530d 100644 --- a/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/ProjectTask.php +++ b/pkg/vtiger/translations/MexicanSpanishLanguagePack_es_mx/modules/ProjectTask.php @@ -41,7 +41,7 @@ $languageStrings = array( 'Open' => 'Abierta', 'In Progress' => 'En progreso', 'Deferred' => 'Diferida', - 'Canceled ' => 'Cancelada', + 'Canceled' => 'Cancelada', 'Completed' => 'Terminada', 'LBL_NO_DATE_VALUE_MSG' => 'Las tareas del proyecto no tienen fecha de inicio ni / o de término', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/PolishLanguagePack_pl_pl/modules/ProjectTask.php b/pkg/vtiger/translations/PolishLanguagePack_pl_pl/modules/ProjectTask.php index 65c22a1a19bf1b675a55ad2778a5c2d66be13ebc..01f310e8213169d5f1d305f7b5201445b99eddc7 100644 --- a/pkg/vtiger/translations/PolishLanguagePack_pl_pl/modules/ProjectTask.php +++ b/pkg/vtiger/translations/PolishLanguagePack_pl_pl/modules/ProjectTask.php @@ -46,6 +46,6 @@ $languageStrings = array( 'In Progress' => 'W toku', 'Completed' => 'ZakoÅ„czony', 'Deferred' => 'Odroczona', - 'Canceled ' => 'OdwoÅ‚any', + 'Canceled' => 'OdwoÅ‚any', 'LBL_NO_DATE_VALUE_MSG' => 'lub Zadania projektowe nie majÄ… rozpoczÄ™cia i / lub zakoÅ„czenia Data', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/RomanianLanguagePack_rm_rm/modules/ProjectTask.php b/pkg/vtiger/translations/RomanianLanguagePack_rm_rm/modules/ProjectTask.php index 4c411107c5914c01a647d0a3330a83284b4091b1..b14a827fcc8213063dd05ddd47c720b6ac9e2101 100644 --- a/pkg/vtiger/translations/RomanianLanguagePack_rm_rm/modules/ProjectTask.php +++ b/pkg/vtiger/translations/RomanianLanguagePack_rm_rm/modules/ProjectTask.php @@ -43,6 +43,6 @@ $languageStrings = array( 'Canceled' => "Anulat", 'LBL_NO_DATE_VALUE_MSG' => 'sau sarcini de proiect nu au Start È™i / sau Data de încheiere', - 'Canceled ' => 'Anulat', + 'Canceled' => 'Anulat', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/Russian/modules/ProjectTask.php b/pkg/vtiger/translations/Russian/modules/ProjectTask.php index eb9e36761a02d1f316f78789d9424f69e01d2fd2..aa17be0fc196effda70a90e5f60881cd090ae691 100644 --- a/pkg/vtiger/translations/Russian/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Russian/modules/ProjectTask.php @@ -43,6 +43,6 @@ $languageStrings = array( 'Canceled' => "Отменен", 'LBL_NO_DATE_VALUE_MSG' => 'или Задачи проекта не имеют начала и / или окончаниÑ', - 'Canceled ' => 'Отменен', + 'Canceled' => 'Отменен', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/Spanish/modules/ProjectTask.php b/pkg/vtiger/translations/Spanish/modules/ProjectTask.php index c97a50b5d7e0c1de994785df8ff41fd19172422a..28c6793e6988421afced3451e1d1a7c435f1010e 100644 --- a/pkg/vtiger/translations/Spanish/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Spanish/modules/ProjectTask.php @@ -50,6 +50,6 @@ $languageStrings = array( 'In Progress' => 'In Progress', 'Completed' => 'Completed', 'Deferred' => 'Deferred', - 'Canceled ' => 'Canceled', + 'Cancele' => 'Canceled', 'LBL_NO_DATE_VALUE_MSG' => 'o Tareas del proyecto no tienen inicio y / o fecha de finalización', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/Sweden_sv_se/modules/ProjectTask.php b/pkg/vtiger/translations/Sweden_sv_se/modules/ProjectTask.php index 9b7c92ff08cf0be828b2351e07365de1fdf66542..69811f62d40df7d87dff6d13dccf2f14a268bedb 100755 --- a/pkg/vtiger/translations/Sweden_sv_se/modules/ProjectTask.php +++ b/pkg/vtiger/translations/Sweden_sv_se/modules/ProjectTask.php @@ -45,6 +45,6 @@ $languageStrings = array( 'In Progress' => 'In Progress', 'Completed' => 'Avslutat', 'Deferred' => 'Deferred', - 'Canceled ' => 'Canceled', + 'Canceled' => 'Canceled', 'LBL_NO_DATE_VALUE_MSG' => 'eller projektuppgifter inte har start och / eller slutdatum', ); \ No newline at end of file diff --git a/pkg/vtiger/translations/TurkishLanguagePack_tr_tr/modules/ProjectTask.php b/pkg/vtiger/translations/TurkishLanguagePack_tr_tr/modules/ProjectTask.php index e316ddb32704f727512b1317143395354f3dc418..f5fd0b2d0dc7e97fdb6edfa5fc02ed6abdc03140 100644 --- a/pkg/vtiger/translations/TurkishLanguagePack_tr_tr/modules/ProjectTask.php +++ b/pkg/vtiger/translations/TurkishLanguagePack_tr_tr/modules/ProjectTask.php @@ -43,6 +43,6 @@ $languageStrings = array( 'Canceled' => "Iptal edildi", 'LBL_NO_DATE_VALUE_MSG' => 'veya Proje Görevleri BaÅŸlat ve / veya BitiÅŸ Tarihi yok', - 'Canceled ' => 'Ä°ptal', + 'Canceled' => 'Ä°ptal', ); \ No newline at end of file diff --git a/schema/DatabaseSchema.xml b/schema/DatabaseSchema.xml index dea47f06c780250a343ced1ed1e8cebea95fabb6..6a15fe17f76baecaa826d0eb438843bcef56b515 100644 --- a/schema/DatabaseSchema.xml +++ b/schema/DatabaseSchema.xml @@ -6190,7 +6190,7 @@ </field> <field name="schdayofweek" type="C" size="100"> </field> - <field name="schannualdates" type="C" size="100"> + <field name="schannualdates" type="C" size="500"> </field> <field name="schtime" type="C" size="50"> </field> diff --git a/vtlib/Vtiger/Functions.php b/vtlib/Vtiger/Functions.php index be26f3d920f24a221b57ec38e12a5b7cfb007c9b..5464064a8ee7c4ddeb60d3a1e2233845e44439f8 100644 --- a/vtlib/Vtiger/Functions.php +++ b/vtlib/Vtiger/Functions.php @@ -1287,6 +1287,28 @@ class Vtiger_Functions { } } + /** + * Function to check if a string is a valid time value or not + * @param string $value string to check if that is a time value or not + * @return boolean Returns true if $value is time else returns false + */ + static function isTimeValue($value) { + $value = trim($value); + $patterns = array( + '/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/', + '/^(1[0-2]|0?[1-9]):[0-5][0-9] (AM|PM)$/i', + '/^([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/' + ); + + foreach ($patterns as $pattern) { + if (preg_match($pattern, $value)) { + return true; + } + } + + return false; + } + /** * Function to get name and email value from a string of format <b>Your Name<youremail@company.com></b> * @param String $string Name and email value in required format.