1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-31 22:54:11 +00:00
Files
seahub/seahub/views/ajax.py
2016-09-19 20:42:47 +08:00

2174 lines
79 KiB
Python

# Copyright (c) 2012-2016 Seafile Ltd.
# -*- coding: utf-8 -*-
import os
import stat
import logging
import json
import posixpath
import csv
import chardet
import StringIO
from django.core.urlresolvers import reverse
from django.http import HttpResponse, Http404, HttpResponseBadRequest
from django.template import RequestContext
from django.template.loader import render_to_string
from django.utils.http import urlquote
from django.utils.html import escape
from django.utils.translation import ugettext as _
from django.contrib import messages
from django.conf import settings as dj_settings
from django.template.defaultfilters import filesizeformat
import seaserv
from seaserv import seafile_api, is_passwd_set, \
get_related_users_by_repo, get_related_users_by_org_repo, \
seafserv_threaded_rpc, ccnet_threaded_rpc, edit_repo
from pysearpc import SearpcError
from seahub.auth.decorators import login_required_ajax
from seahub.base.decorators import require_POST
from seahub.contacts.models import Contact
from seahub.forms import RepoNewDirentForm, RepoRenameDirentForm, \
RepoCreateForm, SharedRepoCreateForm, RepoSettingForm
from seahub.options.models import UserOptions, CryptoOptionNotSetError
from seahub.notifications.models import UserNotification
from seahub.notifications.views import add_notice_from_info
from seahub.message.models import UserMessage
from seahub.share.models import UploadLinkShare
from seahub.group.models import PublicGroup
from seahub.signals import upload_file_successful, repo_created, repo_deleted
from seahub.views import validate_owner, \
get_unencry_rw_repos_by_user, is_registered_user, \
get_system_default_repo_id, get_diff, \
get_owned_repo_list, check_folder_permission, is_registered_user
from seahub.views.modules import get_enabled_mods_by_group, \
get_available_mods_by_group, enable_mod_for_group, \
disable_mod_for_group, MOD_GROUP_WIKI, MOD_PERSONAL_WIKI, \
enable_mod_for_user, disable_mod_for_user
from seahub.group.views import is_group_staff
from seahub.group.utils import is_group_member, is_group_admin_or_owner, \
get_group_member_info
import seahub.settings as settings
from seahub.settings import ENABLE_THUMBNAIL, THUMBNAIL_ROOT, \
THUMBNAIL_DEFAULT_SIZE, ENABLE_SUB_LIBRARY, \
ENABLE_FOLDER_PERM, SHOW_TRAFFIC, MEDIA_URL
from constance import config
from seahub.utils import check_filename_with_rename, EMPTY_SHA1, \
gen_block_get_url, TRAFFIC_STATS_ENABLED, get_user_traffic_stat,\
new_merge_with_no_conflict, get_commit_before_new_merge, \
get_repo_last_modify, gen_file_upload_url, is_org_context, \
get_org_user_events, get_user_events, get_file_type_and_ext, \
is_valid_username, send_perm_audit_msg, get_origin_repo_info, is_pro_version
from seahub.utils.repo import get_sub_repo_abbrev_origin_path
from seahub.utils.star import star_file, unstar_file, get_dir_starred_files
from seahub.base.accounts import User
from seahub.thumbnail.utils import get_thumbnail_src
from seahub.utils.file_types import IMAGE
from seahub.base.templatetags.seahub_tags import translate_seahub_time, \
file_icon_filter, email2nickname, tsstr_sec
from seahub.avatar.templatetags.group_avatar_tags import grp_avatar
# Get an instance of a logger
logger = logging.getLogger(__name__)
########## Seafile API Wrapper
def get_repo(repo_id):
return seafile_api.get_repo(repo_id)
def get_commit(repo_id, repo_version, commit_id):
return seaserv.get_commit(repo_id, repo_version, commit_id)
def get_group(gid):
return seaserv.get_group(gid)
def is_group_user(gid, username):
return seaserv.is_group_user(gid, username)
########## repo related
@login_required_ajax
def get_dirents(request, repo_id):
"""
Get dirents in a dir for file tree
"""
content_type = 'application/json; charset=utf-8'
# permission checking
user_perm = check_folder_permission(request, repo_id, '/')
if user_perm is None:
err_msg = _(u"You don't have permission to access the library.")
return HttpResponse(json.dumps({"err_msg": err_msg}), status=403,
content_type=content_type)
path = request.GET.get('path', '')
dir_only = request.GET.get('dir_only', False)
all_dir = request.GET.get('all_dir', False)
if not path:
err_msg = _(u"No path.")
return HttpResponse(json.dumps({"error": err_msg}), status=400,
content_type=content_type)
# get dirents for every path element
if all_dir:
all_dirents = []
path_eles = path.split('/')[:-1]
for i, x in enumerate(path_eles):
ele_path = '/'.join(path_eles[:i+1]) + '/'
try:
ele_path_dirents = seafile_api.list_dir_by_path(repo_id, ele_path.encode('utf-8'))
except SearpcError, e:
ele_path_dirents = []
ds = []
for d in ele_path_dirents:
if stat.S_ISDIR(d.mode):
ds.append(d.obj_name)
ds.sort(lambda x, y : cmp(x.lower(), y.lower()))
all_dirents.append(ds)
return HttpResponse(json.dumps(all_dirents), content_type=content_type)
# get dirents in path
try:
dirents = seafile_api.list_dir_by_path(repo_id, path.encode('utf-8'))
except SearpcError, e:
return HttpResponse(json.dumps({"error": e.msg}), status=500,
content_type=content_type)
d_list = []
f_list = []
for dirent in dirents:
if stat.S_ISDIR(dirent.mode):
dirent.has_subdir = False
if dir_only:
dirent_path = posixpath.join(path, dirent.obj_name)
try:
dirent_dirents = seafile_api.list_dir_by_path(repo_id, dirent_path.encode('utf-8'))
except SearpcError, e:
dirent_dirents = []
for dirent_dirent in dirent_dirents:
if stat.S_ISDIR(dirent_dirent.props.mode):
dirent.has_subdir = True
break
subdir = {
'name': dirent.obj_name,
'id': dirent.obj_id,
'type': 'dir',
'has_subdir': dirent.has_subdir, # to decide node 'state' ('closed' or not) in jstree
}
d_list.append(subdir)
else:
if not dir_only:
f = {
'id': dirent.obj_id,
'name': dirent.obj_name,
'type': 'file',
}
f_list.append(f)
d_list.sort(lambda x, y : cmp(x['name'].lower(), y['name'].lower()))
f_list.sort(lambda x, y : cmp(x['name'].lower(), y['name'].lower()))
return HttpResponse(json.dumps(d_list + f_list), content_type=content_type)
@login_required_ajax
def get_unenc_group_repos(request, group_id):
'''
Get unenc repos in a group.
Used in selecting a library for a group wiki
'''
content_type = 'application/json; charset=utf-8'
group_id_int = int(group_id)
group = get_group(group_id_int)
if not group:
err_msg = _(u"The group doesn't exist")
return HttpResponse(json.dumps({"error": err_msg}), status=400,
content_type=content_type)
joined = is_group_user(group_id_int, request.user.username)
if not joined and not request.user.is_staff:
err_msg = _(u"Permission denied")
return HttpResponse(json.dumps({"error": err_msg}), status=403,
content_type=content_type)
repo_list = []
if is_org_context(request):
org_id = request.user.org.org_id
repos = seafile_api.get_org_group_repos(org_id, group_id_int)
for repo in repos:
if not repo.encrypted:
repo_list.append({"name": repo.repo_name, "id": repo.repo_id})
else:
repos = seafile_api.get_repos_by_group(group_id_int)
for repo in repos:
if not repo.encrypted:
repo_list.append({"name": repo.name, "id": repo.id})
repo_list.sort(lambda x, y : cmp(x['name'].lower(), y['name'].lower()))
return HttpResponse(json.dumps(repo_list), content_type=content_type)
@login_required_ajax
def get_my_unenc_repos(request):
"""Get my owned and unencrypted repos.
"""
content_type = 'application/json; charset=utf-8'
repos = get_owned_repo_list(request)
repo_list = []
for repo in repos:
if repo.encrypted or repo.is_virtual:
continue
repo_list.append({"name": repo.name, "id": repo.id})
repo_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
return HttpResponse(json.dumps(repo_list), content_type=content_type)
@login_required_ajax
def unenc_rw_repos(request):
"""Get a user's unencrypt repos that he/she can read-write.
Arguments:
- `request`:
"""
content_type = 'application/json; charset=utf-8'
acc_repos = get_unencry_rw_repos_by_user(request)
repo_list = []
acc_repos = filter(lambda r: not r.is_virtual, acc_repos)
for repo in acc_repos:
repo_list.append({"name": repo.name, "id": repo.id})
repo_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
return HttpResponse(json.dumps(repo_list), content_type=content_type)
@login_required_ajax
def list_lib_dir(request, repo_id):
'''
New ajax API for list library directory
'''
content_type = 'application/json; charset=utf-8'
result = {}
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
username = request.user.username
path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
# perm for current dir
user_perm = check_folder_permission(request, repo.id, path)
if user_perm is None:
err_msg = _(u'Permission denied.')
return HttpResponse(json.dumps({'error': err_msg}),
status=403, content_type=content_type)
if repo.encrypted \
and not seafile_api.is_password_set(repo.id, username):
err_msg = _(u'Library is encrypted.')
return HttpResponse(json.dumps({'error': err_msg, 'lib_need_decrypt': True}),
status=403, content_type=content_type)
head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id)
if not head_commit:
err_msg = _(u'Error: no head commit id')
return HttpResponse(json.dumps({'error': err_msg}),
status=500, content_type=content_type)
dir_list = []
file_list = []
try:
dir_id = seafile_api.get_dir_id_by_path(repo.id, path)
except SearpcError as e:
logger.error(e)
err_msg = 'Internal Server Error'
return HttpResponse(json.dumps({'error': err_msg}),
status=500, content_type=content_type)
if not dir_id:
err_msg = 'Folder not found.'
return HttpResponse(json.dumps({'error': err_msg}),
status=404, content_type=content_type)
dirs = seafserv_threaded_rpc.list_dir_with_perm(repo_id, path, dir_id,
username, -1, -1)
starred_files = get_dir_starred_files(username, repo_id, path)
for dirent in dirs:
dirent.last_modified = dirent.mtime
if stat.S_ISDIR(dirent.mode):
dpath = posixpath.join(path, dirent.obj_name)
if dpath[-1] != '/':
dpath += '/'
dir_list.append(dirent)
else:
if repo.version == 0:
file_size = seafile_api.get_file_size(repo.store_id, repo.version, dirent.obj_id)
else:
file_size = dirent.size
dirent.file_size = file_size if file_size else 0
dirent.starred = False
fpath = posixpath.join(path, dirent.obj_name)
if fpath in starred_files:
dirent.starred = True
file_list.append(dirent)
if is_org_context(request):
repo_owner = seafile_api.get_org_repo_owner(repo.id)
else:
repo_owner = seafile_api.get_repo_owner(repo.id)
result["is_repo_owner"] = True if repo_owner == username else False
result["is_virtual"] = repo.is_virtual
result["repo_name"] = repo.name
result["user_perm"] = user_perm
# check quota for fileupload
result["no_quota"] = True if seaserv.check_quota(repo.id) < 0 else False
result["encrypted"] = repo.encrypted
dirent_list = []
for d in dir_list:
d_ = {}
d_['is_dir'] = True
d_['obj_name'] = d.obj_name
d_['last_modified'] = d.last_modified
d_['last_update'] = translate_seahub_time(d.last_modified)
d_['p_dpath'] = posixpath.join(path, d.obj_name)
d_['perm'] = d.permission # perm for sub dir in current dir
dirent_list.append(d_)
size = int(request.GET.get('thumbnail_size', THUMBNAIL_DEFAULT_SIZE))
for f in file_list:
f_ = {}
f_['is_file'] = True
f_['file_icon'] = file_icon_filter(f.obj_name)
f_['obj_name'] = f.obj_name
f_['last_modified'] = f.last_modified
f_['last_update'] = translate_seahub_time(f.last_modified)
f_['starred'] = f.starred
f_['file_size'] = filesizeformat(f.file_size)
f_['obj_id'] = f.obj_id
f_['perm'] = f.permission # perm for file in current dir
file_type, file_ext = get_file_type_and_ext(f.obj_name)
if file_type == IMAGE:
f_['is_img'] = True
if not repo.encrypted and ENABLE_THUMBNAIL and \
os.path.exists(os.path.join(THUMBNAIL_ROOT, str(size), f.obj_id)):
file_path = posixpath.join(path, f.obj_name)
src = get_thumbnail_src(repo_id, size, file_path)
f_['encoded_thumbnail_src'] = urlquote(src)
if is_pro_version():
f_['is_locked'] = True if f.is_locked else False
f_['lock_owner'] = f.lock_owner
f_['lock_owner_name'] = email2nickname(f.lock_owner)
if username == f.lock_owner:
f_['locked_by_me'] = True
else:
f_['locked_by_me'] = False
dirent_list.append(f_)
result["dirent_list"] = dirent_list
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
def rename_dirent(request, repo_id):
"""
Rename a file/dir in a repo, with ajax
"""
if request.method != 'POST':
raise Http404
result = {}
username = request.user.username
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
result['error'] = _(u'Library does not exist.')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# argument checking
parent_dir = request.GET.get('parent_dir', None)
if not parent_dir:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# form validation
form = RepoRenameDirentForm(request.POST)
if form.is_valid():
oldname = form.cleaned_data["oldname"]
newname = form.cleaned_data["newname"]
else:
result['error'] = str(form.errors.values()[0])
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
full_path = posixpath.join(parent_dir, oldname)
if seafile_api.get_dir_id_by_path(repo.id, full_path) is not None:
# when dirent is a dir, check current dir perm
if check_folder_permission(request, repo.id, full_path) != 'rw':
err_msg = _('Permission denied')
return HttpResponse(json.dumps({'error': err_msg}), status=403,
content_type=content_type)
if seafile_api.get_file_id_by_path(repo.id, full_path) is not None:
# when dirent is a file, check parent dir perm
if check_folder_permission(request, repo.id, parent_dir) != 'rw':
err_msg = _('Permission denied')
return HttpResponse(json.dumps({'error': err_msg}), status=403,
content_type=content_type)
if newname == oldname:
return HttpResponse(json.dumps({'success': True}),
content_type=content_type)
# rename duplicate name
newname = check_filename_with_rename(repo_id, parent_dir, newname)
# rename file/dir
try:
seafile_api.rename_file(repo_id, parent_dir, oldname, newname, username)
except SearpcError, e:
result['error'] = str(e)
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
return HttpResponse(json.dumps({'success': True, 'newname': newname}),
content_type=content_type)
@login_required_ajax
@require_POST
def delete_dirent(request, repo_id):
"""
Delete a file/dir with ajax.
"""
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
# argument checking
parent_dir = request.GET.get("parent_dir", None)
dirent_name = request.GET.get("name", None)
if not (parent_dir and dirent_name):
err_msg = _(u'Argument missing.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
full_path = posixpath.join(parent_dir, dirent_name)
username = request.user.username
if seafile_api.get_dir_id_by_path(repo.id, full_path) is not None:
# when dirent is a dir, check current dir perm
if check_folder_permission(request, repo.id, full_path) != 'rw':
err_msg = _('Permission denied')
return HttpResponse(json.dumps({'error': err_msg}), status=403,
content_type=content_type)
if seafile_api.get_file_id_by_path(repo.id, full_path) is not None:
# when dirent is a file, check parent dir perm
if check_folder_permission(request, repo.id, parent_dir) != 'rw':
err_msg = _('Permission denied')
return HttpResponse(json.dumps({'error': err_msg}), status=403,
content_type=content_type)
# delete file/dir
try:
seafile_api.del_file(repo_id, parent_dir, dirent_name, username)
return HttpResponse(json.dumps({'success': True}),
content_type=content_type)
except SearpcError, e:
logger.error(e)
err_msg = _(u'Internal error. Failed to delete %s.') % escape(dirent_name)
return HttpResponse(json.dumps({'error': err_msg}),
status=500, content_type=content_type)
@login_required_ajax
@require_POST
def delete_dirents(request, repo_id):
"""
Delete multi files/dirs with ajax.
"""
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
# argument checking
parent_dir = request.GET.get("parent_dir")
dirents_names = request.POST.getlist('dirents_names')
if not (parent_dir and dirents_names):
err_msg = _(u'Argument missing.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
# permission checking
username = request.user.username
deleted = []
undeleted = []
for dirent_name in dirents_names:
full_path = posixpath.join(parent_dir, dirent_name)
if check_folder_permission(request, repo.id, full_path) != 'rw':
undeleted.append(dirent_name)
continue
try:
seafile_api.del_file(repo_id, parent_dir, dirent_name, username)
deleted.append(dirent_name)
except SearpcError, e:
logger.error(e)
undeleted.append(dirent_name)
return HttpResponse(json.dumps({'deleted': deleted, 'undeleted': undeleted}),
content_type=content_type)
def copy_move_common():
"""Decorator for common logic in copying/moving dir/file.
"""
def _method_wrapper(view_method):
def _arguments_wrapper(request, repo_id, *args, **kwargs):
if request.method != 'POST':
raise Http404
result = {}
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
result['error'] = _(u'Library does not exist.')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# arguments validation
path = request.GET.get('path')
obj_name = request.GET.get('obj_name')
dst_repo_id = request.POST.get('dst_repo')
dst_path = request.POST.get('dst_path')
if not (path and obj_name and dst_repo_id and dst_path):
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# check file path
if len(dst_path + obj_name) > settings.MAX_PATH:
result['error'] = _('Destination path is too long.')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# return error when dst is the same as src
if repo_id == dst_repo_id and path == dst_path:
result['error'] = _('Invalid destination path')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# check whether user has write permission to dest repo
if check_folder_permission(request, dst_repo_id, dst_path) != 'rw':
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
# Leave src folder/file permission checking to corresponding
# views.
# For 'move', check has read-write perm to src folder;
# For 'cp', check has read perm to src folder.
return view_method(request, repo_id, path, dst_repo_id, dst_path,
obj_name)
return _arguments_wrapper
return _method_wrapper
@login_required_ajax
@copy_move_common()
def mv_file(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name):
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
# check parent dir perm
if check_folder_permission(request, src_repo_id, src_path) != 'rw':
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
res = seafile_api.move_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
replace=False, username=username, need_progress=1)
except SearpcError as e:
logger.error(e)
res = None
# res can be None or an object
if not res:
result['error'] = _(u'Internal server error')
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
result['success'] = True
msg = _(u'Successfully moved %(name)s') % {"name": escape(obj_name)}
result['msg'] = msg
if res.background:
result['task_id'] = res.task_id
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
@copy_move_common()
def cp_file(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name):
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
# check parent dir perm
if not check_folder_permission(request, src_repo_id, src_path):
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
res = seafile_api.copy_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
username, need_progress=1)
except SearpcError as e:
res = None
if not res:
result['error'] = _(u'Internal server error')
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
result['success'] = True
msg = _(u'Successfully copied %(name)s') % {"name": escape(obj_name)}
result['msg'] = msg
if res.background:
result['task_id'] = res.task_id
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
@copy_move_common()
def mv_dir(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name):
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
src_dir = posixpath.join(src_path, obj_name)
if dst_path.startswith(src_dir + '/'):
error_msg = _(u'Can not move directory %(src)s to its subdirectory %(des)s') \
% {'src': escape(src_dir), 'des': escape(dst_path)}
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
# check dir perm
if check_folder_permission(request, src_repo_id, src_dir) != 'rw':
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
res = seafile_api.move_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
replace=False, username=username, need_progress=1)
except SearpcError as e:
logger.error(e)
res = None
# res can be None or an object
if not res:
result['error'] = _(u'Internal server error')
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
result['success'] = True
msg = _(u'Successfully moved %(name)s') % {"name": escape(obj_name)}
result['msg'] = msg
if res.background:
result['task_id'] = res.task_id
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
@copy_move_common()
def cp_dir(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name):
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
# check src dir perm
if not check_folder_permission(request, src_repo_id, src_path):
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
src_dir = posixpath.join(src_path, obj_name)
if dst_path.startswith(src_dir):
error_msg = _(u'Can not copy directory %(src)s to its subdirectory %(des)s') \
% {'src': escape(src_dir), 'des': escape(dst_path)}
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
res = seafile_api.copy_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
username, need_progress=1)
except SearpcError, e:
res = None
# res can be None or an object
if not res:
result['error'] = _(u'Internal server error')
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
result['success'] = True
msg = _(u'Successfully copied %(name)s') % {"name": escape(obj_name)}
result['msg'] = msg
if res.background:
result['task_id'] = res.task_id
return HttpResponse(json.dumps(result), content_type=content_type)
def dirents_copy_move_common():
"""
Decorator for common logic in copying/moving dirs/files in batch.
"""
def _method_wrapper(view_method):
def _arguments_wrapper(request, repo_id, *args, **kwargs):
if request.method != 'POST':
raise Http404
result = {}
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
result['error'] = _(u'Library does not exist.')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# arguments validation
parent_dir = request.GET.get('parent_dir')
obj_file_names = request.POST.getlist('file_names')
obj_dir_names = request.POST.getlist('dir_names')
dst_repo_id = request.POST.get('dst_repo')
dst_path = request.POST.get('dst_path')
if not (parent_dir and dst_repo_id and dst_path) and \
not (obj_file_names or obj_dir_names):
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# check file path
for obj_name in obj_file_names + obj_dir_names:
if len(dst_path+obj_name) > settings.MAX_PATH:
result['error'] = _('Destination path is too long for %s.') % escape(obj_name)
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# when dst is the same as src
if repo_id == dst_repo_id and parent_dir == dst_path:
result['error'] = _('Invalid destination path')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# check whether user has write permission to dest repo
if check_folder_permission(request, dst_repo_id, dst_path) != 'rw':
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
# Leave src folder/file permission checking to corresponding
# views, only need to check folder permission when perform 'move'
# operation, 1), if move file, check parent dir perm, 2), if move
# folder, check that folder perm.
return view_method(request, repo_id, parent_dir, dst_repo_id,
dst_path, obj_file_names, obj_dir_names)
return _arguments_wrapper
return _method_wrapper
@login_required_ajax
@dirents_copy_move_common()
def mv_dirents(request, src_repo_id, src_path, dst_repo_id, dst_path,
obj_file_names, obj_dir_names):
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
failed = []
allowed_files = []
allowed_dirs = []
# check parent dir perm for files
if check_folder_permission(request, src_repo_id, src_path) != 'rw':
allowed_files = []
failed += obj_file_names
else:
allowed_files = obj_file_names
for obj_name in obj_dir_names:
src_dir = posixpath.join(src_path, obj_name)
if dst_path.startswith(src_dir + '/'):
error_msg = _(u'Can not move directory %(src)s to its subdirectory %(des)s') \
% {'src': escape(src_dir), 'des': escape(dst_path)}
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
# check every folder perm
if check_folder_permission(request, src_repo_id, src_dir) != 'rw':
failed.append(obj_name)
else:
allowed_dirs.append(obj_name)
success = []
url = None
for obj_name in allowed_files + allowed_dirs:
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
res = seafile_api.move_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name,
replace=False, username=username, need_progress=1)
except SearpcError as e:
logger.error(e)
res = None
if not res:
failed.append(obj_name)
else:
success.append(obj_name)
if len(success) > 0:
url = reverse("view_common_lib_dir", args=[dst_repo_id, dst_path.strip('/')])
result = {'success': success, 'failed': failed, 'url': url}
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
@dirents_copy_move_common()
def cp_dirents(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_file_names, obj_dir_names):
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
if check_folder_permission(request, src_repo_id, src_path) is None:
error_msg = _(u'You do not have permission to copy files/folders in this directory')
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=403, content_type=content_type)
for obj_name in obj_dir_names:
src_dir = posixpath.join(src_path, obj_name)
if dst_path.startswith(src_dir):
error_msg = _(u'Can not copy directory %(src)s to its subdirectory %(des)s') \
% {'src': escape(src_dir), 'des': escape(dst_path)}
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
failed = []
success = []
url = None
for obj_name in obj_file_names + obj_dir_names:
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
res = seafile_api.copy_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name, username, need_progress=1)
except SearpcError as e:
logger.error(e)
res = None
if not res:
failed.append(obj_name)
else:
success.append(obj_name)
if len(success) > 0:
url = reverse("view_common_lib_dir", args=[dst_repo_id, dst_path.strip('/')])
result = {'success': success, 'failed': failed, 'url': url}
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
def get_cp_progress(request):
'''
Fetch progress of file/dir mv/cp.
'''
content_type = 'application/json; charset=utf-8'
result = {}
task_id = request.GET.get('task_id')
if not task_id:
result['error'] = _(u'Argument missing')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
res = seafile_api.get_copy_task(task_id)
# res can be None
if not res:
result['error'] = _(u'Error')
return HttpResponse(json.dumps(result), status=500, content_type=content_type)
result['done'] = res.done
result['total'] = res.total
result['canceled'] = res.canceled
result['failed'] = res.failed
result['successful'] = res.successful
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
def cancel_cp(request):
'''
cancel file/dir mv/cp.
'''
content_type = 'application/json; charset=utf-8'
result = {}
task_id = request.GET.get('task_id')
if not task_id:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
res = seafile_api.cancel_copy_task(task_id) # returns 0 or -1
if res == 0:
result['success'] = True
return HttpResponse(json.dumps(result), content_type=content_type)
else:
result['error'] = _('Cancel failed')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
########## contacts related
@login_required_ajax
def get_contacts(request):
content_type = 'application/json; charset=utf-8'
username = request.user.username
contacts = Contact.objects.get_contacts_by_user(username)
contact_list = []
from seahub.avatar.templatetags.avatar_tags import avatar
for c in contacts:
try:
user = User.objects.get(email=c.contact_email)
if user.is_active:
contact_list.append({
"email": c.contact_email,
"avatar": avatar(c.contact_email, 32),
"name": email2nickname(c.contact_email),
})
except User.DoesNotExist:
continue
return HttpResponse(json.dumps({"contacts":contact_list}), content_type=content_type)
@login_required_ajax
def get_current_commit(request, repo_id):
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
username = request.user.username
user_perm = check_folder_permission(request, repo_id, '/')
if user_perm is None:
err_msg = _(u'Permission denied.')
return HttpResponse(json.dumps({'error': err_msg}),
status=403, content_type=content_type)
try:
server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set.
server_crypto = False
if repo.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \
and not seafile_api.is_password_set(repo.id, username):
err_msg = _(u'Library is encrypted.')
return HttpResponse(json.dumps({'error': err_msg}),
status=403, content_type=content_type)
head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id)
if not head_commit:
err_msg = _(u'Error: no head commit id')
return HttpResponse(json.dumps({'error': err_msg}),
status=500, content_type=content_type)
if new_merge_with_no_conflict(head_commit):
info_commit = get_commit_before_new_merge(head_commit)
else:
info_commit = head_commit
ctx = {
'repo': repo,
'info_commit': info_commit
}
html = render_to_string('snippets/current_commit.html', ctx,
context_instance=RequestContext(request))
return HttpResponse(json.dumps({'html': html}),
content_type=content_type)
@login_required_ajax
def download_enc_file(request, repo_id, file_id):
content_type = 'application/json; charset=utf-8'
result = {}
op = 'downloadblks'
blklist = []
if file_id == EMPTY_SHA1:
result = { 'blklist':blklist, 'url':None, }
return HttpResponse(json.dumps(result), content_type=content_type)
try:
blks = seafile_api.list_blocks_by_file_id(repo_id, file_id)
except SearpcError as e:
logger.error(e)
result['error'] = _(u'Failed to get file block list')
return HttpResponse(json.dumps(result), content_type=content_type)
blklist = blks.split('\n')
blklist = [i for i in blklist if len(i) == 40]
token = seafile_api.get_fileserver_access_token(repo_id, file_id,
op, request.user.username)
url = gen_block_get_url(token, None)
result = {
'blklist':blklist,
'url':url,
}
return HttpResponse(json.dumps(result), content_type=content_type)
def upload_file_done(request):
"""Send a message when a file is uploaded.
Arguments:
- `request`:
"""
ct = 'application/json; charset=utf-8'
result = {}
filename = request.GET.get('fn', '')
if not filename:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400, content_type=ct)
repo_id = request.GET.get('repo_id', '')
if not repo_id:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400, content_type=ct)
path = request.GET.get('p', '')
if not path:
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400, content_type=ct)
# a few checkings
if not seafile_api.get_repo(repo_id):
result['error'] = _('Wrong repo id')
return HttpResponse(json.dumps(result), status=400, content_type=ct)
owner = seafile_api.get_repo_owner(repo_id)
if not owner: # this is an org repo, get org repo owner
owner = seafile_api.get_org_repo_owner(repo_id)
file_path = path.rstrip('/') + '/' + filename
if seafile_api.get_file_id_by_path(repo_id, file_path) is None:
result['error'] = _('File does not exist')
return HttpResponse(json.dumps(result), status=400, content_type=ct)
# send singal
upload_file_successful.send(sender=None,
repo_id=repo_id,
file_path=file_path,
owner=owner)
return HttpResponse(json.dumps({'success': True}), content_type=ct)
@login_required_ajax
def unseen_notices_count(request):
"""Count user's unseen notices.
Arguments:
- `request`:
"""
content_type = 'application/json; charset=utf-8'
username = request.user.username
count = UserNotification.objects.count_unseen_user_notifications(username)
result = {}
result['count'] = count
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
def get_popup_notices(request):
"""Get user's notifications.
If unseen notices > 5, return all unseen notices.
If unseen notices = 0, return last 5 notices.
Otherwise return all unseen notices, plus some seen notices to make the
sum equal to 5.
Arguments:
- `request`:
"""
content_type = 'application/json; charset=utf-8'
username = request.user.username
result_notices = []
unseen_notices = []
seen_notices = []
list_num = 5
unseen_num = UserNotification.objects.count_unseen_user_notifications(username)
if unseen_num == 0:
seen_notices = UserNotification.objects.get_user_notifications(
username)[:list_num]
elif unseen_num > list_num:
unseen_notices = UserNotification.objects.get_user_notifications(
username, seen=False)
else:
unseen_notices = UserNotification.objects.get_user_notifications(
username, seen=False)
seen_notices = UserNotification.objects.get_user_notifications(
username, seen=True)[:list_num - unseen_num]
result_notices += unseen_notices
result_notices += seen_notices
# Add 'msg_from' or 'default_avatar_url' to notice.
result_notices = add_notice_from_info(result_notices)
ctx_notices = {"notices": result_notices}
notice_html = render_to_string(
'snippets/notice_html.html', ctx_notices,
context_instance=RequestContext(request))
return HttpResponse(json.dumps({
"notice_html": notice_html,
}), content_type=content_type)
@login_required_ajax
@require_POST
def set_notices_seen(request):
"""Set user's notices seen:
Arguments:
- `request`:
"""
content_type = 'application/json; charset=utf-8'
username = request.user.username
unseen_notices = UserNotification.objects.get_user_notifications(username,
seen=False)
for notice in unseen_notices:
notice.seen = True
notice.save()
# mark related user msg as read
if notice.is_user_message():
d = notice.user_message_detail_to_dict()
msg_from = d.get('msg_from')
UserMessage.objects.update_unread_messages(msg_from, username)
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
@login_required_ajax
@require_POST
def set_notice_seen_by_id(request):
"""
Arguments:
- `request`:
"""
content_type = 'application/json; charset=utf-8'
notice_id = request.GET.get('notice_id')
try:
notice = UserNotification.objects.get(id=notice_id)
except UserNotification.DoesNotExist as e:
logger.error(e)
return HttpResponse(json.dumps({
'error': _(u'Failed')
}), status=400, content_type=content_type)
if not notice.seen:
notice.seen = True
notice.save()
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
@login_required_ajax
def space_and_traffic(request):
content_type = 'application/json; charset=utf-8'
username = request.user.username
# space & quota calculation
org = ccnet_threaded_rpc.get_orgs_by_user(username)
if not org:
space_quota = seafile_api.get_user_quota(username)
space_usage = seafile_api.get_user_self_usage(username)
else:
org_id = org[0].org_id
space_quota = seafserv_threaded_rpc.get_org_user_quota(org_id,
username)
space_usage = seafserv_threaded_rpc.get_org_user_quota_usage(
org_id, username)
rates = {}
if space_quota > 0:
rates['space_usage'] = str(float(space_usage) / space_quota * 100) + '%'
else: # no space quota set in config
rates['space_usage'] = '0%'
# traffic calculation
traffic_stat = 0
if TRAFFIC_STATS_ENABLED:
# User's network traffic stat in this month
try:
stat = get_user_traffic_stat(username)
except Exception as e:
logger.error(e)
stat = None
if stat:
traffic_stat = stat['file_view'] + stat['file_download'] + stat['dir_download']
# payment url, TODO: need to remove from here.
payment_url = ''
ENABLE_PAYMENT = getattr(settings, 'ENABLE_PAYMENT', False)
if ENABLE_PAYMENT:
if is_org_context(request):
if request.user.org and bool(request.user.org.is_staff) is True:
# payment for org admin
payment_url = reverse('org_plan')
else:
# no payment for org members
ENABLE_PAYMENT = False
else:
# payment for personal account
payment_url = reverse('plan')
ctx = {
"org": org,
"space_quota": space_quota,
"space_usage": space_usage,
"rates": rates,
"SHOW_TRAFFIC": SHOW_TRAFFIC,
"TRAFFIC_STATS_ENABLED": TRAFFIC_STATS_ENABLED,
"traffic_stat": traffic_stat,
"ENABLE_PAYMENT": ENABLE_PAYMENT,
"payment_url": payment_url,
}
html = render_to_string('snippets/space_and_traffic.html', ctx,
context_instance=RequestContext(request))
return HttpResponse(json.dumps({"html": html}), content_type=content_type)
def get_share_in_repo_list(request, start, limit):
"""List share in repos.
"""
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
repo_list = seafile_api.get_org_share_in_repo_list(org_id, username,
-1, -1)
else:
repo_list = seafile_api.get_share_in_repo_list(username, -1, -1)
for repo in repo_list:
repo.user_perm = check_folder_permission(request, repo.repo_id, '/')
return repo_list
def get_groups_by_user(request):
"""List user groups.
"""
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
return seaserv.get_org_groups_by_user(org_id, username)
else:
return seaserv.get_personal_groups_by_user(username)
def get_group_repos(request, groups):
"""Get repos shared to groups.
"""
group_repos = []
if is_org_context(request):
org_id = request.user.org.org_id
# For each group I joined...
for grp in groups:
# Get group repos, and for each group repos...
for r_id in seafile_api.get_org_group_repoids(org_id, grp.id):
repo_owner = seafile_api.get_org_repo_owner(r_id)
# Convert repo properties due to the different collumns in Repo
# and SharedRepo
r = get_repo(r_id)
if not r:
continue
r.repo_id = r.id
r.repo_name = r.name
r.repo_desc = r.desc
r.last_modified = get_repo_last_modify(r)
r.share_type = 'group'
r.user = repo_owner
r.user_perm = check_folder_permission(request, r_id, '/')
r.group = grp
group_repos.append(r)
else:
# For each group I joined...
for grp in groups:
# Get group repos, and for each group repos...
for r_id in seafile_api.get_group_repoids(grp.id):
repo_owner = seafile_api.get_repo_owner(r_id)
# Convert repo properties due to the different collumns in Repo
# and SharedRepo
r = get_repo(r_id)
if not r:
continue
r.repo_id = r.id
r.repo_name = r.name
r.repo_desc = r.desc
r.last_modified = get_repo_last_modify(r)
r.share_type = 'group'
r.user = repo_owner
r.user_perm = check_folder_permission(request, r_id, '/')
r.group = grp
group_repos.append(r)
return group_repos
def get_file_uploaded_bytes(request, repo_id):
"""
For resumable fileupload
"""
content_type = 'application/json; charset=utf-8'
parent_dir = request.GET.get('parent_dir')
file_name = request.GET.get('file_name')
if not parent_dir or not file_name:
err_msg = _(u'Argument missing')
return HttpResponse(json.dumps({"error": err_msg}), status=400,
content_type=content_type)
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist')
return HttpResponse(json.dumps({"error": err_msg}), status=400,
content_type=content_type)
file_path = os.path.join(parent_dir, file_name)
uploadedBytes = seafile_api.get_upload_tmp_file_offset(repo_id, file_path)
return HttpResponse(json.dumps({"uploadedBytes": uploadedBytes}),
content_type=content_type)
@login_required_ajax
def get_file_op_url(request, repo_id):
"""Get file upload/update url for AJAX.
"""
content_type = 'application/json; charset=utf-8'
op_type = request.GET.get('op_type') # value can be 'upload', 'update', 'upload-blks', 'update-blks'
path = request.GET.get('path')
if not (op_type and path):
err_msg = _(u'Argument missing')
return HttpResponse(json.dumps({"error": err_msg}), status=400,
content_type=content_type)
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist')
return HttpResponse(json.dumps({"error": err_msg}), status=400,
content_type=content_type)
# permission checking
if check_folder_permission(request, repo.id, path) != 'rw':
err_msg = _(u'Permission denied')
return HttpResponse(json.dumps({"error": err_msg}), status=403,
content_type=content_type)
username = request.user.username
if op_type == 'upload':
if request.user.is_staff and get_system_default_repo_id() == repo.id:
# Set username to 'system' to let fileserver release permission
# check.
username = 'system'
if op_type.startswith('update'):
token = seafile_api.get_fileserver_access_token(repo_id, 'dummy',
op_type, username)
else:
token = seafile_api.get_fileserver_access_token(repo_id, 'dummy',
op_type, username,
use_onetime=False)
url = gen_file_upload_url(token, op_type + '-aj')
return HttpResponse(json.dumps({"url": url}), content_type=content_type)
def get_file_upload_url_ul(request, token):
"""Get file upload url in dir upload link.
Arguments:
- `request`:
- `token`:
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
uls = UploadLinkShare.objects.get_valid_upload_link_by_token(token)
if uls is None:
return HttpResponse(json.dumps({"error": _("Bad upload link token.")}),
status=400, content_type=content_type)
repo_id = uls.repo_id
r = request.GET.get('r', '')
if repo_id != r: # perm check
return HttpResponse(json.dumps({"error": _("Bad repo id in upload link.")}),
status=403, content_type=content_type)
username = request.user.username or request.session.get('anonymous_email') or ''
args = [repo_id, json.dumps({'anonymous_user': username}), 'upload', '']
kwargs = {
'use_onetime': False,
}
if (is_pro_version() and dj_settings.ENABLE_UPLOAD_LINK_VIRUS_CHECK):
kwargs.update({'check_virus': True})
try:
acc_token = seafile_api.get_fileserver_access_token(*args, **kwargs)
except SearpcError as e:
logger.error(e)
return HttpResponse(json.dumps({"error": _("Internal Server Error")}),
status=500, content_type=content_type)
url = gen_file_upload_url(acc_token, 'upload-aj')
return HttpResponse(json.dumps({"url": url}), content_type=content_type)
@login_required_ajax
def repo_history_changes(request, repo_id):
changes = {}
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
# perm check
if check_folder_permission(request, repo_id, '/') is None:
if request.user.is_staff is True:
pass # Allow system staff to check repo changes
else:
err_msg = _(u"Permission denied")
return HttpResponse(json.dumps({"error": err_msg}), status=403,
content_type=content_type)
username = request.user.username
try:
server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set.
server_crypto = False
if repo.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \
and not is_passwd_set(repo_id, username):
err_msg = _(u'Library is encrypted.')
return HttpResponse(json.dumps({'error': err_msg}),
status=403, content_type=content_type)
commit_id = request.GET.get('commit_id', '')
if not commit_id:
err_msg = _(u'Argument missing')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
changes = get_diff(repo_id, '', commit_id)
c = get_commit(repo.id, repo.version, commit_id)
if c.parent_id is None:
# A commit is a first commit only if it's parent id is None.
changes['cmt_desc'] = repo.desc
elif c.second_parent_id is None:
# Normal commit only has one parent.
if c.desc.startswith('Changed library'):
changes['cmt_desc'] = _('Changed library name or description')
else:
# A commit is a merge only if it has two parents.
changes['cmt_desc'] = _('No conflict in the merge.')
changes['date_time'] = tsstr_sec(c.ctime)
return HttpResponse(json.dumps(changes), content_type=content_type)
def _create_repo_common(request, repo_name, repo_desc, encryption,
uuid, magic_str, encrypted_file_key):
"""Common logic for creating repo.
Returns:
newly created repo id. Or ``None`` if error raised.
"""
username = request.user.username
try:
if not encryption:
if is_org_context(request):
org_id = request.user.org.org_id
repo_id = seafile_api.create_org_repo(repo_name, repo_desc,
username, None, org_id)
else:
repo_id = seafile_api.create_repo(repo_name, repo_desc,
username, None)
else:
if is_org_context(request):
org_id = request.user.org.org_id
repo_id = seafile_api.create_org_enc_repo(
uuid, repo_name, repo_desc, username, magic_str,
encrypted_file_key, enc_version=2, org_id=org_id)
else:
repo_id = seafile_api.create_enc_repo(
uuid, repo_name, repo_desc, username,
magic_str, encrypted_file_key, enc_version=2)
except SearpcError as e:
logger.error(e)
repo_id = None
return repo_id
@login_required_ajax
def ajax_repo_change_passwd(request, repo_id):
"""Handle ajax post request to change library password.
"""
if request.method != 'POST':
raise Http404
content_type = 'application/json; charset=utf-8'
username = request.user.username
repo = seafile_api.get_repo(repo_id)
if not repo:
raise Http404
# check permission
if is_org_context(request):
repo_owner = seafile_api.get_org_repo_owner(repo.id)
else:
repo_owner = seafile_api.get_repo_owner(repo.id)
is_owner = True if username == repo_owner else False
if not is_owner:
return HttpResponse(json.dumps({
'error': 'Permission denied'}),
status=403, content_type=content_type)
old_passwd = request.POST.get('old_passwd', '')
new_passwd = request.POST.get('new_passwd', '')
try:
seafile_api.change_repo_passwd(repo_id, old_passwd, new_passwd, username)
except SearpcError, e:
return HttpResponse(json.dumps({
'error': e.msg,
}), status=400, content_type=content_type)
return HttpResponse(json.dumps({'success': True}),
content_type=content_type)
@login_required_ajax
def get_folder_perm_by_path(request, repo_id):
"""
Get user/group folder permission by path
"""
result = {}
content_type = 'application/json; charset=utf-8'
if not (is_pro_version() and ENABLE_FOLDER_PERM):
return HttpResponse(json.dumps({"error": True}),
status=403, content_type=content_type)
path = request.GET.get('path', None)
if not path:
return HttpResponse(json.dumps({"error": _('Argument missing')}),
status=400, content_type=content_type)
user_perms = seafile_api.list_folder_user_perm_by_repo(repo_id)
group_perms = seafile_api.list_folder_group_perm_by_repo(repo_id)
user_perms.reverse()
group_perms.reverse()
user_result_perms = []
for user_perm in user_perms:
if path == user_perm.path:
user_result_perm = {
"perm": user_perm.permission,
"user": user_perm.user,
"user_name": email2nickname(user_perm.user),
}
user_result_perms.append(user_result_perm)
group_result_perms = []
for group_perm in group_perms:
if path == group_perm.path:
group_result_perm = {
"perm": group_perm.permission,
"group_id": group_perm.group_id,
"group_name": get_group(group_perm.group_id).group_name,
}
group_result_perms.append(group_result_perm)
result['user_perms'] = user_result_perms
result['group_perms'] = group_result_perms
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
def set_user_folder_perm(request, repo_id):
"""
Add or modify or delete folder permission to a user
"""
if request.method != 'POST':
raise Http404
content_type = 'application/json; charset=utf-8'
if not (is_pro_version() and ENABLE_FOLDER_PERM):
return HttpResponse(json.dumps({"error": _(u"Permission denied")}),
status=403, content_type=content_type)
user = request.POST.get('user', None)
path = request.POST.get('path', None)
perm = request.POST.get('perm', None)
op_type = request.POST.get('type', None)
username = request.user.username
## check params
if not user or not path or not perm or \
op_type != 'add' and op_type != 'modify' and op_type != 'delete':
return HttpResponse(json.dumps({"error": _('Argument missing')}),
status=400, content_type=content_type)
if not seafile_api.get_repo(repo_id):
return HttpResponse(json.dumps({"error": _('Library does not exist')}),
status=400, content_type=content_type)
if is_org_context(request):
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if username != repo_owner:
return HttpResponse(json.dumps({"error": _('Permission denied')}),
status=403, content_type=content_type)
if perm is not None:
if perm != 'r' and perm != 'rw':
return HttpResponse(json.dumps({
"error": _('Invalid folder permission, should be "rw" or "r"')
}), status=400, content_type=content_type)
if not path.startswith('/'):
return HttpResponse(json.dumps({"error": _('Path should start with "/"')}),
status=400, content_type=content_type)
if path != '/' and path.endswith('/'):
return HttpResponse(json.dumps({"error": _('Path should not end with "/"')}),
status=400, content_type=content_type)
if seafile_api.get_dir_id_by_path(repo_id, path) is None:
return HttpResponse(json.dumps({"error": _('Invalid path')}),
status=400, content_type=content_type)
## add perm for user(s)
if op_type == 'add':
return add_user_folder_perm(request, repo_id, user, path, perm)
if not is_registered_user(user):
return HttpResponse(json.dumps({"error": _('Invalid user, should be registered')}),
status=400, content_type=content_type)
user_folder_perm = seafile_api.get_folder_user_perm(repo_id, path, user)
if op_type == 'modify':
if user_folder_perm and user_folder_perm != perm:
try:
seafile_api.set_folder_user_perm(repo_id, path, perm, user)
send_perm_audit_msg('modify-repo-perm', username, user, repo_id, path, perm)
except SearpcError as e:
logger.error(e)
return HttpResponse(json.dumps({"error": _('Operation failed')}),
status=500, content_type=content_type)
else:
return HttpResponse(json.dumps({"error": _('Wrong folder permission')}),
status=400, content_type=content_type)
if op_type == 'delete':
if user_folder_perm:
try:
seafile_api.rm_folder_user_perm(repo_id, path, user)
send_perm_audit_msg('delete-repo-perm', username, user, repo_id, path, perm)
except SearpcError as e:
logger.error(e)
return HttpResponse(json.dumps({"error": _('Operation failed')}),
status=500, content_type=content_type)
else:
return HttpResponse(json.dumps({"error": _('Please add folder permission first')}),
status=400, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
def add_user_folder_perm(request, repo_id, users, path, perm):
"""
Add folder permission for user(s)
"""
content_type = 'application/json; charset=utf-8'
emails = users.split(',')
success, failed = [], []
username = request.user.username
for user in [e.strip() for e in emails if e.strip()]:
if not is_valid_username(user):
failed.append(user)
continue
if not is_registered_user(user):
failed.append(user)
continue
user_folder_perm = seafile_api.get_folder_user_perm(repo_id, path, user)
if user_folder_perm:
# Already add this folder permission
continue
try:
seafile_api.add_folder_user_perm(repo_id, path, perm, user)
send_perm_audit_msg('add-repo-perm', username, user, repo_id, path, perm)
success.append({
'user': user,
'user_name': email2nickname(user)
})
except SearpcError as e:
logger.error(e)
failed.append(user)
if len(success) > 0:
data = json.dumps({"success": success, "failed": failed})
return HttpResponse(data, content_type=content_type)
else:
data = json.dumps({
"error": _("Please check the email(s) you entered and the contacts you selected")
})
return HttpResponse(data, status=400, content_type=content_type)
@login_required_ajax
def set_group_folder_perm(request, repo_id):
"""
Add or modify or delete folder permission to a group
"""
if request.method != 'POST':
raise Http404
content_type = 'application/json; charset=utf-8'
if not (is_pro_version() and ENABLE_FOLDER_PERM):
return HttpResponse(json.dumps({"error": _(u"Permission denied")}),
status=403, content_type=content_type)
group_id = request.POST.get('group_id', None)
path = request.POST.get('path', None)
perm = request.POST.get('perm', None)
op_type = request.POST.get('type', None)
username = request.user.username
if not group_id or not path or not perm or \
op_type != 'add' and op_type != 'modify' and op_type != 'delete':
return HttpResponse(json.dumps({"error": _('Argument missing')}),
status=400, content_type=content_type)
## check params
if not seafile_api.get_repo(repo_id):
return HttpResponse(json.dumps({"error": _('Library does not exist')}),
status=400, content_type=content_type)
if is_org_context(request):
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if username != repo_owner:
return HttpResponse(json.dumps({"error": _('Permission denied')}),
status=403, content_type=content_type)
if perm is not None:
if perm != 'r' and perm != 'rw':
return HttpResponse(json.dumps({
"error": _('Invalid folder permission, should be "rw" or "r"')
}), status=400, content_type=content_type)
if not path.startswith('/'):
return HttpResponse(json.dumps({"error": _('Path should start with "/"')}),
status=400, content_type=content_type)
if path != '/' and path.endswith('/'):
return HttpResponse(json.dumps({"error": _('Path should not end with "/"')}),
status=400, content_type=content_type)
if seafile_api.get_dir_id_by_path(repo_id, path) is None:
return HttpResponse(json.dumps({"error": _('Invalid path')}),
status=400, content_type=content_type)
## add perm for group(s)
if op_type == 'add':
return add_group_folder_perm(request, repo_id, group_id, path, perm)
group_id = int(group_id)
if not seaserv.get_group(group_id):
return HttpResponse(json.dumps({"error": _('Invalid group')}),
status=400, content_type=content_type)
group_folder_perm = seafile_api.get_folder_group_perm(repo_id, path, group_id)
if op_type == 'modify':
if group_folder_perm and group_folder_perm != perm:
try:
seafile_api.set_folder_group_perm(repo_id, path, perm, group_id)
send_perm_audit_msg('modify-repo-perm', username, group_id, repo_id, path, perm)
except SearpcError as e:
logger.error(e)
return HttpResponse(json.dumps({"error": _('Operation failed')}),
status=500, content_type=content_type)
else:
return HttpResponse(json.dumps({"error": _('Wrong folder permission')}),
status=400, content_type=content_type)
if op_type == 'delete':
if group_folder_perm:
try:
seafile_api.rm_folder_group_perm(repo_id, path, group_id)
send_perm_audit_msg('delete-repo-perm', username, group_id, repo_id, path, perm)
except SearpcError as e:
logger.error(e)
return HttpResponse(json.dumps({"error": _('Operation failed')}),
status=500, content_type=content_type)
else:
return HttpResponse(json.dumps({"error": _('Please add folder permission first')}),
status=400, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
def add_group_folder_perm(request, repo_id, group_ids, path, perm):
"""
Add folder permission for group(s)
"""
content_type = 'application/json; charset=utf-8'
group_id_list = group_ids.split(',') # 'user'
success, failed = [], []
username = request.user.username
for group_id in group_id_list:
group_id = int(group_id)
if not seaserv.get_group(group_id):
failed.append(group_id)
group_folder_perm = seafile_api.get_folder_group_perm(repo_id, path, group_id)
if group_folder_perm:
#Already add this folder permission
continue
try:
seafile_api.add_folder_group_perm(repo_id, path, perm, group_id)
send_perm_audit_msg('add-repo-perm', username, group_id, repo_id, path, perm)
success.append({
'group_id': group_id,
"group_name": get_group(group_id).group_name,
})
except SearpcError as e:
logger.error(e)
failed.append(group_id)
if len(success) > 0:
data = json.dumps({"success": success, "failed": failed})
return HttpResponse(data, content_type=content_type)
else:
data = json.dumps({"error": _("Failed")})
return HttpResponse(data, status=400, content_type=content_type)
@login_required_ajax
def toggle_group_modules(request, group_id):
content_type = 'application/json; charset=utf-8'
result = {}
group_id_int = int(group_id) # Checked by URL Conf
group = get_group(group_id_int)
if not group:
result["error"] = _('Group does not exist.')
return HttpResponse(json.dumps(result),
status=400, content_type=content_type)
group.is_staff = is_group_staff(group, request.user)
if not group.is_staff:
result["error"] = _('Permission denied.')
return HttpResponse(json.dumps(result),
status=403, content_type=content_type)
group_wiki = request.POST.get('group_wiki', '')
if group_wiki == 'true':
enable_mod_for_group(group.id, MOD_GROUP_WIKI)
else:
disable_mod_for_group(group.id, MOD_GROUP_WIKI)
return HttpResponse(json.dumps({ "success": True }),
content_type=content_type)
@login_required_ajax
def toggle_personal_modules(request):
content_type = 'application/json; charset=utf-8'
result = {}
if not request.user.permissions.can_add_repo:
result["error"] = _('Permission denied.')
return HttpResponse(json.dumps(result),
status=403, content_type=content_type)
username = request.user.username
personal_wiki = request.POST.get('personal_wiki', '')
if personal_wiki == 'true':
enable_mod_for_user(username, MOD_PERSONAL_WIKI)
else:
disable_mod_for_user(username, MOD_PERSONAL_WIKI)
return HttpResponse(json.dumps({ "success": True }),
content_type=content_type)
@login_required_ajax
def ajax_group_members_import(request, group_id):
"""Import users to group.
Permission checking:
1. Only group admin can add import group members
"""
result = {}
username = request.user.username
content_type = 'application/json; charset=utf-8'
group_id = int(group_id)
try:
group = seaserv.get_group(group_id)
if not group:
result['error'] = 'Group %s not found.' % group_id
return HttpResponse(json.dumps(result), status=404,
content_type=content_type)
# check permission
if not is_group_admin_or_owner(group_id, username):
result['error'] = 'Permission denied.'
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
except SearpcError as e:
logger.error(e)
result['error'] = 'Internal Server Error'
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
# get and convert uploaded file
uploaded_file = request.FILES['file']
if uploaded_file.size > 10 * 1024 * 1024:
result['error'] = _(u'Failed, file is too large')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
try:
content = uploaded_file.read()
encoding = chardet.detect(content)['encoding']
if encoding != 'utf-8':
content = content.decode(encoding, 'replace').encode('utf-8')
filestream = StringIO.StringIO(content)
reader = csv.reader(filestream)
except Exception as e:
logger.error(e)
result['error'] = 'Internal Server Error'
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
# prepare email list from uploaded file
emails_list = []
for row in reader:
if not row:
continue
email = row[0].strip().lower()
emails_list.append(email)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
result = {}
result['failed'] = []
result['success'] = []
emails_need_add = []
# check email validation
for email in emails_list:
try:
User.objects.get(email=email)
except User.DoesNotExist:
result['failed'].append({
'email': email,
'error_msg': 'User %s not found.' % email
})
continue
if is_group_member(group_id, email):
result['failed'].append({
'email': email,
'error_msg': _(u'User %s is already a group member.') % email
})
continue
# Can only invite organization users to group
if org_id and not \
seaserv.ccnet_threaded_rpc.org_user_exists(org_id, email):
result['failed'].append({
'email': email,
'error_msg': _(u'User %s not found in organization.') % email
})
continue
emails_need_add.append(email)
# Add email to group.
for email in emails_need_add:
try:
seaserv.ccnet_threaded_rpc.group_add_member(group_id,
username, email)
member_info = get_group_member_info(request, group_id, email)
result['success'].append(member_info)
except SearpcError as e:
logger.error(e)
result['failed'].append({
'email': email,
'error_msg': 'Internal Server Error'
})
return HttpResponse(json.dumps(result), content_type=content_type)
@login_required_ajax
def ajax_repo_dir_recycle_more(request, repo_id):
"""
List 'more' repo/dir trash.
"""
result = {}
content_type = 'application/json; charset=utf-8'
repo = seafile_api.get_repo(repo_id)
if not repo:
err_msg = 'Library %s not found.' % repo_id
return HttpResponse(json.dumps({'error': err_msg}),
status=404, content_type=content_type)
path = request.GET.get('path', '/')
path = '/' if path == '' else path
if check_folder_permission(request, repo_id, path) != 'rw':
err_msg = 'Permission denied.'
return HttpResponse(json.dumps({'error': err_msg}),
status=403, content_type=content_type)
scan_stat = request.GET.get('scan_stat', None)
try:
deleted_entries = seafile_api.get_deleted(repo_id, 0, path, scan_stat)
except SearpcError as e:
logger.error(e)
result['error'] = 'Internal server error'
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
new_scan_stat = deleted_entries[-1].scan_stat
trash_more = True if new_scan_stat is not None else False
more_entries_html = ''
# since there will always have one 'deleted_entry' to tell scan_stat,
# so if len of deleted_entries = 1, means have not get any trash dir/file
# if len of deleted_entries > 1,
# means have get trash dir/file from current scanned commits
if len(deleted_entries) > 1:
deleted_entries = deleted_entries[0:-1]
for dirent in deleted_entries:
if stat.S_ISDIR(dirent.mode):
dirent.is_dir = True
else:
dirent.is_dir = False
# Entries sort by deletion time in descending order.
deleted_entries.sort(lambda x, y : cmp(y.delete_time,
x.delete_time))
ctx = {
'show_recycle_root': True,
'repo': repo,
'dir_entries': deleted_entries,
'dir_path': path,
'MEDIA_URL': MEDIA_URL
}
more_entries_html = render_to_string("snippets/repo_dir_trash_tr.html", ctx)
result = {
'html': more_entries_html,
'trash_more': trash_more,
'new_scan_stat': new_scan_stat,
}
return HttpResponse(json.dumps(result), content_type=content_type)