(js) Improve sg-search directive
Will now respect the "listRequiresDot" source parameter and uses ng-messages to show an error if the minimum number of characters is not reached. Fixes #438, #3464pull/201/head
parent
4b816677b0
commit
70fbeab27a
|
@ -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`,
|
||||
|
|
2
NEWS
2
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
|
||||
|
|
|
@ -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";
|
|
@ -615,11 +615,6 @@
|
|||
);
|
||||
}
|
||||
|
||||
- (int) minimumSearchLength
|
||||
{
|
||||
return [[[context activeUser] domainDefaults] searchMinimumWordLength];
|
||||
}
|
||||
|
||||
@end /* UIxPageFrame */
|
||||
|
||||
@interface UIxSidenavToolbarTemplate : UIxComponent
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -86,6 +86,10 @@
|
|||
/* SoUser */
|
||||
- (NSString *) shortUserNameForDisplay;
|
||||
|
||||
/* Common defaults and settings */
|
||||
- (int) minimumSearchLength;
|
||||
- (NSString *) minimumSearchLengthLabel;
|
||||
|
||||
/* labels */
|
||||
- (NSString *) labelForKey:(NSString *)_key;
|
||||
- (NSString *) commonLabelForKey:(NSString *)_key;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#import <Foundation/NSKeyValueCoding.h>
|
||||
#import <Foundation/NSUserDefaults.h> /* for locale strings */
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGObjWeb/SoObjects.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
|
@ -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
|
||||
|
|
|
@ -274,17 +274,21 @@
|
|||
</md-button>
|
||||
</div>
|
||||
<!-- search mode -->
|
||||
<div class="md-toolbar-tools sg-toolbar-secondary"
|
||||
<form name="searchForm" class="md-toolbar-tools sg-toolbar-secondary"
|
||||
layout="row"
|
||||
ng-show="addressbook.mode.search"
|
||||
sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })">
|
||||
sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })"
|
||||
sg-allow-dot="addressbook.selectedFolder.listRequiresDot">
|
||||
<md-button class="md-icon-button"
|
||||
sg-search-cancel="addressbook.cancelSearch()"
|
||||
label:aria-label="Back">
|
||||
<md-icon>arrow_back</md-icon>
|
||||
</md-button>
|
||||
<md-input-container class="md-flex" md-no-float="md-no-float">
|
||||
<input name="folderSearch" type="search" label:placeholder="Search"/>
|
||||
<input name="folderSearch" type="search" var:minlength="minimumSearchLength" label:placeholder="Search"/>
|
||||
<div ng-messages="searchForm.folderSearch.$error" ng-show="searchForm.folderSearch.$dirty">
|
||||
<div ng-message="minlength"><var:string value="minimumSearchLengthLabel"/></div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
<md-input-container flex="25">
|
||||
<md-select>
|
||||
|
@ -293,7 +297,7 @@
|
|||
<md-option value="organization"><var:string label:value="Organization"/></md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
</md-toolbar>
|
||||
|
||||
<!-- multiple selection mode -->
|
||||
|
@ -362,7 +366,7 @@
|
|||
</span>
|
||||
</md-subheader>
|
||||
<md-subheader ng-hide="addressbook.service.$query.value">
|
||||
<span ng-switch="addressbook.selectedFolder.isRemote">
|
||||
<span ng-switch="addressbook.selectedFolder.listRequiresDot">
|
||||
<span ng-switch-when="1">
|
||||
<var:string label:value="Start a search to browse this address book"/>
|
||||
</span>
|
||||
|
|
|
@ -116,10 +116,12 @@
|
|||
</md-button>
|
||||
</div>
|
||||
<!-- search mode -->
|
||||
<div class="md-toolbar-tools sg-toolbar-secondary"
|
||||
layout="row"
|
||||
ng-show="mailbox.mode.search"
|
||||
sg-search="mailbox.selectedFolder.$filter(null, [{ searchBy: searchField, searchInput: searchText }])">
|
||||
<form class="md-toolbar-tools sg-toolbar-secondary"
|
||||
name="searchForm"
|
||||
layout="row"
|
||||
ng-show="mailbox.mode.search"
|
||||
sg-search="mailbox.selectedFolder.$filter(null, [{ searchBy: searchField, searchInput: searchText }])"
|
||||
sg-allow-dot="false">
|
||||
<md-button class="sg-icon-button"
|
||||
sg-search-cancel="mailbox.cancelSearch()"
|
||||
label:aria-label="Back">
|
||||
|
@ -137,7 +139,7 @@
|
|||
<md-option value="body"><var:string label:value="Entire Message"/></md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
</md-toolbar>
|
||||
|
||||
<!-- multiple-selection mode -->
|
||||
|
|
|
@ -457,10 +457,12 @@
|
|||
</md-button>
|
||||
</div>
|
||||
<!-- search mode -->
|
||||
<div class="md-toolbar-tools sg-toolbar-secondary"
|
||||
layout="row"
|
||||
ng-show="list.mode.search"
|
||||
sg-search="list.component.$filter(list.componentType, { value: searchText, search: searchField })">
|
||||
<form class="md-toolbar-tools sg-toolbar-secondary"
|
||||
name="searchForm"
|
||||
layout="row"
|
||||
ng-show="list.mode.search"
|
||||
sg-search="list.component.$filter(list.componentType, { value: searchText, search: searchField })"
|
||||
sg-allow-dot="false">
|
||||
<md-button class="md-icon-button"
|
||||
sg-search-cancel="list.cancelSearch()"
|
||||
label:aria-label="Back">
|
||||
|
@ -475,7 +477,7 @@
|
|||
<md-option value="entireContent"><var:string label:value="Entire content"/></md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
</md-toolbar>
|
||||
|
||||
<!-- multiple-selection mode -->
|
||||
|
|
|
@ -53,11 +53,11 @@
|
|||
<!-- MAIN CONTENT ROW -->
|
||||
<var:component-content />
|
||||
|
||||
<!-- JAVASCRIPT IMPORTS -->
|
||||
<script type="text/javascript">
|
||||
var ApplicationBaseURL = '<var:string value="modulePath" />';
|
||||
var ResourcesURL = '<var:string value="applicationPath" />.woa/WebServerResources';
|
||||
var minimumSearchLength = <var:string value="minimumSearchLength" />;
|
||||
var minimumSearchLengthLabel = '<var:string value="minimumSearchLengthLabel" />';
|
||||
<var:if condition="isUIxDebugEnabled">
|
||||
var DebugEnabled = true;
|
||||
</var:if>
|
||||
|
@ -121,6 +121,7 @@
|
|||
<script type="text/javascript" rsrc:src="js/vendor/angular-animate.min.js"><!-- space --></script>
|
||||
<script type="text/javascript" rsrc:src="js/vendor/angular-sanitize.min.js"><!-- space --></script>
|
||||
<script type="text/javascript" rsrc:src="js/vendor/angular-aria.min.js"><!-- space --></script>
|
||||
<script type="text/javascript" rsrc:src="js/vendor/angular-messages.min.js"><!-- space --></script>
|
||||
<script type="text/javascript" rsrc:src="js/vendor/angular-material.js"><!-- space --></script>
|
||||
<script type="text/javascript" rsrc:src="js/vendor/angular-ui-router.min.js"><!-- space --></script>
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue