diff --git a/config/config.sample.php b/config/config.sample.php index 863ad023d972..4a68e34885d0 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -287,7 +287,7 @@ * the minimum characters have been entered. The search is case insensitive. * e.g. entering "tom" will always return "Tom" if there is an exact match. */ -'user.search_min_length' => 4, +'user.search_min_length' => 2, /** * Mail Parameters diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 7082e4a811af..a24c85c490c3 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -274,6 +274,16 @@ if (!view.configModel.get('allowGroupSharing')) { title = t('core', 'No users found for {search}', {search: $('.shareWithField').val()}); } + var suggestStarts = OC.getCapabilities().files_sharing.search_min_length; + if (suggestStarts > $('.shareWithField').val().length) { + title = title + '. ' + n( + 'core', + 'Please enter at least {chars} character for suggestions', + 'Please enter at least {chars} characters for suggestions', + suggestStarts, + {chars: suggestStarts} + ); + } $('.shareWithField').addClass('error') .attr('data-original-title', title) .tooltip('hide') diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js index 006796d24e81..591e146bd2c0 100644 --- a/core/js/tests/specs/sharedialogviewSpec.js +++ b/core/js/tests/specs/sharedialogviewSpec.js @@ -2,7 +2,7 @@ * ownCloud * * @author Vincent Petry -* @copyright Copyright (c) 2015 Vincent Petry +* @copyright Copyright (c) 2018 Vincent Petry * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -108,7 +108,7 @@ describe('OC.Share.ShareDialogView', function() { capsSpec = sinon.stub(OC, 'getCapabilities'); capsSpec.returns({ 'files_sharing': { - 'search_min_length': 4 + 'search_min_length': 2 } }); }); @@ -774,6 +774,87 @@ describe('OC.Share.ShareDialogView', function() { addShareStub.restore(); }); + + it('displays message when not enough characters were typed in and the server returned no matches', function() { + dialog.render(); + var $shareWithField = $('.shareWithField'); + $shareWithField.val('b'); + var response = sinon.stub(); + dialog.autocompleteHandler({term: 'b'}, response); + + var jsonData = JSON.stringify({ + 'ocs': { + 'data': { + 'exact': { + 'users': [], + 'groups': [], + 'remotes': [], + }, + 'users': [], + 'groups': [], + 'remotes': [] + }, + 'meta': { + 'status': 'success', + 'statuscode': 100 + } + } + }); + expect(fakeServer.requests.length).toEqual(1); + fakeServer.requests[0].respond( + 200, + {'Content-Type': 'application/json'}, + jsonData + ); + expect(response.calledOnce).toEqual(true); + expect(response.getCall(0).args[0]).toBeUndefined(); + + expect($shareWithField.attr('data-original-title')) + .toEqual('No users found for b. Please enter at least 2 characters for suggestions'); + }); + + it('does not display a message when not enough characters were typed in but the server returned an exact match', function() { + dialog.render(); + var $shareWithField = $('.shareWithField'); + var response = sinon.stub(); + dialog.autocompleteHandler({term: '成'}, response); + + var jsonData = JSON.stringify({ + 'ocs': { + 'data': { + 'exact' : { + 'users' : [{ + 'label': '成 龙', + 'value': { + 'shareType': 0, + 'shareWith': 'jackie_chan' + } + }], + 'groups' : [], + 'remotes': [] + }, + 'users': [], + 'groups': [], + 'remotes': [] + }, + 'meta': { + 'status': 'success', + 'statuscode': 100 + } + } + }); + expect(fakeServer.requests.length).toEqual(1); + fakeServer.requests[0].respond( + 200, + {'Content-Type': 'application/json'}, + jsonData + ); + expect(response.calledOnce).toEqual(true); + expect(response.getCall(0).args[0]).toBeDefined(); + + expect($shareWithField.attr('data-original-title')) + .not.toContain('for suggestions'); + }); }); describe('reshare permissions', function() { it('does not show sharing options when sharing not allowed', function() { diff --git a/lib/public/Util/UserSearch.php b/lib/public/Util/UserSearch.php index 55874ea93ea0..3bb749d50d6b 100644 --- a/lib/public/Util/UserSearch.php +++ b/lib/public/Util/UserSearch.php @@ -66,6 +66,6 @@ public function isSearchable($pattern) { * @since 10.0.8 */ public function getSearchMinLength() { - return $this->config->getSystemValue('user.search_min_length', 4); + return $this->config->getSystemValue('user.search_min_length', 2); } } diff --git a/tests/acceptance/features/bootstrap/Provisioning.php b/tests/acceptance/features/bootstrap/Provisioning.php index f7ee5e339bcc..f9bceb132baf 100644 --- a/tests/acceptance/features/bootstrap/Provisioning.php +++ b/tests/acceptance/features/bootstrap/Provisioning.php @@ -82,7 +82,33 @@ public function getCreatedGroups() { } /** - * returns an array of the real displayed names + * returns the display name of the current user + * if no "Display Name" is set the user-name is returned instead + * + * @return array + */ + public function getCurrentUserDisplayName() { + return $this->getUserDisplayName($this->getCurrentUser()); + } + + /** + * returns the display name of a user + * if no "Display Name" is set the user-name is returned instead + * + * @param string $username + * + * @return array + */ + public function getUserDisplayName($username) { + $displayName = $this->createdUsers[$username]['displayname']; + if (($displayName === null) || ($displayName === '')) { + $displayName = $username; + } + return $displayName; + } + + /** + * returns an array of the display names, keyed by username * if no "Display Name" is set the user-name is returned instead * * @return array @@ -90,11 +116,7 @@ public function getCreatedGroups() { public function getCreatedUserDisplayNames() { $result = array(); foreach ($this->getCreatedUsers() as $username => $user) { - if (is_null($user['displayname'])) { - $result[] = $username; - } else { - $result[] = $user['displayname']; - } + $result[$username] = $this->getUserDisplayName($username); } return $result; } diff --git a/tests/acceptance/features/bootstrap/WebUISharingContext.php b/tests/acceptance/features/bootstrap/WebUISharingContext.php index 2ef286ac3769..f646619d9e4e 100644 --- a/tests/acceptance/features/bootstrap/WebUISharingContext.php +++ b/tests/acceptance/features/bootstrap/WebUISharingContext.php @@ -470,6 +470,30 @@ public function userReactsToShareOfferedByUsingWebUI( ); } } + + /** + * @Then only :userOrGroupName should be listed in the autocomplete list on the webUI + * + * @param string $userOrGroupName + * + * @return void + */ + public function onlyUserOrGroupNameShouldBeListedInTheAutocompleteList( + $userOrGroupName + ) { + $autocompleteItems = $this->sharingDialog->getAutocompleteItemsList(); + PHPUnit_Framework_Assert::assertCount( + 1, + $autocompleteItems, + "expected 1 autocomplete item but there are " . \count($autocompleteItems) + ); + PHPUnit_Framework_Assert::assertContains( + $userOrGroupName, + $autocompleteItems, + "'" . $userOrGroupName . "' not in autocomplete list" + ); + } + /** * @Then all users and groups that contain the string :requiredString in their name should be listed in the autocomplete list on the webUI * @@ -502,25 +526,38 @@ public function allUsersAndGroupsThatContainTheStringInTheirNameShouldBeListedIn = $this->sharingDialog->groupStringsToMatchAutoComplete($notToBeListed); } $autocompleteItems = $this->sharingDialog->getAutocompleteItemsList(); - $createdGroups = $this->sharingDialog->groupStringsToMatchAutoComplete( + // Keep separate arrays of users and groups, because the names can overlap + $createdElements = []; + $createdElements['groups'] = $this->sharingDialog->groupStringsToMatchAutoComplete( $this->featureContext->getCreatedGroups() ); - $usersAndGroups = array_merge( - $this->featureContext->getCreatedUserDisplayNames(), - $createdGroups - ); - foreach ($usersAndGroups as $expectedUserOrGroup) { - if (strpos($expectedUserOrGroup, $requiredString) !== false - && $expectedUserOrGroup !== $notToBeListed - && $expectedUserOrGroup !== $this->featureContext->getCurrentUser() - ) { - PHPUnit_Framework_Assert::assertContains( - $expectedUserOrGroup, - $autocompleteItems, - "'" . $expectedUserOrGroup . "' not in autocomplete list" - ); + $createdElements['users'] = $this->featureContext->getCreatedUserDisplayNames(); + $numExpectedItems = 0; + foreach ($createdElements as $elementArray) { + foreach ($elementArray as $internalName => $displayName) { + // Matching should be case-insensitive on the internal or display name + if (((\stripos($internalName, $requiredString) !== false) + || (\stripos($displayName, $requiredString) !== false)) + && ($displayName !== $notToBeListed) + && ($displayName !== $this->featureContext->getCurrentUser()) + && ($displayName !== $this->featureContext->getCurrentUserDisplayName()) + ) { + PHPUnit_Framework_Assert::assertContains( + $displayName, + $autocompleteItems, + "'" . $displayName . "' not in autocomplete list" + ); + $numExpectedItems = $numExpectedItems + 1; + } } } + + PHPUnit_Framework_Assert::assertCount( + $numExpectedItems, + $autocompleteItems, + 'expected ' . $numExpectedItems . ' in autocomplete list but there are ' . \count($autocompleteItems) + ); + PHPUnit_Framework_Assert::assertNotContains( $notToBeListed, $this->sharingDialog->getAutocompleteItemsList() diff --git a/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php b/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php index 730bc0eca48f..ca143f7b045b 100644 --- a/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php +++ b/tests/acceptance/features/lib/FilesPageElement/SharingDialog.php @@ -119,13 +119,13 @@ public function getAutocompleteNodeElement() { * * @param string|array $groupNames * - * @return array + * @return string|array */ public function groupStringsToMatchAutoComplete($groupNames) { if (is_array($groupNames)) { $autocompleteStrings = array(); foreach ($groupNames as $groupName => $groupData) { - $autocompleteStrings[] = $groupName . $this->suffixToIdentifyGroups; + $autocompleteStrings[$groupName] = $groupName . $this->suffixToIdentifyGroups; } } else { $autocompleteStrings = $groupNames . $this->suffixToIdentifyGroups; diff --git a/tests/acceptance/features/webUISharingInternalUsers/shareAutocompletion.feature b/tests/acceptance/features/webUISharingInternalUsers/shareAutocompletion.feature index b91fccc78a61..76b16432846e 100644 --- a/tests/acceptance/features/webUISharingInternalUsers/shareAutocompletion.feature +++ b/tests/acceptance/features/webUISharingInternalUsers/shareAutocompletion.feature @@ -6,68 +6,107 @@ So that I can efficiently share my files with other users or groups Background: Given these users have been created but not initialized: - | username | password | displayname | email | - | user1 | 1234 | User One | u1@oc.com.np | - | user2 | 1234 | User Two | u2@oc.com.np | - | user3 | 1234 | User Three | u2@oc.com.np | - | usergrp | 1234 | User Grp | u@oc.com.np | - | regularuser | 1234 | User Regular | regularuser@oc.com.np | + | username | password | displayname | email | + | user1 | 1234 | User One | u1@oc.com.np | + | two | 1234 | User Two | u2@oc.com.np | + | user3 | 1234 | Three | u3@oc.com.np | + | u444 | 1234 | Four | u3@oc.com.np | + | usergrp | 1234 | User Ggg | u@oc.com.np | + | five | 1234 | User Group | five@oc.com.np | + | regularuser | 1234 | User Regular | regularuser@oc.com.np | + | usersmith | 1234 | John Finn Smith | js@oc.com.np | And these groups have been created: - |groupname| - |group1 | - |group2 | - |group3 | - |groupuser | + | groupname | + | finance1 | + | finance2 | + | finance3 | + | users-finance | + | other | And the user has browsed to the login page - And the user has logged in with username "regularuser" and password "1234" using the webUI - And the user has browsed to the files page @skipOnLDAP @user_ldap#175 Scenario: autocompletion of regular existing users - Given the user has opened the share dialog for the folder "simple-folder" - When the user types "user" in the share-with-field - Then all users and groups that contain the string "user" in their name should be listed in the autocomplete list on the webUI + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has opened the share dialog for the folder "simple-folder" + When the user types "us" in the share-with-field + Then all users and groups that contain the string "us" in their name should be listed in the autocomplete list on the webUI And the users own name should not be listed in the autocomplete list on the webUI Scenario: autocompletion of regular existing groups - Given the user has opened the share dialog for the folder "simple-folder" - When the user types "grou" in the share-with-field - Then all users and groups that contain the string "grou" in their name should be listed in the autocomplete list on the webUI + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has opened the share dialog for the folder "simple-folder" + When the user types "fi" in the share-with-field + Then all users and groups that contain the string "fi" in their name should be listed in the autocomplete list on the webUI And the users own name should not be listed in the autocomplete list on the webUI Scenario: autocompletion for a pattern that does not match any user or group - Given the user has opened the share dialog for the folder "simple-folder" + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has opened the share dialog for the folder "simple-folder" When the user types "doesnotexist" in the share-with-field Then a tooltip with the text "No users or groups found for doesnotexist" should be shown near the share-with-field on the webUI And the autocomplete list should not be displayed on the webUI @skipOnLDAP - Scenario: autocomplete short user names when completely typed - Given these users have been created but not initialized: - | username | password | displayname | email | - | uu1 | 1234 | UU One | uu1@oc.com.np | + Scenario: autocomplete short user/display names when completely typed + Given the administrator has set the minimum characters for sharing autocomplete to "4" + And the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And these users have been created but not initialized: + | username | password | displayname | email | + | use | 1234 | Use | uz@oc.com.np | + And the user has opened the share dialog for the folder "simple-folder" + When the user types "Use" in the share-with-field + Then only "Use" should be listed in the autocomplete list on the webUI + + @skipOnLDAP + Scenario: autocomplete short group names when completely typed + Given the administrator has set the minimum characters for sharing autocomplete to "3" + And these groups have been created: + | groupname | + | fi | + And the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has opened the share dialog for the folder "simple-folder" + When the user types "fi" in the share-with-field + Then only "fi (group)" should be listed in the autocomplete list on the webUI + + @skipOnLDAP + Scenario: autocompletion when minimum characters is the default (2) and not enough characters are typed + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page And the user has opened the share dialog for the folder "simple-folder" - When the user types "uu1" in the share-with-field - Then all users and groups that contain the string "uu1" in their name should be listed in the autocomplete list on the webUI + When the user types "u" in the share-with-field + Then a tooltip with the text "No users or groups found for u. Please enter at least 2 characters for suggestions" should be shown near the share-with-field on the webUI + And the autocomplete list should not be displayed on the webUI @skipOnLDAP - Scenario: autocompletion when not enough characters typed - Given the user has opened the share dialog for the folder "simple-folder" + Scenario: autocompletion when minimum characters is increased and not enough characters are typed + Given the administrator has set the minimum characters for sharing autocomplete to "4" + And the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has opened the share dialog for the folder "simple-folder" When the user types "use" in the share-with-field - Then a tooltip with the text "No users or groups found for use" should be shown near the share-with-field on the webUI + Then a tooltip with the text "No users or groups found for use. Please enter at least 4 characters for suggestions" should be shown near the share-with-field on the webUI And the autocomplete list should not be displayed on the webUI @skipOnLDAP - Scenario: autocompletion when changing minimum characters for sharing autocomplete - Given the administrator has set the minimum characters for sharing autocomplete to "2" + Scenario: autocompletion when increasing the minimum characters for sharing autocomplete + Given the administrator has set the minimum characters for sharing autocomplete to "3" + And the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page And the user has opened the share dialog for the folder "simple-folder" - When the user types "us" in the share-with-field - Then all users and groups that contain the string "us" in their name should be listed in the autocomplete list on the webUI + When the user types "use" in the share-with-field + Then all users and groups that contain the string "use" in their name should be listed in the autocomplete list on the webUI And the users own name should not be listed in the autocomplete list on the webUI @skipOnLDAP @user_ldap#175 Scenario: autocompletion of a pattern that matches regular existing users but also a user with whom the item is already shared (folder) - Given the user has shared the folder "simple-folder" with the user "User One" using the webUI + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has shared the folder "simple-folder" with the user "User One" using the webUI And the user has opened the share dialog for the folder "simple-folder" When the user types "user" in the share-with-field Then all users and groups that contain the string "user" in their name should be listed in the autocomplete list on the webUI except user "User One" @@ -75,23 +114,28 @@ So that I can efficiently share my files with other users or groups @skipOnLDAP @user_ldap#175 Scenario: autocompletion of a pattern that matches regular existing users but also a user with whom the item is already shared (file) - Given the user has shared the file "data.zip" with the user "User Grp" using the webUI + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user has shared the file "data.zip" with the user "User Ggg" using the webUI And the user has opened the share dialog for the file "data.zip" When the user types "user" in the share-with-field - Then all users and groups that contain the string "user" in their name should be listed in the autocomplete list on the webUI except user "User Grp" + Then all users and groups that contain the string "user" in their name should be listed in the autocomplete list on the webUI except user "User Ggg" And the users own name should not be listed in the autocomplete list on the webUI Scenario: autocompletion of a pattern that matches regular existing groups but also a group with whom the item is already shared (folder) - Given the user shares the folder "simple-folder" with the group "group1" using the webUI + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user shares the folder "simple-folder" with the group "finance1" using the webUI And the user has opened the share dialog for the folder "simple-folder" - When the user types "grou" in the share-with-field - Then all users and groups that contain the string "grou" in their name should be listed in the autocomplete list on the webUI except group "group1" + When the user types "fi" in the share-with-field + Then all users and groups that contain the string "fi" in their name should be listed in the autocomplete list on the webUI except group "finance1" And the users own name should not be listed in the autocomplete list on the webUI Scenario: autocompletion of a pattern that matches regular existing groups but also a group with whom the item is already shared (file) - Given the user shares the file "data.zip" with the group "groupuser" using the webUI + Given the user has logged in with username "regularuser" and password "1234" using the webUI + And the user has browsed to the files page + And the user shares the file "data.zip" with the group "finance1" using the webUI And the user has opened the share dialog for the file "data.zip" - When the user types "grou" in the share-with-field - Then all users and groups that contain the string "grou" in their name should be listed in the autocomplete list on the webUI except group "groupuser" + When the user types "fi" in the share-with-field + Then all users and groups that contain the string "fi" in their name should be listed in the autocomplete list on the webUI except group "finance1" And the users own name should not be listed in the autocomplete list on the webUI -