diff --git a/.gitignore b/.gitignore index 612d9fa7ef..e872b626d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.pyc *~ +*# seahub.db* local_settings.py startserver.sh diff --git a/base/templatetags/seahub_tags.py b/base/templatetags/seahub_tags.py index f8c70d59ee..a97c813f51 100644 --- a/base/templatetags/seahub_tags.py +++ b/base/templatetags/seahub_tags.py @@ -62,3 +62,14 @@ def translate_commit_time(value): return u'%d 分钟前' % (seconds/60) else: return u'%d 秒前' % (seconds) + +@register.filter(name='translate_remain_time') +def translate_remain_time(value): + if value > 24 * 60 * 60: + return u'%d 天' % (value/24/3600) + elif value > 60 * 60: + return u'%d 小时' % (value/3600) + elif value > 60: + return u'%d 分钟' % (value/60) + else: + return u'%d 秒' % (value) diff --git a/contacts/views.py b/contacts/views.py index 0965844aba..b26bfc3f20 100644 --- a/contacts/views.py +++ b/contacts/views.py @@ -32,7 +32,7 @@ def contact_add(request): emailuser = ccnet_rpc.get_emailuser(contact_email) if not emailuser: error_msg = u"用户不存在" - elif cmp(contact_email, request.user.username) == 0: + elif contact_email == request.user.username: error_msg = u"不能添加自己为联系人" elif Contact.objects.filter(user_email=request.user.username, contact_email=contact_email).count() > 0: diff --git a/media/css/seahub.css b/media/css/seahub.css index 4774704638..db0e6e5d4c 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -324,7 +324,8 @@ h2.repo-history { padding:0 0 6px 0; } /*repo-share-form*/ -#to_email, +#email_or_group, +#share-link, #added-member-name { width:260px; height:80px; diff --git a/settings.py b/settings.py index 56f2255943..7f4cf0fffe 100644 --- a/settings.py +++ b/settings.py @@ -65,6 +65,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfResponseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', 'auth.middleware.AuthenticationMiddleware', 'seahub.base.middleware.InfobarMiddleware', # 'seahub.base.middleware.UseridMiddleware', @@ -89,6 +90,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.media', 'djblets.util.context_processors.siteRoot', 'django.core.context_processors.request', + 'django.contrib.messages.context_processors.messages', 'seahub.base.context_processors.base', ) @@ -99,6 +101,7 @@ INSTALLED_APPS = ( 'django.contrib.sessions', # 'django.contrib.sites', # 'django.contrib.admin', + 'django.contrib.messages', 'registration', 'avatar', 'seahub.notifications', @@ -106,7 +109,7 @@ INSTALLED_APPS = ( 'seahub.profile', 'seahub.contacts', 'seahub.group', -# 'seahub.share', + 'seahub.share', ) AUTHENTICATION_BACKENDS = ( diff --git a/share/forms.py b/share/forms.py index 0effbef950..982df1d764 100644 --- a/share/forms.py +++ b/share/forms.py @@ -1,43 +1,9 @@ from django import forms -from django.contrib.auth.models import User - -class GroupAddRepoForm(forms.Form): +class RepoShareForm(forms.Form): """ - Form for adding repo to a group. - + Form for sharing repo to user or group. """ + email_or_group = forms.CharField(max_length=512) repo_id = forms.CharField(max_length=36) - - def __init__(self, *args, **kwargs): - super(GroupAddRepoForm, self).__init__(*args, **kwargs) - - -class UserShareForm(forms.Form): - """ - Form for sharing repo to a user. - """ - - user_email = forms.EmailField() - repo_id = forms.CharField(max_length=36) - - def __init__(self, *args, **kwargs): - super(UserShareForm, self).__init__(*args, **kwargs) - - def clean_user_email(self): - data = self.cleaned_data['user_email'] - try: - # put the user in form.to_user for further use - self.to_user = User.objects.get(email=data) - except User.DoesNotExist: - raise forms.ValidationError("No user with such email") - - return data - - def clean_repo_id(self): - data = self.cleaned_data['repo_id'] - if len(data) != 36: - raise forms.ValidationError("Invalid repo id") - - return data diff --git a/share/models.py b/share/models.py index 53401f0caa..2f826b4cf0 100644 --- a/share/models.py +++ b/share/models.py @@ -1,19 +1,8 @@ from django.db import models -from django.contrib.auth.models import User -class UserShare(models.Model): - """Record a repo shared to a user.""" - - from_user = models.ForeignKey(User, related_name="myshare_items") - to_user = models.ForeignKey(User, related_name="share2me_items") +class AnonymousShare(models.Model): + repo_owner = models.EmailField(max_length=255) repo_id = models.CharField(max_length=36) - - -class GroupShare(models.Model): - """A repo shared to a group.""" - - group_id = models.CharField(max_length=36) - repo_id = models.CharField(max_length=36) - - class Meta: - unique_together = ("group_id", "repo_id") + anonymous_email = models.EmailField(max_length=255) + token = models.CharField(max_length=25, unique=True) + diff --git a/share/settings.py b/share/settings.py new file mode 100644 index 0000000000..38e8f66a1f --- /dev/null +++ b/share/settings.py @@ -0,0 +1,4 @@ +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/share/templates/repo/anonymous_share_confirm.html b/share/templates/repo/anonymous_share_confirm.html new file mode 100644 index 0000000000..cdf077fecf --- /dev/null +++ b/share/templates/repo/anonymous_share_confirm.html @@ -0,0 +1,9 @@ +{% extends "myhome_base.html" %} + +{% block title %}{% endblock %} + +{% block main_panel %} +
+

