diff --git a/.travis.yml b/.travis.yml index 9999063405..36c7efaf2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python before_install: # build/init/start ccnet-server/seafile-server - - git clone --depth=1 --branch=master git://github.com/haiwen/seafile-test-deploy /tmp/seafile-test-deploy + - git clone --depth=1 --branch=5.0 git://github.com/haiwen/seafile-test-deploy /tmp/seafile-test-deploy - cd /tmp/seafile-test-deploy && ./bootstrap.sh && cd - # install phantomjs - ./tests/install-deps.sh diff --git a/seahub/api2/endpoints/account.py b/seahub/api2/endpoints/account.py index 0f7ce50257..2df6eaf58c 100644 --- a/seahub/api2/endpoints/account.py +++ b/seahub/api2/endpoints/account.py @@ -88,8 +88,8 @@ class Account(APIView): if storage is not None: seafile_api.set_user_quota(email, int(storage)) - if sharing is not None: - seafile_api.set_user_share_quota(email, int(sharing)) + # if sharing is not None: + # seafile_api.set_user_share_quota(email, int(sharing)) def _create_account(self, request, email): copy = request.data.copy() diff --git a/seahub/api2/endpoints/dir_shared_items.py b/seahub/api2/endpoints/dir_shared_items.py index d7818bcf92..f456e2e3ff 100644 --- a/seahub/api2/endpoints/dir_shared_items.py +++ b/seahub/api2/endpoints/dir_shared_items.py @@ -18,7 +18,6 @@ from seahub.api2.utils import api_error from seahub.base.templatetags.seahub_tags import email2nickname from seahub.base.accounts import User from seahub.share.signals import share_repo_to_user_successful -from seahub.share.views import check_user_share_quota from seahub.utils import (is_org_context, is_valid_username, send_perm_audit_msg) @@ -268,24 +267,29 @@ class DirSharedItemsEndpoint(APIView): return api_error(status.HTTP_400_BAD_REQUEST, 'permission invalid.') shared_repo = repo if path == '/' else sub_repo - success, failed = [], [] + result = {} + result['failed'] = [] + result['success'] = [] + if share_type == 'user': share_to_users = request.data.getlist('username') for to_user in share_to_users: if not is_valid_username(to_user): - failed.append(to_user) + result['failed'].append({ + 'email': to_user, + 'error_msg': 'username invalid.' + }) continue try: User.objects.get(email=to_user) except User.DoesNotExist: - failed.append(to_user) + result['failed'].append({ + 'email': to_user, + 'error_msg': 'User %s not found.' % to_user + }) continue - if not check_user_share_quota(username, shared_repo, users=[to_user]): - return api_error(status.HTTP_403_FORBIDDEN, - 'Not enough quota.') - try: if is_org_context(request): org_id = request.user.org.org_id @@ -301,7 +305,7 @@ class DirSharedItemsEndpoint(APIView): from_user=username, to_user=to_user, repo=shared_repo) - success.append({ + result['success'].append({ "share_type": "user", "user_info": { "name": to_user, @@ -314,7 +318,10 @@ class DirSharedItemsEndpoint(APIView): repo_id, path, permission) except SearpcError as e: logger.error(e) - failed.append(to_user) + result['failed'].append({ + 'email': to_user, + 'error_msg': 'Internal Server Error' + }) continue if share_type == 'group': @@ -328,10 +335,6 @@ class DirSharedItemsEndpoint(APIView): if not group: return api_error(status.HTTP_404_NOT_FOUND, 'Group %s not found' % gid) - if not check_user_share_quota(username, shared_repo, groups=[group]): - return api_error(status.HTTP_403_FORBIDDEN, - 'Not enough quota.') - try: if is_org_context(request): org_id = request.user.org.org_id @@ -342,7 +345,7 @@ class DirSharedItemsEndpoint(APIView): seafile_api.set_group_repo(shared_repo.repo_id, gid, username, permission) - success.append({ + result['success'].append({ "share_type": "group", "group_info": { "id": gid, @@ -355,13 +358,14 @@ class DirSharedItemsEndpoint(APIView): repo_id, path, permission) except SearpcError as e: logger.error(e) - failed.append(group.group_name) + result['failed'].append({ + 'group_name': group.group_name, + 'error_msg': 'Internal Server Error' + }) continue - return HttpResponse(json.dumps({ - "success": success, - "failed": failed - }), status=200, content_type=json_content_type) + return HttpResponse(json.dumps(result), + status=200, content_type=json_content_type) def delete(self, request, repo_id, format=None): username = request.user.username diff --git a/seahub/api2/endpoints/send_share_link_email.py b/seahub/api2/endpoints/send_share_link_email.py new file mode 100644 index 0000000000..0f3f41f5b6 --- /dev/null +++ b/seahub/api2/endpoints/send_share_link_email.py @@ -0,0 +1,113 @@ +import os +import logging + +from rest_framework.authentication import SessionAuthentication +from rest_framework.permissions import IsAuthenticated +from rest_framework.throttling import UserRateThrottle +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 seahub.api2.authentication import TokenAuthentication +from seahub.api2.utils import api_error + +from seahub.utils import IS_EMAIL_CONFIGURED, is_valid_username, \ + string2list, gen_shared_link, send_html_email +from seahub.share.models import FileShare + +from seahub.settings import REPLACE_FROM_EMAIL, ADD_REPLY_TO_HEADER, SITE_NAME + +logger = logging.getLogger(__name__) + +class SendShareLinkView(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication ) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def post(self, request): + + if not IS_EMAIL_CONFIGURED: + error_msg = _(u'Sending shared link failed. Email service is not properly configured, please contact administrator.') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # check args + email = request.POST.get('email', None) + if not email: + error_msg = 'email invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + token = request.POST.get('token', None) + if not token: + error_msg = 'token invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + extra_msg = request.POST.get('extra_msg', '') + + # check if token exists + try: + link = FileShare.objects.get(token=token) + except FileShare.DoesNotExist: + error_msg = 'token %s not found.' % token + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # check if is share link owner + username = request.user.username + if not link.is_owner(username): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + result = {} + result['failed'] = [] + result['success'] = [] + to_email_list = string2list(email) + for to_email in to_email_list: + + failed_info = {} + + if not is_valid_username(to_email): + failed_info['email'] = to_email + failed_info['error_msg'] = 'email invalid.' + result['failed'].append(failed_info) + continue + + # prepare basic info + c = { + 'email': username, + 'to_email': to_email, + 'extra_msg': extra_msg, + } + + if REPLACE_FROM_EMAIL: + from_email = username + else: + from_email = None # use default from email + + if ADD_REPLY_TO_HEADER: + reply_to = username + else: + reply_to = None + + c['file_shared_link'] = gen_shared_link(token, link.s_type) + c['file_shared_name'] = os.path.basename(link.path.rstrip('/')) + template = 'shared_link_email.html' + + if link.s_type == 'f': + c['file_shared_type'] = _(u"file") + title = _(u'A file is shared to you on %s') % SITE_NAME + else: + c['file_shared_type'] = _(u"directory") + title = _(u'A directory is shared to you on %s') % SITE_NAME + + # send email + try: + send_html_email(title, template, c, from_email, [to_email], reply_to=reply_to) + result['success'].append(to_email) + except Exception as e: + logger.error(e) + failed_info['email'] = to_email + failed_info['error_msg'] = 'Internal Server Error' + result['failed'].append(failed_info) + + return Response(result) diff --git a/seahub/api2/endpoints/send_upload_link_email.py b/seahub/api2/endpoints/send_upload_link_email.py new file mode 100644 index 0000000000..7c2c6e7316 --- /dev/null +++ b/seahub/api2/endpoints/send_upload_link_email.py @@ -0,0 +1,105 @@ +import logging + +from rest_framework.authentication import SessionAuthentication +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.throttling import UserRateThrottle +from rest_framework.views import APIView +from rest_framework import status +from django.utils.translation import ugettext as _ + +from seahub.api2.authentication import TokenAuthentication +from seahub.api2.utils import api_error + +from seahub.utils import IS_EMAIL_CONFIGURED, is_valid_username, \ + string2list, gen_shared_upload_link, send_html_email +from seahub.share.models import UploadLinkShare + +from seahub.settings import REPLACE_FROM_EMAIL, ADD_REPLY_TO_HEADER, SITE_NAME + +logger = logging.getLogger(__name__) + +class SendUploadLinkView(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication ) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def post(self, request): + + if not IS_EMAIL_CONFIGURED: + error_msg = _(u'Sending shared link failed. Email service is not properly configured, please contact administrator.') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # check args + email = request.POST.get('email', None) + if not email: + error_msg = 'email invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + token = request.POST.get('token', None) + if not token: + error_msg = 'token invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + extra_msg = request.POST.get('extra_msg', '') + + # check if token exists + try: + link = UploadLinkShare.objects.get(token=token) + except UploadLinkShare.DoesNotExist: + error_msg = 'token %s not found.' % token + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # check if is upload link owner + username = request.user.username + if not link.is_owner(username): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + result = {} + result['failed'] = [] + result['success'] = [] + to_email_list = string2list(email) + for to_email in to_email_list: + + failed_info = {} + if not is_valid_username(to_email): + failed_info['email'] = to_email + failed_info['error_msg'] = 'email invalid.' + result['failed'].append(failed_info) + continue + + # prepare basic info + c = { + 'email': username, + 'to_email': to_email, + 'extra_msg': extra_msg, + } + + if REPLACE_FROM_EMAIL: + from_email = username + else: + from_email = None # use default from email + + if ADD_REPLY_TO_HEADER: + reply_to = username + else: + reply_to = None + + c['shared_upload_link'] = gen_shared_upload_link(token) + title = _(u'An upload link is shared to you on %s') % SITE_NAME + template = 'shared_upload_link_email.html' + + # send email + try: + send_html_email(title, template, c, from_email, [to_email], reply_to=reply_to) + result['success'].append(to_email) + except Exception as e: + logger.error(e) + failed_info['email'] = to_email + failed_info['error_msg'] = 'Internal Server Error' + result['failed'].append(failed_info) + + return Response(result) + diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py index 0920ec60fb..a13264d073 100644 --- a/seahub/api2/urls.py +++ b/seahub/api2/urls.py @@ -10,6 +10,8 @@ from .endpoints.be_shared_repo import BeSharedReposView from .endpoints.search_user import SearchUser from .endpoints.group_discussions import GroupDiscussions from .endpoints.group_discussion import GroupDiscussion +from .endpoints.send_share_link_email import SendShareLinkView +from .endpoints.send_upload_link_email import SendUploadLinkView urlpatterns = patterns('', url(r'^ping/$', Ping.as_view()), @@ -64,6 +66,8 @@ urlpatterns = patterns('', url(r'^beshared-repos/$', BeShared.as_view(), name='beshared'), url(r'^beshared-repos/(?P[-0-9-a-f]{36})/$', BeSharedReposView.as_view(), name='beshared-repos'), url(r'^default-repo/$', DefaultRepoView.as_view(), name='api2-defaultrepo'), + url(r'^send-share-link/$', SendShareLinkView.as_view(), name='api2-send-share-link'), + url(r'^send-upload-link/$', SendUploadLinkView.as_view(), name='api2-send-upload-link'), url(r'^shared-links/$', SharedLinksView.as_view()), url(r'^shared-upload-links/$', SharedUploadLinksView.as_view()), url(r'^shared-files/$', SharedFilesView.as_view()), diff --git a/seahub/api2/views.py b/seahub/api2/views.py index dae353bdce..aa0b13ce4e 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -78,6 +78,7 @@ from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \ from seahub.utils.repo import get_sub_repo_abbrev_origin_path from seahub.utils.star import star_file, unstar_file from seahub.utils.file_types import IMAGE, DOCUMENT +from seahub.utils.file_size import get_file_size_unit from seahub.utils.timeutils import utc_to_local from seahub.views import validate_owner, is_registered_user, check_file_lock, \ group_events_data, get_diff, create_default_library, get_owned_repo_list, \ @@ -4278,7 +4279,7 @@ class OrganizationView(APIView): OrgMemberQuota.objects.set_quota(new_org.org_id, member_limit) # set quota - quota = quota_mb * (1 << 20) + quota = quota_mb * get_file_size_unit('MB') seafserv_threaded_rpc.set_org_quota(new_org.org_id, quota) return Response('success', status=status.HTTP_201_CREATED) diff --git a/seahub/base/templatetags/seahub_tags.py b/seahub/base/templatetags/seahub_tags.py index 825629e1e4..1ec1e3a856 100644 --- a/seahub/base/templatetags/seahub_tags.py +++ b/seahub/base/templatetags/seahub_tags.py @@ -7,10 +7,10 @@ import time from django import template from django.core.cache import cache from django.utils.safestring import mark_safe -from django.utils import translation +from django.utils import translation, formats from django.utils.dateformat import DateFormat from django.utils.translation import ugettext as _ -from django.utils.translation import ungettext +from django.utils.translation import ugettext, ungettext from django.utils.translation import pgettext from django.utils.html import escape @@ -22,6 +22,8 @@ from seahub.cconvert import CConvert from seahub.po import TRANSLATION_MAP from seahub.shortcuts import get_first_object_or_none from seahub.utils import normalize_cache_key, CMMT_DESC_PATT +from seahub.utils.html import avoid_wrapping +from seahub.utils.file_size import get_file_size_unit register = template.Library() @@ -356,20 +358,24 @@ def translate_seahub_time_str(val): @register.filter(name='email2nickname') def email2nickname(value): """ - Return nickname or short email. + Return nickname if it exists and it's not an empty string, + otherwise return short email. """ if not value: return '' key = normalize_cache_key(value, NICKNAME_CACHE_PREFIX) - nickname = cache.get(key) - if not nickname: - profile = get_first_object_or_none(Profile.objects.filter(user=value)) - if profile is not None and profile.nickname: - nickname = profile.nickname - else: - nickname = value.split('@')[0] - cache.set(key, nickname, NICKNAME_CACHE_TIMEOUT) + cached_nickname = cache.get(key) + if cached_nickname and cached_nickname.strip(): + return cached_nickname.strip() + + profile = get_first_object_or_none(Profile.objects.filter(user=value)) + if profile is not None and profile.nickname and profile.nickname.strip(): + nickname = profile.nickname.strip() + else: + nickname = value.split('@')[0] + + cache.set(key, nickname, NICKNAME_CACHE_TIMEOUT) return nickname @register.filter(name='email2id') @@ -464,3 +470,38 @@ def trim(value, length): @register.filter(name='strip_slash') def strip_slash(value): return value.strip('/') + +@register.filter(is_safe=True) +def seahub_filesizeformat(bytes): + """ + Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, + 102 bytes, etc). + """ + try: + bytes = float(bytes) + except (TypeError, ValueError, UnicodeDecodeError): + value = ungettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0} + return avoid_wrapping(value) + + filesize_number_format = lambda value: formats.number_format(round(value, 1), 1) + + KB = get_file_size_unit('KB') + MB = get_file_size_unit('MB') + GB = get_file_size_unit('GB') + TB = get_file_size_unit('TB') + PB = get_file_size_unit('PB') + + if bytes < KB: + value = ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes} + elif bytes < MB: + value = ugettext("%s KB") % filesize_number_format(bytes / KB) + elif bytes < GB: + value = ugettext("%s MB") % filesize_number_format(bytes / MB) + elif bytes < TB: + value = ugettext("%s GB") % filesize_number_format(bytes / GB) + elif bytes < PB: + value = ugettext("%s TB") % filesize_number_format(bytes / TB) + else: + value = ugettext("%s PB") % filesize_number_format(bytes / PB) + + return avoid_wrapping(value) diff --git a/seahub/forms.py b/seahub/forms.py index 8eed7d8239..c4356753c4 100644 --- a/seahub/forms.py +++ b/seahub/forms.py @@ -146,9 +146,6 @@ class RepoPassowrdForm(forms.Form): except SearpcError, e: if e.msg == 'Bad arguments': raise forms.ValidationError(_(u'Bad url format')) - # elif e.msg == 'Repo is not encrypted': - # return HttpResponseRedirect(reverse('repo', - # args=[self.repo_id])) elif e.msg == 'Incorrect password': raise forms.ValidationError(_(u'Wrong password')) elif e.msg == 'Internal server error': @@ -164,8 +161,6 @@ class SetUserQuotaForm(forms.Form): space_quota = forms.IntegerField(min_value=0, error_messages={'required': _('Space quota can\'t be empty'), 'min_value': _('Space quota is too low (minimum value is 0)')}) - share_quota = forms.IntegerField(min_value=0, required = False, - error_messages={'min_value': _('Share quota is too low (minimum value is 0)')}) class RepoSettingForm(forms.Form): """ diff --git a/seahub/notifications/management/commands/send_notices.py b/seahub/notifications/management/commands/send_notices.py index b7bdf2b242..0cb97a9536 100644 --- a/seahub/notifications/management/commands/send_notices.py +++ b/seahub/notifications/management/commands/send_notices.py @@ -124,7 +124,7 @@ class Command(BaseCommand): if repo is None: notice.delete() - notice.repo_url = reverse('repo', args=[repo.id]) + notice.repo_url = reverse("view_common_lib_dir", args=[repo_id, '/']) notice.notice_from = escape(email2nickname(d['share_from'])) notice.repo_name = repo.name notice.avatar_src = self.get_avatar_src(d['share_from']) @@ -138,7 +138,7 @@ class Command(BaseCommand): uploaded_to = d['uploaded_to'].rstrip('/') file_path = uploaded_to + '/' + file_name file_link = reverse('view_lib_file', args=[repo_id, urlquote(file_path)]) - folder_link = reverse('repo', args=[repo_id]) + '?p=' + urlquote(uploaded_to) + folder_link = reverse('view_common_lib_dir', args=[repo_id, urlquote(uploaded_to).strip('/')]) folder_name = os.path.basename(uploaded_to) notice.file_link = file_link @@ -210,11 +210,10 @@ class Command(BaseCommand): email_ctx = {} for notice in unseen_notices: - to_email = Profile.objects.get_contact_email_by_user(notice.to_user) if notice.to_user in email_ctx: - email_ctx[to_email] += 1 + email_ctx[notice.to_user] += 1 else: - email_ctx[to_email] = 1 + email_ctx[notice.to_user] = 1 for to_user, count in email_ctx.items(): # save current language @@ -223,12 +222,14 @@ class Command(BaseCommand): # get and active user language user_language = self.get_user_language(to_user) translation.activate(user_language) - logger.info('Set language code to %s' % user_language) + logger.debug('Set language code to %s for user: %s' % (user_language, to_user)) self.stdout.write('[%s] Set language code to %s' % ( str(datetime.datetime.now()), user_language)) notices = [] for notice in unseen_notices: + logger.info('Processing unseen notice: [%s]' % (notice)) + if notice.to_user != to_user: continue @@ -261,6 +262,8 @@ class Command(BaseCommand): if not notices: continue + contact_email = Profile.objects.get_contact_email_by_user(to_user) + to_user = contact_email # use contact email if any c = { 'to_user': to_user, 'notice_count': count, diff --git a/seahub/profile/models.py b/seahub/profile/models.py index 56bf1be189..1d343b44d2 100644 --- a/seahub/profile/models.py +++ b/seahub/profile/models.py @@ -39,7 +39,7 @@ class ProfileManager(models.Manager): """Get a user's contact email, use username(login email) if not found. """ p = self.get_profile_by_user(username) - if p and p.contact_email != '': + if p and p.contact_email: return p.contact_email else: return username diff --git a/seahub/settings.py b/seahub/settings.py index cebe3bce78..2f17665ee2 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -4,8 +4,6 @@ import sys import os import re -import random -import string from seaserv import FILE_SERVER_ROOT, FILE_SERVER_PORT, SERVICE_URL @@ -272,6 +270,11 @@ FORCE_SERVER_CRYPTO = True # Enable or disable repo history setting ENABLE_REPO_HISTORY_SETTING = True +# Enable or disable org repo creation by user +ENABLE_USER_CREATE_ORG_REPO = True + +DISABLE_SYNC_WITH_ANY_FOLDER = False + # File preview FILE_PREVIEW_MAX_SIZE = 30 * 1024 * 1024 OFFICE_PREVIEW_MAX_SIZE = 2 * 1024 * 1024 @@ -353,8 +356,8 @@ REQUIRE_DETAIL_ON_REGISTRATION = False # Account initial password, for password resetting. # INIT_PASSWD can either be a string, or a function (function has to be set without the brackets) def genpassword(): - return ''.join([random.choice(string.digits + string.letters) for i in range(0, 10)]) - + from django.utils.crypto import get_random_string + return get_random_string(10) INIT_PASSWD = genpassword # browser tab title @@ -616,13 +619,13 @@ INNER_FILE_SERVER_ROOT = 'http://127.0.0.1:' + FILE_SERVER_PORT CONSTANCE_CONFIG = { 'SERVICE_URL': (SERVICE_URL,''), 'FILE_SERVER_ROOT': (FILE_SERVER_ROOT,''), - 'DISABLE_SYNC_WITH_ANY_FOLDER': (False,''), + 'DISABLE_SYNC_WITH_ANY_FOLDER': (DISABLE_SYNC_WITH_ANY_FOLDER,''), 'ENABLE_SIGNUP': (ENABLE_SIGNUP,''), 'ACTIVATE_AFTER_REGISTRATION': (ACTIVATE_AFTER_REGISTRATION,''), 'REGISTRATION_SEND_MAIL': (REGISTRATION_SEND_MAIL ,''), 'LOGIN_REMEMBER_DAYS': (LOGIN_REMEMBER_DAYS,''), - 'ENABLE_ORGANIZATION_LIBRARY': (True, ''), + 'ENABLE_USER_CREATE_ORG_REPO': (ENABLE_USER_CREATE_ORG_REPO, ''), 'ENABLE_ENCRYPTED_LIBRARY': (ENABLE_ENCRYPTED_LIBRARY,''), 'REPO_PASSWORD_MIN_LENGTH': (REPO_PASSWORD_MIN_LENGTH,''), diff --git a/seahub/share/settings.py b/seahub/share/settings.py deleted file mode 100644 index 38e8f66a1f..0000000000 --- a/seahub/share/settings.py +++ /dev/null @@ -1,4 +0,0 @@ -from django.conf import settings - -ANONYMOUS_SHARE_COOKIE_TIMEOUT = getattr(settings, 'ANONYMOUS_SHARE_COOKIE_TIMEOUT', 24*60*60) -ANONYMOUS_SHARE_LINK_TIMEOUT = getattr(settings, 'ANONYMOUS_SHARE_LINK_TIMEOUT', 2) diff --git a/seahub/share/tokens.py b/seahub/share/tokens.py deleted file mode 100644 index dd943b5b0a..0000000000 --- a/seahub/share/tokens.py +++ /dev/null @@ -1,87 +0,0 @@ -import random -from datetime import date -from datetime import datetime as dt -from django.conf import settings -from django.utils.http import int_to_base36, base36_to_int - -from settings import ANONYMOUS_SHARE_LINK_TIMEOUT - -class AnonymousShareTokenGenerator(object): - """ - Strategy object used to generate and check tokens for the repo anonymous - share mechanism. - """ - def make_token(self): - """ - Returns a token that can be used once to do a anonymous share for repo. - """ - return self._make_token_with_timestamp(self._num_days(self._today())) - - def check_token(self, token): - """ - Check that a anonymous share token is valid. - """ - # Parse the token - try: - ts_b36, hash = token.split("-") - except ValueError: - return False - - try: - ts = base36_to_int(ts_b36) - except ValueError: - return False - - # Check the timestamp is within limit - if (self._num_days(self._today()) - ts) > ANONYMOUS_SHARE_LINK_TIMEOUT: - return False - - return True - - def get_remain_time(self, token): - """ - Get token remain time. - """ - try: - ts_b36, hash = token.split("-") - except ValueError: - return None - - try: - ts = base36_to_int(ts_b36) - except ValueError: - return None - - days = ANONYMOUS_SHARE_LINK_TIMEOUT - (self._num_days(self._today()) - ts) - if days < 0: - return None - - now = dt.now() - tomorrow = dt(now.year, now.month, now.day+1) - - return (tomorrow - now).seconds + days * 24 * 60 * 60 - - def _make_token_with_timestamp(self, timestamp): - # timestamp is number of days since 2001-1-1. Converted to - # base 36, this gives us a 3 digit string until about 2121 - ts_b36 = int_to_base36(timestamp) - - # We limit the hash to 20 chars to keep URL short - import datetime - import hashlib - now = datetime.datetime.now() - hash = hashlib.sha1(settings.SECRET_KEY + - unicode(random.randint(0, 999999)) + - now.strftime('%Y-%m-%d %H:%M:%S') + - unicode(timestamp)).hexdigest()[::2] - - return "%s-%s" % (ts_b36, hash) - - def _num_days(self, dt): - return (dt - date(2001,1,1)).days - - def _today(self): - # Used for mocking in tests - return date.today() - -anon_share_token_generator = AnonymousShareTokenGenerator() diff --git a/seahub/share/views.py b/seahub/share/views.py index a648c1ba4a..e5545f3748 100644 --- a/seahub/share/views.py +++ b/seahub/share/views.py @@ -28,8 +28,6 @@ from seahub.share.forms import RepoShareForm, FileLinkShareForm, \ from seahub.share.models import FileShare, PrivateFileDirShare, \ UploadLinkShare, OrgFileShare from seahub.share.signals import share_repo_to_user_successful -# from settings import ANONYMOUS_SHARE_COOKIE_TIMEOUT -# from tokens import anon_share_token_generator from seahub.auth.decorators import login_required, login_required_ajax from seahub.base.accounts import User from seahub.base.decorators import user_mods_check, require_POST @@ -154,40 +152,6 @@ def share_to_user(request, repo, to_user, permission): to_user=to_user, repo=repo) return True -def check_user_share_quota(username, repo, users=[], groups=[]): - """Check whether user has enough share quota when share repo to - users/groups. Only used for personal account on cloud service. - """ - if not users and not groups: - return True - - if not seaserv.CALC_SHARE_USAGE: - return True - - if ccnet_threaded_rpc.get_orgs_by_user(username): - return True # no share quota check for org user - - check_pass = False - share_quota = seafile_api.get_user_share_quota(username) - if share_quota == -2: - return True # share quota is unlimited - - current_share_usage = seafile_api.get_user_share_usage(username) - - share_usage = 0 - if users: - share_usage += seafile_api.get_repo_size(repo.id) * (len(users)) - - if groups: - grp_members = [] - for group in groups: - grp_members += [e.user_name for e in seaserv.get_group_members(group.id)] - grp_members = set(grp_members) - share_usage += seafile_api.get_repo_size(repo.id) * (len(grp_members) - 1) - if share_usage + current_share_usage < share_quota: - check_pass = True - - return check_pass ########## views @login_required @@ -251,14 +215,6 @@ def share_repo(request): send_perm_audit_msg('add-repo-perm', username, 'all', \ perm_repo_id, perm_path, permission) - if not check_user_share_quota(username, repo, users=share_to_users, - groups=share_to_groups): - messages.error(request, _( - 'Failed to share "%s", no enough quota. ' - 'Upgrade account.' - ) % escape(repo.name), extra_tags='safe') - return HttpResponseRedirect(next) - for group in share_to_groups: if share_to_group(request, repo, group, permission): send_perm_audit_msg('add-repo-perm', username, group.id, \ @@ -715,88 +671,6 @@ def share_permission_admin(request): else: return HttpResponse(json.dumps({'success': False}), status=400, content_type=content_type) - -# 2 views for anonymous share: -# - anonymous_share records share infomation to db and sends the mail -# - anonymous_share_confirm checks the link use clicked and -# adds token to client COOKIE, then redirect client to repo page - -# def anonymous_share(request, email_template_name='repo/anonymous_share_email.html', **kwargs): -# repo_id = kwargs['repo_id'] -# repo_owner = kwargs['repo_owner'] -# anon_email = kwargs['anon_email'] -# is_encrypted = kwargs['is_encrypted'] - -# # Encrypt repo can not be shared to unregistered user. -# if is_encrypted: -# msg = _(u'Failed to share to %s, as encrypted libraries cannot be shared to emails outside the site.') % anon_email -# messages.error(request, msg) -# return - -# token = anon_share_token_generator.make_token() - -# anon_share = AnonymousShare() -# anon_share.repo_owner = repo_owner -# anon_share.repo_id = repo_id -# anon_share.anonymous_email = anon_email -# anon_share.token = token - -# try: -# anon_share.save() -# except: -# msg = _(u'Failed to share to %s.') % anon_email -# messages.add_message(request, messages.ERROR, msg) -# else: -# # send mail -# use_https = request.is_secure() -# site_name = domain = RequestSite(request).domain - -# t = loader.get_template(email_template_name) -# c = { -# 'email': repo_owner, -# 'anon_email': anon_email, -# 'domain': domain, -# 'site_name': site_name, -# 'token': token, -# 'protocol': use_https and 'https' or 'http', -# } - -# try: -# send_mail(_(u'You are shared with a library in Seafile'), t.render(Context(c)), None, -# [anon_email], fail_silently=False) -# except: -# AnonymousShare.objects.filter(token=token).delete() -# msg = _(u'Failed to share to %s.') % anon_email -# messages.add_message(request, messages.ERROR, msg) -# else: -# msg = _(u'Shared to %(email)s successfully, go check it at Share.') % \ -# {'email':anon_email, 'share':reverse('share_admin')} -# messages.add_message(request, messages.INFO, msg) - -# def anonymous_share_confirm(request, token=None): -# assert token is not None # checked by URLconf - -# # Check whether token in db -# try: -# anon_share = AnonymousShare.objects.get(token=token) -# except AnonymousShare.DoesNotExist: -# raise Http404 -# else: -# res = HttpResponseRedirect(reverse('repo', args=[anon_share.repo_id])) -# res.set_cookie("anontoken", token, -# max_age=ANONYMOUS_SHARE_COOKIE_TIMEOUT) -# return res - -# def remove_anonymous_share(request, token): -# AnonymousShare.objects.filter(token=token).delete() - -# next = request.META.get('HTTP_REFERER', None) -# if not next: -# next = reverse('share_admin') - -# messages.add_message(request, messages.INFO, _(u'Deleted successfully.')) - -# return HttpResponseRedirect(next) ########## share link @login_required_ajax @@ -1398,11 +1272,6 @@ def ajax_private_share_dir(request): for group_id in groups: share_to_groups.append(seaserv.get_group(group_id)) - if not check_user_share_quota(username, shared_repo, users=share_to_users, - groups=share_to_groups): - result['error'] = _(('Failed to share "%s", no enough quota. Upgrade account.') % escape(shared_repo.name)) - return HttpResponse(json.dumps(result), status=400, content_type=content_type) - for email in share_to_users: # Add email to contacts. mail_sended.send(sender=None, user=request.user.username, email=email) diff --git a/seahub/templates/repo.html b/seahub/templates/repo.html deleted file mode 100644 index d12e9be693..0000000000 --- a/seahub/templates/repo.html +++ /dev/null @@ -1,2367 +0,0 @@ -{% extends "myhome_base.html" %} - -{% load seahub_tags avatar_tags i18n upload_tags %} - -{% block sub_title %}{{repo.name}} - {% endblock %} -{% block extra_style %} - - - - -{% endblock %} - -{% block main_panel %} -
-

