diff --git a/Documentation/SOGoInstallationGuide.asciidoc b/Documentation/SOGoInstallationGuide.asciidoc index 73e562e4c..6148ee942 100644 --- a/Documentation/SOGoInstallationGuide.asciidoc +++ b/Documentation/SOGoInstallationGuide.asciidoc @@ -1035,6 +1035,10 @@ not work for entries in this source and thus, freebusy lookups. |If set as an address book, the human identification name of the LDAP repository +|listRequiresDot (optional) +|If set to `YES`, listing of this LDAP source is only possible when performing a search (respecting the SOGoSearchMinimumWordLength parameter) or when explicitely typing a single dot. +Defaults to `YES` when unset. + |ModulesConstraints (optional) |Limits the access of any module through a constraint based on an LDAP attribute; must be a dictionary with keys `Mail`, and/or `Calendar`, diff --git a/NEWS b/NEWS index c2dd98748..944af02ef 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ Enhancements - [web] improved confirm dialogs for deletions - [web] allow resources to prevent invitations (#3410) - [web] warn when double-booking attendees and offer force save option + - [web] list search now displays a warning regarding the minlength constraint + - [web] loading an LDAP-based addressbook is now instantaneous when listRequiresDot is disabled (#438, #3464) - [eas] now support EAS MIME truncation Bug fixes diff --git a/UI/Common/English.lproj/Localizable.strings b/UI/Common/English.lproj/Localizable.strings index 54f38f4a2..68d15c050 100644 --- a/UI/Common/English.lproj/Localizable.strings +++ b/UI/Common/English.lproj/Localizable.strings @@ -104,3 +104,6 @@ "Loading" = "Loading"; "No such user." = "No such user."; "You cannot (un)subscribe to a folder that you own!" = "You cannot (un)subscribe to a folder that you own!"; + +/* Error message display bellow search field when the search string has less than the required number of characters */ +"Enter at least %{minimumSearchLength} characters" = "Enter at least %{minimumSearchLength} characters"; \ No newline at end of file diff --git a/UI/Common/UIxPageFrame.m b/UI/Common/UIxPageFrame.m index 0b78dfb4f..b748022d1 100644 --- a/UI/Common/UIxPageFrame.m +++ b/UI/Common/UIxPageFrame.m @@ -615,11 +615,6 @@ ); } -- (int) minimumSearchLength -{ - return [[[context activeUser] domainDefaults] searchMinimumWordLength]; -} - @end /* UIxPageFrame */ @interface UIxSidenavToolbarTemplate : UIxComponent diff --git a/UI/Contacts/UIxContactFoldersView.m b/UI/Contacts/UIxContactFoldersView.m index 8b24dc109..016097c9c 100644 --- a/UI/Contacts/UIxContactFoldersView.m +++ b/UI/Contacts/UIxContactFoldersView.m @@ -339,6 +339,8 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK; [NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoGCSFolderK]], @"isEditable", [NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoContactSourceFolderK] && ![currentFolder isPersonalSource]], @"isRemote", + [NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoContactSourceFolderK] + && [currentFolder listRequiresDot]], @"listRequiresDot", acls, @"acls", urls, @"urls", nil]; diff --git a/UI/SOGoUI/UIxComponent.h b/UI/SOGoUI/UIxComponent.h index f4e5531c2..1b7cebd11 100644 --- a/UI/SOGoUI/UIxComponent.h +++ b/UI/SOGoUI/UIxComponent.h @@ -86,6 +86,10 @@ /* SoUser */ - (NSString *) shortUserNameForDisplay; +/* Common defaults and settings */ +- (int) minimumSearchLength; +- (NSString *) minimumSearchLengthLabel; + /* labels */ - (NSString *) labelForKey:(NSString *)_key; - (NSString *) commonLabelForKey:(NSString *)_key; diff --git a/UI/SOGoUI/UIxComponent.m b/UI/SOGoUI/UIxComponent.m index 46b368a46..0ab260b8f 100644 --- a/UI/SOGoUI/UIxComponent.m +++ b/UI/SOGoUI/UIxComponent.m @@ -22,6 +22,7 @@ #import #import /* for locale strings */ +#import #import #import @@ -509,6 +510,22 @@ static SoProduct *commonProduct = nil; return [[context activeUser] login]; } +/* Common defaults and settings */ + +- (int) minimumSearchLength +{ + return [[[context activeUser] domainDefaults] searchMinimumWordLength]; +} + +- (NSString *) minimumSearchLengthLabel +{ + NSDictionary *defaults; + + defaults = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: [self minimumSearchLength]] + forKey: @"minimumSearchLength"]; + return [defaults keysWithFormat: [self commonLabelForKey: @"Enter at least %{minimumSearchLength} characters"]]; +} + /* labels */ - (NSString *) labelForKey: (NSString *) _str diff --git a/UI/Templates/ContactsUI/UIxContactFoldersView.wox b/UI/Templates/ContactsUI/UIxContactFoldersView.wox index faece92cb..08d3ae3fd 100644 --- a/UI/Templates/ContactsUI/UIxContactFoldersView.wox +++ b/UI/Templates/ContactsUI/UIxContactFoldersView.wox @@ -274,17 +274,21 @@ -
+ sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })" + sg-allow-dot="addressbook.selectedFolder.listRequiresDot"> arrow_back - + +
+
+
@@ -293,7 +297,7 @@ -
+ @@ -362,7 +366,7 @@ - + diff --git a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox index 6465668c2..dab826b9f 100644 --- a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox +++ b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox @@ -116,10 +116,12 @@ -
+
@@ -137,7 +139,7 @@ -
+ diff --git a/UI/Templates/SchedulerUI/UIxCalMainView.wox b/UI/Templates/SchedulerUI/UIxCalMainView.wox index 425197b53..9cbf31ab6 100644 --- a/UI/Templates/SchedulerUI/UIxCalMainView.wox +++ b/UI/Templates/SchedulerUI/UIxCalMainView.wox @@ -457,10 +457,12 @@ -
+
@@ -475,7 +477,7 @@ -
+ diff --git a/UI/Templates/UIxPageFrame.wox b/UI/Templates/UIxPageFrame.wox index 6c0b9efb2..61249cf71 100644 --- a/UI/Templates/UIxPageFrame.wox +++ b/UI/Templates/UIxPageFrame.wox @@ -53,11 +53,11 @@ - + diff --git a/UI/WebServerResources/js/Common/sgSearch.directive.js b/UI/WebServerResources/js/Common/sgSearch.directive.js index 17b431120..38cc205a6 100644 --- a/UI/WebServerResources/js/Common/sgSearch.directive.js +++ b/UI/WebServerResources/js/Common/sgSearch.directive.js @@ -60,6 +60,13 @@ return function postLink(scope, iElement, iAttr, controller) { var compiledButtonEl = iElement.find('button'); + // Retrive the form and input names to check the form's validity in the controller + controller.formName = iElement.attr('name'); + controller.inputName = inputEl.attr('name'); + + // Associate the sg-allow-dot parameter (boolean) to the controller + controller.allowDot = $parse(iElement.attr('sg-allow-dot'))(scope); + // Associate callback to controller controller.doSearch = $parse(iElement.attr('sg-search')); @@ -99,7 +106,6 @@ minLength = angular.isNumber($window.minimumSearchLength)? $window.minimumSearchLength : 2; // Controller variables - vm.previous = { searchText: '', searchField: '' }; vm.searchText = null; // Model options @@ -113,20 +119,22 @@ // Method to call on data changes vm.onChange = function() { - if (typeof vm.searchText !== 'undefined' && vm.searchText !== null) { - if (vm.searchText != vm.previous.searchText || vm.searchField != vm.previous.searchField) { - if (vm.searchText.length > minLength || vm.searchText.length === 0 || vm.searchText == '.') { - // doSearch is the compiled expression of the sg-search attribute - vm.doSearch($scope, { searchText: vm.searchText, searchField: vm.searchField }); - } - vm.previous = { searchText: vm.searchText, searchField: vm.searchField }; - } + var form = $scope[vm.formName], + input = form[vm.inputName], + rawSearchText = input.$viewValue; + + if (vm.allowDot && rawSearchText == '.' || form.$valid) { + if (rawSearchText == '.') + // Ignore the minlength constraint when using the dot operator + input.$setValidity('minlength', true); + + // doSearch is the compiled expression of the sg-search attribute + vm.doSearch($scope, { searchText: rawSearchText, searchField: vm.searchField }); } }; // Reset input field when cancelling the search vm.cancelSearch = function() { - vm.previous = { searchText: '', searchField: '' }; vm.searchText = null; }; }