该访问链接已失效,如有需要,请联系目录拥有者 {{ repo_owner }}!

+
+{% endblock %} diff --git a/share/templates/repo/anonymous_share_email.html b/share/templates/repo/anonymous_share_email.html new file mode 100644 index 0000000000..bf7b8fce1f --- /dev/null +++ b/share/templates/repo/anonymous_share_email.html @@ -0,0 +1,12 @@ +{% autoescape off %} +亲爱的:{{ anon_email }} 您好! + +{{ email }} 在SeaCloud上共享了一个同步目录给你,请点击以下链接查看: + +{{ protocol }}://{{ domain }}{% url share.views.anonymous_share_confirm token=token %} + +感谢使用我们的网站! + +Seafile团队 + +{% endautoescape %} diff --git a/templates/share_repos.html b/share/templates/repo/share_admin.html similarity index 57% rename from templates/share_repos.html rename to share/templates/repo/share_admin.html index 5d5db90fa0..a03fd27693 100644 --- a/templates/share_repos.html +++ b/share/templates/repo/share_admin.html @@ -1,4 +1,5 @@ {% extends "myhome_base.html" %} +{% load seahub_tags %} {% block nav_shareadmin_class %}class="cur"{% endblock %} @@ -30,6 +31,37 @@ {% else %}

暂无

{% endif %} + +

我管理的共享链接

+{% if out_links %} + + + + + + + + {% for link in out_links %} + + + + {% if link.remain_time %} + + {% else %} + + {% endif %} + + + {% endfor %} + + +{% endif %} + + + {% endblock %} {% block extra_script %} @@ -46,5 +78,14 @@ $("table tr:gt(0)").hover( $(this).find('img').addClass('vh'); } ); + +$(".view-link").click(function() { + var t = $(this).attr('data'); + var l = '{{ protocol }}://' + '{{ domain }}{{ SITE_ROOT }}share/' + t + '/'; + $('textarea[name="share-link"]').val(l); + $("#view-link-form").modal({appendTo: "#main", containerCss:{padding:18}}); + return false; +}); + {% endblock %} diff --git a/share/templates/repo/share_repo.html b/share/templates/repo/share_repo.html deleted file mode 100644 index b828d7aa07..0000000000 --- a/share/templates/repo/share_repo.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "myhome_base.html" %} - -{% block left_panel %} - -{% endblock %} - -{% block right_panel %} - -

共享同步目录

- -
- {{ form.non_field_errors }} - {{ form.user_email.errors }} -
- {{ form.user_email }}
- {{ form.repo_id.errors }} -
- {{ form.repo_id }}
- -
- -{% endblock %} diff --git a/share/templates/repo/shared_repo_list.html b/share/templates/repo/shared_repo_list.html deleted file mode 100644 index b80c4122a6..0000000000 --- a/share/templates/repo/shared_repo_list.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends "myhome_base.html" %} - -{% block left_panel %} - -{% endblock %} - -{% block right_panel %} - -

共享给我的同步目录

- - - - - - - {% for item in share2me_items %} - - - - - - {% endfor %} -
共享人ID操作
{{ item.from_user.email }}{{ item.repo_id }} -
- -

所有共享的同步目录

