1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-05 22:46:53 +00:00
seahub/share/views.py

514 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# encoding: utf-8
import os
import logging
import simplejson as json
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.db import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect, Http404, \
HttpResponseBadRequest
from django.shortcuts import render_to_response
from django.template import Context, loader, RequestContext
from django.utils.translation import ugettext as _
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, \
ccnet_threaded_rpc, get_personal_groups, list_personal_shared_repos, \
is_personal_repo, check_group_staff, is_org_group, get_org_id_by_group, \
del_org_group_repo
from forms import RepoShareForm, FileLinkShareForm
from models import AnonymousShare
from settings import ANONYMOUS_SHARE_COOKIE_TIMEOUT
from tokens import anon_share_token_generator
from seahub.contacts.signals import mail_sended
from seahub.share.models import FileShare
from seahub.views import validate_owner, is_registered_user
from seahub.utils import render_permission_error, string2list, render_error, \
gen_token, gen_shared_link, IS_EMAIL_CONFIGURED
try:
from seahub.settings import CLOUD_MODE
except ImportError:
CLOUD_MODE = False
from seahub.settings import SITE_ROOT
# Get an instance of a logger
logger = logging.getLogger(__name__)
@login_required
def share_repo(request):
"""
Handle repo share request
"""
if request.method != 'POST':
raise Http404
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']
permission = form.cleaned_data['permission']
from_email = request.user.username
repo = get_repo(repo_id)
if not repo:
raise Http404
is_encrypted = True if repo.encrypted else False
# Test whether user is the repo owner.
if not validate_owner(request, repo_id):
return render_permission_error(request, _(u'Only the owner of the library has permission to share it.'))
to_email_list = string2list(email_or_group)
for to_email in to_email_list:
if to_email == 'all':
''' Share to public '''
# ignore 'all' if we're running in cloud mode
if not CLOUD_MODE:
try:
seafserv_threaded_rpc.set_inner_pub_repo(repo_id, permission)
except:
messages.error(request, _(u'Failed to share to all members'))
continue
msg = _(u'Shared to all members successfully, go check it at <a href="%s">Share</a>.') % \
(reverse('share_admin'))
messages.add_message(request, messages.INFO, msg)
elif to_email.find('@') == -1:
''' Share repo to group '''
# TODO: if we know group id, then we can simplly call group_share_repo
group_name = to_email
# get all personal groups
groups = get_personal_groups(-1, -1)
find = False
for group in groups:
# for every group that user joined, if group name matchs,
# then has find the group
if group.props.group_name == group_name:
from seahub.group.views import group_share_repo
group_share_repo(request, repo_id, int(group.props.id),
from_email, permission)
find = True
msg = _(u'Shared to %(group)s successfullygo check it at <a href="%(share)s">Share</a>.') % \
{'group':group_name, 'share':reverse('share_admin')}
messages.add_message(request, messages.INFO, msg)
break
if not find:
msg = _(u'Failed to share to %sas it does not exists.') % group_name
messages.add_message(request, messages.ERROR, msg)
else:
''' Share repo to user '''
# Add email to contacts.
mail_sended.send(sender=None, user=request.user.username,
email=to_email)
if not is_registered_user(to_email):
# Generate shared link and send mail if user has not registered.
# kwargs = {'repo_id': repo_id,
# 'repo_owner': from_email,
# 'anon_email': to_email,
# 'is_encrypted': is_encrypted,
# }
# anonymous_share(request, **kwargs)
msg = _(u'Failed to share to %s, as the email is not registered.') % to_email
messages.add_message(request, messages.ERROR, msg)
continue
else:
# Record share info to db.
try:
seafserv_threaded_rpc.add_share(repo_id, from_email, to_email,
permission)
except SearpcError, e:
msg = _(u'Failed to share to %s .') % to_email
messages.add_message(request, messages.ERROR, msg)
continue
msg = _(u'Shared to %(email)s successfullygo check it at <a href="%(share)s">Share</a>.') % \
{'email':to_email, 'share':reverse('share_admin')}
messages.add_message(request, messages.INFO, msg)
return HttpResponseRedirect(reverse('myhome'))
@login_required
def repo_remove_share(request):
"""
If repo is shared from one person to another person, only these two peson
can remove share.
If repo is shared from one person to a group, then only the one share the
repo and group staff can remove share.
"""
repo_id = request.GET.get('repo_id', '')
group_id = request.GET.get('gid', '')
from_email = request.GET.get('from', '')
# if request params don't have 'gid', then remove repos that share to
# to other person; else, remove repos that share to groups
if not group_id:
to_email = request.GET.get('to', '')
if request.user.username != from_email and \
request.user.username != to_email:
return render_permission_error(request, _(u'Failed to remove share'))
seafserv_threaded_rpc.remove_share(repo_id, from_email, to_email)
else:
try:
group_id_int = int(group_id)
except:
return render_error(request, _(u'group id is not valid'))
if not check_group_staff(group_id_int, request.user) \
and request.user.username != from_email:
return render_permission_error(request, _(u'Failed to remove share'))
if is_org_group(group_id_int):
org_id = get_org_id_by_group(group_id_int)
del_org_group_repo(repo_id, org_id, group_id_int)
else:
from group.views import group_unshare_repo
group_unshare_repo(request, repo_id, group_id_int, from_email)
messages.success(request, _('Successfully removed share'))
next = request.META.get('HTTP_REFERER', None)
if not next:
next = SITE_ROOT
return HttpResponseRedirect(next)
@login_required
def share_admin(request):
"""
List personal shared repos and shared links.
"""
username = request.user.username
shared_repos = []
# personal repos shared by this user
shared_repos += seafserv_threaded_rpc.list_share_repos(username, 'from_email',
-1, -1)
# repos shared to groups
group_repos = seafserv_threaded_rpc.get_group_repos_by_owner(username)
for repo in group_repos:
group = ccnet_threaded_rpc.get_group(int(repo.group_id))
if not group:
repo.props.user = ''
continue
repo.props.user = group.props.group_name
repo.props.user_info = repo.group_id
shared_repos += group_repos
if not CLOUD_MODE:
# public repos shared by this user
pub_repos = seafserv_threaded_rpc.list_inner_pub_repos_by_owner(username)
for repo in pub_repos:
repo.props.user = _(u'all members')
repo.props.user_info = 'all'
shared_repos += pub_repos
for repo in shared_repos:
if repo.props.permission == 'rw':
repo.share_permission = _(u'Read-Write')
elif repo.props.permission == 'r':
repo.share_permission = _(u'Read-Only')
else:
repo.share_permission = ''
if repo.props.share_type == 'personal':
repo.props.user_info = repo.props.user
shared_repos.sort(lambda x, y: cmp(x.repo_id, y.repo_id))
# 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)
# Shared links
fileshares = FileShare.objects.filter(username=username)
p_fileshares = [] # personal file share
for fs in fileshares:
if is_personal_repo(fs.repo_id): # only list files in personal repos
if fs.s_type == 'f':
fs.filename = os.path.basename(fs.path)
fs.shared_link = gen_shared_link(request, fs.token, 'f')
else:
fs.filename = os.path.basename(fs.path[:-1])
fs.shared_link = gen_shared_link(request, fs.token, 'd')
r = get_repo(fs.repo_id)
if not r: # get_repo may returns None
continue
fs.repo = r
p_fileshares.append(fs)
return render_to_response('repo/share_admin.html', {
"org": None,
"shared_repos": shared_repos,
# "out_links": out_links,
"fileshares": p_fileshares,
}, context_instance=RequestContext(request))
@login_required
def share_permission_admin(request):
share_type = request.GET.get('share_type', '')
content_type = 'application/json; charset=utf-8'
form = RepoShareForm(request.POST)
form.is_valid()
email_or_group = form.cleaned_data['email_or_group']
repo_id = form.cleaned_data['repo_id']
permission = form.cleaned_data['permission']
from_email = request.user.username
if share_type == 'personal':
try:
seafserv_threaded_rpc.set_share_permission(repo_id, from_email, email_or_group, permission)
except:
return HttpResponse(json.dumps({'success': False}), status=500, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
if share_type == 'group':
try:
seafserv_threaded_rpc.set_group_repo_permission(int(email_or_group), repo_id, permission)
except:
return HttpResponse(json.dumps({'success': False}), status=500, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
if share_type == 'public':
try:
seafserv_threaded_rpc.set_inner_pub_repo(repo_id, permission)
except:
return HttpResponse(json.dumps({'success': False}), status=500, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), 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 <a href="%(share)s">Share</a>.') % \
{'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)
@login_required
def get_shared_link(request):
"""
Handle ajax request to generate file or dir shared link.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
repo_id = request.GET.get('repo_id', '')
share_type = request.GET.get('type', 'f') # `f` or `d`
path = request.GET.get('p', '')
if not (repo_id and path):
err = _('Invalid arguments')
data = json.dumps({'error': err})
return HttpResponse(data, status=400, content_type=content_type)
if share_type == 'f':
if path[-1] == '/': # cut out last '/' at end of path
path = path[:-1]
else:
if path == '/': # can not share root dir
err = _('You cannot share the library in this way.')
data = json.dumps({'error': err})
return HttpResponse(data, status=400, content_type=content_type)
else:
if path[-1] != '/': # append '/' at end of path
path += '/'
l = FileShare.objects.filter(repo_id=repo_id).filter(
username=request.user.username).filter(path=path)
if len(l) > 0:
fs = l[0]
token = fs.token
else:
token = gen_token(max_length=10)
fs = FileShare()
fs.username = request.user.username
fs.repo_id = repo_id
fs.path = path
fs.token = token
fs.s_type = 'f' if share_type == 'f' else 'd'
try:
fs.save()
except IntegrityError, e:
err = _('Failed to get the link, please retry later.')
data = json.dumps({'error': err})
return HttpResponse(data, status=500, content_type=content_type)
shared_link = gen_shared_link(request, token, fs.s_type)
data = json.dumps({'token': token, 'shared_link': shared_link})
return HttpResponse(data, status=200, content_type=content_type)
@login_required
def remove_shared_link(request):
"""
Handle request to remove file shared link.
"""
token = request.GET.get('t', '')
if not request.is_ajax():
FileShare.objects.filter(token=token).delete()
next = request.META.get('HTTP_REFERER', None)
if not next:
next = reverse('share_admin')
messages.success(request, _(u'Removed successfully'))
return HttpResponseRedirect(next)
content_type = 'application/json; charset=utf-8'
FileShare.objects.filter(token=token).delete()
msg = _('Deleted successfully')
data = json.dumps({'msg': msg})
return HttpResponse(data, status=200, content_type=content_type)
@login_required
def send_shared_link(request):
"""
Handle ajax post request to send file shared link.
"""
if not request.is_ajax() and not request.method == 'POST':
raise Http404
result = {}
content_type = 'application/json; charset=utf-8'
if not IS_EMAIL_CONFIGURED:
data = json.dumps({'error':_(u'Sending shared link failed. Email service is not properly configured, please contact administrator.')})
return HttpResponse(data, status=500, content_type=content_type)
from seahub.settings import SITE_NAME
form = FileLinkShareForm(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
file_shared_link = form.cleaned_data['file_shared_link']
t = loader.get_template('shared_link_email.html')
to_email_list = string2list(email)
for to_email in to_email_list:
# Add email to contacts.
mail_sended.send(sender=None, user=request.user.username,
email=to_email)
c = {
'email': request.user.username,
'to_email': to_email,
'file_shared_link': file_shared_link,
'site_name': SITE_NAME,
}
try:
send_mail(_(u'Your friend shared a file to you on Seafile'),
t.render(Context(c)), None, [to_email],
fail_silently=False)
except Exception, e:
logger.error(str(e))
data = json.dumps({'error':_(u'Internal server error. Send failed.')})
return HttpResponse(data, status=500, content_type=content_type)
data = json.dumps({"msg": _(u'Successfully sent.')})
return HttpResponse(data, status=200, content_type=content_type)
else:
return HttpResponseBadRequest(json.dumps(form.errors),
content_type=content_type)