# -*- coding: utf-8 -*- import os import stat import logging import simplejson as json from django.core.urlresolvers import reverse from django.http import HttpResponse, Http404 from django.template import RequestContext from django.template.loader import render_to_string from django.utils.http import urlquote from django.utils.translation import ugettext as _ import seaserv from seaserv import seafile_api, seafserv_rpc, \ get_related_users_by_repo, get_related_users_by_org_repo, \ is_org_repo_owner, CALC_SHARE_USAGE, seafserv_threaded_rpc, \ get_user_quota_usage, get_user_share_usage from pysearpc import SearpcError from seahub.auth.decorators import login_required from seahub.contacts.models import Contact from seahub.forms import RepoNewDirentForm, RepoRenameDirentForm from seahub.options.models import UserOptions, CryptoOptionNotSetError from seahub.notifications.models import UserNotification from seahub.signals import upload_file_successful from seahub.views import get_repo_dirents, validate_owner, \ check_repo_access_permission, get_unencry_rw_repos_by_user, \ get_system_default_repo_id from seahub.views.repo import get_nav_path, get_fileshare, get_dir_share_link, \ get_uploadlink, get_dir_shared_upload_link import seahub.settings as settings from seahub.signals import repo_deleted from seahub.utils import check_filename_with_rename, EMPTY_SHA1, gen_block_get_url, \ check_and_get_org_by_repo, 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 from seahub.utils.star import star_file, unstar_file # 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 def get_dirents(request, repo_id): """ Get dirents in a dir for file tree """ if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' username = request.user.username # permission checking user_perm = check_repo_access_permission(repo_id, request.user) 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({"err_msg": 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({"err_msg": 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 = os.path.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 def get_unenc_group_repos(request, group_id): ''' Get unenc repos in a group. ''' if not request.is_ajax(): raise Http404 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({"err_msg": 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({"err_msg": err_msg}), status=403, content_type=content_type) repos = seafile_api.get_group_repo_list(group_id_int) repo_list = [] for repo in repos: if not repo.encrypted: repo_list.append({"name": repo.props.name, "id": repo.props.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 def get_my_unenc_repos(request): """Get my owned and unencrypted repos. """ if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' repos = seafile_api.get_owned_repo_list(request.user.username) repo_list = [] for repo in repos: if repo.encrypted: continue repo_list.append({"name": repo.name, "id": repo.id}) return HttpResponse(json.dumps(repo_list), content_type=content_type) @login_required def unenc_rw_repos(request): """Get a user's unencrypt repos that he/she can read-write. Arguments: - `request`: """ if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' username = request.user.username acc_repos = get_unencry_rw_repos_by_user(username) repo_list = [] for repo in acc_repos: repo_list.append({"name": repo.name, "id": repo.id}) return HttpResponse(json.dumps(repo_list), content_type=content_type) @login_required def list_dir(request, repo_id): """ List directory entries in AJAX. """ if not request.is_ajax(): raise Http404 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_repo_access_permission(repo.id, request.user) if user_perm is None: err_msg = _(u'Permission denied.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username) try: server_crypto = UserOptions.objects.is_server_crypto(username) except CryptoOptionNotSetError: # Assume server_crypto is ``False`` if this option is not set. server_crypto = False if repo.encrypted and \ (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \ and not seafile_api.is_password_set(repo.id, username): err_msg = _(u'Library is encrypted.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id) if not head_commit: err_msg = _(u'Error: no head commit id') return HttpResponse(json.dumps({'error': err_msg}), status=500, content_type=content_type) if new_merge_with_no_conflict(head_commit): info_commit = get_commit_before_new_merge(head_commit) else: info_commit = head_commit path = request.GET.get('p', '/') if path[-1] != '/': path = path + '/' more_start = None file_list, dir_list, dirent_more = get_repo_dirents(request, repo, head_commit, path, offset=0, limit=100) if dirent_more: more_start = 100 zipped = get_nav_path(path, repo.name) fileshare = get_fileshare(repo.id, username, path) dir_shared_link = get_dir_share_link(fileshare) uploadlink = get_uploadlink(repo.id, username, path) dir_shared_upload_link = get_dir_shared_upload_link(uploadlink) ctx = { 'repo': repo, 'zipped': zipped, 'user_perm': user_perm, 'path': path, 'server_crypto': server_crypto, 'fileshare': fileshare, 'dir_shared_link': dir_shared_link, 'uploadlink': uploadlink, 'dir_shared_upload_link': dir_shared_upload_link, 'dir_list': dir_list, 'file_list': file_list, 'dirent_more': dirent_more, 'more_start': more_start, 'ENABLE_SUB_LIBRARY': settings.ENABLE_SUB_LIBRARY, "sub_lib_enabled": sub_lib_enabled, 'current_commit': head_commit, 'info_commit': info_commit, } html = render_to_string('snippets/repo_dir_data.html', ctx, context_instance=RequestContext(request)) return HttpResponse(json.dumps({'html': html, 'path': path}), content_type=content_type) @login_required def list_dir_more(request, repo_id): """ List 'more' entries in a directory with AJAX. """ if not request.is_ajax(): raise Http404 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_repo_access_permission(repo.id, request.user) if user_perm is None: err_msg = _(u'Permission denied.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username) try: server_crypto = UserOptions.objects.is_server_crypto(username) except CryptoOptionNotSetError: # Assume server_crypto is ``False`` if this option is not set. server_crypto = False if repo.encrypted and \ (repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)) \ and not seafile_api.is_password_set(repo.id, username): err_msg = _(u'Library is encrypted.') return HttpResponse(json.dumps({'error': err_msg}), status=403, content_type=content_type) head_commit = get_commit(repo.id, repo.version, repo.head_cmmt_id) if not head_commit: err_msg = _(u'Error: no head commit id') return HttpResponse(json.dumps({'error': err_msg}), status=500, content_type=content_type) path = request.GET.get('p', '/') if path[-1] != '/': path = path + '/' offset = int(request.GET.get('start')) if not offset: err_msg = _(u'Argument missing') return HttpResponse(json.dumps({'error': err_msg}), status=400, content_type=content_type) more_start = None file_list, dir_list, dirent_more = get_repo_dirents(request, repo, head_commit, path, offset, limit=100) if dirent_more: more_start = offset + 100 ctx = { 'repo': repo, 'user_perm': user_perm, 'path': path, 'server_crypto': server_crypto, 'dir_list': dir_list, 'file_list': file_list, 'ENABLE_SUB_LIBRARY': settings.ENABLE_SUB_LIBRARY, "sub_lib_enabled": sub_lib_enabled, } html = render_to_string('snippets/repo_dirents.html', ctx, context_instance=RequestContext(request)) return HttpResponse(json.dumps({'html': html, 'dirent_more': dirent_more, 'more_start': more_start}), content_type=content_type) def new_dirent_common(func): """Decorator for common logic in creating directory and file. """ def _decorated(request, repo_id, *args, **kwargs): if request.method != 'POST' or not request.is_ajax(): 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) # permission checking username = request.user.username if check_repo_access_permission(repo.id, request.user) != 'rw': result['error'] = _('Permission denied') return HttpResponse(json.dumps(result), status=403, content_type=content_type) # form validation form = RepoNewDirentForm(request.POST) if form.is_valid(): dirent_name = form.cleaned_data["dirent_name"] else: result['error'] = str(form.errors.values()[0]) return HttpResponse(json.dumps(result), status=400, content_type=content_type) # arguments 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) # rename duplicate name dirent_name = check_filename_with_rename(repo.id, parent_dir, dirent_name) return func(repo.id, parent_dir, dirent_name, username) return _decorated @login_required @new_dirent_common def new_dir(repo_id, parent_dir, dirent_name, username): """ Create a new dir with ajax. """ result = {} content_type = 'application/json; charset=utf-8' # create new dirent try: seafile_api.post_dir(repo_id, parent_dir, dirent_name, 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, 'name': dirent_name}), content_type=content_type) @login_required @new_dirent_common def new_file(repo_id, parent_dir, dirent_name, username): """ Create a new file with ajax. """ result = {} content_type = 'application/json; charset=utf-8' # create new dirent try: seafile_api.post_empty_file(repo_id, parent_dir, dirent_name, 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, 'name': dirent_name}), content_type=content_type) @login_required def rename_dirent(request, repo_id): """ Rename a file/dir in a repo, with ajax """ if request.method != 'POST' or not request.is_ajax(): 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) # permission checking if check_repo_access_permission(repo.id, request.user) != 'rw': result['error'] = _('Permission denied') return HttpResponse(json.dumps(result), status=403, 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) if newname == oldname: return HttpResponse(json.dumps({'success': True}), 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) # 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}), content_type=content_type) @login_required def delete_dirent(request, repo_id): """ Delete a file/dir with ajax. """ if not request.is_ajax(): raise Http404 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) # permission checking username = request.user.username if check_repo_access_permission(repo.id, request.user) != 'rw': err_msg = _(u'Permission denied.') return HttpResponse(json.dumps({'error': err_msg}), status=403, 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) # 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.') % dirent_name return HttpResponse(json.dumps({'error': err_msg}), status=500, content_type=content_type) @login_required def delete_dirents(request, repo_id): """ Delete multi files/dirs with ajax. """ if not request.is_ajax(): raise Http404 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) # permission checking username = request.user.username if check_repo_access_permission(repo.id, request.user) != 'rw': err_msg = _(u'Permission denied.') return HttpResponse(json.dumps({'error': err_msg}), status=403, 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) deleted = [] undeleted = [] for dirent_name in dirents_names: 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(func): """Decorator for common logic in copying/moving dir/file. """ def _decorated(request, repo_id, *args, **kwargs): if request.method != 'POST' or not request.is_ajax(): 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) # permission checking username = request.user.username if check_repo_access_permission(repo.id, request.user) != 'rw': result['error'] = _('Permission denied') return HttpResponse(json.dumps(result), status=403, 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) # check whether user has write permission to dest repo if check_repo_access_permission(dst_repo_id, request.user) != 'rw': result['error'] = _('Permission denied') return HttpResponse(json.dumps(result), status=403, content_type=content_type) # do nothing 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) return func(repo_id, path, dst_repo_id, dst_path, obj_name, username) return _decorated @login_required @copy_move_common def mv_file(src_repo_id, src_path, dst_repo_id, dst_path, obj_name, username): result = {} content_type = 'application/json; charset=utf-8' 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, 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_url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) msg = _(u'Successfully moved %(name)s view') % \ {"name":obj_name, "url":msg_url} result['msg'] = msg if res.background: result['task_id'] = res.task_id return HttpResponse(json.dumps(result), content_type=content_type) @login_required @copy_move_common def cp_file(src_repo_id, src_path, dst_repo_id, dst_path, obj_name, username): result = {} content_type = 'application/json; charset=utf-8' 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 if not res: result['error'] = _(u'Internal server error') return HttpResponse(json.dumps(result), status=500, content_type=content_type) result['success'] = True msg_url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) msg = _(u'Successfully copied %(name)s view') % \ {"name":obj_name, "url":msg_url} result['msg'] = msg if res.background: result['task_id'] = res.task_id return HttpResponse(json.dumps(result), content_type=content_type) @login_required @copy_move_common def mv_dir(src_repo_id, src_path, dst_repo_id, dst_path, obj_name, username): result = {} content_type = 'application/json; charset=utf-8' src_dir = os.path.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': src_dir, 'des': 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.move_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_url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) msg = _(u'Successfully moved %(name)s view') % \ {"name":obj_name, "url":msg_url} result['msg'] = msg if res.background: result['task_id'] = res.task_id return HttpResponse(json.dumps(result), content_type=content_type) @login_required @copy_move_common def cp_dir(src_repo_id, src_path, dst_repo_id, dst_path, obj_name, username): result = {} content_type = 'application/json; charset=utf-8' src_dir = os.path.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': src_dir, 'des': 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_url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) msg = _(u'Successfully copied %(name)s view') % \ {"name":obj_name, "url":msg_url} 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(func): """ Decorator for common logic in copying/moving dirs/files. """ def _decorated(request, repo_id, *args, **kwargs): if request.method != 'POST' or not request.is_ajax(): 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) # permission checking username = request.user.username if check_repo_access_permission(repo.id, request.user) != 'rw': result['error'] = _('Permission denied') return HttpResponse(json.dumps(result), status=403, 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.') % obj_name return HttpResponse(json.dumps(result), status=400, content_type=content_type) # check whether user has write permission to dest repo if check_repo_access_permission(dst_repo_id, request.user) != 'rw': result['error'] = _('Permission denied') return HttpResponse(json.dumps(result), status=403, 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) return func(repo_id, parent_dir, dst_repo_id, dst_path, obj_file_names, obj_dir_names, username) return _decorated @login_required @dirents_copy_move_common def mv_dirents(src_repo_id, src_path, dst_repo_id, dst_path, obj_file_names, obj_dir_names, username): result = {} content_type = 'application/json; charset=utf-8' for obj_name in obj_dir_names: src_dir = os.path.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': src_dir, 'des': dst_path} result['error'] = error_msg return HttpResponse(json.dumps(result), status=400, content_type=content_type) success = [] failed = [] url = None task_ids = [] 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.move_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 if not res: failed.append(obj_name) else: success.append(obj_name) if res.background: task_ids.append(res.task_id) if len(success) > 0: url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) result = {'success': success, 'failed': failed, 'url': url, 'task_ids': task_ids} return HttpResponse(json.dumps(result), content_type=content_type) @login_required @dirents_copy_move_common def cp_dirents(src_repo_id, src_path, dst_repo_id, dst_path, obj_file_names, obj_dir_names, username): result = {} content_type = 'application/json; charset=utf-8' for obj_name in obj_dir_names: src_dir = os.path.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': src_dir, 'des': dst_path} result['error'] = error_msg return HttpResponse(json.dumps(result), status=400, content_type=content_type) success = [] failed = [] url = None task_ids = [] 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, e: res = None if not res: failed.append(obj_name) else: success.append(obj_name) if res.background: task_ids.append(res.task_id) if len(success) > 0: url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path) result = {'success': success, 'failed': failed, 'url': url, 'task_ids': task_ids} return HttpResponse(json.dumps(result), content_type=content_type) @login_required def get_cp_progress(request): ''' Fetch progress of file/dir mv/cp. ''' if not request.is_ajax(): raise Http404 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 def get_multi_cp_progress(request): ''' Fetch progress of multi files/dirs mv/cp. ''' if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' result = {} task_ids = request.GET.getlist('task_ids') if not task_ids: result['error'] = _(u'Argument missing') return HttpResponse(json.dumps(result), status=400, content_type=content_type) success = 0 fail = 0 for task_id in task_ids: res = seafile_api.get_copy_task(task_id) if not res: fail += 1 else: if res.failed: fail += 1 elif res.successful: success += 1 result['success'] = success result['fail'] = fail return HttpResponse(json.dumps(result), content_type=content_type) @login_required def cancel_cp(request): ''' cancel file/dir mv/cp. ''' if not request.is_ajax(): raise Http404 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'] = _('Failed') return HttpResponse(json.dumps(result), status=400, content_type=content_type) @login_required def repo_star_file(request, repo_id): if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' path = request.GET.get('file', '') if not path: return HttpResponse(json.dumps({'error': _(u'Invalid arguments')}), status=400, content_type=content_type) is_dir = False star_file(request.user.username, repo_id, path, is_dir) return HttpResponse(json.dumps({'success':True}), content_type=content_type) @login_required def repo_unstar_file(request, repo_id): if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' path = request.GET.get('file', '') if not path: return HttpResponse(json.dumps({'error': _(u'Invalid arguments')}), status=400, content_type=content_type) unstar_file(request.user.username, repo_id, path) return HttpResponse(json.dumps({'success':True}), content_type=content_type) ########## contacts related @login_required def get_contacts(request): if not request.is_ajax(): raise Http404 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: contact_list.append({"email": c.contact_email, "avatar": avatar(c.contact_email, 16)}) return HttpResponse(json.dumps({"contacts":contact_list}), content_type=content_type) @login_required def get_current_commit(request, repo_id): if not request.is_ajax(): raise Http404 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_repo_access_permission(repo.id, request.user) 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 def sub_repo(request, repo_id): ''' check if a dir has a corresponding sub_repo if it does not have, create one ''' if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' result = {} path = request.GET.get('p') name = request.GET.get('name') if not (path and name): result['error'] = _('Argument missing') return HttpResponse(json.dumps(result), status=400, content_type=content_type) username = request.user.username # check if the sub-lib exist try: sub_repo = seafile_api.get_virtual_repo(repo_id, path, username) except SearpcError, e: result['error'] = e.msg return HttpResponse(json.dumps(result), status=500, content_type=content_type) if sub_repo: result['sub_repo_id'] = sub_repo.id else: # create a sub-lib try: # use name as 'repo_name' & 'repo_desc' for sub_repo sub_repo_id = seafile_api.create_virtual_repo(repo_id, path, name, name, username) result['sub_repo_id'] = sub_repo_id except SearpcError, e: result['error'] = e.msg return HttpResponse(json.dumps(result), status=500, content_type=content_type) return HttpResponse(json.dumps(result), content_type=content_type) def download_enc_file(request, repo_id, file_id): if not request.is_ajax(): raise Http404 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_file_by_file_id(repo_id, file_id) except SearpcError, 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 = seafserv_rpc.web_get_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: result['error'] = _('Wrong repo id') return HttpResponse(json.dumps(result), status=400, content_type=ct) 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 def unseen_notices_count(request): """Count user's unseen notices. Arguments: - `request`: """ if not request.is_ajax(): raise Http404 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 def repo_remove(request, repo_id): if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' result = {} if get_system_default_repo_id() == repo_id: result['error'] = _(u'System library can not be deleted.') return HttpResponse(json.dumps(result), status=403, content_type=content_type) 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) user = request.user.username org, base_template = check_and_get_org_by_repo(repo_id, user) if org: # Remove repo in org context, only repo owner or org staff can # perform this operation. if request.user.is_staff or org.is_staff or \ is_org_repo_owner(org.org_id, repo_id, user): # Must get related useres before remove the repo usernames = get_related_users_by_org_repo(org.org_id, repo_id) seafile_api.remove_repo(repo_id) repo_deleted.send(sender=None, org_id=org.org_id, usernames=usernames, repo_owner=user, repo_id=repo_id, repo_name=repo.name, ) result['success'] = True return HttpResponse(json.dumps(result), content_type=content_type) else: result['error'] = _(u'Permission denied.') return HttpResponse(json.dumps(result), status=400, content_type=content_type) else: # Remove repo in personal context, only repo owner or site staff can # perform this operation. if validate_owner(request, repo_id) or request.user.is_staff: usernames = get_related_users_by_repo(repo_id) seafile_api.remove_repo(repo_id) repo_deleted.send(sender=None, org_id=-1, usernames=usernames, repo_owner=user, repo_id=repo_id, repo_name=repo.name, ) result['success'] = True return HttpResponse(json.dumps(result), content_type=content_type) else: result['error'] = _(u'Permission denied.') return HttpResponse(json.dumps(result), status=400, content_type=content_type) @login_required def space_and_traffic(request): if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' result = {} username = request.user.username quota = seafserv_threaded_rpc.get_user_quota(username) quota_usage = 0 share_usage = 0 my_usage = get_user_quota_usage(username) rates = {} if CALC_SHARE_USAGE: share_usage = get_user_share_usage(username) quota_usage = my_usage + share_usage if quota > 0: rates['my_usage'] = str(float(my_usage)/quota * 100) + '%' rates['share_usage'] = str(float(share_usage)/quota * 100) + '%' else: quota_usage = my_usage if quota > 0: rates['quota_usage'] = str(float(my_usage)/quota * 100) + '%' 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'] ctx = { "CALC_SHARE_USAGE": CALC_SHARE_USAGE, "quota": quota, "quota_usage": quota_usage, "share_usage": share_usage, "my_usage": my_usage, "rates": rates, "TRAFFIC_STATS_ENABLED": TRAFFIC_STATS_ENABLED, "traffic_stat": traffic_stat, "ENABLE_PAYMENT": getattr(settings, 'ENABLE_PAYMENT', False), } html = render_to_string('snippets/space_and_traffic.html', ctx, context_instance=RequestContext(request)) return HttpResponse(json.dumps({"html": html}), content_type=content_type) @login_required def my_shared_and_group_repos(request): """Return html snippet of repos that shared to user and group repos. Arguments: - `request`: """ if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' username = request.user.username shared_repos = seafile_api.get_share_in_repo_list(username, -1, -1) for repo in shared_repos: repo.user_perm = seafile_api.check_repo_access_permission(repo.repo_id, username) shared_repos.sort(lambda x, y: cmp(y.last_modified, x.last_modified)) group_repos = [] # Get all personal groups I joined. joined_groups = request.user.joined_groups # For each group I joined... for grp in joined_groups: # Get group repos, and for each group repos... for r_id in seaserv.get_group_repoids(grp.id): # No need to list my own repo repo_owner = seafile_api.get_repo_owner(r_id) if repo_owner == username: continue # Convert repo properties due to the different collumns in Repo # and SharedRepo r = seaserv.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 = seaserv.check_permission(r_id, username) r.group = grp group_repos.append(r) group_repos.sort(key=lambda x: x.group.group_name) for i, repo in enumerate(group_repos): if i == 0: repo.show_group_name = True else: if repo.group.group_name != group_repos[i-1].group.group_name: repo.show_group_name = True ctx_shared = { "shared_repos": shared_repos, } ctx_group = { "group_repos": group_repos, } shared_repos_html = render_to_string('snippets/my_shared_repos.html', ctx_shared, context_instance=RequestContext(request)) group_repos_html = render_to_string('snippets/my_group_repos.html', ctx_group, context_instance=RequestContext(request)) return HttpResponse(json.dumps({"shared": shared_repos_html, "group": group_repos_html}), content_type=content_type) @login_required def get_file_op_url(request, repo_id): """Get file upload/update url for AJAX. """ if not request.is_ajax(): raise Http404 content_type = 'application/json; charset=utf-8' op_type = request.GET.get('op_type') # value can be 'upload', 'update', 'upload-blks', 'update-blks' if not op_type: err_msg = _(u'Argument missing') return HttpResponse(json.dumps({"error": err_msg}), status=400, content_type=content_type) username = request.user.username url = '' if check_repo_access_permission(repo_id, request.user) == 'rw': token = seafile_api.get_httpserver_access_token(repo_id, 'dummy', op_type, username) url = gen_file_upload_url(token, op_type + '-aj') return HttpResponse(json.dumps({"url": url}), content_type=content_type)