- - - - - - - {% for item in share_items %} - - - - - - {% endfor %} -
共享给ID操作
{{ item.to_user.email }}{{ item.repo_id }}删除 -
- -{% endblock %} diff --git a/share/tokens.py b/share/tokens.py new file mode 100644 index 0000000000..59899e7d91 --- /dev/null +++ b/share/tokens.py @@ -0,0 +1,87 @@ +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 + from django.utils.hashcompat import sha_constructor + import datetime + now = datetime.datetime.now() + hash = sha_constructor(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/share/urls.py b/share/urls.py index 4d99a1439d..b8a34d6d3b 100644 --- a/share/urls.py +++ b/share/urls.py @@ -2,10 +2,8 @@ from django.conf.urls.defaults import * from views import * - urlpatterns = patterns('', - url(r'^$', list_shared_repos), - url(r'^list/$', list_shared_repos, name='shared_repo_list'), url(r'^add/$', share_repo), - url(r'^delete/(?P[^/]+)/$', delete_share_item), + url('^remove/(?P.+)/$', remove_anonymous_share, name='remove_anonymous_share'), + url('^(?P.+)/$', anonymous_share_confirm, name='anonymous_share_confirm'), ) diff --git a/share/views.py b/share/views.py index d25d17b056..15fd8c3a58 100644 --- a/share/views.py +++ b/share/views.py @@ -1,62 +1,221 @@ -from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render_to_response +# encoding: utf-8 +from django.core.mail import send_mail from django.core.urlresolvers import reverse -from django.template import RequestContext -from django.contrib.auth.decorators import login_required +from django.http import HttpResponse, HttpResponseRedirect, Http404 +from django.shortcuts import render_to_response +from django.template import Context, loader, RequestContext -from forms import UserShareForm -from models import UserShare +from auth.decorators import login_required +from django.contrib import messages +from django.contrib.sites.models import Site, RequestSite +from pysearpc import SearpcError +from seaserv import seafserv_threaded_rpc, get_repo, ccnet_rpc - -@login_required -def list_shared_repos(request): - """Show the repos I shared.""" - - share_items = UserShare.objects.filter(from_user=request.user) - share2me_items = UserShare.objects.filter(to_user=request.user) - #for repo in s_repos: - # s_repos - # pass - return render_to_response("repo/shared_repo_list.html", - { 'share_items': share_items, - "share2me_items": share2me_items }, - context_instance=RequestContext(request)) +from forms import RepoShareForm +from models import AnonymousShare +#from seahub.contacts.models import Contact +from seahub.views import validate_owner, validate_emailuser +from seahub.utils import go_permission_error +from settings import ANONYMOUS_SHARE_COOKIE_TIMEOUT +from tokens import anon_share_token_generator @login_required def share_repo(request): - """Share a repo to a user.""" - - if request.method == 'POST': - form = UserShareForm(request.POST) - if form.is_valid(): - repo_share = UserShare() - repo_share.from_user = request.user - repo_share.to_user = form.to_user - repo_share.repo_id = form.cleaned_data['repo_id'] - try: - repo_share.save() - except IntegrityError: - # catch the case repo added to group before - pass - return HttpResponseRedirect(reverse('shared_repo_list', args=[])) - else: - user_email = request.REQUEST.get('user_email', '') - repo_id = request.REQUEST.get('repo_id', '') - form = UserShareForm(initial={'user_email': user_email, 'repo_id': repo_id}) + """ + Handle repo share request + """ + if request.method != 'POST': + raise Http404 - return render_to_response("repo/share_repo.html", { - 'form': form, - }, context_instance=RequestContext(request)) + form = RepoShareForm(request.POST) + if not form.is_valid(): + # TODO: may display error msg on form + raise Http404 + + email_or_group = form.cleaned_data['email_or_group'] + repo_id = form.cleaned_data['repo_id'] + from_email = request.user.username + # Test whether user is the repo owner + if not validate_owner(request, repo_id): + return go_permission_error(request, u'只有目录拥有者有权共享目录') + + # Handle the diffent separator + to_email_str = email_or_group.replace(';',',') + to_email_str = to_email_str.replace('\n',',') + to_email_str = to_email_str.replace('\r',',') + to_email_list = to_email_str.split(',') + + for to_email in to_email_list: + to_email = to_email.strip(' ') + if not to_email: + continue + + # if to_email is user name, the format is: 'example@mail.com'; + # if to_email is group, the format is 'group_name ' + if (to_email.split(' ')[0].find('@') == -1): + # share repo to group + # TODO: if we know group id, then we can simplly call group_share_repo + if len(to_email.split(' ')) < 2: + messages.add_message(request, messages.ERROR, to_email) + continue + + group_name = to_email.split(' ')[0] + group_creator = to_email.split(' ')[1] + # get all the groups the user joined + groups = ccnet_rpc.get_groups(request.user.username) + find = False + for group in groups: + # for every group that user joined, if group name and + # group creator matchs, then has find the group + if group.props.group_name == group_name and \ + group_creator.find(group.props.creator_name) >= 0: + from seahub.group.views import group_share_repo + group_share_repo(request, repo_id, int(group.props.id), + from_email) + find = True + messages.add_message(request, messages.INFO, group_name) + break + if not find: + messages.add_message(request, messages.ERROR, group_name) + else: + if validate_emailuser(to_email): + # share repo to registered user + try: + seafserv_threaded_rpc.add_share(repo_id, from_email, + to_email, 'rw') + messages.add_message(request, messages.INFO, to_email) + except SearpcError, e: + messages.add_message(request, messages.ERROR, to_email) +# else: +# # add email to contacts if not in contacts list +# # TODO: condition should be removed +# if from_email != to_email and Contact.objects.filter(user_email=from_email, contact_email=to_email).count() = 0: +# contact = Contact() +# contact.user_email = from_email +# contact.contact_email = to_email +# contact.contact_name = '' +# contact.note = '' +# contact.save() + else: + # share repo to anonymous user + kwargs = {'repo_id': repo_id, + 'repo_owner': from_email, + 'anon_email': to_email + } + anonymous_share(request, **kwargs) + + return HttpResponseRedirect(reverse('myhome')) @login_required -def delete_share_item(request, item_id): - """Delete a share item.""" +def share_admin(request): + """ + List repos I share to others or groups, and list my anonymous share links + """ + username = request.user.username + + # repos that are share to user + out_repos = seafserv_threaded_rpc.list_share_repos(username, 'from_email', -1, -1) + + # repos that are share to groups + group_repos = seafserv_threaded_rpc.get_group_my_share_repos(request.user.username) + for group_repo in group_repos: + repo_id = group_repo.props.repo_id + if not repo_id: + continue + repo = get_repo(repo_id) + if not repo: + continue + group_id = group_repo.props.group_id + group = ccnet_rpc.get_group(int(group_id)) + if not group: + continue + repo.props.shared_email = group.props.group_name + repo.gid = group_id + + out_repos.append(repo) + + # anonymous share links + out_links = AnonymousShare.objects.filter(repo_owner=request.user.username) + for link in out_links: + repo = get_repo(link.repo_id) + link.repo_name = repo.name + link.remain_time = anon_share_token_generator.get_remain_time(link.token) + + return render_to_response('repo/share_admin.html', { + "out_repos": out_repos, + "out_links": out_links, + "protocol": request.is_secure() and 'https' or 'http', + "domain": RequestSite(request).domain, + }, context_instance=RequestContext(request)) + +# 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'] + 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: - item = UserShare.objects.get(pk=item_id) - item.delete() - except UserShare.DoesNotExist: - pass + anon_share.save() + except: + messages.add_message(request, messages.ERROR, kwargs['anon_email']) + 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'您在SeaCloud上收到一个同步目录', t.render(Context(c)), None, + [anon_email], fail_silently=False) + except: + AnonymousShare.objects.filter(token=token).delete() + messages.add_message(request, messages.ERROR, anon_email) + else: + messages.add_message(request, messages.INFO, anon_email) + +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 + + context_instance = RequestContext(request) + context_instance['repo_owner'] = anon_share.repo_owner + if anon_share_token_generator.check_token(token): + res = HttpResponseRedirect(reverse('repo', args=[anon_share.repo_id])) + res.set_cookie("anontoken", token, + max_age=ANONYMOUS_SHARE_COOKIE_TIMEOUT) + return res + else: + return render_to_response('repo/anonymous_share_confirm.html', + context_instance=context_instance) + +def remove_anonymous_share(request, token): + AnonymousShare.objects.filter(token=token).delete() + + return HttpResponseRedirect(reverse('share_admin')) - return HttpResponseRedirect(request.META['HTTP_REFERER']) \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 619b38687b..42b0ae3bd7 100644 --- a/templates/base.html +++ b/templates/base.html @@ -13,13 +13,15 @@
+ {% block info_bar_message %} {% if request.user.is_authenticated and request.cur_note %}
{{ request.cur_note.message|urlize }}
{% endif %} - + {% endblock info_bar_message %} +
diff --git a/templates/myhome.html b/templates/myhome.html index 24164f472b..def6de8736 100644 --- a/templates/myhome.html +++ b/templates/myhome.html @@ -39,6 +39,20 @@ {% block right_panel %} +{% if messages %} +
    + {% for message in messages %} + {% if message.tags == 'info' %} +
  • 共享给 {{ message }} 成功,请前往共享管理查看。
  • + {% endif %} + {% if message.tags == 'error' %} +
  • 共享给 {{ message }} 失败
  • + {% endif %} + {% endfor %} + +
+{% endif %} + {% if output_msg %} {% for key, value in output_msg.items %} {% if key == 'info_msg' %} @@ -101,10 +115,10 @@

暂无

{% endif %} -
+
-
- +
+

输入不能为空。

@@ -125,14 +139,14 @@ $(function() { {% endfor %} $(".repo-share-btn").click(function() { - $("#share_repo_id").val($(this).attr("data")); + $("#repo_id").val($(this).attr("data")); $("#repo-share-form").modal({appendTo: "#main"}); - addAutocomplete('#to_email', '#repo-share-form', share_list); + addAutocomplete('#email_or_group', '#repo-share-form', share_list); }); //check before post $('#share-submit-btn').click(function() { - if (!$.trim($('#to_email').attr('value'))) { + if (!$.trim($('#email_or_group').attr('value'))) { $('#repo-share-form .error').removeClass('hide'); return false; } diff --git a/templates/myhome_base.html b/templates/myhome_base.html index 6dba38ac5f..be32727bbe 100644 --- a/templates/myhome_base.html +++ b/templates/myhome_base.html @@ -34,4 +34,3 @@ --> {% endblock %} - diff --git a/templates/repo.html b/templates/repo.html index cd1c33b018..0302575810 100644 --- a/templates/repo.html +++ b/templates/repo.html @@ -1,93 +1,103 @@ {% extends "myhome_base.html" %} {% load seahub_tags %} +{% block info_bar_message %} +{% if request.user.is_authenticated %} + {{ block.super }} +{% else %} +
+ 当前链接会在短期内失效,欢迎您 加入Seafile 体验更多功能。 +
+{% endif %} +{% endblock %} + {% block main_panel %}

{{repo.props.name}}

-

基本信息

-

{{repo.props.desc}}

-

大小:{{ repo_size|filesizeformat }}

- - {% if not repo.props.encrypted or password_set %} - {% if is_owner or repo_ap == 'public' or share_to_me %} -
+

基本信息

+

{{repo.props.desc}}

+

大小:{{ repo_size|filesizeformat }}

+ + {% if not repo.props.encrypted or password_set %} + {% if can_access %} +

最新修改(更多)

{{ latest_commit.props.desc|translate_commit_desc }}

- by - {% if latest_commit.props.creator_name %} - {{ latest_commit.props.creator_name }} - {% else %} - 未知 - {% endif %} - - {{ latest_commit.props.ctime|translate_commit_time }} + by + {% if latest_commit.props.creator_name %} + {{ latest_commit.props.creator_name }} + {% else %} + 未知 + {% endif %} + + {{ latest_commit.props.ctime|translate_commit_time }}

-
- {% endif %} - {% endif %} +
+ {% endif %} + {% endif %}
- {% if repo.props.encrypted and not password_set %} -

该目录已加密。如需在线查看里面的内容,请输入解密密码。密码只会在服务器上暂存1小时。

-
- -
- {% if error %} -

{{ error }}

- {% endif %} - -
- - {% else %} - {% if not is_owner and repo_ap == 'own' and not share_to_me %} -

该同步目录web匿名访问未开启,不能在线查看。

- {% else %} -

- 当前路径: - {% for name, link in zipped %} - {% if not forloop.last %} - {{ name }} / - {% else %} - {{ name }} - {% endif %} - {% endfor %} -

- - - - - - - - - {% for dirent in dir_list %} - - - - - - - {% endfor %} - - {% for dirent in file_list %} - - - - - - - {% endfor %} -
名字大小操作
目录{{ dirent.obj_name }}
文件{{ dirent.props.obj_name }}{{ dirent.file_size|filesizeformat }} - 查看 - 下载 -
- {% endif %} + {% if repo.props.encrypted and not password_set %} +

该目录已加密。如需在线查看里面的内容,请输入解密密码。密码只会在服务器上暂存1小时。

+
+ +
+ {% if error %} +

{{ error }}

{% endif %} + +
+ + {% else %} + {% if not can_access %} +

无法在线查看该同步目录。

+ {% else %} +

+ 当前路径: + {% for name, link in zipped %} + {% if not forloop.last %} + {{ name }} / + {% else %} + {{ name }} + {% endif %} + {% endfor %} +

+ + + + + + + + + {% for dirent in dir_list %} + + + + + + + {% endfor %} + + {% for dirent in file_list %} + + + + + + + {% endfor %} +
名字大小操作
目录{{ dirent.obj_name }}
文件{{ dirent.props.obj_name }}{{ dirent.file_size|filesizeformat }} + 查看 + 下载 +
+ {% endif %} + {% endif %}
{% endblock %} diff --git a/templates/repo_history_dir.html b/templates/repo_history_dir.html index b00ba3cfb0..c69eed5f1b 100644 --- a/templates/repo_history_dir.html +++ b/templates/repo_history_dir.html @@ -8,7 +8,7 @@
-{% if is_owner or repo_ap == 'public' or share_to_me %} +{% if can_access %}

修改信息

{{ current_commit.props.desc|translate_commit_desc }}

@@ -24,6 +24,7 @@

{% endif %} +

操作

-{% if not is_owner and repo_ap == 'own' and not share_to_me %} -

该同步目录web匿名访问未开启,不能在线查看。

+{% if not can_access %} +

无法在线查看该同步目录。

{% else %}

当前路径: {% for name, link in zipped %} diff --git a/urls.py b/urls.py index 450e82e4ce..e7b17cb14d 100644 --- a/urls.py +++ b/urls.py @@ -7,9 +7,10 @@ from seahub.views import root, peers, myhome, \ activate_user, user_add, user_remove, \ ownerhome, repo_history_dir, repo_history_revert, \ user_info, repo_set_access_property, repo_access_file, \ - repo_add_share, repo_list_share, repo_remove_share, repo_download, \ + repo_remove_share, repo_download, \ seafile_access_check, back_local, group_admin, repo_history_changes from seahub.notifications.views import notification_list +from seahub.share.views import share_admin # Uncomment the next two lines to enable the admin: #from django.contrib import admin @@ -32,9 +33,9 @@ urlpatterns = patterns('', #url(r'^home/$', direct_to_template, { 'template': 'home.html' } ), url(r'^home/my/$', myhome, name='myhome'), url(r'^home/owner/(?P[^/]+)/$', ownerhome, name='ownerhome'), - - url(r'^shareadmin/$', repo_list_share, name='repo_list_share'), - url(r'^shareadmin/addshare/$', repo_add_share, name='repo_add_share'), + + (r'^share/', include('share.urls')), + url(r'^shareadmin/$', share_admin, name='share_admin'), (r'^shareadmin/removeshare/$', repo_remove_share), url(r'^repo/(?P[^/]+)/$', repo, name='repo'), @@ -66,7 +67,6 @@ urlpatterns = patterns('', (r'^group/', include('seahub.group.urls')), url(r'^groupadmin/$', group_admin, name='group_admin'), (r'^profile/', include('seahub.profile.urls')), - (r'^share/', include('share.urls')), (r'^back/local/$', back_local), ) diff --git a/views.py b/views.py index a2ff52aace..344ec33dfa 100644 --- a/views.py +++ b/views.py @@ -4,6 +4,7 @@ import stat import simplejson as json from urllib import quote from django.core.urlresolvers import reverse +from django.contrib import messages from django.db import IntegrityError from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import render_to_response, redirect @@ -22,8 +23,6 @@ from pysearpc import SearpcError from seahub.base.accounts import CcnetUser from seahub.contacts.models import Contact -from seahub.share.forms import GroupAddRepoForm -from seahub.share.models import GroupShare, UserShare from forms import AddUserForm from utils import go_permission_error, go_error, list_to_string, \ get_httpserver_root, get_ccnetapplet_root, gen_token @@ -54,29 +53,11 @@ def peers(request): }, context_instance=RequestContext(request)) def validate_owner(request, repo_id): - # check whether email in the request own the repo - return seafserv_threaded_rpc.is_repo_owner(request.user.username, repo_id) - -def check_shared_repo(request, repo_id): """ - check whether user has been shared this repo or - the repo share to the groups user join + Check whether email in the request own the repo """ - repos = seafserv_threaded_rpc.list_share_repos(request.user.username, 'to_email', -1, -1) - for repo in repos: - if repo.props.id == repo_id: - return True - - groups = ccnet_rpc.get_groups(request.user.username) - # for every group that user joined... - for group in groups: - # ...get repo ids in that group, and check whether repo ids contains that repo id - repo_ids = get_group_repoids(group.props.id) - if repo_ids.__contains__(repo_id): - return True - - return False + return seafserv_threaded_rpc.is_repo_owner(request.user.username, repo_id) def validate_emailuser(emailuser): """ @@ -93,14 +74,41 @@ def validate_emailuser(emailuser): else: return False +def check_shared_repo(request, repo_id): + """ + Check whether user has been shared this repo or + the repo share to the groups user join or + got token if user is not logged in + + """ + if not request.user.is_authenticated(): + token = request.COOKIES.get('anontoken', None) + if token: + return True + else: + return False + + repos = seafserv_threaded_rpc.list_share_repos(request.user.username, 'to_email', -1, -1) + for repo in repos: + if repo.props.id == repo_id: + return True + + groups = ccnet_rpc.get_groups(request.user.username) + # for every group that user joined... + for group in groups: + # ...get repo ids in that group, and check whether repo ids contains that repo id + repo_ids = get_group_repoids(group.props.id) + if repo_id in repo_ids: + return True + + return False + def access_to_repo(request, repo_id, repo_ap): """ Check whether user in the request can access to repo, which means user can view directory entries on repo page, and repo_history_dir page. """ - # if repo is 'own' and user is not staff and is not owner - # and not shared this repo, then goto 404 page.. if repo_ap == 'own' and not validate_owner(request, repo_id) \ and not check_shared_repo(request, repo_id) and not request.user.is_staff: return False @@ -133,16 +141,26 @@ def render_repo(request, repo_id, error=''): # get repo web access property, if no repo access property in db, then # assume repo ap is 'own' repo_ap = seafserv_threaded_rpc.repo_query_access_property(repo_id) - if repo_ap == None: + if not repo_ap: repo_ap = 'own' - - if not access_to_repo(request, repo_id, repo_ap): - return go_permission_error(request, u'该同步目录未公开') + # check whether user can view repo + if access_to_repo(request, repo_id, repo_ap): + can_access = True + else: + can_access = False + + # check whether use is repo owner + if validate_owner(request, repo_id): + is_owner = True + else: + is_owner = False + repo = get_repo(repo_id) if not repo: return go_error(request, u'该同步目录不存在') + # query whether set password if repo is encrypted password_set = False if repo.props.encrypted: try: @@ -152,9 +170,11 @@ def render_repo(request, repo_id, error=''): except SearpcError, e: return go_error(request, e.msg) + # query repo infomation repo_size = seafserv_threaded_rpc.server_repo_size(repo_id) latest_commit = get_commits(repo_id, 0, 1)[0] + # get repo dirents dirs = [] path = '' zipped = [] @@ -190,22 +210,9 @@ def render_repo(request, repo_id, error=''): # generate path and link zipped = gen_path_link(path, repo.name) - # check whether use is repo owner - is_owner = False - if request.user.is_authenticated(): - if validate_owner(request, repo_id): - is_owner = True - - # used to determin whether show repo content in repo.html - # if a repo is shared to me, or repo shared to the group I joined, - # then I can view repo content on the web - if check_shared_repo(request, repo_id): - share_to_me = True - else: - share_to_me = False - return render_to_response('repo.html', { "repo": repo, + "can_access": can_access, "latest_commit": latest_commit, "is_owner": is_owner, "password_set": password_set, @@ -213,7 +220,6 @@ def render_repo(request, repo_id, error=''): "repo_size": repo_size, "dir_list": dir_list, "file_list": file_list, - "share_to_me": share_to_me, "path" : path, "zipped" : zipped, "error" : error, @@ -307,14 +313,24 @@ def repo_history_dir(request, repo_id): repo_ap = seafserv_threaded_rpc.repo_query_access_property(repo_id) if not repo_ap: repo_ap = 'own' - - if not access_to_repo(request, repo_id, repo_ap): - raise Http404 + # check whether user can view repo + if access_to_repo(request, repo_id, repo_ap): + can_access = True + else: + can_access = False + + # check whether use is repo owner + if validate_owner(request, repo_id): + is_owner = True + else: + is_owner = False + repo = get_repo(repo_id) if not repo: raise Http404 + # query whether set password if repo is encrypted password_set = False if repo.props.encrypted: try: @@ -334,11 +350,7 @@ def repo_history_dir(request, repo_id): if not current_commit: raise Http404 - is_owner = False - if request.user.is_authenticated(): - if validate_owner(request, repo_id): - is_owner = True - + # get repo dirents dirs = [] path = '' zipped = [] @@ -367,22 +379,14 @@ def repo_history_dir(request, repo_id): # generate path and link zipped = gen_path_link(path, repo.name) - # used to determin whether show repo content in repo.html - # if a repo is shared to me, or repo shared to the group I joined, - # then I can view repo content on the web - if check_shared_repo(request, repo_id): - share_to_me = True - else: - share_to_me = False - return render_to_response('repo_history_dir.html', { "repo": repo, + "can_access": can_access, "current_commit": current_commit, "is_owner": is_owner, "repo_ap": repo_ap, "dir_list": dir_list, "file_list": file_list, - "share_to_me": share_to_me, "path" : path, "zipped" : zipped, }, context_instance=RequestContext(request)) @@ -554,10 +558,6 @@ def myhome(request): in_repos = seafserv_threaded_rpc.list_share_repos(request.user.username, 'to_email', -1, -1) - # handle share repo request - if request.method == 'POST': - output_msg = repo_add_share(request) - # my contacts contacts = Contact.objects.filter(user_email=email) @@ -570,12 +570,11 @@ def myhome(request): groups_manage.append(group) else: groups_join.append(group) - + return render_to_response('myhome.html', { "owned_repos": owned_repos, "quota_usage": quota_usage, "in_repos": in_repos, - "output_msg": output_msg, "contacts": contacts, "groups": groups, "groups_manage": groups_manage, @@ -658,96 +657,6 @@ def repo_access_file(request, repo_id, obj_id): token, request.user.username) return HttpResponseRedirect(redirect_url) - -@login_required -def repo_add_share(request): - output_msg = {} - - if request.method == 'POST': - from_email = request.user.username - repo_id = request.POST.get('share_repo_id', '') - - # Handle the diffent separator - to_email_str = request.POST.get('to_email', '').replace(';',',') - to_email_str = to_email_str.replace('\n',',') - to_email_str = to_email_str.replace('\r',',') - - to_email_list = to_email_str.split(',') - info_emails = [] - err_emails = [] - for to_email in to_email_list: - to_email = to_email.strip(' ') - if not to_email: - continue - - # if to_email is user name, the format is: 'example@mail.com'; - # if to_email is group, the format is 'group_name ' - if (to_email.split(' ')[0].find('@') == -1): - group_name = to_email.split(' ')[0] - group_creator = to_email.split(' ')[1] - if validate_owner(request, repo_id): - # get all the groups the user joined - groups = ccnet_rpc.get_groups(request.user.username) - find = False - for group in groups: - # for every group that user joined, if group name and - # group creator matchs, then has find the group - if group.props.group_name == group_name and \ - group_creator.find(group.props.creator_name) >= 0: - from seahub.group.views import group_share_repo - group_share_repo(request, repo_id, int(group.props.id), from_email) - find = True - info_emails.append(group_name) - - if not find: - err_emails.append(group_name) - else: - err_emails.append(group_name) - else: - if validate_emailuser(to_email) and validate_owner(request, repo_id): - try: - seafserv_threaded_rpc.add_share(repo_id, from_email, to_email, 'rw') - info_emails.append(to_email) - except SearpcError, e: - err_emails.append(to_email) - else: - err_emails.append(to_email) - - if info_emails: - output_msg['info_msg'] = u'共享给%s成功,' % list_to_string(info_emails) - if err_emails: - output_msg['err_msg'] = u'共享给%s失败' % list_to_string(err_emails) - - return output_msg - -@login_required -def repo_list_share(request): - username = request.user.username - - # repos that are share to user - out_repos = seafserv_threaded_rpc.list_share_repos(username, 'from_email', -1, -1) - - # repos that are share to groups - group_repos = seafserv_threaded_rpc.get_group_my_share_repos(request.user.username) - for group_repo in group_repos: - repo_id = group_repo.props.repo_id - if not repo_id: - continue - repo = get_repo(repo_id) - if not repo: - continue - group_id = group_repo.props.group_id - group = ccnet_rpc.get_group(int(group_id)) - if not group: - continue - repo.props.shared_email = group.props.group_name - repo.gid = group_id - - out_repos.append(repo) - - return render_to_response('share_repos.html', { - "out_repos": out_repos, - }, context_instance=RequestContext(request)) @login_required def repo_download(request): @@ -816,7 +725,7 @@ def repo_remove_share(request): referer = request.META.get('HTTP_REFERER', None) if not referer: - referer = 'repo_list_share' + referer = 'share_admin' return HttpResponseRedirect(reverse(referer)) else: return HttpResponseRedirect(referer)