diff --git a/media/img/favicon.ico b/media/img/favicon.ico new file mode 100644 index 0000000000..23d8953706 Binary files /dev/null and b/media/img/favicon.ico differ diff --git a/media/img/favicon.png b/media/img/favicon.png deleted file mode 100644 index 379988d7e8..0000000000 Binary files a/media/img/favicon.png and /dev/null differ diff --git a/seahub/api2/endpoints/admin/default_library.py b/seahub/api2/endpoints/admin/default_library.py new file mode 100644 index 0000000000..bfa429a8ee --- /dev/null +++ b/seahub/api2/endpoints/admin/default_library.py @@ -0,0 +1,132 @@ +# Copyright (c) 2012-2016 Seafile Ltd. +import logging + +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 rest_framework import status +from django.utils.translation import ugettext as _ + +from seaserv import seafile_api + +from seahub.options.models import UserOptions +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.views import get_system_default_repo_id + +logger = logging.getLogger(__name__) + + +class AdminDefaultLibrary(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + throttle_classes = (UserRateThrottle,) + permission_classes = (IsAdminUser,) + + def create_default_repo(self, username): + + default_repo_id = seafile_api.create_repo(name=_("My Library"), + desc=_("My Library"), username=username, passwd=None) + + sys_repo_id = get_system_default_repo_id() + if not sys_repo_id or not seafile_api.get_repo(sys_repo_id): + return None + + dirents = seafile_api.list_dir_by_path(sys_repo_id, '/') + for dirent in dirents: + obj_name = dirent.obj_name + seafile_api.copy_file(sys_repo_id, '/', obj_name, + default_repo_id, '/', obj_name, username, 0) + + UserOptions.objects.set_default_repo(username, default_repo_id) + + return default_repo_id + + def get(self, request): + """ Get info of common user's default library. + + Permission checking: + 1. only admin can perform this action. + """ + + # argument check + user_email = request.GET.get('user_email', None) + if not user_email: + error_msg = 'user_email invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + User.objects.get(email=user_email) + except User.DoesNotExist: + error_msg = 'User %s not found.' % user_email + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # get default library info + try: + default_repo_id = UserOptions.objects.get_default_repo(user_email) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + default_repo_info = {} + default_repo_info['user_email'] = user_email + if default_repo_id and seafile_api.get_repo(default_repo_id) is not None: + default_repo_info['exists'] = True + default_repo_info['repo_id'] = default_repo_id + else: + default_repo_info['exists'] = False + + return Response(default_repo_info) + + def post(self, request): + """ Create a default library for a common user. + + Permission checking: + 1. only admin can perform this action. + """ + + # argument check + user_email = request.POST.get('user_email', None) + if not user_email: + error_msg = 'user_email invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + common_user = User.objects.get(email=user_email) + except User.DoesNotExist: + error_msg = 'User %s not found.' % user_email + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # permission check + if not common_user.permissions.can_add_repo(): + error_msg = 'Permission denied, %s can not create library.' % user_email + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # create default library for common use + try: + default_repo_id = UserOptions.objects.get_default_repo(user_email) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + default_repo_info = {} + default_repo_info['user_email'] = user_email + default_repo_info['exists'] = True + + try: + if default_repo_id and seafile_api.get_repo(default_repo_id) is not None: + default_repo_info['repo_id'] = default_repo_id + else: + new_default_repo_id = self.create_default_repo(user_email) + default_repo_info['repo_id'] = new_default_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) + + return Response(default_repo_info) diff --git a/seahub/api2/endpoints/admin/devices.py b/seahub/api2/endpoints/admin/devices.py index c081054fe5..ba3e285b11 100644 --- a/seahub/api2/endpoints/admin/devices.py +++ b/seahub/api2/endpoints/admin/devices.py @@ -84,7 +84,7 @@ class AdminDevices(APIView): error_msg = 'device_id invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - if not user or not is_registered_user(user): + if not user: error_msg = 'user invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) @@ -98,4 +98,3 @@ class AdminDevices(APIView): return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) return Response({'success': True}) - diff --git a/seahub/api2/endpoints/admin/libraries.py b/seahub/api2/endpoints/admin/libraries.py index 51ecd92a1e..8531e92ce2 100644 --- a/seahub/api2/endpoints/admin/libraries.py +++ b/seahub/api2/endpoints/admin/libraries.py @@ -28,11 +28,13 @@ except ImportError: logger = logging.getLogger(__name__) def get_repo_info(repo): + repo_owner = seafile_api.get_repo_owner(repo.repo_id) + org_repo_owner = seafile_api.get_org_repo_owner(repo.repo_id) result = {} result['id'] = repo.repo_id result['name'] = repo.repo_name - result['owner'] = seafile_api.get_repo_owner(repo.repo_id) + result['owner'] = repo_owner or org_repo_owner result['size'] = repo.size result['size_formatted'] = filesizeformat(repo.size) result['encrypted'] = repo.encrypted @@ -149,8 +151,17 @@ class AdminLibrary(APIView): repo = seafile_api.get_repo(repo_id) if not repo: - return api_error(status.HTTP_404_NOT_FOUND, - 'Library %s not found.' % repo_id) + # for case of `seafile-data` has been damaged + # no `repo object` will be returned from seafile api + # delete the database record anyway + try: + seafile_api.remove_repo(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) + + return Response({'success': True}) repo_name = repo.name repo_owner = seafile_api.get_repo_owner(repo_id) diff --git a/seahub/api2/endpoints/admin/org_users.py b/seahub/api2/endpoints/admin/org_users.py new file mode 100644 index 0000000000..37440fc58c --- /dev/null +++ b/seahub/api2/endpoints/admin/org_users.py @@ -0,0 +1,239 @@ +# Copyright (c) 2012-2016 Seafile Ltd. +import logging + +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 rest_framework import status + +from constance import config +from seaserv import ccnet_api + +from seahub.utils import clear_token, is_valid_email +from seahub.utils.licenseparse import user_number_over_limit +from seahub.base.accounts import User +from seahub.base.templatetags.seahub_tags import email2nickname +from seahub.profile.models import Profile +from seahub.options.models import UserOptions +from seahub.api2.authentication import TokenAuthentication +from seahub.api2.throttling import UserRateThrottle +from seahub.api2.utils import api_error +from seahub.api2.permissions import IsProVersion + +try: + from seahub.settings import ORG_MEMBER_QUOTA_ENABLED +except ImportError: + ORG_MEMBER_QUOTA_ENABLED= False + +logger = logging.getLogger(__name__) + +def get_org_user_info(org_id, user_obj): + user_info = {} + email = user_obj.username + user_info['org_id'] = org_id + user_info['active'] = user_obj.is_active + user_info['email'] = email + user_info['name'] = email2nickname(email) + user_info['contact_email'] = Profile.objects.get_contact_email_by_user(email) + + return user_info + +class AdminOrgUsers(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + throttle_classes = (UserRateThrottle,) + permission_classes = (IsAdminUser, IsProVersion) + + def post(self, request, org_id): + """ Add new user to org. + + Permission checking: + 1. only admin can perform this action. + """ + # argument check + org_id = int(org_id) + if org_id == 0: + error_msg = 'org_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + org = ccnet_api.get_org_by_id(org_id) + if not org: + error_msg = 'Organization %d not found.' % org_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + email = request.POST.get('email', None) + if not email or not is_valid_email(email): + error_msg = 'email invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + password = request.POST.get('password', None) + if not password: + error_msg = 'password invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + User.objects.get(email=email) + user_exists = True + except User.DoesNotExist: + user_exists = False + + if user_exists: + error_msg = 'User %s already exists.' % email + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # check user number limit by license + if user_number_over_limit(): + error_msg = 'The number of users exceeds the limit.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # check user number limit by org member quota + org_members = len(ccnet_api.get_org_emailusers(org.url_prefix, -1, -1)) + if ORG_MEMBER_QUOTA_ENABLED: + from seahub_extra.organizations.models import OrgMemberQuota + org_members_quota = OrgMemberQuota.objects.get_quota(org_id) + if org_members_quota is not None and org_members >= org_members_quota: + error_msg = 'Failed. You can only invite %d members.' % org_members_quota + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # create user + try: + user = User.objects.create_user(email, + password, is_staff=False, is_active=True) + except User.DoesNotExist as e: + logger.error(e) + error_msg = 'Fail to add user %s.' % email + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + # add user to org + # set `is_staff` parameter as `0` + try: + ccnet_api.add_org_user(org_id, email, 0) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + name = request.POST.get('name', None) + if name: + Profile.objects.add_or_update(email, name) + + if config.FORCE_PASSWORD_CHANGE: + UserOptions.objects.set_force_passwd_change(email) + + user_info = get_org_user_info(org_id, user) + return Response(user_info) + + +class AdminOrgUser(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + throttle_classes = (UserRateThrottle,) + permission_classes = (IsAdminUser, IsProVersion) + + def put(self, request, org_id, email): + """ update active of a org user + + Permission checking: + 1. only admin can perform this action. + """ + + # argument check + org_id = int(org_id) + if org_id == 0: + error_msg = 'org_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + org = ccnet_api.get_org_by_id(org_id) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not org: + error_msg = 'Organization %d not found.' % org_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + try: + user = User.objects.get(email=email) + except User.DoesNotExist: + error_msg = 'User %s not found.' % email + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + active = request.data.get('active', None) + if not active: + error_msg = 'active invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if active.lower() not in ('true', 'false'): + error_msg = "active invalid, should be 'true' or 'false'." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + active_user = True if active.lower() == 'true' else False + + try: + if active_user: + user.is_active = True + else: + user.is_active = False + + # update user status + result_code = user.save() + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if result_code == -1: + error_msg = 'Fail to add user %s.' % email + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # clear web api and repo sync token + # when inactive an user + try: + if not active_user: + clear_token(email) + except Exception as e: + logger.error(e) + + user_info = get_org_user_info(org_id, user) + return Response(user_info) + + def delete(self, request, org_id, email): + """ Delete an user from org + + Permission checking: + 1. only admin can perform this action. + """ + # argument check + org_id = int(org_id) + if org_id == 0: + error_msg = 'org_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + org = ccnet_api.get_org_by_id(org_id) + if not org: + error_msg = 'Organization %d not found.' % org_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + try: + user = User.objects.get(email=email) + except User.DoesNotExist: + error_msg = 'User %s not found.' % email + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # permission check + if org.creator == email: + error_msg = 'Failed to delete: %s is an organization creator.' % email + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + try: + ccnet_api.remove_org_user(org_id, email) + user.delete() + 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/permissions.py b/seahub/api2/permissions.py index a3643918e3..76ab915984 100644 --- a/seahub/api2/permissions.py +++ b/seahub/api2/permissions.py @@ -8,6 +8,7 @@ from rest_framework.permissions import BasePermission from django.conf import settings from seaserv import check_permission, is_repo_owner, ccnet_api +from seahub.utils import is_pro_version from seahub.utils import is_pro_version @@ -67,12 +68,14 @@ class CanInviteGuest(BasePermission): return settings.ENABLE_GUEST_INVITATION and \ request.user.permissions.can_invite_guest() + class CanGenerateShareLink(BasePermission): """Check user has permission to generate share link. """ def has_permission(self, request, *args, **kwargs): return request.user.permissions.can_generate_share_link() + class CanGenerateUploadLink(BasePermission): """Check user has permission to generate upload link. """ diff --git a/seahub/base/accounts.py b/seahub/base/accounts.py index f778366e31..a9d2ce0764 100644 --- a/seahub/base/accounts.py +++ b/seahub/base/accounts.py @@ -16,11 +16,10 @@ from constance import config from registration import signals from seahub.auth import login -from seahub.constants import DEFAULT_USER from seahub.profile.models import Profile, DetailedProfile from seahub.role_permissions.utils import get_enabled_role_permissions_by_role from seahub.utils import is_user_password_strong, \ - clear_token, get_system_admins + clear_token, get_system_admins, is_pro_version from seahub.utils.mail import send_html_email_with_dj_template, MAIL_PRIORITY from seahub.utils.licenseparse import user_number_over_limit @@ -215,7 +214,10 @@ class User(object): source = "LDAP" username = self.username - orgs = ccnet_threaded_rpc.get_orgs_by_user(username) + + orgs = [] + if is_pro_version(): + orgs = ccnet_threaded_rpc.get_orgs_by_user(username) # remove owned repos owned_repos = [] diff --git a/seahub/base/context_processors.py b/seahub/base/context_processors.py index 2685f0ab51..3df80bced6 100644 --- a/seahub/base/context_processors.py +++ b/seahub/base/context_processors.py @@ -15,7 +15,8 @@ from constance import config from seahub.settings import SEAFILE_VERSION, SITE_TITLE, SITE_NAME, \ MAX_FILE_NAME, BRANDING_CSS, LOGO_PATH, LOGO_WIDTH, LOGO_HEIGHT,\ - SHOW_REPO_DOWNLOAD_BUTTON, SITE_ROOT, ENABLE_GUEST_INVITATION + SHOW_REPO_DOWNLOAD_BUTTON, SITE_ROOT, ENABLE_GUEST_INVITATION, \ + FAVICON_PATH try: from seahub.settings import SEACLOUD_MODE @@ -61,6 +62,7 @@ def base(request): 'seafile_version': SEAFILE_VERSION, 'site_title': SITE_TITLE, 'branding_css': BRANDING_CSS, + 'favicon_path': FAVICON_PATH, 'logo_path': LOGO_PATH, 'logo_width': LOGO_WIDTH, 'logo_height': LOGO_HEIGHT, diff --git a/seahub/group/templates/group/add_member_email.html b/seahub/group/templates/group/add_member_email.html index 88932fca60..f5890e7db8 100644 --- a/seahub/group/templates/group/add_member_email.html +++ b/seahub/group/templates/group/add_member_email.html @@ -8,7 +8,7 @@
{% trans "Hi, " %}
-{% blocktrans with grp_name=group.group_name %}{{ email }} invited you to join group {{ grp_name }} on {{ site_name }}: {% endblocktrans %}
+{% blocktrans with grp_name=group.group_name %}{{ email|email2nickname|escape }} invited you to join group {{ grp_name }} on {{ site_name }}: {% endblocktrans %}
{{ url_base }}{% url 'registration_register' %}?src={{ to_email }}