{{repo.props.name}}{% if user_perm == 'r' %} ({% trans "Read-Only" %}){% endif %}

-
- {{ repo.props.desc|truncatechars:25 }} - {% trans 'Size: ' %}{{ repo_size|filesizeformat }} - {% if show_repo_download_button or show_repo_settings or user_perm == 'rw' %} - - {% if show_repo_download_button %} - {% trans "Download" %} - {% endif %} - {% if show_repo_settings %} - {% trans "Settings" %} - {% endif %} - {% if user_perm == 'rw' %} - {% trans "Trash"%} - {% endif %} - - {% endif %} -
-
- - {% if repo.enc_version == 2 and not server_crypto and repo.encrypted %} -
- {% else %} -
- {% include 'snippets/repo_dir_data.html' %} -
- {% endif %} - - - {% if user_perm == 'rw' %} -
- - - -
- {% endif %} - -
- {% if no_quota %} -

{% trans "Upload Files" %}

-

{% trans "The owner of this library has run out of space." %}

- - {% else %} - - {% if repo.enc_version == 2 and not server_crypto and repo.encrypted %} -
{% csrf_token %} -

{% trans "Upload Files" %}

-
-

{% trans "Please select a file at first." %}

- -
- - {% else %} -

{% trans "File Upload" %}

