diff --git a/media/js/base.js b/media/js/base.js
index 338cf59c14..3a53c711ac 100644
--- a/media/js/base.js
+++ b/media/js/base.js
@@ -861,7 +861,7 @@ function userInputOPtionsForSelect2(user_search_url) {
// for search. both name & email can be searched.
// use ' '(space) to separate name & email
"text": users[i].name + ' ' + users[i].email,
- "avatar": users[i].avatar,
+ "avatar_url": users[i].avatar_url,
"name": users[i].name
});
}
@@ -873,8 +873,8 @@ function userInputOPtionsForSelect2(user_search_url) {
// format items shown in the drop-down menu
formatResult: function(item) {
- if (item.avatar) {
- return item.avatar + '' + HTMLescape(item.name) + '
' + HTMLescape(item.id) + '';
+ if (item.avatar_url) {
+ return '
' + '' + HTMLescape(item.name) + '
' + HTMLescape(item.id) + '';
} else {
return; // if no match, show nothing
}
diff --git a/seahub/api2/endpoints/search_user.py b/seahub/api2/endpoints/search_user.py
new file mode 100644
index 0000000000..a8bcdc01c4
--- /dev/null
+++ b/seahub/api2/endpoints/search_user.py
@@ -0,0 +1,165 @@
+import json
+
+from django.db.models import Q
+from django.http import HttpResponse
+
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.views import APIView
+from rest_framework import status
+
+import seaserv
+
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import UserRateThrottle
+from seahub.api2.utils import api_error
+
+from seahub.utils import is_org_context
+from seahub.base.accounts import User
+from seahub.base.templatetags.seahub_tags import email2nickname
+from seahub.profile.models import Profile
+from seahub.contacts.models import Contact
+from seahub.avatar.templatetags.avatar_tags import api_avatar_url
+from seahub.settings import ENABLE_GLOBAL_ADDRESSBOOK, ENABLE_SEARCH_FROM_LDAP_DIRECTLY
+
+
+class SearchUser(APIView):
+ """ Search user from contacts/all users
+ """
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ permission_classes = (IsAuthenticated,)
+ throttle_classes = (UserRateThrottle, )
+
+ def _can_use_global_address_book(self, request):
+
+ return request.user.permissions.can_use_global_address_book()
+
+ def get(self, request, format=None):
+
+
+ if not self._can_use_global_address_book(request):
+ return api_error(status.HTTP_403_FORBIDDEN,
+ 'Guest user can not use global address book.')
+
+ q = request.GET.get('q', None)
+
+ if not q:
+ return api_error(status.HTTP_400_BAD_REQUEST, 'Argument missing.')
+
+ users_from_ccnet = []
+ users_from_profile = []
+ users_result = []
+ username = request.user.username
+
+ if request.cloud_mode:
+ if is_org_context(request):
+ url_prefix = request.user.org.url_prefix
+ users = seaserv.get_org_users_by_url_prefix(url_prefix, -1, -1)
+
+ # search user from ccnet
+ users_from_ccnet = filter(lambda u: q in u.email, users)
+
+ # when search profile, only search users in org
+ # 'nickname__contains' for search by nickname
+ # 'contact_email__contains' for search by contact email
+ users_from_profile = Profile.objects.filter(Q(user__in=[u.email for u in users]) & \
+ (Q(nickname__contains=q)) | \
+ Q(contact_email__contains=q)).values('user')
+ elif ENABLE_GLOBAL_ADDRESSBOOK:
+ users_from_ccnet = search_user_from_ccnet(q)
+ users_from_profile = Profile.objects.filter(Q(contact_email__contains=q) | \
+ Q(nickname__contains=q)).values('user')
+ else:
+ # TODO delete this ?
+ users = []
+ contacts = Contact.objects.get_contacts_by_user(username)
+ for c in contacts:
+ try:
+ user = User.objects.get(email = c.contact_email)
+ c.is_active = user.is_active
+ except User.DoesNotExist:
+ continue
+
+ c.email = c.contact_email
+ users.append(c)
+
+ users_from_ccnet = filter(lambda u: q in u.email, users)
+ # 'user__in' for only get profile of contacts
+ # 'nickname__contains' for search by nickname
+ # 'contact_email__contains' for search by contact
+ users_from_profile = Profile.objects.filter(Q(user__in=[u.email for u in users]) & \
+ (Q(nickname__contains=q)) | \
+ Q(contact_email__contains=q)).values('user')
+ else:
+ users_from_ccnet = search_user_from_ccnet(q)
+ users_from_profile = Profile.objects.filter(Q(contact_email__contains=q) | \
+ Q(nickname__contains=q)).values('user')
+
+ # remove inactive users and add to result
+ for u in users_from_ccnet[:10]:
+ if u.is_active:
+ users_result.append(u.email)
+
+ for p in users_from_profile[:10]:
+ try:
+ user = User.objects.get(email = p['user'])
+ except User.DoesNotExist:
+ continue
+
+ if not user.is_active:
+ continue
+
+ users_result.append(p['user'])
+
+ # remove duplicate emails
+ users_result = {}.fromkeys(users_result).keys()
+
+ try:
+ include_self = int(request.GET.get('include_self', 1))
+ except ValueError:
+ include_self = 1
+
+ if include_self == 0 and username in users_result:
+ # reomve myself
+ users_result.remove(username)
+
+ try:
+ size = int(request.GET.get('avatar_size', 32))
+ except ValueError:
+ size = 32
+
+ formated_result = format_searched_user_result(request, users_result, size)[:10]
+ return HttpResponse(json.dumps({"users": formated_result}), status=200,
+ content_type='application/json; charset=utf-8')
+
+def format_searched_user_result(request, users, size):
+ results = []
+
+ for email in users:
+ url, is_default, date_uploaded = api_avatar_url(email, size)
+ results.append({
+ "email": email,
+ "avatar_url": request.build_absolute_uri(url),
+ "name": email2nickname(email),
+ "contact_email": Profile.objects.get_contact_email_by_user(email),
+ })
+
+ return results
+
+def search_user_from_ccnet(q):
+ users = []
+
+ db_users = seaserv.ccnet_threaded_rpc.search_emailusers('DB', q, 0, 10)
+ users.extend(db_users)
+
+ count = len(users)
+ if count < 10:
+ ldap_imported_users = seaserv.ccnet_threaded_rpc.search_emailusers('LDAP', q, 0, 10 - count)
+ users.extend(ldap_imported_users)
+
+ count = len(users)
+ if count < 10 and ENABLE_SEARCH_FROM_LDAP_DIRECTLY:
+ all_ldap_users = seaserv.ccnet_threaded_rpc.search_ldapusers(q, 0, 10 - count)
+ users.extend(all_ldap_users)
+
+ return users
diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py
index 036b8fa977..404527364d 100644
--- a/seahub/api2/urls.py
+++ b/seahub/api2/urls.py
@@ -7,6 +7,7 @@ from .endpoints.dir_shared_items import DirSharedItemsEndpoint
from .endpoints.account import Account
from .endpoints.shared_upload_links import SharedUploadLinksView
from .endpoints.be_shared_repo import BeSharedReposView
+from .endpoints.search_user import SearchUser
urlpatterns = patterns('',
url(r'^ping/$', Ping.as_view()),
diff --git a/seahub/api2/views.py b/seahub/api2/views.py
index bfc172a80f..035cbaaca2 100644
--- a/seahub/api2/views.py
+++ b/seahub/api2/views.py
@@ -415,6 +415,7 @@ def get_searched_users(q):
return searched_users
+
class Search(APIView):
""" Search all the repos
"""
diff --git a/seahub/settings.py b/seahub/settings.py
index 958025b574..a5258486a7 100644
--- a/seahub/settings.py
+++ b/seahub/settings.py
@@ -227,6 +227,9 @@ ACCOUNT_ACTIVATION_DAYS = 7
# allow seafile amdin view user's repo
ENABLE_SYS_ADMIN_VIEW_REPO = False
+#allow search from LDAP directly during auto-completion (not only search imported users)
+ENABLE_SEARCH_FROM_LDAP_DIRECTLY = False
+
# show traffic on the UI
SHOW_TRAFFIC = True
diff --git a/static/scripts/common.js b/static/scripts/common.js
index eb12128a8c..eb248b963b 100644
--- a/static/scripts/common.js
+++ b/static/scripts/common.js
@@ -564,7 +564,7 @@ define([
// for search. both name & email can be searched.
// use ' '(space) to separate name & email
"text": users[i].name + ' ' + users[i].email,
- "avatar": users[i].avatar,
+ "avatar_url": users[i].avatar_url,
"name": users[i].name
});
}
@@ -577,8 +577,8 @@ define([
// format items shown in the drop-down menu
formatResult: function(item) {
- if (item.avatar) {
- return item.avatar + '' + _this.HTMLescape(item.name) + '
' + _this.HTMLescape(item.id) + '';
+ if (item.avatar_url) {
+ return '
' + _this.HTMLescape(item.name) + '
' + _this.HTMLescape(item.id) + '';
} else {
return; // if no match, show nothing
}
diff --git a/tests/api/test_search_user.py b/tests/api/test_search_user.py
index a419b2073c..3318b2dc07 100644
--- a/tests/api/test_search_user.py
+++ b/tests/api/test_search_user.py
@@ -12,20 +12,55 @@ class SearchUserTest(BaseTestCase):
self.endpoint = reverse('search-user')
def test_can_search(self):
- p = Profile.objects.add_or_update(self.user.email, nickname="test")
- p.contact_email = 'new_mail@test.com'
+ email = self.admin.email
+ nickname = 'admin_test'
+ contact_email= 'new_admin_test@test.com'
+ p = Profile.objects.add_or_update(email, nickname=nickname)
+ p.contact_email = contact_email
p.save()
- resp = self.client.get(self.endpoint + '?q=' + self.user.email)
+ resp = self.client.get(self.endpoint + '?q=' + email)
json_resp = json.loads(resp.content)
self.assertEqual(200, resp.status_code)
assert json_resp['users'] is not None
- assert json_resp['users'][0]['email'] == self.user.email
- assert json_resp['users'][0]['avatar'] is not None
+ assert json_resp['users'][0]['email'] == email
assert json_resp['users'][0]['avatar_url'] is not None
- assert json_resp['users'][0]['name'] == 'test'
- assert json_resp['users'][0]['contact_email'] == 'new_mail@test.com'
+ assert json_resp['users'][0]['name'] == nickname
+ assert json_resp['users'][0]['contact_email'] == contact_email
+
+ def test_search_myself(self):
+ email = self.user.email
+ nickname = 'user_test'
+ contact_email= 'new_user_test@test.com'
+ p = Profile.objects.add_or_update(email, nickname=nickname)
+ p.contact_email = contact_email
+ p.save()
+
+ resp = self.client.get(self.endpoint + '?q=' + email)
+ json_resp = json.loads(resp.content)
+
+ self.assertEqual(200, resp.status_code)
+ assert json_resp['users'] is not None
+ assert json_resp['users'][0]['email'] == email
+ assert json_resp['users'][0]['avatar_url'] is not None
+ assert json_resp['users'][0]['name'] == nickname
+ assert json_resp['users'][0]['contact_email'] == contact_email
+
+ def test_search_without_myself(self):
+ email = self.user.email
+ resp = self.client.get(self.endpoint + '?include_self=0&q=' + email)
+ json_resp = json.loads(resp.content)
+
+ self.assertEqual(200, resp.status_code)
+ assert len(json_resp['users']) == 0
+
+ def test_search_unregistered_user(self):
+ resp = self.client.get(self.endpoint + '?q=unregistered_user@seafile.com')
+ json_resp = json.loads(resp.content)
+
+ self.assertEqual(200, resp.status_code)
+ assert len(json_resp['users']) == 0
def test_can_search_by_nickname(self):
admin_email = self.admin.email
@@ -42,7 +77,6 @@ class SearchUserTest(BaseTestCase):
self.assertEqual(200, resp.status_code)
assert json_resp['users'] is not None
assert json_resp['users'][0]['email'] == admin_email
- assert json_resp['users'][0]['avatar'] is not None
assert json_resp['users'][0]['avatar_url'] is not None
assert json_resp['users'][0]['name'] == 'Carl Smith'
assert json_resp['users'][0]['contact_email'] == 'new_mail@test.com'
@@ -63,7 +97,6 @@ class SearchUserTest(BaseTestCase):
self.assertEqual(200, resp.status_code)
assert json_resp['users'] is not None
assert json_resp['users'][0]['email'] == admin_email
- assert json_resp['users'][0]['avatar'] is not None
assert json_resp['users'][0]['avatar_url'] is not None
assert json_resp['users'][0]['name'] == 'Carl Smith'
assert json_resp['users'][0]['contact_email'] == 'new_mail@test.com'
@@ -75,7 +108,6 @@ class SearchUserTest(BaseTestCase):
self.assertEqual(200, resp.status_code)
assert json_resp['users'] is not None
assert json_resp['users'][0]['email'] == admin_email
- assert json_resp['users'][0]['avatar'] is not None
assert json_resp['users'][0]['avatar_url'] is not None
assert json_resp['users'][0]['name'] == 'Carl Smith'
assert json_resp['users'][0]['contact_email'] == 'new_mail@test.com'