diff --git a/seahub/api2/endpoints/admin/library_group_shares.py b/seahub/api2/endpoints/admin/library_group_shares.py
new file mode 100644
index 0000000000..982645d4f7
--- /dev/null
+++ b/seahub/api2/endpoints/admin/library_group_shares.py
@@ -0,0 +1,223 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+import logging
+from rest_framework import status
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAdminUser
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+from seaserv import seafile_api, ccnet_api
+
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import UserRateThrottle
+from seahub.api2.utils import api_error
+
+logger = logging.getLogger(__name__)
+
+def get_library_group_share_info(share_item):
+
+ group_id = share_item.group_id
+ group = ccnet_api.get_group(group_id)
+
+ result = {}
+ result['group_id'] = group_id
+ result['group_name'] = group.group_name
+ result['permission'] = share_item.perm
+ result['repo_id'] = share_item.repo_id
+
+ return result
+
+
+class AdminLibraryGroupShares(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ throttle_classes = (UserRateThrottle,)
+ permission_classes = (IsAdminUser,)
+
+ def get(self, request, repo_id):
+ """ List all group shares of a repo
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+ try:
+ share_items = seafile_api.list_repo_shared_group_by_user(repo_owner, repo_id)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ result = []
+ for share_item in share_items:
+ share_item_info = get_library_group_share_info(share_item)
+ result.append(share_item_info)
+
+ return Response(result)
+
+ def post(self, request, repo_id):
+ """ Admin share a library to group.
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # argument check
+ permission = request.data.get('permission', None)
+ if not permission or permission not in ('r', 'rw'):
+ error_msg = 'permission invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ result = {}
+ result['failed'] = []
+ result['success'] = []
+ group_ids = request.data.getlist('group_id')
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+
+ for group_id in group_ids:
+ try:
+ group_id = int(group_id)
+ except ValueError as e:
+ logger.error(e)
+ result['failed'].append({
+ 'group_id': group_id,
+ 'error_msg': 'group_id %s invalid.' % group_id
+ })
+
+ continue
+
+ group = ccnet_api.get_group(group_id)
+ if not group:
+ result['failed'].append({
+ 'group_id': group_id,
+ 'error_msg': 'Group %s not found' % group_id
+ })
+
+ continue
+
+ try:
+ seafile_api.set_group_repo(repo_id, group_id, repo_owner, permission)
+ except Exception as e:
+ logger.error(e)
+ result['failed'].append({
+ "group_id": group_id,
+ 'error_msg': 'Internal Server Error'
+ })
+
+ continue
+
+ result['success'].append({
+ "group_id": group_id,
+ "group_name": group.group_name,
+ "permission": permission,
+ "repo_id": repo_id,
+ })
+
+ return Response(result)
+
+class AdminLibraryGroupShare(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ throttle_classes = (UserRateThrottle,)
+ permission_classes = (IsAdminUser,)
+
+ def put(self, request, repo_id, format=None):
+ """ Update library group share permission.
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # argument check
+ permission = request.data.get('permission', None)
+ if not permission or permission not in ('r', 'rw'):
+ error_msg = 'permission invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ group_id = request.data.get('group_id', None)
+
+ try:
+ group_id = int(group_id)
+ except ValueError:
+ error_msg = 'group_id %s invalid.' % group_id
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ group = ccnet_api.get_group(group_id)
+ if not group:
+ error_msg = 'Group %s not found' % group_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ try:
+ seafile_api.set_group_repo_permission(group_id, repo_id, permission)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ result = {}
+ result['group_id'] = group_id
+ result['group_name'] = group.group_name
+ result['permission'] = permission
+ result['repo_id'] = repo_id
+
+ return Response(result)
+
+ def delete(self, request, repo_id, format=None):
+ """ Delete library group share permission.
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # argument check
+ permission = request.data.get('permission', None)
+ if not permission or permission not in ('r', 'rw'):
+ error_msg = 'permission invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ group_id = request.data.get('group_id')
+ try:
+ group_id = int(group_id)
+ except ValueError:
+ return api_error(status.HTTP_400_BAD_REQUEST, 'group_id %s invalid' % group_id)
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+
+ try:
+ seafile_api.unset_group_repo(repo_id, group_id, repo_owner)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ return Response({'success': True})
diff --git a/seahub/api2/endpoints/admin/library_user_shares.py b/seahub/api2/endpoints/admin/library_user_shares.py
new file mode 100644
index 0000000000..5e74efc9b1
--- /dev/null
+++ b/seahub/api2/endpoints/admin/library_user_shares.py
@@ -0,0 +1,233 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+import logging
+from rest_framework import status
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAdminUser
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from seaserv import seafile_api
+
+from seahub.api2.authentication import TokenAuthentication
+from seahub.api2.throttling import UserRateThrottle
+from seahub.api2.utils import api_error
+
+from seahub.base.accounts import User
+from seahub.base.templatetags.seahub_tags import email2nickname
+
+from seahub.utils import (is_valid_username)
+
+logger = logging.getLogger(__name__)
+
+def get_library_user_share_info(share_item):
+ result = {}
+ result['user_email'] = share_item.user
+ result['user_name'] = email2nickname(share_item.user)
+ result['permission'] = share_item.perm
+ result['repo_id'] = share_item.repo_id
+
+ return result
+
+
+class AdminLibraryUserShares(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ throttle_classes = (UserRateThrottle,)
+ permission_classes = (IsAdminUser,)
+
+ def get(self, request, repo_id):
+ """ List all user shares of a repo
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+ try:
+ share_items = seafile_api.list_repo_shared_to(repo_owner, repo_id)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ result = []
+ for share_item in share_items:
+ share_item_info = get_library_user_share_info(share_item)
+ result.append(share_item_info)
+
+ return Response(result)
+
+ def post(self, request, repo_id):
+ """ Admin share a library to user.
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # argument check
+ permission = request.data.get('permission', None)
+ if not permission or permission not in ('r', 'rw'):
+ error_msg = 'permission invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ result = {}
+ result['failed'] = []
+ result['success'] = []
+ share_to_users = request.data.getlist('email')
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+
+ for to_user in share_to_users:
+ if repo_owner == to_user:
+ result['failed'].append({
+ 'user_email': to_user,
+ 'error_msg': 'email %s is library owner.' % to_user
+ })
+
+ continue
+
+ if not is_valid_username(to_user):
+ result['failed'].append({
+ 'user_email': to_user,
+ 'error_msg': 'email %s invalid.' % to_user
+ })
+
+ continue
+
+ try:
+ User.objects.get(email=to_user)
+ except User.DoesNotExist:
+ result['failed'].append({
+ 'user_email': to_user,
+ 'error_msg': 'User %s not found.' % to_user
+ })
+
+ continue
+
+ try:
+ seafile_api.share_repo(repo_id,
+ repo_owner, to_user, permission)
+ except Exception as e:
+ logger.error(e)
+ result['failed'].append({
+ 'user_email': to_user,
+ 'error_msg': 'Internal Server Error'
+ })
+
+ continue
+
+ new_perm = seafile_api.check_permission_by_path(repo_id, '/', to_user)
+ result['success'].append({
+ "repo_id": repo_id,
+ "user_email": to_user,
+ "user_name": email2nickname(to_user),
+ "permission": new_perm,
+ })
+
+ return Response(result)
+
+class AdminLibraryUserShare(APIView):
+ authentication_classes = (TokenAuthentication, SessionAuthentication)
+ throttle_classes = (UserRateThrottle,)
+ permission_classes = (IsAdminUser,)
+
+ def put(self, request, repo_id, format=None):
+ """ Update library user share permission.
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # argument check
+ permission = request.data.get('permission', None)
+ if not permission or permission not in ('r', 'rw'):
+ error_msg = 'permission invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ to_user = request.data.get('user_email', None)
+ if not to_user or not is_valid_username(to_user):
+ error_msg = 'user_email invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ try:
+ User.objects.get(email=to_user)
+ except User.DoesNotExist:
+ error_msg = 'User %s not found.' % to_user
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+
+ try:
+ seafile_api.set_share_permission(
+ repo_id, repo_owner, to_user, permission)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ new_perm = seafile_api.check_permission_by_path(repo_id, '/', to_user)
+ result = {}
+ result['user_email'] = to_user
+ result['user_name'] = email2nickname(to_user)
+ result['permission'] = new_perm
+ result['repo_id'] = repo_id
+
+ return Response(result)
+
+ def delete(self, request, repo_id, format=None):
+ """ Delete library user share permission.
+
+ Permission checking:
+ 1. admin user.
+ """
+
+ # argument check
+ permission = request.data.get('permission', None)
+ if not permission or permission not in ('r', 'rw'):
+ error_msg = 'permission invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ to_user = request.data.get('user_email', None)
+ if not to_user or not is_valid_username(to_user):
+ error_msg = 'user_email invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # resource check
+ repo = seafile_api.get_repo(repo_id)
+ if not repo:
+ error_msg = 'Library %s not found.' % repo_id
+ return api_error(status.HTTP_404_NOT_FOUND, error_msg)
+
+ # current `request.user.username` is admin user,
+ # so need to identify the repo owner specifically.
+ repo_owner = seafile_api.get_repo_owner(repo_id)
+ try:
+ seafile_api.remove_share(repo_id, repo_owner, to_user)
+ except Exception as e:
+ logger.error(e)
+ error_msg = 'Internal Server Error'
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ return Response({'success': True})
diff --git a/seahub/api2/endpoints/search_group.py b/seahub/api2/endpoints/search_group.py
new file mode 100644
index 0000000000..ff43b469e6
--- /dev/null
+++ b/seahub/api2/endpoints/search_group.py
@@ -0,0 +1,81 @@
+# Copyright (c) 2012-2016 Seafile Ltd.
+
+from rest_framework.authentication import SessionAuthentication
+from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from rest_framework import status
+
+from django.conf import settings
+
+from seaserv import ccnet_api
+
+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.utils.timeutils import timestamp_to_isoformat_timestr
+
+
+def get_group_info(group_id):
+ group = ccnet_api.get_group(group_id)
+ isoformat_timestr = timestamp_to_isoformat_timestr(group.timestamp)
+ group_info = {
+ "id": group.id,
+ "name": group.group_name,
+ "owner": group.creator_name,
+ "created_at": isoformat_timestr,
+ }
+
+ return group_info
+
+class SearchGroup(APIView):
+
+ 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):
+ """ Search group.
+
+ Permission checking:
+ 1. default(NOT guest) user;
+ """
+
+ # argument check
+ q = request.GET.get('q', None)
+ if not q:
+ error_msg = 'q invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ # permission check
+ if not self._can_use_global_address_book(request):
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ if not settings.ENABLE_GLOBAL_ADDRESSBOOK:
+ error_msg = 'Feature disabled.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ if is_org_context(request):
+ org_id = request.user.org.org_id
+ groups = ccnet_api.get_org_groups(org_id, -1, -1)
+ else:
+ groups = ccnet_api.get_all_groups(-1, -1)
+
+ result = []
+ for group in groups:
+ group_name = group.group_name
+ if not group_name:
+ continue
+
+ if q.lower() in group_name.lower():
+ group_info = get_group_info(group.id)
+ result.append(group_info)
+
+ return Response(result)
diff --git a/seahub/templates/js/sysadmin-templates.html b/seahub/templates/js/sysadmin-templates.html
index 4c69f1ae68..c9b6e305c1 100644
--- a/seahub/templates/js/sysadmin-templates.html
+++ b/seahub/templates/js/sysadmin-templates.html
@@ -372,6 +372,9 @@
+ <% if (!encrypted) { %>
+
+ <% } %>
|
@@ -559,3 +562,106 @@
+
+
+
+
diff --git a/seahub/urls.py b/seahub/urls.py
index 9cc2e7c753..565ecfe594 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -20,6 +20,7 @@ from seahub.views.sysadmin import *
from seahub.views.ajax import *
from seahub.api2.endpoints.groups import Groups, Group
from seahub.api2.endpoints.group_members import GroupMembers, GroupMembersBulk, GroupMember
+from seahub.api2.endpoints.search_group import SearchGroup
from seahub.api2.endpoints.share_links import ShareLinks, ShareLink
from seahub.api2.endpoints.shared_folders import SharedFolders
from seahub.api2.endpoints.shared_repos import SharedRepos, SharedRepo
@@ -46,6 +47,8 @@ from seahub.api2.endpoints.admin.library_dirents import AdminLibraryDirents, Adm
from seahub.api2.endpoints.admin.system_library import AdminSystemLibrary
from seahub.api2.endpoints.admin.trash_libraries import AdminTrashLibraries, AdminTrashLibrary
from seahub.api2.endpoints.admin.groups import AdminGroups, AdminGroup
+from seahub.api2.endpoints.admin.library_user_shares import AdminLibraryUserShares, AdminLibraryUserShare
+from seahub.api2.endpoints.admin.library_group_shares import AdminLibraryGroupShares, AdminLibraryGroupShare
# Uncomment the next two lines to enable the admin:
#from django.contrib import admin
@@ -186,6 +189,7 @@ urlpatterns = patterns(
url(r'^api/v2.1/groups/(?P\d+)/members/$', GroupMembers.as_view(), name='api-v2.1-group-members'),
url(r'^api/v2.1/groups/(?P\d+)/members/bulk/$', GroupMembersBulk.as_view(), name='api-v2.1-group-members-bulk'),
url(r'^api/v2.1/groups/(?P\d+)/members/(?P[^/]+)/$', GroupMember.as_view(), name='api-v2.1-group-member'),
+ url(r'^api/v2.1/search-group/$', SearchGroup.as_view(), name='api-v2.1-search-group'),
url(r'^api/v2.1/shared-folders/$', SharedFolders.as_view(), name='api-v2.1-shared-folders'),
url(r'^api/v2.1/shared-repos/$', SharedRepos.as_view(), name='api-v2.1-shared-repos'),
url(r'^api/v2.1/shared-repos/(?P[-0-9a-f]{36})/$', SharedRepo.as_view(), name='api-v2.1-shared-repo'),
@@ -205,7 +209,6 @@ urlpatterns = patterns(
url(r'^api/v2.1/admin/device-errors/$', AdminDeviceErrors.as_view(), name='api-v2.1-admin-device-errors'),
url(r'^api/v2.1/invitations/$', InvitationsView.as_view()),
url(r'^api/v2.1/invitations/(?P[a-f0-9]{32})/$', InvitationView.as_view()),
-
url(r'^api/v2.1/admin/libraries/$', AdminLibraries.as_view(), name='api-v2.1-admin-libraries'),
url(r'^api/v2.1/admin/libraries/(?P[-0-9a-f]{36})/$', AdminLibrary.as_view(), name='api-v2.1-admin-library'),
url(r'^api/v2.1/admin/libraries/(?P[-0-9a-f]{36})/dirents/$', AdminLibraryDirents.as_view(), name='api-v2.1-admin-library-dirents'),
@@ -215,6 +218,10 @@ urlpatterns = patterns(
url(r'^api/v2.1/admin/system-library/$', AdminSystemLibrary.as_view(), name='api-v2.1-admin-system-library'),
url(r'^api/v2.1/admin/trash-libraries/$', AdminTrashLibraries.as_view(), name='api-v2.1-admin-trash-libraries'),
url(r'^api/v2.1/admin/trash-libraries/(?P[-0-9a-f]{36})/$', AdminTrashLibrary.as_view(), name='api-v2.1-admin-trash-library'),
+ url(r'^api/v2.1/admin/libraries/(?P[-0-9a-f]{36})/user-shares/$', AdminLibraryUserShares.as_view(), name='api-v2.1-admin-library-user-shares'),
+ url(r'^api/v2.1/admin/libraries/(?P[-0-9a-f]{36})/user-share/$', AdminLibraryUserShare.as_view(), name='api-v2.1-admin-library-user-share'),
+ url(r'^api/v2.1/admin/libraries/(?P[-0-9a-f]{36})/group-shares/$', AdminLibraryGroupShares.as_view(), name='api-v2.1-admin-library-group-shares'),
+ url(r'^api/v2.1/admin/libraries/(?P[-0-9a-f]{36})/group-share/$', AdminLibraryGroupShare.as_view(), name='api-v2.1-admin-library-group-share'),
(r'^avatar/', include('seahub.avatar.urls')),
(r'^notification/', include('seahub.notifications.urls')),
diff --git a/static/scripts/common.js b/static/scripts/common.js
index c30269b1c4..defe7ae589 100644
--- a/static/scripts/common.js
+++ b/static/scripts/common.js
@@ -141,6 +141,7 @@ define([
// Group
case 'groups': return siteRoot + 'api/v2.1/groups/';
+ case 'search_group': return siteRoot + 'api/v2.1/search-group/';
case 'group': return siteRoot + 'api/v2.1/groups/' + options.group_id + '/';
case 'group_members': return siteRoot + 'api/v2.1/groups/' + options.group_id + '/members/';
case 'group_member': return siteRoot + 'api/v2.1/groups/' + options.group_id + '/members/' + options.email + '/';
@@ -180,6 +181,10 @@ define([
case 'admin-system-library': return siteRoot + 'api/v2.1/admin/system-library/';
case 'admin-trash-libraries': return siteRoot + 'api/v2.1/admin/trash-libraries/';
case 'admin-trash-library': return siteRoot + 'api/v2.1/admin/trash-libraries/' + options.repo_id + '/';
+ case 'admin_library_user_shares': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/user-shares/';
+ case 'admin_library_user_share': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/user-share/';
+ case 'admin_library_group_shares': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/group-shares/';
+ case 'admin_library_group_share': return siteRoot + 'api/v2.1/admin/libraries/' + options.repo_id + '/group-share/';
}
},
@@ -634,6 +639,73 @@ define([
}
},
+ groupInputOptionsForSelect2: function() {
+ var _this = this;
+ return {
+ placeholder: gettext("Search groups"),
+
+ // with 'tags', the user can directly enter, not just select
+ // tags need ``, not `