-
- - close -
-
-
- -
-

- - {% trans "Saving..." %} -

-
-
- {% endif %} - {% endif %} -
- -
-

{% trans "Update %(file_name)s" %}

- {% if no_quota %} -

{% trans "The owner of this library has run out of space." %}

- {% else %} -
{% csrf_token %} - - - {% if repo.enc_version == 2 and not server_crypto and repo.encrypted %} -
-

{% trans "Please select a file at first." %}

- - {% else %} - -
- - {% trans "Choose a file" %} - - - {% if max_upload_file_size %} - ({% blocktrans with max_file_size=max_upload_file_size|filesizeformat %}Smaller than {{ max_file_size }}{% endblocktrans %}) - {% endif %} -
-
-
-
-
-
-

- - {% trans "Saving..." %} -

-
-
- {% endif %} -
- {% endif %} -
- -
{% csrf_token %} -

{% trans "New Directory" %}

-
-
-

- - -
- -
{% csrf_token %} -

{% trans "New File" %}

- -
-
-

- - -
- -
{% csrf_token %} -
-
{% trans "Current Library"%}
- -
- {% if not repo.encrypted %} -
{% trans "Other Libraries"%}
- -
- {% endif %} -
- - - - - -

{% trans "Please click and choose a directory."%}

- - -
- -
-

-
-

- -
- -
{% csrf_token %} -

{% trans "Rename %(name)s as:" %}

- -
-

- - -
- - {% include "snippets/file_share_popup.html" %} - - {% with attach_type='dir' %} - {% include "snippets/group_recommend_form.html" %} - {% endwith %} - - {% if repo.enc_version == 2 and not server_crypto and repo.encrypted %} -
-

{% trans "This library is encrypted. Please input the password if you want to browse it online. And the password will be kept in the browser for only 1 hour." %}

- - - -

- {% url 'edit_profile' as profile_edit_url %} -

{% blocktrans %}You can change how to view encrypted libraries online here.{% endblocktrans %}

