1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-06-22 21:27:30 +00:00
seahub/views.py
2012-09-02 20:23:57 +08:00

2142 lines
72 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 settings
import os
import stat
import simplejson as json
import re
import tempfile
import sys
import urllib
import urllib2
from urllib import quote
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.core.mail import send_mail
from django.contrib import messages
from django.contrib.sites.models import Site, RequestSite
from django.db import IntegrityError
from django.db.models import F
from django.http import HttpResponse, HttpResponseBadRequest, Http404, \
HttpResponseRedirect
from django.shortcuts import render_to_response, redirect
from django.template import Context, loader, RequestContext
from django.utils.hashcompat import md5_constructor
from django.views.decorators.csrf import csrf_protect
from auth.decorators import login_required
from auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, \
PasswordChangeForm
from auth.tokens import default_token_generator
from share.models import FileShare
from seaserv import ccnet_rpc, ccnet_threaded_rpc, get_repos, get_emailusers, \
get_repo, get_commits, get_branches, is_valid_filename, remove_group_user,\
seafserv_threaded_rpc, seafserv_rpc, get_binding_peerids, is_inner_pub_repo, \
check_group_staff, get_personal_groups, is_repo_owner, \
get_group, get_shared_groups_by_repo, is_group_user, check_permission
from pysearpc import SearpcError
from base.accounts import User
from base.decorators import sys_staff_required, ctx_switch_required
from seahub.base.models import UuidObjidMap, FileComment
from seahub.contacts.models import Contact
from seahub.contacts.signals import mail_sended
from group.models import GroupMessage, MessageAttachment
from group.signals import grpmsg_added
from seahub.notifications.models import UserNotification
from seahub.organizations.utils import access_org_repo
from forms import AddUserForm, FileLinkShareForm, RepoCreateForm, \
RepoNewDirForm, RepoNewFileForm, FileCommentForm
from utils import render_permission_error, render_error, list_to_string, \
get_httpserver_root, get_ccnetapplet_root, gen_token, \
calculate_repo_last_modify, valid_previewed_file, \
check_filename_with_rename, get_accessible_repos, EMPTY_SHA1, \
get_file_revision_id_size, get_ccnet_server_addr_port, \
gen_file_get_url, string2list, MAX_INT, \
gen_file_upload_url, check_and_get_org_by_repo
from seahub.profile.models import Profile
try:
from settings import CROCODOC_API_TOKEN
except ImportError:
CROCODOC_API_TOKEN = None
from settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD
@login_required
def root(request):
return HttpResponseRedirect(reverse(myhome))
def validate_owner(request, repo_id):
"""
Check whether user in the request owns the repo.
"""
ret = is_repo_owner(request.user.username, repo_id)
return True if ret else False
def is_registered_user(email):
"""
Check whether user is registerd.
"""
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
return True if user else False
def access_to_repo(request, repo_id, repo_ap=None):
"""
Check whether user in the request can access to repo, which means user can
view directory entries on repo page. Only repo owner or person who is shared
can access to repo.
NOTE: `repo_ap` may be used in future.
"""
if not request.user.is_authenticated():
token = request.COOKIES.get('anontoken', None)
return True if token else False
else:
return check_permission(repo_id, request.user.username)
def gen_path_link(path, repo_name):
"""
Generate navigate paths and links in repo page.
"""
if path and path[-1] != '/':
path += '/'
paths = []
links = []
if path and path != '/':
paths = path[1:-1].split('/')
i=1
for name in paths:
link = '/' + '/'.join(paths[:i])
i = i + 1
links.append(link)
paths.insert(0, repo_name)
links.insert(0, '/')
zipped = zip(paths, links)
return zipped
def render_repo(request, repo_id, error=''):
# Check whether user can view repo page
can_access = access_to_repo(request, repo_id, '')
if not can_access:
return render_permission_error(request, '无法访问该同步目录')
repo = get_repo(repo_id)
if not repo:
return render_error(request, u'该同步目录不存在')
# query whether set password if repo is encrypted
password_set = False
if repo.props.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username)
if ret == 1:
password_set = True
except SearpcError, e:
return render_error(request, e.msg)
# view newest worktree or history worktree
commit_id = request.GET.get('commit_id', '')
view_history = True if commit_id else False
current_commit = seafserv_threaded_rpc.get_commit(commit_id)
if not current_commit:
current_commit = get_commits(repo_id, 0, 1)[0]
# query repo infomation
repo_size = seafserv_threaded_rpc.server_repo_size(repo_id)
# get repo dirents
dirs = []
path = ''
zipped = []
dir_list = []
file_list = []
if not repo.props.encrypted or password_set:
path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
if current_commit.root_id == EMPTY_SHA1:
dirs = []
else:
try:
dirs = seafserv_threaded_rpc.list_dir_by_path(current_commit.id,
path.encode('utf-8'))
except SearpcError, e:
return render_error(request, e.msg)
for dirent in dirs:
if stat.S_ISDIR(dirent.props.mode):
dir_list.append(dirent)
else:
file_list.append(dirent)
try:
dirent.file_size = seafserv_threaded_rpc.get_file_size(dirent.obj_id)
except:
dirent.file_size = 0
dir_list.sort(lambda x, y : cmp(x.obj_name.lower(),
y.obj_name.lower()))
file_list.sort(lambda x, y : cmp(x.obj_name.lower(),
y.obj_name.lower()))
if request.user.is_authenticated() and not view_history:
try:
accessible_repos = get_accessible_repos(request, repo)
except SearpcError, e:
error_msg = e.msg
return render_error(request, error_msg)
else:
accessible_repos = []
# generate path and link
zipped = gen_path_link(path, repo.name)
# get groups this repo is shared
groups = []
repo_shared_groups = get_shared_groups_by_repo(repo_id)
for group in repo_shared_groups:
# check whether user joined this group
if is_group_user(group.id, request.user.username):
groups.append(group)
return render_to_response('repo.html', {
"repo": repo,
"can_access": can_access,
"current_commit": current_commit,
"view_history": view_history,
"password_set": password_set,
"repo_size": repo_size,
"dir_list": dir_list,
"file_list": file_list,
"path": path,
"zipped": zipped,
"error": error,
"accessible_repos": accessible_repos,
"applet_root": get_ccnetapplet_root(),
"groups": groups,
}, context_instance=RequestContext(request))
@login_required
@ctx_switch_required
def repo_upload_file(request, repo_id):
repo = get_repo(repo_id)
if request.method == 'GET':
parent_dir = request.GET.get('p', '/')
zipped = gen_path_link (parent_dir, repo.name)
token = ''
if access_to_repo(request, repo_id, ''):
token = gen_token()
seafserv_rpc.web_save_access_token(token, repo_id,
'dummy', 'upload',
request.user.username)
else:
return render_permission_error(request, u'无法访问该目录')
no_quota = False
if seafserv_threaded_rpc.check_quota(repo_id) < 0:
no_quota = True
upload_url = gen_file_upload_url(token, 'upload')
httpserver_root = get_httpserver_root()
return render_to_response ('repo_upload_file.html', {
"repo": repo,
"upload_url": upload_url,
"httpserver_root": httpserver_root,
"parent_dir": parent_dir,
"zipped": zipped,
"max_upload_file_size": settings.MAX_UPLOAD_FILE_SIZE,
"no_quota": no_quota,
}, context_instance=RequestContext(request))
@login_required
@ctx_switch_required
def repo_update_file(request, repo_id):
repo = get_repo(repo_id)
if request.method == 'GET':
target_file = request.GET.get('p')
if not target_file:
return render_error(request, u'非法链接')
zipped = gen_path_link (target_file, repo.name)
token = ''
if access_to_repo(request, repo_id, ''):
token = gen_token()
seafserv_rpc.web_save_access_token(token, repo_id,
'dummy', 'update',
request.user.username)
else:
return render_permission_error(request, u'无法访问该目录')
no_quota = False
if seafserv_threaded_rpc.check_quota(repo_id) < 0:
no_quota = True
update_url = gen_file_upload_url(token, 'update')
httpserver_root = get_httpserver_root()
return render_to_response ('repo_update_file.html', {
"repo": repo,
"update_url": update_url,
"httpserver_root": httpserver_root,
"target_file": target_file,
"zipped": zipped,
"max_upload_file_size": settings.MAX_UPLOAD_FILE_SIZE,
"no_quota": no_quota,
}, context_instance=RequestContext(request))
def upload_error_msg (code):
err_msg = u'服务器内部错误'
if (code == 0):
err_msg = u'上传的文件名包含非法字符'
elif (code == 1):
err_msg = u'已存在同名的文件'
elif (code == 2):
err_msg = u'文件不存在'
elif (code == 3):
err_msg = u'文件大小超过限制'
elif (code == 4):
err_msg = u'该同步目录所有者的空间已用完,无法上传'
elif (code == 5):
err_msg = u'文件传输出错'
return err_msg
@ctx_switch_required
def upload_file_error(request, repo_id):
if request.method == 'GET':
repo = get_repo(repo_id)
parent_dir = request.GET.get('p')
filename = request.GET.get('fn')
err = request.GET.get('err')
if not parent_dir or not filename or not err:
return render_error(request, u'非法链接')
zipped = gen_path_link (parent_dir, repo.name)
code = int(err)
err_msg = upload_error_msg(code)
return render_to_response('upload_file_error.html', {
'repo': repo,
'zipped': zipped,
'filename': filename,
'err_msg': err_msg,
}, context_instance=RequestContext(request))
@ctx_switch_required
def update_file_error(request, repo_id):
if request.method == 'GET':
repo = get_repo(repo_id)
target_file = request.GET.get('p')
err = request.GET.get('err')
if not target_file or not err:
return render_error(request, u'非法链接')
zipped = gen_path_link (target_file, repo.name)
code = int(err)
err_msg = upload_error_msg(code)
return render_to_response('upload_file_error.html', {
'repo': repo,
'zipped': zipped,
'err_msg': err_msg,
}, context_instance=RequestContext(request))
def get_subdir(request):
repo_id = request.GET.get('repo_id', '')
path = request.GET.get('path', '')
if not (repo_id and path):
return render_error(request)
latest_commit = get_commits(repo_id, 0, 1)[0]
try:
dirents = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id, path.encode('utf-8'))
except SearpcError, e:
return render_error(request, e.msg)
subdirs = []
for dirent in dirents:
if not stat.S_ISDIR(dirent.props.mode):
continue
dirent.has_subdir = False
path_ = os.path.join (path, dirent.obj_name)
try:
dirs_ = seafserv_threaded_rpc.list_dir_by_path(latest_commit.id, path_.encode('utf-8'))
except SearpcError, e:
return render_error(request, e.msg)
for dirent_ in dirs_:
if stat.S_ISDIR(dirent_.props.mode):
dirent.has_subdir = True
break
if dirent.has_subdir:
subdir = {
'data': dirent.obj_name,
'attr': {'repo_id': repo_id },
'state': 'closed'
}
subdirs.append(subdir)
else:
subdirs.append(dirent.obj_name)
content_type = 'application/json; charset=utf-8'
return HttpResponse(json.dumps(subdirs),
content_type=content_type)
@ctx_switch_required
def repo(request, repo_id):
if request.method == 'GET':
return render_repo(request, repo_id)
elif request.method == 'POST':
password = request.POST.get('password', '')
if not password:
return render_repo(request, repo_id, u'密码不能为空')
try:
seafserv_threaded_rpc.set_passwd(repo_id, request.user.username, password)
except SearpcError, e:
if e.msg == 'Bad arguments':
return render_error(request, u'url 格式不正确')
elif e.msg == 'Repo is not encrypted':
return render_repo(request, repo_id)
elif e.msg == 'Incorrect password':
return render_repo(request, repo_id, u'密码不正确,请重新输入')
elif e.msg == 'Internal server error':
return render_error(request, u'服务器内部故障')
else:
return render_error(request, u'未知错误')
return render_repo(request, repo_id)
@login_required
@ctx_switch_required
def repo_history(request, repo_id):
"""
View repo history.
"""
if not access_to_repo(request, repo_id, ''):
return render_permission_error(request, u'无法浏览该同步目录修改历史')
repo = get_repo(repo_id)
password_set = False
if repo.props.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username)
if ret == 1:
password_set = True
except SearpcError, e:
return render_error(request, e.msg)
if repo.props.encrypted and not password_set:
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
commits_all = get_commits(repo_id, per_page * (current_page -1),
per_page + 1)
commits = commits_all[:per_page]
if len(commits_all) == per_page + 1:
page_next = True
else:
page_next = False
is_owner = False
if request.user.is_authenticated():
if validate_owner(request, repo_id):
is_owner = True
return render_to_response('repo_history.html', {
"repo": repo,
"commits": commits,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
'is_owner': is_owner,
}, context_instance=RequestContext(request))
@login_required
def repo_history_revert(request, repo_id):
repo = get_repo(repo_id)
if not repo:
raise Http404
if not access_to_repo(request, repo_id):
return render_permission_error(request, u'您没有权限进行还原操作')
password_set = False
if repo.props.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username)
if ret == 1:
password_set = True
except SearpcError, e:
return render_error(request, e.msg)
if repo.props.encrypted and not password_set:
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return render_error(request, u'请指定历史记录 ID')
try:
seafserv_threaded_rpc.revert_on_server(repo_id, commit_id, request.user.username)
except SearpcError, e:
if e.msg == 'Bad arguments':
return render_error(request, u'非法参数')
elif e.msg == 'No such repo':
return render_error(request, u'同步目录不存在')
elif e.msg == "Commit doesn't exist":
return render_error(request, u'指定的历史记录不存在')
else:
return render_error(request, u'未知错误')
return HttpResponseRedirect(reverse(repo_history, args=[repo_id]))
def get_diff(repo_id, arg1, arg2):
lists = {'new' : [], 'removed' : [], 'renamed' : [], 'modified' : [], \
'newdir' : [], 'deldir' : []}
diff_result = seafserv_threaded_rpc.get_diff(repo_id, arg1, arg2)
if not diff_result:
return lists
for d in diff_result:
if d.status == "add":
lists['new'].append(d.name)
elif d.status == "del":
lists['removed'].append(d.name)
elif d.status == "mov":
lists['renamed'].append(d.name + " ==> " + d.new_name)
elif d.status == "mod":
lists['modified'].append(d.name)
elif d.status == "newdir":
lists['newdir'].append(d.name)
elif d.status == "deldir":
lists['deldir'].append(d.name)
return lists
def repo_history_changes(request, repo_id):
changes = {}
content_type = 'application/json; charset=utf-8'
if not access_to_repo(request, repo_id, ''):
return HttpResponse(json.dumps(changes),
content_type=content_type)
repo = get_repo(repo_id)
if not repo:
return HttpResponse(json.dumps(changes),
content_type=content_type)
password_set = False
if repo.props.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username)
if ret == 1:
password_set = True
except:
return HttpResponse(json.dumps(changes),
content_type=content_type)
if repo.props.encrypted and not password_set:
return HttpResponse(json.dumps(changes),
content_type=content_type)
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return HttpResponse(json.dumps(changes),
content_type=content_type)
changes = get_diff(repo_id, '', commit_id)
return HttpResponse(json.dumps(changes),
content_type=content_type)
@login_required
def modify_token(request, repo_id):
if not validate_owner(request, repo_id):
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
token = request.POST.get('token', '')
if token:
seafserv_threaded_rpc.set_repo_token(repo_id, token)
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
@login_required
def remove_repo(request, repo_id):
# FIXME: no org in request.user, check whether repo is org repo, and then
# check permission
if not validate_owner(request, repo_id) and not request.user.is_staff \
and not request.user.org['is_staff']:
err_msg = u'删除同步目录失败, 只有管理员或目录创建者有权删除目录。'
return render_permission_error(request, err_msg)
seafserv_threaded_rpc.remove_repo(repo_id)
next = request.GET.get('next', '/')
return HttpResponseRedirect(next)
@login_required
def myhome(request):
owned_repos = []
quota_usage = 0
email = request.user.username
quota_usage = seafserv_threaded_rpc.get_user_quota_usage(email)
# Repos that I own
owned_repos = seafserv_threaded_rpc.list_owned_repos(email)
calculate_repo_last_modify(owned_repos)
owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
# Repos shared with me
in_repos = seafserv_threaded_rpc.list_share_repos(email,
'to_email', -1, -1)
calculate_repo_last_modify(in_repos)
in_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
# my contacts
contacts = Contact.objects.filter(user_email=email)
# user notifications
grpmsg_list = []
grpmsg_reply_list = []
orgmsg_list = []
notes = UserNotification.objects.filter(to_user=request.user.username)
for n in notes:
if n.msg_type == 'group_msg':
grp = get_group(n.detail)
if not grp:
continue
grpmsg_list.append(grp)
elif n.msg_type == 'grpmsg_reply':
grpmsg_reply_list.append(n.detail)
elif n.msg_type == 'org_msg':
orgmsg_list.append(n.detail)
# my groups
groups = get_personal_groups(email)
# get nickname
if not Profile.objects.filter(user=request.user.username):
nickname = ''
else:
profile = Profile.objects.filter(user=request.user.username)[0]
nickname = profile.nickname
# ctx_dict = {'base_template': 'myhome_base.html',
# 'org_dict': None}
# set_cur_ctx(request, ctx_dict)
return render_to_response('myhome.html', {
"myname": email,
"nickname": nickname,
"owned_repos": owned_repos,
"quota_usage": quota_usage,
"in_repos": in_repos,
"contacts": contacts,
"groups": groups,
"notes": notes,
"grpmsg_list": grpmsg_list,
"grpmsg_reply_list": grpmsg_reply_list,
"orgmsg_list": orgmsg_list,
}, context_instance=RequestContext(request))
@login_required
def public_home(request):
"""
Show public home page when CLOUD_MODE is False.
"""
users = get_emailusers(-1, -1)
public_repos = seafserv_threaded_rpc.list_inner_pub_repos()
calculate_repo_last_modify(public_repos)
public_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
return render_to_response('public_home.html', {
'users': users,
'public_repos': public_repos,
}, context_instance=RequestContext(request))
@login_required
def public_repo_create(request):
'''
Handle ajax post to create public repo.
'''
if not request.is_ajax() or request.method != 'POST':
return Http404
result = {}
content_type = 'application/json; charset=utf-8'
form = RepoCreateForm(request.POST)
if form.is_valid():
repo_name = form.cleaned_data['repo_name']
repo_desc = form.cleaned_data['repo_desc']
passwd = form.cleaned_data['passwd']
user = request.user.username
try:
# create a repo
repo_id = seafserv_threaded_rpc.create_repo(repo_name, repo_desc,
user, passwd)
# set this repo as inner pub
seafserv_threaded_rpc.set_inner_pub_repo(repo_id)
except:
repo_id = None
if not repo_id:
result['error'] = u"创建目录失败"
else:
result['success'] = True
return HttpResponse(json.dumps(result), content_type=content_type)
else:
return HttpResponseBadRequest(json.dumps(form.errors),
content_type=content_type)
@login_required
def ownerhome(request, owner_name):
owned_repos = []
quota_usage = 0
owned_repos = seafserv_threaded_rpc.list_owned_repos(owner_name)
quota_usage = seafserv_threaded_rpc.get_user_quota_usage(owner_name)
user_dict = user_info(request, owner_name)
return render_to_response('ownerhome.html', {
"owned_repos": owned_repos,
"quota_usage": quota_usage,
"owner": owner_name,
"user_dict": user_dict,
}, context_instance=RequestContext(request))
@login_required
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]))
@login_required
def repo_del_file(request, repo_id):
parent_dir = request.GET.get("p", "/")
file_name = request.GET.get("file_name")
user = request.user.username
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir,file_name, user)
messages.add_message(request, messages.INFO, u'%s 删除成功。' % file_name)
except:
messages.add_message(request, messages.ERROR, u'内部错误。%s 删除失败。' % file_name)
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
return HttpResponseRedirect(url)
@ctx_switch_required
def repo_view_file(request, repo_id):
"""
Preview file on web, including files in current worktree and history.
"""
if request.method == 'POST':
# handle post request to leave comment on file
path = request.GET.get('p', '/')
next = reverse('repo_view_file', args=[repo_id]) + '?p=' + \
urllib2.quote(path.encode('utf-8'))
f = FileCommentForm(request.POST)
if f.is_valid():
repo_id = f.cleaned_data['repo_id']
file_path = f.cleaned_data['file_path']
file_path_hash = md5_constructor(file_path).hexdigest()[:12]
message = f.cleaned_data['message']
fc = FileComment(repo_id=repo_id, file_path=file_path,
file_path_hash=file_path_hash,
from_email=request.user.username, message=message)
fc.save()
# send a group message if the repo shared to any groups
repo_shared_groups = get_shared_groups_by_repo(repo_id)
for group in repo_shared_groups:
# save group message, and length should be less than 500
gm = GroupMessage(group_id=group.id,
from_email=request.user.username,
message=message[:500])
gm.save()
# send signal
grpmsg_added.send(sender=GroupMessage, group_id=group.id,
from_email=request.user.username)
# save attachment
ma = MessageAttachment(group_message=gm, repo_id=repo_id,
attach_type='file', path=path,
src='filecomment')
ma.save()
return HttpResponseRedirect(next)
http_server_root = get_httpserver_root()
path = request.GET.get('p', '/')
u_filename = os.path.basename(path)
filename = urllib2.quote(u_filename.encode('utf-8'))
commit_id = request.GET.get('commit_id', '')
view_history = True if commit_id else False
current_commit = seafserv_threaded_rpc.get_commit(commit_id)
if not current_commit:
current_commit = get_commits(repo_id, 0, 1)[0]
if view_history:
obj_id = request.GET.get('obj_id', '')
else:
try:
obj_id = seafserv_threaded_rpc.get_file_by_path(repo_id, path)
except:
obj_id = None
if not obj_id:
return render_error(request, '文件不存在')
repo = get_repo(repo_id)
if not repo:
raise Http404
token = ''
if access_to_repo(request, repo_id, ''):
# Get a token to visit file
token = gen_token()
seafserv_rpc.web_save_access_token(token, repo_id, obj_id,
'view', request.user.username)
else:
render_permission_error(request, '无法查看该文件')
# generate path and link
zipped = gen_path_link(path, repo.name)
# determin whether file can preview on web
filetype, fileext = valid_previewed_file(filename)
# raw path
raw_path = gen_file_get_url(token, filename)
# get file content
err = ''
file_content = ''
if filetype == 'Text' or filetype == 'Markdown':
err, file_content, encoding, newline_mode = repo_file_get(raw_path)
# file share link
l = FileShare.objects.filter(repo_id=repo_id).filter(\
username=request.user.username).filter(path=path)
fileshare = l[0] if len(l) > 0 else None
http_or_https = request.is_secure() and 'https' or 'http'
domain = RequestSite(request).domain
if fileshare:
file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain,
settings.SITE_ROOT,
fileshare.token)
else:
file_shared_link = ''
# my constacts
contacts = Contact.objects.filter(user_email=request.user.username)
# get groups this repo is shared
groups = []
repo_shared_groups = get_shared_groups_by_repo(repo_id)
for group in repo_shared_groups:
# check whether user joined this group
if is_group_user(group.id, request.user.username):
groups.append(group)
"""file comments"""
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '15'))
except ValueError:
current_page = 1
per_page = 15
file_path_hash = md5_constructor(urllib2.quote(path.encode('utf-8'))).hexdigest()[:12]
comments_plus_one = FileComment.objects.filter(
file_path_hash=file_path_hash,
repo_id=repo_id)[per_page*(current_page-1) : per_page*current_page+1]
if comments_plus_one.count() == per_page + 1:
page_next = True
else:
page_next = False
comments = comments_plus_one[:per_page]
return render_to_response('repo_view_file.html', {
'repo': repo,
'obj_id': obj_id,
'u_filename': u_filename,
'file_name': filename,
'path': path,
'zipped': zipped,
'view_history': view_history,
'current_commit': current_commit,
'token': token,
'filetype': filetype,
'fileext': fileext,
'raw_path': raw_path,
'fileshare': fileshare,
'protocol': http_or_https,
'domain': domain,
'file_shared_link': file_shared_link,
'contacts': contacts,
'err': err,
'file_content': file_content,
"applet_root": get_ccnetapplet_root(),
'groups': groups,
'comments': comments,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
}, context_instance=RequestContext(request))
def repo_file_get(raw_path):
err = ''
file_content = ''
encoding = ''
newline_mode = ''
try:
file_response = urllib2.urlopen(raw_path)
if long(file_response.headers['Content-Length']) > FILE_PREVIEW_MAX_SIZE:
err = '文件超过10M无法在线查看。'
return err, '', '', ''
else:
content = file_response.read()
except urllib2.HTTPError, e:
err = 'HTTPError: 无法在线打开该文件'
return err, '', '', ''
except urllib2.URLError as e:
err = 'URLError: 无法在线打开该文件'
return err, '', '', ''
else:
try:
u_content = content.decode('utf-8')
encoding = 'utf-8'
except UnicodeDecodeError:
# XXX: file in windows is encoded in gbk
try:
u_content = content.decode('gbk')
encoding = 'gbk'
except UnicodeDecodeError:
err = u'文件编码无法识别'
return err, '', '', ''
file_content = u_content
# detect newline mode for ace editor
if '\r\n' in u_content:
newline_mode = 'windows'
elif '\n' in u_content:
newline_mode = 'unix'
else:
newline_mode = 'windows'
return err, file_content, encoding, newline_mode
def pdf_full_view(request):
repo_id = request.GET.get('repo_id', '')
obj_id = request.GET.get('obj_id', '')
file_name = request.GET.get('file_name', '')
token = gen_token()
seafserv_rpc.web_save_access_token(token, repo_id, obj_id,
'view', request.user.username)
file_src = gen_file_get_url(token, file_name)
return render_to_response('pdf_full_view.html', {
'file_src': file_src,
}, context_instance=RequestContext(request))
def update_file_after_edit(request, repo_id):
content_type = 'application/json; charset=utf-8'
def error_json(error_msg=u"内部错误"):
return HttpResponse(json.dumps({'error': error_msg}),
status=400,
content_type=content_type)
def ok_json():
return HttpResponse(json.dumps({'status': 'ok'}),
content_type=content_type)
content = request.POST.get('content')
encoding = request.POST.get('encoding')
path = request.GET.get('p')
if content is None or not path:
return error_json(u"参数错误")
if encoding not in ["gbk", "utf-8"]:
return error_json(u"参数错误")
content = content.encode(encoding)
# first dump the file content to a tmp file, then update the file
fd, tmpfile = tempfile.mkstemp()
def remove_tmp_file():
try:
os.remove(tmpfile_name)
except:
pass
try:
bytesWritten = os.write(fd, content)
except:
bytesWritten = -1
finally:
os.close(fd)
if bytesWritten != len(content):
remove_tmp_file()
return error_json()
parent_dir = os.path.dirname(path).encode('utf-8')
filename = os.path.basename(path).encode('utf-8')
try:
seafserv_threaded_rpc.put_file (repo_id, tmpfile, parent_dir,
filename, request.user.username);
remove_tmp_file()
return ok_json()
except SearpcError, e:
remove_tmp_file()
return error_json(str(e))
@login_required
@ctx_switch_required
def repo_file_edit(request, repo_id):
if request.method == 'POST':
return update_file_after_edit(request, repo_id)
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
u_filename = os.path.basename(path)
filename = urllib2.quote(u_filename.encode('utf-8'))
repo = get_repo(repo_id)
if not repo:
raise Http404
try:
obj_id = seafserv_threaded_rpc.get_file_by_path(repo_id, path)
except:
obj_id = None
if not obj_id:
return render_error(request, '文件不存在')
token = ''
if access_to_repo(request, repo_id, ''):
token = gen_token()
seafserv_rpc.web_save_access_token(token, repo_id, obj_id,
'view', request.user.username)
else:
render_permission_error(request, '无法查看该文件')
# generate path and link
zipped = gen_path_link(path, repo.name)
filetype, fileext = valid_previewed_file(filename)
# get file content
raw_path = gen_file_get_url(token, filename)
err, file_content, encoding, newline_mode = repo_file_get(raw_path)
return render_to_response('repo_edit_file.html', {
'repo':repo,
'u_filename':u_filename,
'path':path,
'zipped':zipped,
'fileext':fileext,
'err':err,
'file_content':file_content,
'encoding': encoding,
'newline_mode': newline_mode,
}, context_instance=RequestContext(request))
def repo_access_file(request, repo_id, obj_id):
repo = get_repo(repo_id)
if not repo:
raise Http404
password_set = False
if repo.props.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo_id, request.user.username)
if ret == 1:
password_set = True
except SearpcError, e:
return render_error(request, e.msg)
if repo.props.encrypted and not password_set:
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
op = request.GET.get('op', 'view')
file_name = request.GET.get('file_name', '')
if op == 'del':
return repo_del_file(request, repo_id)
# If vistor's file shared token in url params matches the token in db,
# then we know the vistor is from file shared link.
share_token = request.GET.get('t', '')
path = '/' + file_name
if FileShare.objects.filter(token=share_token).filter(path=path) > 0:
from_shared_link = True
else:
from_shared_link = False
token = ''
if access_to_repo(request, repo_id, '') or from_shared_link:
# Get a token to access file
token = gen_token()
seafserv_rpc.web_save_access_token(token, repo_id, obj_id,
op, request.user.username)
else:
render_permission_error(request, '无法访问文件')
redirect_url = gen_file_get_url(token, file_name)
return HttpResponseRedirect(redirect_url)
@login_required
def repo_download(request):
repo_id = request.GET.get('repo_id', '')
repo = seafserv_threaded_rpc.get_repo(repo_id)
repo_name = repo.props.name
quote_repo_name = quote(repo_name.encode('utf-8'))
encrypted = repo.props.encrypted
if encrypted:
enc = '1'
else:
enc = ''
relay_id = ccnet_rpc.get_session_info().id
if not relay_id:
return render_to_response('error.html', {
"error_msg": u"下载失败:无法取得中继"
}, context_instance=RequestContext(request))
try:
token = seafserv_threaded_rpc.get_repo_token_nonnull \
(repo_id, request.user.username)
except Exception, e:
return render_error(request, str(e))
addr, port = get_ccnet_server_addr_port ()
if not (addr and port):
return render_error(request, u"服务器设置错误")
ccnet_applet_root = get_ccnetapplet_root()
email = urllib2.quote(request.user.username)
url = ccnet_applet_root + "/repo/download/"
url += "?relay_id=%s&relay_addr=%s&relay_port=%s" % (relay_id, addr, port)
url += "&email=%s&token=%s" % (email, token)
url += "&repo_id=%s&repo_name=%s&encrypted=%s" % (repo_id, quote_repo_name, enc)
return HttpResponseRedirect(url)
@login_required
def file_move(request):
src_repo_id = request.POST.get('src_repo')
src_path = request.POST.get('src_path')
dst_repo_id = request.POST.get('dst_repo')
dst_path = request.POST.get('dst_path')
obj_name = request.POST.get('obj_name')
obj_type = request.POST.get('obj_type') # dir or file
op = request.POST.get('operation')
if not (src_repo_id and src_path and dst_repo_id \
and dst_path and obj_name and obj_type and op):
return render_error(request)
# do nothing when dst is the same as src
if src_repo_id == dst_repo_id and src_path == dst_path:
url = reverse('repo', args=[src_repo_id]) + ('?p=%s' % urllib2.quote(src_path.encode('utf-8')))
return HttpResponseRedirect(url)
# Error when moving/copying a dir to its subdir
if obj_type == 'dir':
src_dir = os.path.join(src_path, obj_name)
if dst_path.startswith(src_dir):
error_msg = u"不能把目录 %s %s到它的子目录 %s" \
% (src_dir, u"复制" if op == 'cp' else u"移动", dst_path)
return render_error(request, error_msg)
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
msg_url = reverse('repo', args=[dst_repo_id]) + u'?p=' + urllib2.quote(dst_path.encode('utf-8'))
if op == 'cp':
seafserv_threaded_rpc.copy_file (src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
request.user.username)
messages.add_message(request, messages.INFO, u'%s 复制成功:<a href="%s">查看</a>' % (obj_name, msg_url))
elif op == 'mv':
seafserv_threaded_rpc.move_file (src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
request.user.username)
messages.add_message(request, messages.INFO, u'%s 移动成功:<a href="%s">查看</a>' % (obj_name, msg_url))
except Exception, e:
return render_error(request, str(e))
url = reverse('repo', args=[src_repo_id]) + ('?p=%s' % urllib2.quote(src_path.encode('utf-8')))
return HttpResponseRedirect(url)
def seafile_access_check(request):
repo_id = request.GET.get('repo_id', '')
applet_root = get_ccnetapplet_root()
return render_to_response(
'seafile_access_check.html', {
'repo_id': repo_id,
'applet_root': applet_root,
},
context_instance=RequestContext(request))
@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'取消共享失败')
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 不是有效参数')
if not check_group_staff(group_id_int, request.user) \
and request.user.username != from_email:
return render_permission_error(request, u'取消共享失败')
from seahub.group.views import group_unshare_repo
group_unshare_repo(request, repo_id, group_id_int, from_email)
referer = request.META.get('HTTP_REFERER', None)
if not referer:
referer = 'share_admin'
return HttpResponseRedirect(reverse(referer))
else:
return HttpResponseRedirect(referer)
# @login_required
# def mypeers(request):
# cid = get_user_cid(request.user)
@login_required
@sys_staff_required
def sys_seafadmin(request):
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
repos_all = seafserv_threaded_rpc.get_repo_list(per_page *
(current_page -1),
per_page + 1)
repos = repos_all[:per_page]
if len(repos_all) == per_page + 1:
page_next = True
else:
page_next = False
for repo in repos:
try:
repo.owner = seafserv_threaded_rpc.get_repo_owner(repo.props.id)
except:
repo.owner = None
return render_to_response(
'sys_seafadmin.html', {
'repos': repos,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
},
context_instance=RequestContext(request))
@login_required
@sys_staff_required
def sys_useradmin(request):
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
users_plus_one = get_emailusers(per_page * (current_page - 1), per_page + 1)
if len(users_plus_one) == per_page + 1:
page_next = True
else:
page_next = False
users = users_plus_one[:per_page]
for user in users:
if user.props.id == request.user.id:
user.is_self = True
return render_to_response(
'sys_useradmin.html', {
'users': users,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
},
context_instance=RequestContext(request))
@login_required
@sys_staff_required
def user_info(request, email):
owned_repos = []
quota_usage = 0
owned_repos = seafserv_threaded_rpc.list_owned_repos(email)
quota_usage = seafserv_threaded_rpc.get_user_quota_usage(email)
# Repos that are share to user
in_repos = seafserv_threaded_rpc.list_share_repos(email, 'to_email',
-1, -1)
return render_to_response(
'userinfo.html', {
'owned_repos': owned_repos,
'quota_usage': quota_usage,
"in_repos": in_repos,
'email': email
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def user_remove(request, user_id):
"""Remove user, also remove group relationship."""
try:
user = User.objects.get(id=int(user_id))
user.delete()
except User.DoesNotExist:
pass
return HttpResponseRedirect(reverse('sys_useradmin'))
@login_required
@sys_staff_required
def activate_user(request, user_id):
try:
user = User.objects.get(id=int(user_id))
user.is_active = True
user.save()
except User.DoesNotExist:
pass
return HttpResponseRedirect(reverse('sys_useradmin'))
def send_user_reset_email(request, email, password):
"""
Send email when reset user password.
"""
use_https = request.is_secure()
domain = RequestSite(request).domain
t = loader.get_template('user_reset_email.html')
c = {
'email': email,
'password': password,
}
try:
send_mail(u'密码重置', t.render(Context(c)),
None, [email], fail_silently=False)
messages.add_message(request, messages.INFO, '通知邮件已成功。')
except:
messages.add_message(request, messages.ERROR, '邮件发送失败。')
@login_required
@sys_staff_required
def user_reset(request, user_id):
"""Reset password for user."""
try:
user = User.objects.get(id=int(user_id))
user.set_password(INIT_PASSWD)
user.save()
messages.add_message(request, messages.INFO, u'密码重置成功。')
if hasattr(settings, 'EMAIL_HOST'):
send_user_reset_email(request, user.email, INIT_PASSWD)
except User.DoesNotExist:
msg =u'密码重置失败,用户不存在。'
messages.add_message(request, messages.ERROR, msg)
return HttpResponseRedirect(reverse('sys_useradmin'))
def send_user_add_mail(request, email, password):
"""Send email when add new user."""
use_https = request.is_secure()
domain = RequestSite(request).domain
t = loader.get_template('user_add_email.html')
c = {
'user': request.user.username,
'org': request.user.org,
'email': email,
'password': password,
'domain': domain,
'protocol': use_https and 'https' or 'http',
}
try:
send_mail(u'SeaCloud注册信息', t.render(Context(c)),
None, [email], fail_silently=False)
messages.add_message(request, messages.INFO, '邮件发送成功。')
except:
messages.add_message(request, messages.ERROR, '邮件发送失败。')
@login_required
def user_add(request):
"""Add a user"""
if not request.user.is_staff and not request.user.org['is_staff']:
raise Http404
base_template = 'org_admin_base.html' if request.user.org else 'admin_base.html'
if request.method == 'POST':
form = AddUserForm(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
password = form.cleaned_data['password1']
user = User.objects.create_user(email, password,
is_staff=False,
is_active=True)
if request.user.org:
org_id = request.user.org['org_id']
url_prefix = request.user.org['url_prefix']
ccnet_threaded_rpc.add_org_user(org_id, email, 0)
if hasattr(settings, 'EMAIL_HOST'):
send_user_add_mail(request, email, password)
return HttpResponseRedirect(reverse('org_useradmin',
args=[url_prefix]))
else:
if hasattr(settings, 'EMAIL_HOST'):
send_user_add_mail(request, email, password)
return HttpResponseRedirect(reverse('sys_useradmin', args=[]))
else:
form = AddUserForm()
return render_to_response("add_user_form.html", {
'form': form,
'base_template': base_template,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def sys_group_admin(request):
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page= int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
groups_plus_one = ccnet_threaded_rpc.get_all_groups(per_page * (current_page -1),
per_page +1)
groups = groups_plus_one[:per_page]
if len(groups_plus_one) == per_page + 1:
page_next = True
else:
page_next = False
return render_to_response('sys_group_admin.html', {
'groups': groups,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def sys_org_admin(request):
try:
orgs = ccnet_threaded_rpc.get_all_orgs(0, MAX_INT)
except:
orgs = []
return render_to_response('sys_org_admin.html', {
'orgs': orgs,
}, context_instance=RequestContext(request))
@login_required
@sys_staff_required
def org_remove(request, org_id):
try:
org_id_int = int(org_id)
except ValueError:
return HttpResponseRedirect(reverse('sys_org_admin'))
# Remove repos in that org
seafserv_threaded_rpc.remove_org_repo_by_org_id(org_id_int)
# TODO: Remove repos in org's groups
ccnet_threaded_rpc.remove_org(org_id_int)
return HttpResponseRedirect(reverse('sys_org_admin'))
@login_required
def org_info(request):
if not request.user.org:
raise Http404
org = request.user.org
org_members = ccnet_threaded_rpc.get_org_emailusers(org.url_prefix, 0, MAX_INT)
for member in org_members:
member.short_username = member.email.split('@')[0]
groups = ccnet_threaded_rpc.get_org_groups(org.org_id, 0, MAX_INT)
return render_to_response('org_info.html', {
'org': org,
'org_users': org_members,
'groups': groups,
}, context_instance=RequestContext(request))
@login_required
def file_upload_progress_page(request):
'''
As iframe in repo_upload_file.html, for solving problem in chrome.
'''
uuid = request.GET.get('uuid', '')
httpserver_root = get_httpserver_root()
return render_to_response('file_upload_progress_page.html', {
'uuid': uuid,
'httpserver_root': httpserver_root,
}, context_instance=RequestContext(request))
@login_required
def repo_new_dir(request):
form = RepoNewDirForm(request.POST)
if form.is_valid():
repo_id = form.cleaned_data["repo_id"]
parent_dir = form.cleaned_data["parent_dir"]
new_dir_name = form.cleaned_data["new_dir_name"]
user = request.user.username
else:
return render_error(request, form.errors.values()[0])
new_dir_name = check_filename_with_rename(repo_id, parent_dir, new_dir_name)
try:
seafserv_threaded_rpc.post_dir(repo_id, parent_dir, new_dir_name, user)
except Exception, e:
return render_error(request, str(e))
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
return HttpResponseRedirect(url)
@login_required
def repo_new_file(request):
form = RepoNewFileForm(request.POST)
if form.is_valid():
repo_id = form.cleaned_data["repo_id"]
parent_dir = form.cleaned_data["parent_dir"]
new_file_name = form.cleaned_data["new_file_name"]
user = request.user.username
else:
return render_error(request, form.errors.values()[0])
new_file_name = check_filename_with_rename(repo_id, parent_dir, new_file_name)
try:
seafserv_threaded_rpc.post_empty_file(repo_id, parent_dir, new_file_name, user)
except Exception, e:
return render_error(request, str(e))
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
return HttpResponseRedirect(url)
@login_required
def repo_rename_file(request):
repo_id = request.POST.get("repo_id")
parent_dir = request.POST.get("parent_dir")
oldname = request.POST.get("oldname")
newname = request.POST.get("newname")
user = request.user.username
if not newname:
error_msg = u"新文件名不能为空"
return render_error(request, error_msg)
if newname == oldname:
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
return HttpResponseRedirect(url)
newname = check_filename_with_rename(repo_id, parent_dir, newname)
if len(newname) > settings.MAX_UPLOAD_FILE_NAME_LEN:
error_msg = u"新文件名太长"
return render_error(request, error_msg)
if not (repo_id and parent_dir and oldname):
return render_error(request)
try:
seafserv_threaded_rpc.rename_file (repo_id, parent_dir,
oldname, newname, user)
messages.add_message(request, messages.INFO, u'%s 已重命名为 %s' % (oldname, newname))
except Exception, e:
return render_error(request, str(e))
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
return HttpResponseRedirect(url)
@login_required
def validate_filename(request):
repo_id = request.GET.get('repo_id')
filename = request.GET.get('filename')
if not (repo_id and filename):
return render_error(request)
result = {'ret':'yes'}
try:
ret = is_valid_filename(filename);
except SearpcError:
result['ret'] = 'error'
else:
result['ret'] = 'yes' if ret == 1 else 'no'
content_type = 'application/json; charset=utf-8'
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required
def repo_create(request):
'''
Handle ajax post.
'''
if not request.is_ajax() or request.method != 'POST':
return Http404
result = {}
content_type = 'application/json; charset=utf-8'
form = RepoCreateForm(request.POST)
if form.is_valid():
repo_name = form.cleaned_data['repo_name']
repo_desc = form.cleaned_data['repo_desc']
passwd = form.cleaned_data['passwd']
user = request.user.username
try:
repo_id = seafserv_threaded_rpc.create_repo(repo_name, repo_desc,
user, passwd)
except:
repo_id = None
if not repo_id:
result['error'] = u"创建目录失败"
else:
result['success'] = True
return HttpResponse(json.dumps(result), content_type=content_type)
else:
return HttpResponseBadRequest(json.dumps(form.errors),
content_type=content_type)
def render_file_revisions (request, repo_id):
"""List all history versions of a file."""
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
u_filename = os.path.basename(path)
filename = urllib2.quote(u_filename.encode('utf-8'))
if not path:
return render_error(request)
repo = get_repo(repo_id)
if not repo:
error_msg = u"同步目录不存在"
return render_error(request, error_msg)
try:
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, path)
except SearpcError, e:
return render_error(request, e.msg)
if not commits:
return render_error(request)
# Check whether use is repo owner
if validate_owner(request, repo_id):
is_owner = True
else:
is_owner = False
try:
current_commit = get_commits(repo_id, 0, 1)[0]
current_file_id = get_file_revision_id_size (current_commit.id, path)[0]
for commit in commits:
file_id, file_size = get_file_revision_id_size (commit.id, path)
if not file_id or file_size is None:
# do not use no file_size, since it's ok to have file_size = 0
return render_error(request)
commit.revision_file_size = file_size
if file_id == current_file_id:
commit.is_current_version = True
else:
commit.is_current_version = False
except Exception, e:
return render_error(request, str(e))
zipped = gen_path_link(path, repo.name)
return render_to_response('file_revisions.html', {
'repo': repo,
'path': path,
'u_filename': u_filename,
'zipped': zipped,
'commits': commits,
'is_owner': is_owner,
}, context_instance=RequestContext(request))
@login_required
def repo_revert_file (request, repo_id):
commit_id = request.GET.get('commit')
path = request.GET.get('p')
if not (commit_id and path):
return render_error(request, u"参数错误")
try:
ret = seafserv_threaded_rpc.revert_file (repo_id, commit_id,
path.encode('utf-8'), request.user.username)
except Exception, e:
return render_error(request, str(e))
else:
url = reverse('repo', args=[repo_id]) + u'?commit_id=%s&history=y' % commit_id
file_view_url = reverse('repo_view_file', args=[repo_id]) + u'?p=' + urllib2.quote(path.encode('utf-8'))
if ret == 1:
msg = u'<a href="%s">%s</a> 已还原到根目录下' % (file_view_url, path.lstrip('/'))
messages.add_message(request, messages.INFO, msg)
else:
msg = u'<a href="%s">%s</a> 已经还原' % (file_view_url, path.lstrip('/'))
messages.add_message(request, messages.INFO, msg)
return HttpResponseRedirect(url)
@login_required
@ctx_switch_required
def file_revisions(request, repo_id):
if request.method != 'GET':
return render_error(request)
op = request.GET.get('op')
if not op:
return render_file_revisions(request, repo_id)
elif op != 'download' and op != 'view':
return render_error(request)
commit_id = request.GET.get('commit')
path = request.GET.get('p')
if not (commit_id and path):
return render_error(request)
if op == 'download':
def handle_download():
parent_dir = os.path.dirname(path)
file_name = os.path.basename(path)
seafdir = seafserv_threaded_rpc.list_dir_by_path (commit_id, \
parent_dir.encode('utf-8'))
if not seafdir:
return render_error(request)
# for ... else ...
for dirent in seafdir:
if dirent.obj_name == file_name:
break
else:
return render_error(request)
url = reverse('repo_access_file', args=[repo_id, dirent.obj_id])
url += '?file_name=%s&op=download' % urllib2.quote(file_name.encode('utf-8'))
return HttpResponseRedirect(url)
try:
return handle_download()
except Exception, e:
return render_error(request, str(e))
elif op == 'view':
seafile_id = get_file_revision_id_size (commit_id, path)[0]
if not seafile_id:
return render_error(request)
file_name = os.path.basename(path)
url = reverse(repo_view_file, args=[repo_id])
url += '?obj_id=%s&commit_id=%s&p=%s' % (seafile_id, commit_id, path)
return HttpResponseRedirect(url)
@login_required
def get_shared_link(request):
"""
Handle ajax request to generate file shared link.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
repo_id = request.GET.get('repo_id')
obj_id = request.GET.get('obj_id')
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
l = FileShare.objects.filter(repo_id=repo_id).filter(\
username=request.user.username).filter(path=path)
if len(l) > 0:
fileshare = l[0]
token = fileshare.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
try:
fs.save()
except IntegrityError, e:
err = '获取分享链接失败,请重新获取'
data = json.dumps([{'error': err}])
return HttpResponse(data, status=500, content_type=content_type)
data = json.dumps([{'token': token}])
return HttpResponse(data, status=200, content_type=content_type)
def view_shared_file(request, token):
"""
Preview file via shared link.
"""
assert token is not None # Checked by URLconf
try:
fileshare = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
raise Http404
username = fileshare.username
repo_id = fileshare.repo_id
path = fileshare.path
http_server_root = get_httpserver_root()
if path[-1] == '/':
path = path[:-1]
filename = os.path.basename(path)
quote_filename = urllib2.quote(filename.encode('utf-8'))
try:
obj_id = seafserv_threaded_rpc.get_file_by_path(repo_id, path)
except:
obj_id = None
if not obj_id:
return render_error(request, '文件不存在')
repo = get_repo(repo_id)
if not repo:
raise Http404
access_token = gen_token()
seafserv_rpc.web_save_access_token(access_token, repo.id, obj_id,
'view', '')
filetype, fileext = valid_previewed_file(filename)
# Raw path
raw_path = gen_file_get_url(access_token, quote_filename)
# get file content
err = ''
file_content = ''
if filetype == 'Text' or filetype == 'Markdown':
err, file_content, encoding, newline_mode = repo_file_get(raw_path)
# Increase file shared link view_cnt, this operation should be atomic
fileshare = FileShare.objects.get(token=token)
fileshare.view_cnt = F('view_cnt') + 1
fileshare.save()
return render_to_response('view_shared_file.html', {
'repo': repo,
'obj_id': obj_id,
'path': path,
'file_name': filename,
'shared_token': token,
'access_token': access_token,
'filetype': filetype,
'fileext': fileext,
'raw_path': raw_path,
'username': username,
'err': err,
'file_content': file_content,
}, context_instance=RequestContext(request))
@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()
return HttpResponseRedirect(reverse('share_admin'))
content_type = 'application/json; charset=utf-8'
FileShare.objects.filter(token=token).delete()
msg = '删除成功'
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 share file shared link.
"""
if not request.is_ajax() and not request.method == 'POST':
raise Http404
content_type = 'application/json; charset=utf-8'
form = FileLinkShareForm(request.POST)
if not form.is_valid():
err = '发送失败'
data = json.dumps([{'error':err}])
return HttpResponse(data, status=400, content_type=content_type)
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,
}
try:
send_mail('您的好友通过SeaCloud分享了一个文件给您',
t.render(Context(c)), None, [to_email],
fail_silently=False)
except:
err = '发送失败'
data = json.dumps([{'error':err}])
return HttpResponse(data, status=500, content_type=content_type)
msg = '发送成功。'
data = json.dumps([{'msg': msg}])
return HttpResponse(data, status=200, content_type=content_type)
def crocodoc_upload(request):
"""
Handle ajax request to upload document to crocodoc.com.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
raw_path = request.GET.get('raw_path', '')
# Fetch obj_id according token, if this obj_id already has uuid,
# send uuid; else, upload file.
obj_id = seafserv_rpc.web_query_access_token(raw_path.split('/files/')[1][:5]).obj_id
if not obj_id:
# Should nerver reach here.
data = json.dumps([{'error': '缺少obj_id'}])
return HttpResponse(data, status=500, content_type=content_type)
try:
uo = UuidObjidMap.objects.get(obj_id=obj_id)
except UuidObjidMap.DoesNotExist:
uo = None
if uo:
data = json.dumps([{'uuid': uo.uuid, 'obj_id': obj_id}])
return HttpResponse(data, status=200, content_type=content_type)
curl = "https://crocodoc.com/api/v2/document/upload"
data = {'token': CROCODOC_API_TOKEN,
'url': raw_path}
try:
f = urllib2.urlopen(url=curl, data=urllib.urlencode(data))
except urllib2.URLError, e:
data = json.dumps([{'error': e.msg}])
return HttpResponse(data, status=500, content_type=content_type)
else:
ret = f.read()
ret_dict = json.loads(ret)
if ret_dict.has_key('error'):
data = json.dumps([{'error': ret_dict['error']}])
return HttpResponse(data, status=500, content_type=content_type)
else:
data = json.dumps([{'uuid': ret_dict['uuid'], 'obj_id': obj_id}])
return HttpResponse(data, status=200, content_type=content_type)
def crocodoc_status(request):
"""
Handle ajax request to get status of the document from crocodoc.com.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
uuids = request.GET.get('uuids', '')
obj_id = request.GET.get('obj_id', '')
curl = 'https://crocodoc.com/api/v2/document/status?token=%s&uuids=%s' % (
CROCODOC_API_TOKEN, uuids
)
f = urllib2.urlopen(url=curl)
ret = f.read()
ret_list = json.loads(ret)
ret_dict = ret_list[0]
if ret_dict.has_key('error'):
# Delete obj_id-uuid in db
UuidObjidMap.objects.filter(obj_id=obj_id).delete()
data = json.dumps([{'error': '文档转换出错:' + ret_dict['error']}])
return HttpResponse(data, status=500, content_type=content_type)
viewable = ret_dict['viewable'] # TODO: this may used in large file preview
status = ret_dict['status']
if status == 'QUEUED':
data = json.dumps([{'status': status}])
return HttpResponse(data, status=200, content_type=content_type)
elif status == 'PROCESSING':
data = json.dumps([{'status': status}])
return HttpResponse(data, status=200, content_type=content_type)
elif status == 'DONE':
# Cache obj_id and uuid in db
uo = UuidObjidMap(uuid=uuids, obj_id=obj_id)
try:
uo.save()
except IntegrityError, e:
pass
data = json.dumps([{'status': status}])
return HttpResponse(data, status=200, content_type=content_type)
elif status == 'ERROR':
# Delete obj_id-uuid in db
UuidObjidMap.objects.filter(obj_id=obj_id).delete()
err_msg = '文档转换出错:' + ret_dict['error'] if ret_dict.has_key('error') \
else '文档转换出错'
data = json.dumps([{'error': err_msg}])
return HttpResponse(data, status=500, content_type=content_type)
def crocodoc_session(request):
"""
Handle ajax reqeust to create session.
Session expires 60 minutes after it's generated.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
uuid = request.GET.get('uuid', '')
curl = 'https://crocodoc.com/api/v2/session/create'
data = {'token': CROCODOC_API_TOKEN,
'uuid': uuid,
'editable': 'true',
'user': '1337,备注',
'downloadable': 'true',
}
f = urllib2.urlopen(url=curl, data=urllib.urlencode(data))
ret = f.read()
ret_dict = json.loads(ret)
session = ret_dict['session']
doc_src = 'https://crocodoc.com/view/%s' % session
data = json.dumps([{'doc_src': doc_src}])
return HttpResponse(data, status=200, content_type=content_type)