-
- {% endif %} - -
- -
- -{% endblock %} - -{% block extra_script %} -{% if repo.enc_version == 2 and not server_crypto and repo.encrypted %} - -{% else %} - {% if not no_quota and user_perm == 'rw' %} -{% upload_js %} - -{% endif %} -{% endif %} - -{% endblock %} diff --git a/seahub/templates/snippets/bottom_bar.html b/seahub/templates/snippets/bottom_bar.html deleted file mode 100644 index 127a7100ab..0000000000 --- a/seahub/templates/snippets/bottom_bar.html +++ /dev/null @@ -1,161 +0,0 @@ -{% load i18n %} -{% load url from future %} - -// set 'side toolbar' & 'groups' popup position -function setToolbarPos() { - var toolbar = $('#side-toolbar'); - - // set toolbar pos - toolbar.css({'top': ($(window).height() - toolbar.outerHeight())/2}); - - // set 'groups' popup position - $('#to-group').css({'top': parseInt(toolbar.css('top')) + 59}); -} -$(window).load(function() { - var to_top = $('
  • {% trans
'); - to_top.click(function() { - $(window).scrollTop(0); - }).appendTo($('#side-toolbar')); - $(window).scroll(function() { - if ($(window).scrollTop() > 0) { - to_top.show(); - } else { - to_top.hide(); - } - }); - setToolbarPos(); -}).resize(setToolbarPos); - - -{% if groups %} -function getAndHandleDiscussions(data_html) { - $('#discussions-to-grp').html(data_html).css({'max-height': parseInt($(window).height() * 0.8 - $('#discuss-to-group-form').outerHeight())}).removeClass('hide'); // 'parseInt' to fix '.msg-main top border missing when the ul's max-height is xxx.5px' - var popup = $('#discuss-to-group'); - popup.css({'top':($(window).height() - popup.outerHeight())/2}); - {% include 'group/msg_js.html' %} -} -$('#discuss').click(function() { - var popup = $('#discuss-to-group'), - form = $('#discuss-to-group-form'), - path = '{{ path }}'; - - // for repo.html: when get dir data with ajax - if ($('#repo-file-list').length > 0) { - path = cur_path; - $('[name="path"]', form).val(path); - // modify attachment dir name - $('.att-name', form).html($('#cur-dir-name').html()); - } - - if (popup.hasClass('hide')) { - $.ajax({ - url: form.attr('action'), - dataType: 'json', - data: { - 'repo_id': '{{ repo.id }}', - 'path': path - }, - success: function(data) { - getAndHandleDiscussions(data['html']); - popup.removeClass('hide'); - }, - error: function(jqXHR, textStatus, errorThrown) { - var err_str = ''; - if (jqXHR.responseText) { - err_str = $.parseJSON(jqXHR.responseText).error; - feedback(err_str, 'error'); - } - } - }); - } else { - popup.addClass('hide'); - } -}); - -// click 'close' to close discuss popup -$('#discuss-to-group .close').click(function () { - $('#discuss-to-group').addClass('hide'); -}); - -$(document).click(function(e) { - var target = e.target || event.srcElement; - if (!$('#discuss, #discuss-to-group').is(target) && !($('#discuss, #discuss-to-group').find('*').is(target))) { - $('#discuss-to-group').addClass('hide'); - } -}); - -$('#discuss-submit').click(function() { - var form = $('#discuss-to-group-form'), - form_id = form.attr('id'); - - if (form.find('.checkbox-checked').length == 0) { - apply_form_error(form_id, "{% trans "Please select at least 1 group." %}"); - return false; - } - - if (!$.trim($('#discuss-to-group-form .input').val())) { - apply_form_error(form_id, "{% trans "Please input a discussion." %}"); - return false; - } - form.find('.error').addClass('hide'); - - var groups = []; - form.find('.checkbox-checked .checkbox-orig').each(function() { - groups.push($(this).val()); - }); - - $.ajax({ - url: form.attr('action'), - type: 'POST', - dataType: 'json', - beforeSend: prepareCSRFToken, - traditional: true, - data: { - 'groups': groups, - 'message': $('[name="message"]', form).val(), - 'repo_id': '{{ repo.id }}', - 'path': $('[name="path"]', form).val(), - 'attach_type': $('[name="attach_type"]', form).val() - }, - success: function(data) { - if(data['success']) { - feedback(data['success'], 'success'); - getAndHandleDiscussions(data['html']); - form.find('.input').val(''); - } - if(data['error']) { - feedback(data['error'], 'error'); - } - }, - error: function(jqXHR, textStatus, errorThrown) { - var err_str = ''; - if (jqXHR.responseText) { - err_str = $.parseJSON(jqXHR.responseText).error; - } else { - err_str = "{% trans "Failed. Please check the network." %}"; - } - apply_form_error(form_id, err_str); - } - }); - return false; -}); - -$('#click-into-group').click(function() { - {% if groups|length == 1 %} - // only one group, just turn to group page - {% for group in groups %} - location.href= "{% url 'group_info' group.id %}"; - {% endfor %} - - {% else %} - // more than one group, then let user choose - $('#to-group').toggleClass('hide'); - {% endif %} -}); -$(document).click(function(e) { - var target = e.target || event.srcElement; - if (!$('#click-into-group, #to-group').is(target) && !($('#click-into-group, #to-group').find('*').is(target))) { - $('#to-group').addClass('hide'); - } -}); -{% endif %} diff --git a/seahub/templates/snippets/current_commit.html b/seahub/templates/snippets/current_commit.html deleted file mode 100644 index 5cde50dd48..0000000000 --- a/seahub/templates/snippets/current_commit.html +++ /dev/null @@ -1,15 +0,0 @@ -{% load seahub_tags avatar_tags i18n %} - - {{ info_commit.props.desc|translate_commit_desc }} - {% trans "Details"%} - - - {% if info_commit.props.creator_name %} - {% avatar info_commit.props.creator_name 20 %} - {{ info_commit.creator_name|email2nickname }} - {% else %} - {% trans "Unknown"%} - {% endif %} - {{ info_commit.props.ctime|translate_seahub_time }} - - {% trans "History"%} diff --git a/seahub/templates/snippets/group_recommend_form.html b/seahub/templates/snippets/group_recommend_form.html deleted file mode 100644 index a1c0e02beb..0000000000 --- a/seahub/templates/snippets/group_recommend_form.html +++ /dev/null @@ -1,37 +0,0 @@ -{% load seahub_tags i18n %} -{% load url from future %} -
-
{% csrf_token %} -

{% trans "Post a discussion to group" %}

-
- {% for group in groups %} - - {% endfor %} -
- - - - -

- - - {% if attach_type == 'dir' %} - {% trans 'Directory icon' %} - {% endif %} - - {% for name,link in zipped %} - {% if forloop.last %} - {% if attach_type == 'file' %} - {% trans 'File' %} - {% endif %} - {{ name }} - {% endif %} - {% endfor %} - -
-
    - {% trans -
    diff --git a/seahub/templates/snippets/repo_dir_data.html b/seahub/templates/snippets/repo_dir_data.html deleted file mode 100644 index 04df791eb3..0000000000 --- a/seahub/templates/snippets/repo_dir_data.html +++ /dev/null @@ -1,73 +0,0 @@ -{% load seahub_tags i18n %} -
    -

    - {% for name, link in zipped %} - {% if forloop.first or not forloop.last %} - {{ name }} / - {% else %} - {{ name }} / - {% endif %} - {% endfor %} -

    -
    - {% if user_perm == 'rw' %} - - {% if repo.encrypted and repo.enc_version == 2 and not server_crypto %} - - {% else %} - {% if no_quota %} - - {% else %} -
    - {% trans "Upload"%} - -
    - {% if enable_upload_folder %} -
      -
    • - {% trans "Upload Files" %} - -
    • -
    • - {% trans "Upload Folder" %} - -
    • -
    - {% endif %} - {% endif %} - {% endif %} - - {% if repo.encrypted and repo.enc_version == 2 and not server_crypto %} - {% else %} - - {% endif %} - {% endif %} - - {% if path != '/' %} - {% if not repo.encrypted %} - - {% endif %} - {% endif %} -
    -
    - {% include 'snippets/current_commit.html' %} -
    -
    - - - - - - - - - - - - {% include 'snippets/repo_dirents.html' %} -
    - - {% trans "Name"%} {% trans "Size"%}{% trans "Last Update" %} {% trans "Operations"%}
    -{% if dirent_more %} - -{% endif %} diff --git a/seahub/templates/snippets/repo_dirents.html b/seahub/templates/snippets/repo_dirents.html deleted file mode 100644 index 96b488f91c..0000000000 --- a/seahub/templates/snippets/repo_dirents.html +++ /dev/null @@ -1,98 +0,0 @@ -{% load seahub_tags i18n %} - {% for dirent in dir_list %} - - - - - - {% if dirent.permission = 'r' %} - {% trans - {% else %} - {% trans - {% endif %} - - {{ dirent.obj_name }} - - - - - {{ dirent.last_modified|translate_seahub_time }} - - -
    -
    - - - - {% if not repo.encrypted %} - - - - {% endif %} -
    - {% if user_perm == 'rw' %} - {% trans 'More operations'%} - - {% endif %} -
    - - - {% endfor %} - {% for dirent in file_list %} - - - - - - {% if dirent.starred %} - - {% else %} - - {% endif %} - - - {% trans - - - {% if repo.encrypted and repo.enc_version == 2 and not server_crypto %} - {{ dirent.obj_name }} - {% else %} - {{ dirent.obj_name }} - {% endif %} - - {{ dirent.file_size|filesizeformat }} - - {{ dirent.last_modified|translate_seahub_time }} - - -
    -
    - - - - {% if not repo.encrypted %} - - - - {% endif %} -
    - {% if user_perm == 'rw' %} - {% trans - - {% endif %} -
    - - - {% endfor %} diff --git a/seahub/templates/snippets/repo_group_list.html b/seahub/templates/snippets/repo_group_list.html deleted file mode 100644 index e5e5073f2c..0000000000 --- a/seahub/templates/snippets/repo_group_list.html +++ /dev/null @@ -1,9 +0,0 @@ -{% load url from future %} -{% autoescape off %} - -{% endautoescape %} - diff --git a/seahub/templates/snippets/space_and_traffic.html b/seahub/templates/snippets/space_and_traffic.html index 0d0a6d949f..6285f4143d 100644 --- a/seahub/templates/snippets/space_and_traffic.html +++ b/seahub/templates/snippets/space_and_traffic.html @@ -1,22 +1,12 @@ -{% load i18n %} +{% load seahub_tags i18n %}
    -

    {% trans "Used:" %} {{ space_usage|filesizeformat }} {% if space_quota > 0 %}/ {{ space_quota|filesizeformat }} {% endif %}

    +

    {% trans "Used:" %} {{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %}/ {{ space_quota|seahub_filesizeformat }} {% endif %}

    - {% if not org %} - {% if CALC_SHARE_USAGE %} -

    {% trans "Sharing:" %} {{ share_usage|filesizeformat }} {% if share_quota > 0 %}/ {{ share_quota|filesizeformat }} {% endif %}

    - {% endif %} - {% endif %} - - {% if space_quota > 0 or share_quota > 0 %} + {% if space_quota > 0 %}
    - {% if show_quota_help %} - {% endif %} - - {% if show_quota_help %} + - {% endif %}
    {% endif %} diff --git a/seahub/templates/sysadmin/admin_repo_view.html b/seahub/templates/sysadmin/admin_repo_view.html index 77056a8045..4bb189a79a 100644 --- a/seahub/templates/sysadmin/admin_repo_view.html +++ b/seahub/templates/sysadmin/admin_repo_view.html @@ -25,8 +25,16 @@ {% endif %} {% endfor %}

    -
    + {% if is_default_repo %} +
    +
    + {% trans "Upload"%} + +
    +
    + {% endif %} +
    @@ -49,6 +57,15 @@ {{ dirent.last_modified|translate_seahub_time }} {% endfor %} @@ -67,6 +84,11 @@
    + {% if is_default_repo %} +
    +
    + + + +
    +
    + {% endif %}
    + {% if is_default_repo %} + + + + {% endif %} @@ -78,9 +100,41 @@
    + {% if is_default_repo %} +
    {% csrf_token %} +

    {% trans "New Directory" %}

    +
    +
    +

    + + +
    + +
    +

    {% trans "File Upload" %}

    +
    + + close +
    +
    +
    + +
    +

    + + {% trans "Saving..." %} +

    +
    +
    +
    + {% endif %} + {% endblock %} {% block extra_script %} +{% if is_default_repo %} +{% upload_js %} +{% endif %} {% endblock %} diff --git a/seahub/templates/sysadmin/org_admin_table.html b/seahub/templates/sysadmin/org_admin_table.html index 74aaa0ace9..33945fa148 100644 --- a/seahub/templates/sysadmin/org_admin_table.html +++ b/seahub/templates/sysadmin/org_admin_table.html @@ -17,7 +17,7 @@ {{ org.creator }} - {{ org.quota_usage|filesizeformat }} {% if org.total_quota > 0 %} / {{ org.total_quota|filesizeformat }} {% endif %} + {{ org.quota_usage|seahub_filesizeformat }} {% if org.total_quota > 0 %} / {{ org.total_quota|seahub_filesizeformat }} {% endif %} {{ org.ctime|tsstr_sec }}
    {% if org.expiration %}{{ org.expiration|date:'Y-m-d H:i:s' }}{% else %}--{% endif %} diff --git a/seahub/templates/sysadmin/repoadmin_table.html b/seahub/templates/sysadmin/repoadmin_table.html index 0eac76c4be..910bc549f1 100644 --- a/seahub/templates/sysadmin/repoadmin_table.html +++ b/seahub/templates/sysadmin/repoadmin_table.html @@ -15,9 +15,7 @@ {{ repo.props.name }} {% else %} {% trans - {% if repo.is_default_repo %} - {{ repo.props.name }} - {% elif enable_sys_admin_view_repo and is_pro_version %} + {% if enable_sys_admin_view_repo and is_pro_version %} {{ repo.props.name }} {% else %} {{ repo.props.name }} diff --git a/seahub/templates/sysadmin/settings.html b/seahub/templates/sysadmin/settings.html index fdc82ef9e2..3e5e4040e3 100644 --- a/seahub/templates/sysadmin/settings.html +++ b/seahub/templates/sysadmin/settings.html @@ -69,10 +69,6 @@ {% include "snippets/web_settings_form.html" %} {% endwith %} - {% with type="checkbox" setting_display_name="organization library" help_tip="Allow user to add organization library. If unchecked, only system admin can add library." setting_name="ENABLE_ORGANIZATION_LIBRARY" setting_val=config_dict.ENABLE_ORGANIZATION_LIBRARY %} - {% include "snippets/web_settings_form.html" %} - {% endwith %} - {% with type="input" setting_display_name="library password minimum length" help_tip="The least number of characters an encrypted library password should include." setting_name="REPO_PASSWORD_MIN_LENGTH" setting_val=config_dict.REPO_PASSWORD_MIN_LENGTH %} {% include "snippets/web_settings_form.html" %} {% endwith %} @@ -83,6 +79,12 @@ {% endwith %}
    +
    + {% with type="checkbox" setting_display_name="ENABLE_USER_CREATE_ORG_REPO" help_tip="Allow user to add organization library. If unchecked, only system admin can add library." setting_name="ENABLE_USER_CREATE_ORG_REPO" setting_val=config_dict.ENABLE_USER_CREATE_ORG_REPO %} + {% include "snippets/web_settings_form.html" %} + {% endwith %} +
    +

    Sync

    diff --git a/seahub/templates/sysadmin/sys_list_system.html b/seahub/templates/sysadmin/sys_list_system.html index 84358aab1b..b12a494e39 100644 --- a/seahub/templates/sysadmin/sys_list_system.html +++ b/seahub/templates/sysadmin/sys_list_system.html @@ -2,12 +2,6 @@ {% load i18n %} {% block cur_repo %}tab-cur{% endblock %} -{% block left_panel %}{{block.super}} -
    - -
    -{% endblock %} - {% block right_panel %}
      @@ -26,7 +20,7 @@ {% for repo in repos %} - {{ repo.props.name }} + {{ repo.props.name }} {{ repo.id }} {{ repo.props.desc }} @@ -36,5 +30,4 @@

      {% trans "None" %}

      {% endif %} -{% include "sysadmin/repo_transfer_form.html" %} {% endblock %} diff --git a/seahub/templates/sysadmin/sys_org_info_base.html b/seahub/templates/sysadmin/sys_org_info_base.html index 76d9e4bdf3..04d926a502 100644 --- a/seahub/templates/sysadmin/sys_org_info_base.html +++ b/seahub/templates/sysadmin/sys_org_info_base.html @@ -1,6 +1,5 @@ {% extends "admin_base.html" %} {% load i18n seahub_tags %} -{% load url from future %} {% block nav_orgadmin_class %}class="cur"{% endblock %} @@ -25,7 +24,7 @@

      {% trans "Space Used" %}

      -

      {{ quota_usage|filesizeformat }} {% if total_quota > 0 %}/ {{ total_quota|filesizeformat }} {% endif %}

      +

      {{ quota_usage|seahub_filesizeformat }} {% if total_quota > 0 %}/ {{ total_quota|seahub_filesizeformat }} {% endif %}

      {% trans "Set Quota" %}
    {% csrf_token %} diff --git a/seahub/templates/sysadmin/sys_org_info_user.html b/seahub/templates/sysadmin/sys_org_info_user.html index 296158864f..7745b088f8 100644 --- a/seahub/templates/sysadmin/sys_org_info_user.html +++ b/seahub/templates/sysadmin/sys_org_info_user.html @@ -1,6 +1,5 @@ {% extends "sysadmin/sys_org_info_base.html" %} {% load i18n seahub_tags %} -{% load url from future %} {% block right_panel %}
    @@ -39,11 +38,7 @@ - {% if CALC_SHARE_USAGE %} - {{ user.self_usage|filesizeformat }} + {{ user.share_usage|filesizeformat }} {% if user.quota > 0 %} / {{ user.quota|filesizeformat }} {% endif %} - {% else %} - {{ user.self_usage|filesizeformat }} {% if user.quota > 0 %} / {{ user.quota|filesizeformat }} {% endif %} - {% endif %} + {{ user.self_usage|seahub_filesizeformat }} {% if user.quota > 0 %} / {{ user.quota|seahub_filesizeformat }} {% endif %} {{ user.ctime|tsstr_sec }} / {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %} diff --git a/seahub/templates/sysadmin/sys_user_admin_ldap_imported.html b/seahub/templates/sysadmin/sys_user_admin_ldap_imported.html index 84055a286d..3b9801553e 100644 --- a/seahub/templates/sysadmin/sys_user_admin_ldap_imported.html +++ b/seahub/templates/sysadmin/sys_user_admin_ldap_imported.html @@ -49,12 +49,7 @@ -

    {{ user.space_usage|filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|filesizeformat }} {% endif %}

    - {% if not user.org %} - {% if CALC_SHARE_USAGE %} -

    {{ user.share_usage|filesizeformat }} {% if user.share_quota > 0 %} / {{ user.share_quota|filesizeformat }} {% endif %}

    - {% endif %} - {% endif %} +

    {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %}

    {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %} diff --git a/seahub/templates/sysadmin/sys_useradmin_ldap.html b/seahub/templates/sysadmin/sys_useradmin_ldap.html index 33ef91e224..2a49d5f0ca 100644 --- a/seahub/templates/sysadmin/sys_useradmin_ldap.html +++ b/seahub/templates/sysadmin/sys_useradmin_ldap.html @@ -32,12 +32,7 @@ -- / {% if user.last_login %}{{user.last_login|translate_seahub_time}} {% else %} -- {% endif %} -

    {{ user.space_usage|filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|filesizeformat }} {% endif %}

    - {% if not user.org %} - {% if CALC_SHARE_USAGE %} -

    {{ user.share_usage|filesizeformat }} {% if user.share_quota > 0 %} / {{ user.share_quota|filesizeformat }} {% endif %}

    - {% endif %} - {% endif %} +

    {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %}

    {% endfor %} diff --git a/seahub/templates/sysadmin/useradmin_table.html b/seahub/templates/sysadmin/useradmin_table.html index 8d7c376906..7a2a48f744 100644 --- a/seahub/templates/sysadmin/useradmin_table.html +++ b/seahub/templates/sysadmin/useradmin_table.html @@ -62,12 +62,7 @@ {% endif %} -

    {{ user.space_usage|filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|filesizeformat }} {% endif %}

    - {% if not user.org %} - {% if CALC_SHARE_USAGE %} -

    {{ user.share_usage|filesizeformat }} {% if user.share_quota > 0 %} / {{ user.share_quota|filesizeformat }} {% endif %}

    - {% endif %} - {% endif %} +

    {{ user.space_usage|seahub_filesizeformat }} {% if user.space_quota > 0 %} / {{ user.space_quota|seahub_filesizeformat }} {% endif %}

    {% if user.source == "DB" %} diff --git a/seahub/templates/sysadmin/userinfo.html b/seahub/templates/sysadmin/userinfo.html index 5ea6bbe43e..1e54461b95 100644 --- a/seahub/templates/sysadmin/userinfo.html +++ b/seahub/templates/sysadmin/userinfo.html @@ -40,11 +40,7 @@

    {% trans "Space Used" %}

    -

    {% trans "Used" %}: {{ space_usage|filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|filesizeformat }} {% endif %}

    - - {% if CALC_SHARE_USAGE %} -

    {% trans "Sharing" %}: {{ share_usage|filesizeformat }} {% if share_quota > 0 %} / {{ share_quota|filesizeformat }} {% endif %}

    - {% endif %} +

    {% trans "Used" %}: {{ space_usage|seahub_filesizeformat }} {% if space_quota > 0 %} / {{ space_quota|seahub_filesizeformat }} {% endif %}

    {% trans "Set Quota" %}
    @@ -52,9 +48,6 @@

    {% trans "Set user storage limit" %}

    MB - {% if CALC_SHARE_USAGE %} -
    MB
    - {% endif %}

    {% trans "Tip: 0 means default limit" %}

    @@ -276,15 +269,6 @@ $('#set-quota-form .submit').click(function() { data = { 'email': $('input[name="email"]', form).val(), 'space_quota': space_quota }; - {% if CALC_SHARE_USAGE %} - var share_quota = $('input[name="share_quota"]', form).val(); - if (!$.trim(share_quota)) { - apply_form_error(form_id, "{% trans "Share Quota can't be empty" %}"); - return false; - } - data['share_quota'] = share_quota; - {% endif %} - var sb_btn = $(this); disable(sb_btn); $.ajax({ diff --git a/seahub/urls.py b/seahub/urls.py index 656299925c..0b9cb0111d 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -8,7 +8,7 @@ from seahub.views.file import view_repo_file, view_history_file, view_trash_file view_snapshot_file, file_edit, view_shared_file, view_file_via_shared_dir,\ text_diff, view_priv_shared_file, view_raw_file, view_raw_shared_file, \ download_file, view_lib_file, file_access -from seahub.views.repo import repo, repo_history_view, view_shared_dir, \ +from seahub.views.repo import repo_history_view, view_shared_dir, \ view_shared_upload_link from notifications.views import notification_list from message.views import user_msg_list, user_msg_remove, user_received_msg_remove @@ -75,7 +75,6 @@ urlpatterns = patterns( url(r'^repo/file_revisions/(?P[-0-9a-f]{36})/$', file_revisions, name='file_revisions'), url(r'^repo/file-access/(?P[-0-9a-f]{36})/$', file_access, name='file_access'), url(r'^repo/text_diff/(?P[-0-9a-f]{36})/$', text_diff, name='text_diff'), - url(r'^repo/(?P[-0-9a-f]{36})/$', repo, name='repo'), url(r'^repo/history/(?P[-0-9a-f]{36})/$', repo_history, name='repo_history'), url(r'^repo/history/view/(?P[-0-9a-f]{36})/$', repo_history_view, name='repo_history_view'), url(r'^repo/recycle/(?P[-0-9a-f]{36})/$', repo_recycle_view, name='repo_recycle_view'), @@ -125,14 +124,11 @@ urlpatterns = patterns( url(r'^choose_register/$', TemplateView.as_view(template_name="choose_register.html"), name="choose_register"), ### Ajax ### - url(r'^ajax/repo/create/$', repo_create, name="repo_create"), (r'^ajax/repo/(?P[-0-9a-f]{36})/remove/$', repo_remove), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/$', get_dirents, name="get_dirents"), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/delete/$', delete_dirents, name='delete_dirents'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/move/$', mv_dirents, name='mv_dirents'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/copy/$', cp_dirents, name='cp_dirents'), - url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/$', list_dir, name='repo_dir_data'), - url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/more/$', list_dir_more, name='list_dir_more'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/new/$', new_dir, name='new_dir'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/rename/$', rename_dirent, name='rename_dir'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/delete/$', delete_dirent, name='delete_dir'), @@ -278,7 +274,7 @@ if EVENTS_ENABLED: urlpatterns += patterns( '', url(r'^sys/virus_scan_records/$', sys_virus_scan_records, name='sys_virus_scan_records'), - url(r'^sys/virus_scan_records/delete/(?P[\d+])/$', sys_delete_virus_scan_records, name='sys_delete_virus_scan_records'), + url(r'^sys/virus_scan_records/delete/(?P\d+)/$', sys_delete_virus_scan_records, name='sys_delete_virus_scan_records'), ) if settings.SERVE_STATIC: diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py index f110fbdf65..34b9f2b3f8 100644 --- a/seahub/utils/__init__.py +++ b/seahub/utils/__init__.py @@ -1318,4 +1318,4 @@ def is_org_repo_creation_allowed(request): if request.user.is_staff: return True else: - return config.ENABLE_ORGANIZATION_LIBRARY + return config.ENABLE_USER_CREATE_ORG_REPO diff --git a/seahub/utils/file_size.py b/seahub/utils/file_size.py new file mode 100644 index 0000000000..406c6f437d --- /dev/null +++ b/seahub/utils/file_size.py @@ -0,0 +1,24 @@ +def get_file_size_unit(unit_type): + """ + File size unit according to https://en.wikipedia.org/wiki/Kibibyte. + """ + table = { + # decimal + 'kb': 10 ** 3, + 'mb': 10 ** 6, + 'gb': 10 ** 9, + 'tb': 10 ** 12, + 'pb': 10 ** 15, + # binary + 'kib': 1 << 10, + 'mib': 1 << 20, + 'gib': 1 << 30, + 'tib': 1 << 40, + 'pib': 1 << 50, + } + + unit_type = unit_type.lower() + if unit_type not in table.keys(): + raise TypeError('Invalid unit type') + + return table.get(unit_type) diff --git a/seahub/utils/html.py b/seahub/utils/html.py new file mode 100644 index 0000000000..0ca7147a76 --- /dev/null +++ b/seahub/utils/html.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals + +def avoid_wrapping(value): + """ + Avoid text wrapping in the middle of a phrase by adding non-breaking + spaces where there previously were normal spaces. + """ + return value.replace(" ", "\xa0") diff --git a/seahub/utils/http.py b/seahub/utils/http.py index feb3f848ae..e22c65c379 100644 --- a/seahub/utils/http.py +++ b/seahub/utils/http.py @@ -1,5 +1,3 @@ -"""Copied from latest django/utils/http.py::is_safe_url -""" from __future__ import unicode_literals import unicodedata @@ -22,39 +20,6 @@ class BadRequestException(_HTTPException): class RequestForbbiddenException(_HTTPException): pass -def is_safe_url(url, host=None): - """ - Return ``True`` if the url is a safe redirection (i.e. it doesn't point to - a different host and uses a safe scheme). - - Always returns ``False`` on an empty url. - """ - if url is not None: - url = url.strip() - if not url: - return False - # Chrome treats \ completely as / - url = url.replace('\\', '/') - # Chrome considers any URL with more than two slashes to be absolute, but - # urlparse is not so flexible. Treat any url with three slashes as unsafe. - if url.startswith('///'): - return False - url_info = urlparse.urlparse(url) - - # Forbid URLs like http:///example.com - with a scheme, but without a hostname. - # In that URL, example.com is not the hostname but, a path component. However, - # Chrome will still consider example.com to be the hostname, so we must not - # allow this syntax. - if not url_info.netloc and url_info.scheme: - return False - # Forbid URLs that start with control characters. Some browsers (like - # Chrome) ignore quite a few control characters at the start of a - # URL and might consider the URL as scheme relative. - if unicodedata.category(url[0])[0] == 'C': - return False - return ((not url_info.netloc or url_info.netloc == host) and - (not url_info.scheme or url_info.scheme in ['http', 'https'])) - JSON_CONTENT_TYPE = 'application/json; charset=utf-8' def json_response(func): @wraps(func) @@ -80,3 +45,39 @@ def int_param(request, key): return int(v) except ValueError: raise BadRequestException() + +def is_safe_url(url, host=None): + """ + https://github.com/django/django/blob/fc6d147a63f89795dbcdecb0559256470fff4380/django/utils/http.py + + Return ``True`` if the url is a safe redirection (i.e. it doesn't point to + a different host and uses a safe scheme). + Always returns ``False`` on an empty url. + """ + if url is not None: + url = url.strip() + if not url: + return False + # Chrome treats \ completely as / in paths but it could be part of some + # basic auth credentials so we need to check both URLs. + return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host) + +def _is_safe_url(url, host): + # Chrome considers any URL with more than two slashes to be absolute, but + # urlparse is not so flexible. Treat any url with three slashes as unsafe. + if url.startswith('///'): + return False + url_info = urlparse.urlparse(url) + # Forbid URLs like http:///example.com - with a scheme, but without a hostname. + # In that URL, example.com is not the hostname but, a path component. However, + # Chrome will still consider example.com to be the hostname, so we must not + # allow this syntax. + if not url_info.netloc and url_info.scheme: + return False + # Forbid URLs that start with control characters. Some browsers (like + # Chrome) ignore quite a few control characters at the start of a + # URL and might consider the URL as scheme relative. + if unicodedata.category(url[0])[0] == 'C': + return False + return ((not url_info.netloc or url_info.netloc == host) and + (not url_info.scheme or url_info.scheme in ['http', 'https'])) diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 4666f75bf5..cdfd2d9505 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -181,93 +181,6 @@ def get_file_download_link(repo_id, obj_id, path): return reverse('download_file', args=[repo_id, obj_id]) + '?p=' + \ urlquote(path) -def get_repo_dirents_with_perm(request, repo, commit, path, offset=-1, limit=-1): - """List repo dirents with perm based on commit id and path. - Use ``offset`` and ``limit`` to do paginating. - - Returns: A tupple of (file_list, dir_list, dirent_more) - - TODO: Some unrelated parts(file sharing, stars, modified info, etc) need - to be pulled out to multiple functions. - """ - - if get_system_default_repo_id() == repo.id: - return get_repo_dirents(request, repo, commit, path, offset, limit) - - dir_list = [] - file_list = [] - dirent_more = False - username = request.user.username - if commit.root_id == EMPTY_SHA1: - return ([], [], False) if limit == -1 else ([], [], False) - else: - try: - dir_id = seafile_api.get_dir_id_by_path(repo.id, path) - if not dir_id: - return ([], [], False) - dirs = seafserv_threaded_rpc.list_dir_with_perm(repo.id, path, - dir_id, username, - offset, limit) - except SearpcError as e: - logger.error(e) - return ([], [], False) - - if limit != -1 and limit == len(dirs): - dirent_more = True - - starred_files = get_dir_starred_files(username, repo.id, path) - fileshares = FileShare.objects.filter(repo_id=repo.id).filter(username=username) - uploadlinks = UploadLinkShare.objects.filter(repo_id=repo.id).filter(username=username) - - view_dir_base = reverse('repo', args=[repo.id]) - dl_dir_base = reverse('repo_download_dir', args=[repo.id]) - view_file_base = reverse('repo_view_file', args=[repo.id]) - file_history_base = reverse('file_revisions', args=[repo.id]) - for dirent in dirs: - dirent.last_modified = dirent.mtime - dirent.sharelink = '' - dirent.uploadlink = '' - if stat.S_ISDIR(dirent.props.mode): - dpath = posixpath.join(path, dirent.obj_name) - if dpath[-1] != '/': - dpath += '/' - for share in fileshares: - if dpath == share.path: - dirent.sharelink = gen_dir_share_link(share.token) - dirent.sharetoken = share.token - break - for link in uploadlinks: - if dpath == link.path: - dirent.uploadlink = gen_shared_upload_link(link.token) - dirent.uploadtoken = link.token - break - p_dpath = posixpath.join(path, dirent.obj_name) - dirent.view_link = view_dir_base + '?p=' + urlquote(p_dpath) - dirent.dl_link = dl_dir_base + '?p=' + urlquote(p_dpath) - dir_list.append(dirent) - else: - file_list.append(dirent) - if repo.version == 0: - dirent.file_size = get_file_size(repo.store_id, repo.version, dirent.obj_id) - else: - dirent.file_size = dirent.size - dirent.starred = False - fpath = posixpath.join(path, dirent.obj_name) - p_fpath = posixpath.join(path, dirent.obj_name) - dirent.view_link = view_file_base + '?p=' + urlquote(p_fpath) - dirent.dl_link = get_file_download_link(repo.id, dirent.obj_id, - p_fpath) - dirent.history_link = file_history_base + '?p=' + urlquote(p_fpath) - if fpath in starred_files: - dirent.starred = True - for share in fileshares: - if fpath == share.path: - dirent.sharelink = gen_file_share_link(share.token) - dirent.sharetoken = share.token - break - - return (file_list, dir_list, dirent_more) - def get_repo_dirents(request, repo, commit, path, offset=-1, limit=-1): """List repo dirents based on commit id and path. Use ``offset`` and ``limit`` to do paginating. @@ -302,7 +215,8 @@ def get_repo_dirents(request, repo, commit, path, offset=-1, limit=-1): fileshares = FileShare.objects.filter(repo_id=repo.id).filter(username=username) uploadlinks = UploadLinkShare.objects.filter(repo_id=repo.id).filter(username=username) - view_dir_base = reverse('repo', args=[repo.id]) + + view_dir_base = reverse("view_common_lib_dir", args=[repo.id, '/']) dl_dir_base = reverse('repo_download_dir', args=[repo.id]) file_history_base = reverse('file_revisions', args=[repo.id]) for dirent in dirs: @@ -739,7 +653,7 @@ def repo_history(request, repo_id): return render_error(request, e.msg) if not password_set: - return HttpResponseRedirect(reverse('repo', args=[repo_id])) + return HttpResponseRedirect(reverse("view_common_lib_dir", args=[repo_id, '/'])) try: current_page = int(request.GET.get('page', '1')) @@ -809,7 +723,7 @@ def repo_revert_history(request, repo_id): return render_error(request, e.msg) if not password_set: - return HttpResponseRedirect(reverse('repo', args=[repo_id])) + return HttpResponseRedirect(reverse("view_common_lib_dir", args=[repo_id, '/'])) commit_id = request.GET.get('commit_id', '') if not commit_id: @@ -1104,7 +1018,7 @@ def repo_set_access_property(request, repo_id): ap = request.GET.get('ap', '') seafserv_threaded_rpc.repo_set_access_property(repo_id, ap) - return HttpResponseRedirect(reverse('repo', args=[repo_id])) + return HttpResponseRedirect(reverse("view_common_lib_dir", args=[repo_id, '/'])) @login_required def file_upload_progress_page(request): diff --git a/seahub/views/ajax.py b/seahub/views/ajax.py index 9e2088e4b6..86c5b39010 100644 --- a/seahub/views/ajax.py +++ b/seahub/views/ajax.py @@ -19,10 +19,9 @@ from django.contrib import messages from django.template.defaultfilters import filesizeformat import seaserv -from seaserv import seafile_api, seafserv_rpc, is_passwd_set, \ +from seaserv import seafile_api, is_passwd_set, \ get_related_users_by_repo, get_related_users_by_org_repo, \ - CALC_SHARE_USAGE, seafserv_threaded_rpc, ccnet_threaded_rpc, \ - get_user_quota_usage, get_user_share_usage, edit_repo + seafserv_threaded_rpc, ccnet_threaded_rpc, edit_repo from pysearpc import SearpcError from seahub.auth.decorators import login_required_ajax @@ -37,12 +36,10 @@ from seahub.message.models import UserMessage from seahub.share.models import UploadLinkShare from seahub.group.models import PublicGroup from seahub.signals import upload_file_successful, repo_created, repo_deleted -from seahub.views import get_repo_dirents_with_perm, validate_owner, \ +from seahub.views import validate_owner, \ get_unencry_rw_repos_by_user, is_registered_user, \ get_system_default_repo_id, get_diff, group_events_data, \ - get_owned_repo_list, check_folder_permission -from seahub.views.repo import get_nav_path, get_fileshare, get_dir_share_link, \ - get_uploadlink, get_dir_shared_upload_link + get_owned_repo_list, check_folder_permission, is_registered_user from seahub.views.modules import get_enabled_mods_by_group, \ get_available_mods_by_group, enable_mod_for_group, \ disable_mod_for_group, MOD_GROUP_WIKI, MOD_PERSONAL_WIKI, \ @@ -52,7 +49,7 @@ from seahub.group.utils import is_group_member, is_group_admin_or_owner, \ get_group_member_info import seahub.settings as settings from seahub.settings import ENABLE_THUMBNAIL, THUMBNAIL_ROOT, \ - THUMBNAIL_DEFAULT_SIZE, ENABLE_SUB_LIBRARY, ENABLE_REPO_HISTORY_SETTING, \ + THUMBNAIL_DEFAULT_SIZE, ENABLE_SUB_LIBRARY, \ ENABLE_FOLDER_PERM, SHOW_TRAFFIC, MEDIA_URL from constance import config from seahub.utils import check_filename_with_rename, EMPTY_SHA1, \ @@ -62,7 +59,7 @@ from seahub.utils import check_filename_with_rename, EMPTY_SHA1, \ get_org_user_events, get_user_events, get_file_type_and_ext, \ is_valid_username, send_perm_audit_msg, get_origin_repo_info, is_pro_version from seahub.utils.repo import get_sub_repo_abbrev_origin_path -from seahub.utils.star import star_file, unstar_file +from seahub.utils.star import star_file, unstar_file, get_dir_starred_files from seahub.base.accounts import User from seahub.thumbnail.utils import get_thumbnail_src from seahub.utils.file_types import IMAGE @@ -241,165 +238,6 @@ def unenc_rw_repos(request): repo_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower())) return HttpResponse(json.dumps(repo_list), content_type=content_type) -@login_required_ajax -def list_dir(request, repo_id): - """ - List directory entries in AJAX. - """ - content_type = 'application/json; charset=utf-8' - - repo = get_repo(repo_id) - if not repo: - err_msg = _(u'Library does not exist.') - return HttpResponse(json.dumps({'error': err_msg}), - status=400, content_type=content_type) - - username = request.user.username - user_perm = check_folder_permission(request, repo_id, '/') - if user_perm is None: - err_msg = _(u'Permission denied.') - return HttpResponse(json.dumps({'error': err_msg}), - status=403, content_type=content_type) - - sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username) - - try: - server_crypto = UserOptions.objects.is_server_crypto(username) - except CryptoOptionNotSetError: - # Assume server_crypto is ``False`` if this option is not set. - server_crypto = False - - if repo.encrypted and \ - (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \ - and not seafile_api.is_password_set(repo.id, username): - err_msg = _(u'Library is encrypted.') - return HttpResponse(json.dumps({'error': err_msg}), - status=403, content_type=content_type) - - head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id) - if not head_commit: - err_msg = _(u'Error: no head commit id') - return HttpResponse(json.dumps({'error': err_msg}), - status=500, content_type=content_type) - - if new_merge_with_no_conflict(head_commit): - info_commit = get_commit_before_new_merge(head_commit) - else: - info_commit = head_commit - - path = request.GET.get('p', '/') - if path[-1] != '/': - path = path + '/' - - more_start = None - file_list, dir_list, dirent_more = get_repo_dirents_with_perm(request, repo, - head_commit, path, - offset=0, limit=100) - if dirent_more: - more_start = 100 - zipped = get_nav_path(path, repo.name) - fileshare = get_fileshare(repo.id, username, path) - dir_shared_link = get_dir_share_link(fileshare) - uploadlink = get_uploadlink(repo.id, username, path) - dir_shared_upload_link = get_dir_shared_upload_link(uploadlink) - - ctx = { - 'repo': repo, - 'zipped': zipped, - 'user_perm': user_perm, - 'path': path, - 'server_crypto': server_crypto, - 'fileshare': fileshare, - 'dir_shared_link': dir_shared_link, - 'uploadlink': uploadlink, - 'dir_shared_upload_link': dir_shared_upload_link, - 'dir_list': dir_list, - 'file_list': file_list, - 'dirent_more': dirent_more, - 'more_start': more_start, - 'ENABLE_SUB_LIBRARY': ENABLE_SUB_LIBRARY, - 'sub_lib_enabled': sub_lib_enabled, - 'enable_upload_folder': settings.ENABLE_UPLOAD_FOLDER, - 'current_commit': head_commit, - 'info_commit': info_commit, - } - html = render_to_string('snippets/repo_dir_data.html', ctx, - context_instance=RequestContext(request)) - return HttpResponse(json.dumps({'html': html, 'path': path}), - content_type=content_type) - -@login_required_ajax -def list_dir_more(request, repo_id): - """ - List 'more' entries in a directory with AJAX. - """ - content_type = 'application/json; charset=utf-8' - - repo = get_repo(repo_id) - if not repo: - err_msg = _(u'Library does not exist.') - return HttpResponse(json.dumps({'error': err_msg}), - status=400, content_type=content_type) - - username = request.user.username - user_perm = check_folder_permission(request, repo_id, '/') - if user_perm is None: - err_msg = _(u'Permission denied.') - return HttpResponse(json.dumps({'error': err_msg}), - status=403, content_type=content_type) - - sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username) - - try: - server_crypto = UserOptions.objects.is_server_crypto(username) - except CryptoOptionNotSetError: - # Assume server_crypto is ``False`` if this option is not set. - server_crypto = False - - if repo.encrypted and \ - (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \ - and not seafile_api.is_password_set(repo.id, username): - err_msg = _(u'Library is encrypted.') - return HttpResponse(json.dumps({'error': err_msg}), - status=403, content_type=content_type) - - head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id) - if not head_commit: - err_msg = _(u'Error: no head commit id') - return HttpResponse(json.dumps({'error': err_msg}), - status=500, content_type=content_type) - - path = request.GET.get('p', '/') - if path[-1] != '/': - path = path + '/' - - offset = int(request.GET.get('start')) - if not offset: - err_msg = _(u'Argument missing') - return HttpResponse(json.dumps({'error': err_msg}), - status=400, content_type=content_type) - more_start = None - file_list, dir_list, dirent_more = get_repo_dirents_with_perm(request, repo, - head_commit, path, - offset, limit=100) - if dirent_more: - more_start = offset + 100 - - ctx = { - 'repo': repo, - 'user_perm': user_perm, - 'path': path, - 'server_crypto': server_crypto, - 'dir_list': dir_list, - 'file_list': file_list, - 'ENABLE_SUB_LIBRARY': ENABLE_SUB_LIBRARY, - 'sub_lib_enabled': sub_lib_enabled, - } - html = render_to_string('snippets/repo_dirents.html', ctx, - context_instance=RequestContext(request)) - return HttpResponse(json.dumps({'html': html, 'dirent_more': dirent_more, 'more_start': more_start}), - content_type=content_type) - @login_required_ajax def list_lib_dir(request, repo_id): ''' @@ -439,9 +277,51 @@ def list_lib_dir(request, repo_id): status=500, content_type=content_type) offset = int(request.GET.get('start', 0)) - file_list, dir_list, dirent_more = get_repo_dirents_with_perm(request, repo, head_commit, path, offset, limit=100) + limit = 100 + dir_list = [] + file_list = [] + dirent_more = False + + try: + dir_id = seafile_api.get_dir_id_by_path(repo.id, path) + except SearpcError as e: + logger.error(e) + err_msg = 'Internal Server Error' + return HttpResponse(json.dumps({'error': err_msg}), + status=500, content_type=content_type) + + if not dir_id: + err_msg = 'Folder not found.' + return HttpResponse(json.dumps({'error': err_msg}), + status=404, content_type=content_type) + + dirs = seafserv_threaded_rpc.list_dir_with_perm(repo_id, path, dir_id, username, offset, limit) + starred_files = get_dir_starred_files(username, repo_id, path) + + for dirent in dirs: + dirent.last_modified = dirent.mtime + if stat.S_ISDIR(dirent.mode): + dpath = posixpath.join(path, dirent.obj_name) + if dpath[-1] != '/': + dpath += '/' + dir_list.append(dirent) + else: + if repo.version == 0: + file_size = seafile_api.get_file_size(repo.store_id, repo.version, dirent.obj_id) + else: + file_size = dirent.size + dirent.file_size = file_size if file_size else 0 + + dirent.starred = False + fpath = posixpath.join(path, dirent.obj_name) + if fpath in starred_files: + dirent.starred = True + + file_list.append(dirent) + more_start = None - if dirent_more: + if limit == len(dirs): + dirent_more = True more_start = offset + 100 if is_org_context(request): @@ -465,8 +345,7 @@ def list_lib_dir(request, repo_id): d_['obj_name'] = d.obj_name d_['last_modified'] = d.last_modified d_['last_update'] = translate_seahub_time(d.last_modified) - p_dpath = posixpath.join(path, d.obj_name) - d_['p_dpath'] = p_dpath # for 'view_link' & 'dl_link' + d_['p_dpath'] = posixpath.join(path, d.obj_name) d_['perm'] = d.permission # perm for sub dir in current dir dirent_list.append(d_) @@ -1076,7 +955,7 @@ def mv_dirents(request, src_repo_id, src_path, dst_repo_id, dst_path, success.append(obj_name) if len(success) > 0: - url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) + url = reverse("view_common_lib_dir", args=[dst_repo_id, urlquote(dst_path).strip('/')]) result = {'success': success, 'failed': failed, 'url': url} return HttpResponse(json.dumps(result), content_type=content_type) @@ -1119,7 +998,7 @@ def cp_dirents(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_file_n success.append(obj_name) if len(success) > 0: - url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) + url = reverse("view_common_lib_dir", args=[dst_repo_id, urlquote(dst_path).strip('/')]) result = {'success': success, 'failed': failed, 'url': url} return HttpResponse(json.dumps(result), content_type=content_type) @@ -1613,35 +1492,19 @@ def space_and_traffic(request): if not org: space_quota = seafile_api.get_user_quota(username) space_usage = seafile_api.get_user_self_usage(username) - if CALC_SHARE_USAGE: - share_quota = seafile_api.get_user_share_quota(username) - share_usage = seafile_api.get_user_share_usage(username) - else: - share_quota = 0 - share_usage = 0 else: org_id = org[0].org_id space_quota = seafserv_threaded_rpc.get_org_user_quota(org_id, username) space_usage = seafserv_threaded_rpc.get_org_user_quota_usage( org_id, username) - share_quota = 0 # no share quota/usage for org account - share_usage = 0 rates = {} - rates['space_quota'] = space_quota - rates['share_quota'] = share_quota - total_quota = space_quota + share_quota if space_quota > 0: - rates['space_usage'] = str(float(space_usage) / total_quota * 100) + '%' + rates['space_usage'] = str(float(space_usage) / space_quota * 100) + '%' else: # no space quota set in config rates['space_usage'] = '0%' - if share_quota > 0: - rates['share_usage'] = str(float(share_usage) / total_quota * 100) + '%' - else: # no share quota set in config - rates['share_usage'] = '0%' - # traffic calculation traffic_stat = 0 if TRAFFIC_STATS_ENABLED: @@ -1674,10 +1537,6 @@ def space_and_traffic(request): "org": org, "space_quota": space_quota, "space_usage": space_usage, - "share_quota": share_quota, - "share_usage": share_usage, - "CALC_SHARE_USAGE": CALC_SHARE_USAGE, - "show_quota_help": not CALC_SHARE_USAGE, "rates": rates, "SHOW_TRAFFIC": SHOW_TRAFFIC, "TRAFFIC_STATS_ENABLED": TRAFFIC_STATS_ENABLED, @@ -1958,69 +1817,6 @@ def _create_repo_common(request, repo_name, repo_desc, encryption, return repo_id -@login_required_ajax -def repo_create(request): - ''' - Handle ajax post to create a library. - - ''' - if request.method != 'POST': - return Http404 - - result = {} - content_type = 'application/json; charset=utf-8' - - if not request.user.permissions.can_add_repo(): - result['error'] = _(u"You do not have permission to create library") - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - form = RepoCreateForm(request.POST) - if not form.is_valid(): - result['error'] = str(form.errors.values()[0]) - return HttpResponseBadRequest(json.dumps(result), - content_type=content_type) - - repo_name = form.cleaned_data['repo_name'] - repo_desc = form.cleaned_data['repo_desc'] - encryption = int(form.cleaned_data['encryption']) - - uuid = form.cleaned_data['uuid'] - magic_str = form.cleaned_data['magic_str'] - encrypted_file_key = form.cleaned_data['encrypted_file_key'] - - repo_id = _create_repo_common(request, repo_name, repo_desc, encryption, - uuid, magic_str, encrypted_file_key) - if repo_id is None: - result['error'] = _(u"Internal Server Error") - return HttpResponse(json.dumps(result), status=500, - content_type=content_type) - - username = request.user.username - try: - default_lib = (int(request.GET.get('default_lib', 0)) == 1) - except ValueError: - default_lib = False - if default_lib: - UserOptions.objects.set_default_repo(username, repo_id) - - if is_org_context(request): - org_id = request.user.org.org_id - else: - org_id = -1 - repo_created.send(sender=None, - org_id=org_id, - creator=username, - repo_id=repo_id, - repo_name=repo_name) - result = { - 'repo_id': repo_id, - 'repo_name': repo_name, - 'repo_desc': repo_desc, - 'repo_enc': encryption, - } - return HttpResponse(json.dumps(result), content_type=content_type) - @login_required_ajax def public_repo_create(request): ''' diff --git a/seahub/views/repo.py b/seahub/views/repo.py index db0d7aef6e..fc7d62da59 100644 --- a/seahub/views/repo.py +++ b/seahub/views/repo.py @@ -4,7 +4,6 @@ import posixpath import logging from django.core.urlresolvers import reverse -from django.contrib.sites.models import RequestSite from django.db.models import F from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response @@ -16,25 +15,21 @@ import seaserv from seaserv import seafile_api from seahub.auth.decorators import login_required -from seahub.avatar.templatetags.group_avatar_tags import grp_avatar -from seahub.forms import RepoPassowrdForm from seahub.options.models import UserOptions, CryptoOptionNotSetError from seahub.share.models import FileShare, UploadLinkShare, \ check_share_link_common from seahub.views import gen_path_link, get_repo_dirents, \ - check_folder_permission, get_repo_dirents_with_perm + check_folder_permission -from seahub.utils import gen_file_upload_url, is_org_context, \ - get_fileserver_root, gen_dir_share_link, gen_shared_upload_link, \ - get_max_upload_file_size, new_merge_with_no_conflict, \ - get_commit_before_new_merge, user_traffic_over_limit, render_error, \ +from seahub.utils import gen_file_upload_url, gen_dir_share_link, \ + gen_shared_upload_link, user_traffic_over_limit, render_error, \ get_file_type_and_ext -from seahub.settings import ENABLE_SUB_LIBRARY, FORCE_SERVER_CRYPTO, \ +from seahub.settings import FORCE_SERVER_CRYPTO, \ ENABLE_UPLOAD_FOLDER, ENABLE_RESUMABLE_FILEUPLOAD, ENABLE_THUMBNAIL, \ THUMBNAIL_ROOT, THUMBNAIL_DEFAULT_SIZE, THUMBNAIL_SIZE_FOR_GRID from seahub.utils import gen_file_get_url from seahub.utils.file_types import IMAGE -from seahub.thumbnail.utils import get_thumbnail_src, get_share_link_thumbnail_src +from seahub.thumbnail.utils import get_share_link_thumbnail_src # Get an instance of a logger logger = logging.getLogger(__name__) @@ -124,148 +119,6 @@ def get_dir_shared_upload_link(uploadlink): dir_shared_upload_link = '' return dir_shared_upload_link -def render_repo(request, repo): - """Steps to show repo page: - If user has permission to view repo - If repo is encrypt and password is not set on server - return decrypt repo page - If repo is not encrypt or password is set on server - Show repo direntries based on requested path - If user does not have permission to view repo - return permission deny page - """ - username = request.user.username - path = get_path_from_request(request) - user_perm = check_folder_permission(request, repo.id, '/') - if user_perm is None: - return render_error(request, _(u'Permission denied')) - - sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username) - - server_crypto = False - if repo.encrypted: - try: - server_crypto = UserOptions.objects.is_server_crypto(username) - except CryptoOptionNotSetError: - return render_to_response('options/set_user_options.html', { - }, context_instance=RequestContext(request)) - - if (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \ - and not is_password_set(repo.id, username): - return render_to_response('decrypt_repo_form.html', { - 'repo': repo, - 'next': get_next_url_from_request(request) or reverse('repo', args=[repo.id]), - 'force_server_crypto': FORCE_SERVER_CRYPTO, - }, context_instance=RequestContext(request)) - - # query context args - fileserver_root = get_fileserver_root() - max_upload_file_size = get_max_upload_file_size() - - protocol = request.is_secure() and 'https' or 'http' - domain = RequestSite(request).domain - - for g in request.user.joined_groups: - g.avatar = grp_avatar(g.id, 20) - - head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id) - if not head_commit: - raise Http404 - - if new_merge_with_no_conflict(head_commit): - info_commit = get_commit_before_new_merge(head_commit) - else: - info_commit = head_commit - - repo_size = get_repo_size(repo.id) - no_quota = is_no_quota(repo.id) - if is_org_context(request): - repo_owner = seafile_api.get_org_repo_owner(repo.id) - else: - repo_owner = seafile_api.get_repo_owner(repo.id) - is_repo_owner = True if repo_owner == username else False - if is_repo_owner and not repo.is_virtual: - show_repo_settings = True - else: - show_repo_settings = False - - file_list, dir_list, dirent_more = get_repo_dirents_with_perm( - request, repo, head_commit, path, offset=0, limit=100) - more_start = None - if dirent_more: - more_start = 100 - zipped = get_nav_path(path, repo.name) - fileshare = get_fileshare(repo.id, username, path) - dir_shared_link = get_dir_share_link(fileshare) - uploadlink = get_uploadlink(repo.id, username, path) - dir_shared_upload_link = get_dir_shared_upload_link(uploadlink) - - for f in file_list: - file_type, file_ext = get_file_type_and_ext(f.obj_name) - if file_type == IMAGE: - f.is_img = True - file_path = posixpath.join(path, f.obj_name) - if os.path.exists(os.path.join(THUMBNAIL_ROOT, str(THUMBNAIL_DEFAULT_SIZE), f.obj_id)): - src = get_thumbnail_src(repo.id, THUMBNAIL_DEFAULT_SIZE, file_path) - f.encoded_thumbnail_src = urlquote(src) - - return render_to_response('repo.html', { - 'repo': repo, - 'user_perm': user_perm, - 'repo_owner': repo_owner, - 'is_repo_owner': is_repo_owner, - 'show_repo_settings': show_repo_settings, - 'current_commit': head_commit, - 'info_commit': info_commit, - 'password_set': True, - 'repo_size': repo_size, - 'dir_list': dir_list, - 'file_list': file_list, - 'dirent_more': dirent_more, - 'more_start': more_start, - 'path': path, - 'zipped': zipped, - 'no_quota': no_quota, - 'max_upload_file_size': max_upload_file_size, - 'fileserver_root': fileserver_root, - 'protocol': protocol, - 'domain': domain, - 'fileshare': fileshare, - 'dir_shared_link': dir_shared_link, - 'uploadlink': uploadlink, - 'dir_shared_upload_link': dir_shared_upload_link, - 'ENABLE_SUB_LIBRARY': ENABLE_SUB_LIBRARY, - 'server_crypto': server_crypto, - 'sub_lib_enabled': sub_lib_enabled, - 'enable_upload_folder': ENABLE_UPLOAD_FOLDER, - 'ENABLE_THUMBNAIL': ENABLE_THUMBNAIL, - }, context_instance=RequestContext(request)) - -@login_required -def repo(request, repo_id): - """Show repo page and handle POST request to decrypt repo. - """ - repo = get_repo(repo_id) - - if not repo: - raise Http404 - - if request.method == 'GET': - return render_repo(request, repo) - elif request.method == 'POST': - form = RepoPassowrdForm(request.POST) - next = get_next_url_from_request(request) or reverse('repo', - args=[repo_id]) - if form.is_valid(): - return HttpResponseRedirect(next) - else: - return render_to_response('decrypt_repo_form.html', { - 'repo': repo, - 'form': form, - 'next': next, - 'force_server_crypto': FORCE_SERVER_CRYPTO, - }, context_instance=RequestContext(request)) - @login_required def repo_history_view(request, repo_id): """View repo in history. @@ -291,13 +144,13 @@ def repo_history_view(request, repo_id): and not is_password_set(repo.id, username): return render_to_response('decrypt_repo_form.html', { 'repo': repo, - 'next': get_next_url_from_request(request) or reverse('repo', args=[repo.id]), + 'next': get_next_url_from_request(request) or reverse("view_common_lib_dir", args=[repo_id, '/']), 'force_server_crypto': FORCE_SERVER_CRYPTO, }, context_instance=RequestContext(request)) commit_id = request.GET.get('commit_id', None) if commit_id is None: - return HttpResponseRedirect(reverse('repo', args=[repo.id])) + return HttpResponseRedirect(reverse("view_common_lib_dir", args=[repo_id, '/'])) current_commit = get_commit(repo.id, repo.version, commit_id) if not current_commit: current_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id) diff --git a/seahub/views/sysadmin.py b/seahub/views/sysadmin.py index b6ce55c866..109db0bc18 100644 --- a/seahub/views/sysadmin.py +++ b/seahub/views/sysadmin.py @@ -22,7 +22,7 @@ from django.utils.translation import ugettext as _ import seaserv from seaserv import ccnet_threaded_rpc, seafserv_threaded_rpc, \ - CALC_SHARE_USAGE, seafile_api, get_group, get_group_members + seafile_api, get_group, get_group_members from pysearpc import SearpcError from seahub.base.accounts import User @@ -39,6 +39,7 @@ from seahub.utils import IS_EMAIL_CONFIGURED, string2list, is_valid_username, \ is_pro_version, send_html_email, get_user_traffic_list, get_server_id, \ clear_token, gen_file_get_url, is_org_context, handle_virus_record, \ get_virus_record_by_id, get_virus_record +from seahub.utils.file_size import get_file_size_unit from seahub.utils.rpc import mute_seafile_api from seahub.utils.licenseparse import parse_license from seahub.utils.sysinfo import get_platform_name @@ -166,9 +167,11 @@ def sys_repo_admin(request): page_next = False repos = filter(lambda r: not r.is_virtual, repos) + default_repo_id = get_system_default_repo_id() + repos = filter(lambda r: not r.repo_id == default_repo_id, repos) + for repo in repos: - repo.is_default_repo = True if repo.id == default_repo_id else False try: repo.owner = seafile_api.get_repo_owner(repo.id) except: @@ -187,22 +190,40 @@ def sys_repo_admin(request): }, context_instance=RequestContext(request)) +def can_view_sys_admin_repo(repo): + default_repo_id = get_system_default_repo_id() + is_default_repo = True if repo.id == default_repo_id else False + + if is_default_repo: + return True + elif repo.encrypted: + return False + elif is_pro_version() and ENABLE_SYS_ADMIN_VIEW_REPO: + return True + else: + return False + @login_required @sys_staff_required def sys_admin_repo_download_file(request, repo_id): - """ - """ - repo = seafile_api.get_repo(repo_id) - path = request.GET.get('p', '') - obj_id = seafile_api.get_file_id_by_path(repo_id, path) next = request.META.get('HTTP_REFERER', None) if not next: next = reverse('sys_admin_repo') - if not repo or repo.encrypted or not is_pro_version() \ - or not ENABLE_SYS_ADMIN_VIEW_REPO or not obj_id: - messages.error(request, _(u'Unable to download file')) + repo = seafile_api.get_repo(repo_id) + if not repo: + messages.error(request, _(u'Library does not exist')) + return HttpResponseRedirect(next) + + path = request.GET.get('p', '') + obj_id = seafile_api.get_file_id_by_path(repo_id, path) + if not obj_id: + messages.error(request, _(u'Unable to download file, invalid file path')) + return HttpResponseRedirect(next) + + if not can_view_sys_admin_repo(repo): + messages.error(request, _(u'Unable to view library')) return HttpResponseRedirect(next) try: @@ -220,21 +241,18 @@ def sys_admin_repo_download_file(request, repo_id): @login_required @sys_staff_required def sys_admin_repo(request, repo_id): + next = request.META.get('HTTP_REFERER', None) if not next: next = reverse('sys_repo_admin') - if not is_pro_version() or not ENABLE_SYS_ADMIN_VIEW_REPO: - messages.error(request, _(u'Unable to view library, this feature is not enabled.')) - return HttpResponseRedirect(next) - repo = seafile_api.get_repo(repo_id) if not repo: messages.error(request, _(u'Library does not exist')) return HttpResponseRedirect(next) - if repo.encrypted: - messages.error(request, _(u'Library is encrypted')) + if not can_view_sys_admin_repo(repo): + messages.error(request, _(u'Unable to view library')) return HttpResponseRedirect(next) path = request.GET.get('p', '/') @@ -275,6 +293,8 @@ def sys_admin_repo(request, repo_id): file_list.append(dirent) zipped = gen_path_link(path, repo.name) + default_repo_id = get_system_default_repo_id() + is_default_repo = True if repo_id == default_repo_id else False return render_to_response('sysadmin/admin_repo_view.html', { 'repo': repo, @@ -283,6 +303,8 @@ def sys_admin_repo(request, repo_id): 'file_list': file_list, 'path': path, 'zipped': zipped, + 'is_default_repo': is_default_repo, + 'max_upload_file_size': seaserv.MAX_UPLOAD_FILE_SIZE, }, context_instance=RequestContext(request)) @login_required @@ -470,19 +492,13 @@ def _populate_user_quota_usage(user): org_id = user.org.org_id user.space_usage = seafserv_threaded_rpc.get_org_user_quota_usage(org_id, user.email) user.space_quota = seafserv_threaded_rpc.get_org_user_quota(org_id, user.email) - user.share_usage = user.share_quota = 0 else: user.space_usage = seafile_api.get_user_self_usage(user.email) user.space_quota = seafile_api.get_user_quota(user.email) - - if CALC_SHARE_USAGE: - user.share_quota = seafile_api.get_user_share_quota(user.email) - user.share_usage = seafile_api.get_user_share_usage(user.email) - else: - user.share_usage = user.share_quota = 0 except SearpcError as e: logger.error(e) - user.space_usage = user.space_quota = user.share_usage = user.share_quota = -1 + user.space_usage = -1 + user.space_quota = -1 @login_required @sys_staff_required @@ -577,7 +593,6 @@ def sys_user_admin(request): 'next_page': current_page+1, 'per_page': per_page, 'page_next': page_next, - 'CALC_SHARE_USAGE': CALC_SHARE_USAGE, 'have_ldap': have_ldap, 'platform': platform, 'server_id': server_id[:8], @@ -705,7 +720,6 @@ def sys_user_admin_ldap_imported(request): 'next_page': current_page+1, 'per_page': per_page, 'page_next': page_next, - 'CALC_SHARE_USAGE': CALC_SHARE_USAGE, 'is_pro': is_pro_version(), }, context_instance=RequestContext(request)) @@ -752,7 +766,6 @@ def sys_user_admin_ldap(request): 'per_page': per_page, 'page_next': page_next, 'is_pro': is_pro_version(), - 'CALC_SHARE_USAGE': CALC_SHARE_USAGE, }, context_instance=RequestContext(request)) @@ -800,7 +813,6 @@ def sys_user_admin_admins(request): 'sysadmin/sys_useradmin_admins.html', { 'users': admin_users, 'not_admin_users': not_admin_users, - 'CALC_SHARE_USAGE': CALC_SHARE_USAGE, 'have_ldap': have_ldap, 'default_user': DEFAULT_USER, 'guest_user': GUEST_USER, @@ -812,7 +824,6 @@ def sys_user_admin_admins(request): def user_info(request, email): org_name = None space_quota = space_usage = 0 - share_quota = share_usage = 0 org = ccnet_threaded_rpc.get_orgs_by_user(email) if not org: @@ -821,9 +832,6 @@ def user_info(request, email): in_repos = mute_seafile_api.get_share_in_repo_list(email, -1, -1) space_usage = mute_seafile_api.get_user_self_usage(email) space_quota = mute_seafile_api.get_user_quota(email) - if CALC_SHARE_USAGE: - share_usage = mute_seafile_api.get_user_share_usage(email) - share_quota = mute_seafile_api.get_user_share_quota(email) else: org_id = org[0].org_id org_name = org[0].org_name @@ -935,9 +943,6 @@ def user_info(request, email): 'owned_repos': owned_repos, 'space_quota': space_quota, 'space_usage': space_usage, - 'share_quota': share_quota, - 'share_usage': share_usage, - 'CALC_SHARE_USAGE': CALC_SHARE_USAGE, 'in_repos': in_repos, 'email': email, 'profile': profile, @@ -961,22 +966,15 @@ def user_set_quota(request, email): if f.is_valid(): email = f.cleaned_data['email'] space_quota_mb = f.cleaned_data['space_quota'] - space_quota = space_quota_mb * (1 << 20) - share_quota_mb = f.cleaned_data['share_quota'] - - share_quota = None - if share_quota_mb is not None: - share_quota = share_quota_mb * (1 << 20) + space_quota = space_quota_mb * get_file_size_unit('MB') org = ccnet_threaded_rpc.get_orgs_by_user(email) try: if not org: seafile_api.set_user_quota(email, space_quota) - if share_quota is not None: - seafile_api.set_user_share_quota(email, share_quota) else: org_id = org[0].org_id - org_quota_mb = seafserv_threaded_rpc.get_org_quota(org_id) / (1 << 20) + org_quota_mb = seafserv_threaded_rpc.get_org_quota(org_id) / get_file_size_unit('MB') if space_quota_mb > org_quota_mb: result['error'] = _(u'Failed to set quota: maximum quota is %d MB' % \ org_quota_mb) @@ -1004,7 +1002,7 @@ def sys_org_set_quota(request, org_id): org_id = int(org_id) quota_mb = int(request.POST.get('quota', 0)) - quota = quota_mb * (1 << 20) + quota = quota_mb * get_file_size_unit('MB') try: seafserv_threaded_rpc.set_org_quota(org_id, quota) @@ -1672,13 +1670,11 @@ def sys_org_info_user(request, org_id): try: user.self_usage =seafserv_threaded_rpc. \ get_org_user_quota_usage(org_id, user.email) - user.share_usage = 0 user.quota = seafserv_threaded_rpc. \ get_org_user_quota(org_id, user.email) except SearpcError as e: logger.error(e) user.self_usage = -1 - user.share_usage = -1 user.quota = -1 # populate user last login time @@ -2164,7 +2160,7 @@ def sys_settings(request): 'ENABLE_REPO_HISTORY_SETTING', 'USER_STRONG_PASSWORD_REQUIRED', 'ENABLE_ENCRYPTED_LIBRARY', 'USER_PASSWORD_MIN_LENGTH', 'USER_PASSWORD_STRENGTH_LEVEL', 'SHARE_LINK_PASSWORD_MIN_LENGTH', - 'ENABLE_ORGANIZATION_LIBRARY' + 'ENABLE_USER_CREATE_ORG_REPO' ) STRING_WEB_SETTINGS = ('SERVICE_URL', 'FILE_SERVER_ROOT',) diff --git a/static/scripts/app/views/share.js b/static/scripts/app/views/share.js index 6b04d98e9d..ba8798e888 100644 --- a/static/scripts/app/views/share.js +++ b/static/scripts/app/views/share.js @@ -604,8 +604,10 @@ define([ }); emails_input.select2("val", ""); if (data.failed.length > 0) { - var err_msg = gettext("Failed to share to {placeholder}") - .replace('{placeholder}', Common.HTMLescape(data.failed.join(', '))); + var err_msg = ''; + $(data.failed).each(function(index, item) { + err_msg += Common.HTMLescape(item.email) + ': ' + item.error_msg + '
    '; + }); $('.error', panel).html(err_msg).removeClass('hide'); } }, @@ -667,6 +669,13 @@ define([ $add_item.after(new_item.el); }); groups_input.select2("val", ""); + if (data.failed.length > 0) { + var err_msg = ''; + $(data.failed).each(function(index, item) { + err_msg += Common.HTMLescape(item.group_name) + ': ' + item.error_msg + '
    '; + }); + $('.error', panel).html(err_msg).removeClass('hide'); + } }, error: function(xhr) { var err_msg; diff --git a/tests/api/endpoints/test_dir_shared_items.py b/tests/api/endpoints/test_dir_shared_items.py index 7a0418f9a9..2ae55737bc 100644 --- a/tests/api/endpoints/test_dir_shared_items.py +++ b/tests/api/endpoints/test_dir_shared_items.py @@ -83,7 +83,7 @@ class DirSharedItemsTest(BaseTestCase): self.assertEqual(200, resp.status_code) json_resp = json.loads(resp.content) assert len(json_resp['failed']) == 1 - assert invalid_email in json_resp['failed'] + assert invalid_email in json_resp['failed'][0]['email'] def test_share_folder_to_unregistered_user(self): self.login_as(self.user) @@ -98,7 +98,7 @@ class DirSharedItemsTest(BaseTestCase): self.assertEqual(200, resp.status_code) json_resp = json.loads(resp.content) assert len(json_resp['failed']) == 1 - assert unregistered_user in json_resp['failed'] + assert unregistered_user in json_resp['failed'][0]['email'] def test_can_share_root_to_groups(self): self.login_as(self.user) diff --git a/tests/api/endpoints/test_send_share_link.py b/tests/api/endpoints/test_send_share_link.py new file mode 100644 index 0000000000..a76bc49e73 --- /dev/null +++ b/tests/api/endpoints/test_send_share_link.py @@ -0,0 +1,47 @@ +#coding: UTF-8 +import json +from django.core.urlresolvers import reverse +from seahub.utils import IS_EMAIL_CONFIGURED +from seahub.share.models import FileShare +from seahub.test_utils import BaseTestCase + + +class SendShareLinkApiTest(BaseTestCase): + + def setUp(self): + fs = FileShare.objects.create_file_link(self.user.username, + self.repo.id, self.file, None) + + self.token = fs.token + + def tearDown(self): + self.remove_repo() + + def test_can_send_email(self): + self.login_as(self.user) + invalid_email = 'invalid' + url = reverse("api2-send-share-link") + data = { + "token": self.token, + "email": self.admin.email + ',' + invalid_email, + } + + resp = self.client.post(url, data) + if not IS_EMAIL_CONFIGURED: + self.assertEqual(403, resp.status_code) + else: + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + assert json_resp['success'][0] == self.admin.email + assert json_resp['failed'][0]['email'] == invalid_email + + def test_can_not_send_email_if_not_link_owner(self): + self.login_as(self.admin) + url = reverse("api2-send-share-link") + data = { + "token": self.token, + "email": self.admin.email, + } + + resp = self.client.post(url, data) + self.assertEqual(403, resp.status_code) diff --git a/tests/api/endpoints/test_send_upload_link.py b/tests/api/endpoints/test_send_upload_link.py new file mode 100644 index 0000000000..4bd41f88aa --- /dev/null +++ b/tests/api/endpoints/test_send_upload_link.py @@ -0,0 +1,46 @@ +#coding: UTF-8 +import json +from django.core.urlresolvers import reverse +from seahub.utils import IS_EMAIL_CONFIGURED +from seahub.test_utils import BaseTestCase +from seahub.share.models import UploadLinkShare + + +class SendUploadLinkApiTest(BaseTestCase): + + def setUp(self): + uls = UploadLinkShare.objects.create_upload_link_share(self.user.username, + self.repo.id, '/') + self.token = uls.token + + def tearDown(self): + self.remove_repo() + + def test_can_send_email(self): + self.login_as(self.user) + invalid_email = 'invalid' + url = reverse("api2-send-upload-link") + data = { + "token": self.token, + "email": self.admin.email + ',' + invalid_email, + } + + resp = self.client.post(url, data) + if not IS_EMAIL_CONFIGURED: + self.assertEqual(403, resp.status_code) + else: + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + assert json_resp['success'][0] == self.admin.email + assert json_resp['failed'][0]['email'] == invalid_email + + def test_can_not_send_email_if_not_link_owner(self): + self.login_as(self.admin) + url = reverse("api2-send-upload-link") + data = { + "token": self.token, + "email": self.admin.email, + } + + resp = self.client.post(url, data) + self.assertEqual(403, resp.status_code) diff --git a/tests/api/test_public_repo.py b/tests/api/test_public_repo.py index 9b73a32bf2..a1732718ec 100644 --- a/tests/api/test_public_repo.py +++ b/tests/api/test_public_repo.py @@ -14,7 +14,7 @@ class RepoPublicTest(BaseTestCase): self.url = '/api2/repos/%s/public/' % self.repo_id self.user_repo_url = '/api2/repos/%s/public/' % self.repo.id - config.ENABLE_ORGANIZATION_LIBRARY = 1 + config.ENABLE_USER_CREATE_ORG_REPO = 1 def tearDown(self): self.remove_repo(self.repo_id) @@ -46,9 +46,9 @@ class RepoPublicTest(BaseTestCase): assert json_resp['success'] is True def test_admin_can_set_pub_repo_when_setting_disalbed(self): - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is True - config.ENABLE_ORGANIZATION_LIBRARY = False - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is True + config.ENABLE_USER_CREATE_ORG_REPO = False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is False self.login_as(self.admin) @@ -58,9 +58,9 @@ class RepoPublicTest(BaseTestCase): assert json_resp['success'] is True def test_user_can_not_set_pub_repo_when_setting_disalbed(self): - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is True - config.ENABLE_ORGANIZATION_LIBRARY = False - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is True + config.ENABLE_USER_CREATE_ORG_REPO = False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is False self.login_as(self.user) diff --git a/tests/api/test_shared_repo.py b/tests/api/test_shared_repo.py index a70b159a1e..ce2645745f 100644 --- a/tests/api/test_shared_repo.py +++ b/tests/api/test_shared_repo.py @@ -12,7 +12,7 @@ class SharedRepoTest(BaseTestCase): username=self.admin.username, passwd=None) self.shared_repo_url = '/api2/shared-repos/%s/?share_type=public&permission=rw' - config.ENABLE_ORGANIZATION_LIBRARY = 1 + config.ENABLE_USER_CREATE_ORG_REPO = 1 def tearDown(self): self.remove_repo(self.repo_id) @@ -34,9 +34,9 @@ class SharedRepoTest(BaseTestCase): assert "success" in resp.content def test_admin_can_set_pub_repo_when_setting_disalbed(self): - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is True - config.ENABLE_ORGANIZATION_LIBRARY = False - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is True + config.ENABLE_USER_CREATE_ORG_REPO = False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is False self.login_as(self.admin) @@ -45,9 +45,9 @@ class SharedRepoTest(BaseTestCase): assert "success" in resp.content def test_user_can_not_set_pub_repo_when_setting_disalbed(self): - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is True - config.ENABLE_ORGANIZATION_LIBRARY = False - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is True + config.ENABLE_USER_CREATE_ORG_REPO = False + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is False self.login_as(self.user) diff --git a/tests/seahub/auth/views/test_login.py b/tests/seahub/auth/views/test_login.py new file mode 100644 index 0000000000..b4d8f0bacf --- /dev/null +++ b/tests/seahub/auth/views/test_login.py @@ -0,0 +1,47 @@ +from django.conf import settings +from django.core.urlresolvers import reverse + +from seahub.test_utils import BaseTestCase + + +class LoginTest(BaseTestCase): + def test_can_login(self): + resp = self.client.post( + reverse('auth_login'), {'login': self.user.username, + 'password': self.user_password} + ) + + self.assertEqual(302, resp.status_code) + self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL) + + def test_redirect_to_after_success_login(self): + resp = self.client.post( + reverse('auth_login') + '?next=/foo/', + {'login': self.user.username, + 'password': self.user_password} + ) + + self.assertEqual(302, resp.status_code) + self.assertRegexpMatches(resp['Location'], r'http://testserver/foo/') + + def test_bad_redirect_to_after_success_login(self): + from django.utils.http import urlquote + resp = self.client.post( + reverse('auth_login') + '?next=' + urlquote('http://testserver\@example.com'), + {'login': self.user.username, + 'password': self.user_password} + ) + + self.assertEqual(302, resp.status_code) + self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL) + + def test_redirect_to_other_host_after_success_login(self): + from django.utils.http import urlquote + resp = self.client.post( + reverse('auth_login') + '?next=' + urlquote('http://foo.com'), + {'login': self.user.username, + 'password': self.user_password} + ) + + self.assertEqual(302, resp.status_code) + self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL) diff --git a/tests/seahub/base/templatetags/test_seahub_tags.py b/tests/seahub/base/templatetags/test_seahub_tags.py new file mode 100644 index 0000000000..e1de35fc3c --- /dev/null +++ b/tests/seahub/base/templatetags/test_seahub_tags.py @@ -0,0 +1,41 @@ +from seahub.test_utils import BaseTestCase + +from seahub.base.templatetags.seahub_tags import email2nickname, \ + seahub_filesizeformat +from seahub.profile.models import Profile + + +class Email2nicknameTest(BaseTestCase): + def test_profile_is_none(self): + assert len(Profile.objects.all()) == 0 + + assert email2nickname(self.user.username) == self.user.username.split('@')[0] + + def test_nickname_is_empty_string(self): + Profile.objects.add_or_update(self.user.username, '') + assert len(Profile.objects.all()) == 1 + assert Profile.objects.all()[0].nickname == '' + + assert email2nickname(self.user.username) == self.user.username.split('@')[0] + + def test_nickname_is_space(self): + Profile.objects.add_or_update(self.user.username, ' ') + assert len(Profile.objects.all()) == 1 + assert Profile.objects.all()[0].nickname == ' ' + + assert email2nickname(self.user.username) == self.user.username.split('@')[0] + + def test_nickname_contains_space(self): + Profile.objects.add_or_update(self.user.username, ' foo bar ') + assert len(Profile.objects.all()) == 1 + assert Profile.objects.all()[0].nickname == ' foo bar ' + + assert email2nickname(self.user.username) == 'foo bar' + + +class SeahubFilesizeformatTest(BaseTestCase): + def test_seahub_filesizeformat(self): + assert seahub_filesizeformat(1) == u'1\xa0byte' + assert seahub_filesizeformat(1000) == u'1.0\xa0KB' + assert seahub_filesizeformat(1000000) == u'1.0\xa0MB' + assert seahub_filesizeformat(1000000000) == u'1.0\xa0GB' diff --git a/tests/seahub/notifications/management/commands/test_send_notices.py b/tests/seahub/notifications/management/commands/test_send_notices.py new file mode 100644 index 0000000000..ada828ab26 --- /dev/null +++ b/tests/seahub/notifications/management/commands/test_send_notices.py @@ -0,0 +1,31 @@ +from django.core import mail +from django.core.management import call_command + +from seahub.notifications.models import UserNotification, repo_share_msg_to_json +from seahub.profile.models import Profile +from seahub.test_utils import BaseTestCase + + +class CommandTest(BaseTestCase): + + def test_can_send(self): + self.assertEqual(len(mail.outbox), 0) + UserNotification.objects.add_repo_share_msg( + self.user.username, repo_share_msg_to_json('bar@bar.com', self.repo.id)) + Profile.objects.add_or_update(self.user.username, 'nickname') + + call_command('send_notices') + self.assertEqual(len(mail.outbox), 1) + assert mail.outbox[0].to[0] == self.user.username + + def test_can_send_to_contact_email(self): + self.assertEqual(len(mail.outbox), 0) + UserNotification.objects.add_repo_share_msg( + self.user.username, repo_share_msg_to_json('bar@bar.com', self.repo.id)) + p = Profile.objects.add_or_update(self.user.username, 'nickname') + p.contact_email = 'contact@foo.com' + p.save() + + call_command('send_notices') + self.assertEqual(len(mail.outbox), 1) + assert mail.outbox[0].to[0] == 'contact@foo.com' diff --git a/tests/seahub/profile/models/test_profile_manager.py b/tests/seahub/profile/models/test_profile_manager.py new file mode 100644 index 0000000000..bac58301f2 --- /dev/null +++ b/tests/seahub/profile/models/test_profile_manager.py @@ -0,0 +1,21 @@ +from seahub.profile.models import Profile +from seahub.test_utils import BaseTestCase + + +class ProfileManagerTest(BaseTestCase): + def setUp(self): + pass + + def test_get_contact_email_by_user(self): + # no profile for user, contact email should be username + username = self.user.username + assert username == Profile.objects.get_contact_email_by_user(username) + + # user has profile, but no contact email, contact email should be username + p = Profile.objects.add_or_update(username, 'nickname') + assert username == Profile.objects.get_contact_email_by_user(username) + + # user has profile, and have contact email + p.contact_email = 'contact@foo.com' + p.save() + assert 'contact@foo.com' == Profile.objects.get_contact_email_by_user(username) diff --git a/tests/seahub/thirdpart/shibboleth/test_middleware.py b/tests/seahub/thirdpart/shibboleth/test_middleware.py new file mode 100644 index 0000000000..e40a4909b7 --- /dev/null +++ b/tests/seahub/thirdpart/shibboleth/test_middleware.py @@ -0,0 +1,79 @@ +from mock import Mock + +from django.conf import settings +from django.test import RequestFactory + +from seahub.profile.models import Profile +from seahub.test_utils import BaseTestCase +from shibboleth.middleware import ShibbolethRemoteUserMiddleware + +settings.AUTHENTICATION_BACKENDS += ( + 'shibboleth.backends.ShibbolethRemoteUserBackend', +) + +class ShibbolethRemoteUserMiddlewareTest(BaseTestCase): + def setUp(self): + self.middleware = ShibbolethRemoteUserMiddleware() + + self.factory = RequestFactory() + + # Create an instance of a GET request. + self.request = self.factory.get('/foo/') + # self.request = Mock() + + self.request.user = self.user + self.request.user.is_authenticated = lambda: False + self.request.cloud_mode = False + self.request.session = {} + + self.request.META = {} + self.request.META['REMOTE_USER'] = self.user.username + self.request.META['eppn'] = 'test eppn' + self.request.META['givenname'] = 'test_gname' + self.request.META['surname'] = 'test_sname' + + # def test_can_process(self): + # assert len(Profile.objects.all()) == 0 + + # self.middleware.process_request(self.request) + + # assert len(Profile.objects.all()) == 1 + # assert self.request.shib_login is True + + def test_make_profile_for_display_name(self): + assert len(Profile.objects.all()) == 0 + + self.middleware.make_profile(self.user, { + 'display_name': 'display name', + 'givenname': 'g', + 'surname': 's', + 'institution': 'i', + 'contact_email': 'foo@foo.com' + }) + + assert len(Profile.objects.all()) == 1 + assert Profile.objects.all()[0].nickname == 'display name' + + def test_make_profile_for_givenname_surname(self): + assert len(Profile.objects.all()) == 0 + + self.middleware.make_profile(self.user, { + 'givenname': 'g', + 'surname': 's', + 'institution': 'i', + 'contact_email': 'foo@foo.com' + }) + + assert len(Profile.objects.all()) == 1 + assert Profile.objects.all()[0].nickname == 'g s' + + def test_make_profile_for_name_missing(self): + assert len(Profile.objects.all()) == 0 + + self.middleware.make_profile(self.user, { + 'institution': 'i', + 'contact_email': 'foo@foo.com' + }) + + assert len(Profile.objects.all()) == 1 + assert Profile.objects.all()[0].nickname == '' diff --git a/tests/seahub/utils/test_file_size.py b/tests/seahub/utils/test_file_size.py new file mode 100644 index 0000000000..4c52c690aa --- /dev/null +++ b/tests/seahub/utils/test_file_size.py @@ -0,0 +1,20 @@ +from seahub.test_utils import BaseTestCase +from seahub.utils.file_size import get_file_size_unit + +class GetFileSizeUnitTest(BaseTestCase): + def test_invalid_type(self): + with self.assertRaises(TypeError): + get_file_size_unit('ff') + + def test_valid_type(self): + assert get_file_size_unit('KB') == 1000 ** 1 + assert get_file_size_unit('MB') == 1000 ** 2 + assert get_file_size_unit('GB') == 1000 ** 3 + assert get_file_size_unit('TB') == 1000 ** 4 + assert get_file_size_unit('PB') == 1000 ** 5 + + assert get_file_size_unit('KiB') == 1024 ** 1 + assert get_file_size_unit('MiB') == 1024 ** 2 + assert get_file_size_unit('GiB') == 1024 ** 3 + assert get_file_size_unit('TiB') == 1024 ** 4 + assert get_file_size_unit('PiB') == 1024 ** 5 diff --git a/tests/seahub/views/init/test_libraries.py b/tests/seahub/views/init/test_libraries.py index b9a335784a..d272f32287 100644 --- a/tests/seahub/views/init/test_libraries.py +++ b/tests/seahub/views/init/test_libraries.py @@ -30,15 +30,15 @@ class LibrariesTest(BaseTestCase): # user self.login_as(self.user) - config.ENABLE_ORGANIZATION_LIBRARY = 1 - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is True + config.ENABLE_USER_CREATE_ORG_REPO = 1 + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is True resp = self.client.get(self.url) self.assertEqual(200, resp.status_code) assert resp.context['can_add_pub_repo'] is True - config.ENABLE_ORGANIZATION_LIBRARY = 0 - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is False + config.ENABLE_USER_CREATE_ORG_REPO = 0 + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is False resp = self.client.get(self.url) self.assertEqual(200, resp.status_code) @@ -50,15 +50,15 @@ class LibrariesTest(BaseTestCase): # admin self.login_as(self.admin) - config.ENABLE_ORGANIZATION_LIBRARY = 1 - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is True + config.ENABLE_USER_CREATE_ORG_REPO = 1 + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is True resp = self.client.get(self.url) self.assertEqual(200, resp.status_code) assert resp.context['can_add_pub_repo'] is True - config.ENABLE_ORGANIZATION_LIBRARY = 0 - assert bool(config.ENABLE_ORGANIZATION_LIBRARY) is False + config.ENABLE_USER_CREATE_ORG_REPO = 0 + assert bool(config.ENABLE_USER_CREATE_ORG_REPO) is False resp = self.client.get(self.url) self.assertEqual(200, resp.status_code) diff --git a/tests/seahub/views/sysadmin/test_sys_virus_scan_records.py b/tests/seahub/views/sysadmin/test_sys_virus_scan_records.py new file mode 100644 index 0000000000..afb45a1f6d --- /dev/null +++ b/tests/seahub/views/sysadmin/test_sys_virus_scan_records.py @@ -0,0 +1,52 @@ +import os +from mock import patch +import pytest + +from django.core.urlresolvers import reverse + +from seahub.test_utils import BaseTestCase + +TRAVIS = 'TRAVIS' in os.environ + +class VirusScanRecord(object): + def __init__(self, repo_id): + self.repo_id = repo_id + + +class SysVirusScanRecordsTest(BaseTestCase): + + # @patch('seahub.utils.EVENTS_ENABLED', True) + # @patch('seahub.utils.get_virus_record') + # def test_can_list_empty(self, mock_get_virus_record): + # mock_get_virus_record.return_value = [] + + # self.login_as(self.admin) + + # resp = self.client.get(reverse('sys_virus_scan_records')) + # self.assertEqual(200, resp.status_code) + # self.assertTemplateUsed(resp, 'sysadmin/sys_virus_scan_records.html') + + def _get_virus_record(self, start, limit): + records = [] + for i in range(11): + record = VirusScanRecord(self.repo.id) + record.vid = i + 1 + record.has_handle = False + records.append(record) + + return records + + @pytest.mark.skipif(TRAVIS, reason="TODO: this test can only be run seperately due to the url module init in django, we may need to reload url conf: https://gist.github.com/anentropic/9ac47f6518c88fa8d2b0") + @patch('seahub.utils.EVENTS_ENABLED') + @patch('seahub.utils.get_virus_record') + def test_can_list_records_num_more_than_10(self, mock_get_virus_record, + mock_events_enabled): + mock_events_enabled = True + mock_get_virus_record.side_effect = self._get_virus_record + + self.login_as(self.admin) + + resp = self.client.get(reverse('sys_virus_scan_records')) + self.assertEqual(200, resp.status_code) + self.assertTemplateUsed(resp, 'sysadmin/sys_virus_scan_records.html') + assert len(resp.context['records']) >= 10 diff --git a/tests/seahub/views/test_list_lib_dir.py b/tests/seahub/views/test_list_lib_dir.py new file mode 100644 index 0000000000..be46797bd1 --- /dev/null +++ b/tests/seahub/views/test_list_lib_dir.py @@ -0,0 +1,24 @@ +import json +import os + +from django.core.urlresolvers import reverse + +from seahub.test_utils import BaseTestCase + +class ListLibDirTest(BaseTestCase): + def setUp(self): + self.login_as(self.user) + self.endpoint = reverse('list_lib_dir', args=[self.repo.id]) + self.folder_name = os.path.basename(self.folder) + + def tearDown(self): + self.remove_repo() + + def test_can_list(self): + resp = self.client.get(self.endpoint, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEqual(200, resp.status_code) + + json_resp = json.loads(resp.content) + assert len(json_resp) == 8 + assert self.folder_name == json_resp['dirent_list'][0]['obj_name'] + assert self.repo.name == json_resp['repo_name'] diff --git a/thirdpart/shibboleth/middleware.py b/thirdpart/shibboleth/middleware.py index c9d9058f0b..332e4524c9 100755 --- a/thirdpart/shibboleth/middleware.py +++ b/thirdpart/shibboleth/middleware.py @@ -112,9 +112,14 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware): Extrat nickname(givenname surname), contact_email, institution from Shib attributs, and add those to user profile. """ - givenname = shib_meta.get('givenname', '') - surname = shib_meta.get('surname', '') - nickname = "%s %s" % (givenname, surname) + # use `display_name` as nickname in shib_meta first + nickname = shib_meta.get('display_name', None) + if nickname is None: + # otherwise, fallback to givenname plus surname in shib_meta + givenname = shib_meta.get('givenname', '') + surname = shib_meta.get('surname', '') + nickname = "%s %s" % (givenname, surname) + institution = shib_meta.get('institution', None) contact_email = shib_meta.get('contact_email', None) @@ -122,7 +127,9 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware): if not p: p = Profile(user=user.username) - p.nickname = nickname + if nickname.strip(): # set nickname when it's not empty + p.nickname = nickname + if institution: p.institution = institution if contact_email: