1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-13 05:39:59 +00:00
Files
seahub/seahub/api2/views.py
Daniel Pan b41333902b Merge pull request #1179 from haiwen/uploadblks
[api2] Add api to check missing blocks before uploading
2016-05-07 16:27:27 +08:00

4648 lines
180 KiB
Python

# encoding: utf-8
import logging
import os
import stat
import json
import datetime
import posixpath
import re
from dateutil.relativedelta import relativedelta
from urllib2 import unquote, quote
from rest_framework import parsers
from rest_framework import status
from rest_framework import renderers
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.reverse import reverse
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth.hashers import check_password
from django.contrib.sites.models import RequestSite
from django.db import IntegrityError
from django.db.models import F, Q
from django.http import HttpResponse
from django.template import RequestContext
from django.template.loader import render_to_string
from django.template.defaultfilters import filesizeformat
from django.shortcuts import render_to_response
from django.utils import timezone
from django.utils.translation import ugettext as _
from .throttling import ScopedRateThrottle, AnonRateThrottle, UserRateThrottle
from .authentication import TokenAuthentication
from .serializers import AuthTokenSerializer
from .utils import get_diff_details, \
api_error, get_file_size, prepare_starred_files, \
get_groups, get_group_and_contacts, prepare_events, \
api_group_check, get_timestamp, json_response, is_seafile_pro, \
api_repo_user_folder_perm_check, api_repo_setting_permission_check, \
api_repo_group_folder_perm_check
from seahub.avatar.templatetags.avatar_tags import api_avatar_url, avatar
from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \
grp_avatar
from seahub.base.accounts import User
from seahub.base.models import UserStarredFiles, DeviceToken
from seahub.base.templatetags.seahub_tags import email2nickname, \
translate_seahub_time, translate_commit_desc_escape
from seahub.group.views import remove_group_common, \
rename_group_with_new_name, is_group_staff
from seahub.group.utils import BadGroupNameError, ConflictGroupNameError, \
validate_group_name
from seahub.thumbnail.utils import generate_thumbnail
from seahub.notifications.models import UserNotification
from seahub.options.models import UserOptions
from seahub.contacts.models import Contact
from seahub.profile.models import Profile, DetailedProfile
from seahub.signals import (repo_created, repo_deleted)
from seahub.share.models import FileShare, OrgFileShare, UploadLinkShare
from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
check_filename_with_rename, is_valid_username, EVENTS_ENABLED, \
get_user_events, EMPTY_SHA1, get_ccnet_server_addr_port, is_pro_version, \
gen_block_get_url, get_file_type_and_ext, HAS_FILE_SEARCH, \
gen_file_share_link, gen_dir_share_link, is_org_context, gen_shared_link, \
get_org_user_events, calculate_repos_last_modify, send_perm_audit_msg, \
gen_shared_upload_link, convert_cmmt_desc_link, is_org_repo_creation_allowed
from seahub.utils.devices import get_user_devices, do_unlink_device
from seahub.utils.repo import get_sub_repo_abbrev_origin_path
from seahub.utils.star import star_file, unstar_file
from seahub.utils.file_types import DOCUMENT
from seahub.utils.file_size import get_file_size_unit
from seahub.utils.timeutils import utc_to_local, datetime_to_isoformat_timestr
from seahub.views import is_registered_user, check_file_lock, \
group_events_data, get_diff, create_default_library, \
list_inner_pub_repos, get_virtual_repos_by_owner, \
check_folder_permission
from seahub.views.ajax import get_share_in_repo_list, get_groups_by_user, \
get_group_repos
from seahub.views.file import get_file_view_path_and_perm, send_file_access_msg
if HAS_FILE_SEARCH:
from seahub_extra.search.views import search_keyword
from seahub.utils import HAS_OFFICE_CONVERTER
if HAS_OFFICE_CONVERTER:
from seahub.utils import query_office_convert_status, prepare_converted_html
import seahub.settings as settings
from seahub.settings import THUMBNAIL_EXTENSION, THUMBNAIL_ROOT, \
ENABLE_GLOBAL_ADDRESSBOOK, FILE_LOCK_EXPIRATION_DAYS, \
ENABLE_THUMBNAIL, ENABLE_FOLDER_PERM
try:
from seahub.settings import CLOUD_MODE
except ImportError:
CLOUD_MODE = False
try:
from seahub.settings import MULTI_TENANCY
except ImportError:
MULTI_TENANCY = False
try:
from seahub.settings import ORG_MEMBER_QUOTA_DEFAULT
except ImportError:
ORG_MEMBER_QUOTA_DEFAULT = None
try:
from seahub.settings import ENABLE_OFFICE_WEB_APP
except ImportError:
ENABLE_OFFICE_WEB_APP = False
try:
from seahub.settings import OFFICE_WEB_APP_FILE_EXTENSION
except ImportError:
OFFICE_WEB_APP_FILE_EXTENSION = ()
from pysearpc import SearpcError, SearpcObjEncoder
import seaserv
from seaserv import seafserv_threaded_rpc, \
get_personal_groups_by_user, get_session_info, is_personal_repo, \
get_repo, check_permission, get_commits, is_passwd_set,\
check_quota, list_share_repos, get_group_repos_by_owner, get_group_repoids, \
list_inner_pub_repos_by_owner, is_group_user, \
remove_share, unset_inner_pub_repo, get_group, \
get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE, edit_repo, \
ccnet_threaded_rpc, get_personal_groups, seafile_api, \
create_org, ccnet_api
from constance import config
logger = logging.getLogger(__name__)
json_content_type = 'application/json; charset=utf-8'
# Define custom HTTP status code. 4xx starts from 440, 5xx starts from 520.
HTTP_440_REPO_PASSWD_REQUIRED = 440
HTTP_441_REPO_PASSWD_MAGIC_REQUIRED = 441
HTTP_520_OPERATION_FAILED = 520
def UTF8Encode(s):
if isinstance(s, unicode):
return s.encode('utf-8')
else:
return s
def check_filename_with_rename_utf8(repo_id, parent_dir, filename):
newname = check_filename_with_rename(repo_id, parent_dir, filename)
return UTF8Encode(newname)
########## Test
class Ping(APIView):
"""
Returns a simple `pong` message when client calls `api2/ping/`.
For example:
curl http://127.0.0.1:8000/api2/ping/
"""
throttle_classes = (ScopedRateThrottle, )
throttle_scope = 'ping'
def get(self, request, format=None):
return Response('pong')
def head(self, request, format=None):
return Response(headers={'foo': 'bar',})
class AuthPing(APIView):
"""
Returns a simple `pong` message when client provided an auth token.
For example:
curl -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" http://127.0.0.1:8000/api2/auth/ping/
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
return Response('pong')
########## Token
class ObtainAuthToken(APIView):
"""
Returns auth token if username and password are valid.
For example:
curl -d "username=foo@example.com&password=123456" http://127.0.0.1:8000/api2/auth-token/
"""
throttle_classes = (AnonRateThrottle, )
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
def post(self, request):
context = { 'request': request }
serializer = AuthTokenSerializer(data=request.data, context=context)
if serializer.is_valid():
key = serializer.validated_data
return Response({'token': key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
########## Accounts
class Accounts(APIView):
"""List all accounts.
Administrator permission is required.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAdminUser, )
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
# list accounts
start = int(request.GET.get('start', '0'))
limit = int(request.GET.get('limit', '100'))
# reading scope user list
scope = request.GET.get('scope', None)
accounts_ldap = []
accounts_db = []
if scope:
scope = scope.upper()
if scope == 'LDAP':
accounts_ldap = seaserv.get_emailusers('LDAP', start, limit)
elif scope == 'DB':
accounts_db = seaserv.get_emailusers('DB', start, limit)
else:
return api_error(status.HTTP_400_BAD_REQUEST, "%s is not a valid scope value" % scope)
else:
# old way - search first in LDAP if available then DB if no one found
accounts_ldap = seaserv.get_emailusers('LDAP', start, limit)
if len(accounts_ldap) == 0:
accounts_db = seaserv.get_emailusers('DB', start, limit)
accounts_json = []
for account in accounts_ldap:
accounts_json.append({'email': account.email, 'source' : 'LDAP'})
for account in accounts_db:
accounts_json.append({'email': account.email, 'source' : 'DB'})
return Response(accounts_json)
class AccountInfo(APIView):
""" Show account info.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
info = {}
email = request.user.username
p = Profile.objects.get_profile_by_user(email)
d_p = DetailedProfile.objects.get_detailed_profile_by_user(email)
info['email'] = email
info['name'] = email2nickname(email)
info['total'] = seafile_api.get_user_quota(email)
info['usage'] = seafile_api.get_user_self_usage(email)
info['login_id'] = p.login_id if p else ""
info['department'] = d_p.department if d_p else ""
info['contact_email'] = p.contact_email if p else ""
info['institution'] = p.institution if p else ""
return Response(info)
class RegDevice(APIView):
"""Reg device for iOS push notification.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def post(self, request, format=None):
version = request.POST.get('version')
platform = request.POST.get('platform')
pversion = request.POST.get('pversion')
devicetoken = request.POST.get('deviceToken')
if not devicetoken or not version or not platform or not pversion:
return api_error(status.HTTP_400_BAD_REQUEST, "Missing argument")
token, modified = DeviceToken.objects.get_or_create(
token=devicetoken, user=request.user.username)
if token.version != version:
token.version = version
modified = True
if token.pversion != pversion:
token.pversion = pversion
modified = True
if token.platform != platform:
token.platform = platform
modified = True
if modified:
token.save()
return Response("success")
class SearchUser(APIView):
""" Search user from contacts/all users
"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
if not request.user.permissions.can_use_global_address_book():
return api_error(status.HTTP_403_FORBIDDEN,
'Guest user can not use global address book.')
q = request.GET.get('q', None)
if not q:
return api_error(status.HTTP_400_BAD_REQUEST, 'Argument missing.')
username = request.user.username
search_result = []
searched_users = []
searched_profiles = []
if request.cloud_mode:
if is_org_context(request):
url_prefix = request.user.org.url_prefix
users = seaserv.get_org_users_by_url_prefix(url_prefix, -1, -1)
searched_users = filter(lambda u: q in u.email, users)
# 'user__in' for only get profile of user in org
# 'nickname__icontains' for search by nickname
searched_profiles = Profile.objects.filter(Q(user__in=[u.email for u in users]) & \
Q(nickname__icontains=q)).values('user')
elif ENABLE_GLOBAL_ADDRESSBOOK:
searched_users = get_searched_users(q)
searched_profiles = Profile.objects.filter(nickname__icontains=q).values('user')
else:
users = []
contacts = Contact.objects.get_contacts_by_user(username)
for c in contacts:
try:
user = User.objects.get(email = c.contact_email)
c.is_active = user.is_active
except User.DoesNotExist:
continue
c.email = c.contact_email
users.append(c)
searched_users = filter(lambda u: q in u.email, users)
# 'user__in' for only get profile of contacts
# 'nickname__icontains' for search by nickname
searched_profiles = Profile.objects.filter(Q(user__in=[u.email for u in users]) & \
Q(nickname__icontains=q)).values('user')
else:
searched_users = get_searched_users(q)
searched_profiles = Profile.objects.filter(nickname__icontains=q).values('user')
# remove inactive users and add to result
for u in searched_users[:10]:
if u.is_active:
search_result.append(u.email)
for p in searched_profiles[:10]:
try:
user = User.objects.get(email = p['user'])
except User.DoesNotExist:
continue
if not user.is_active:
continue
search_result.append(p['user'])
# remove duplicate emails
search_result = {}.fromkeys(search_result).keys()
# reomve myself
if username in search_result:
search_result.remove(username)
if is_valid_username(q) and q not in search_result:
search_result.insert(0, q)
try:
size = int(request.GET.get('avatar_size', 32))
except ValueError:
size = 32
formated_result = format_user_result(request, search_result, size)[:10]
return HttpResponse(json.dumps({"users": formated_result}), status=200,
content_type=json_content_type)
def format_user_result(request, users, size):
results = []
# Get contact_emails from users' profiles
profiles = Profile.objects.filter(user__in=users)
contact_email_dict = {}
for e in profiles:
contact_email_dict[e.user] = e.contact_email
for email in users:
url, is_default, date_uploaded = api_avatar_url(email, size)
results.append({
"email": email,
"avatar": avatar(email, size),
"avatar_url": request.build_absolute_uri(url),
"name": email2nickname(email),
"contact_email": contact_email_dict.get(email, ""),
})
return results
def get_searched_users(q):
searched_users = []
searched_db_users = []
searched_ldap_users = []
searched_db_users = seaserv.ccnet_threaded_rpc.search_emailusers('DB', q, 0, 10)
count = len(searched_db_users)
if count < 10:
searched_ldap_users = seaserv.ccnet_threaded_rpc.search_emailusers('LDAP', q, 0, 10 - count)
searched_users.extend(searched_db_users)
searched_users.extend(searched_ldap_users)
return searched_users
class Search(APIView):
""" Search all the repos
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
if not HAS_FILE_SEARCH:
return api_error(status.HTTP_404_NOT_FOUND, "Search not supported")
keyword = request.GET.get('q', None)
if not keyword:
return api_error(status.HTTP_400_BAD_REQUEST, "Missing argument")
results, total, has_more = search_keyword(request, keyword)
for e in results:
e.pop('repo', None)
e.pop('content_highlight', None)
e.pop('exists', None)
e.pop('last_modified_by', None)
e.pop('name_highlight', None)
e.pop('score', None)
try:
path = e['fullpath'].encode('utf-8')
file_id = seafile_api.get_file_id_by_path(e['repo_id'], path)
e['oid'] = file_id
repo = get_repo(e['repo_id'])
e['size'] = get_file_size(repo.store_id, repo.version, file_id)
except SearpcError, err:
pass
res = { "total":total, "results":results, "has_more":has_more }
return Response(res)
########## Repo related
def repo_download_info(request, repo_id, gen_sync_token=True):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
# generate download url for client
relay_id = get_session_info().id
addr, port = get_ccnet_server_addr_port()
email = request.user.username
if gen_sync_token:
token = seafile_api.generate_repo_token(repo_id, email)
else:
token = ''
repo_name = repo.name
repo_desc = repo.desc
repo_size = repo.size
repo_size_formatted = filesizeformat(repo.size)
enc = 1 if repo.encrypted else ''
magic = repo.magic if repo.encrypted else ''
random_key = repo.random_key if repo.random_key else ''
enc_version = repo.enc_version
repo_version = repo.version
calculate_repos_last_modify([repo])
info_json = {
'relay_id': relay_id,
'relay_addr': addr,
'relay_port': port,
'email': email,
'token': token,
'repo_id': repo_id,
'repo_name': repo_name,
'repo_desc': repo_desc,
'repo_size': repo_size,
'repo_size_formatted': repo_size_formatted,
'mtime': repo.latest_modify,
'mtime_relative': translate_seahub_time(repo.latest_modify),
'encrypted': enc,
'enc_version': enc_version,
'magic': magic,
'random_key': random_key,
'repo_version': repo_version,
}
return Response(info_json)
class Repos(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
# parse request params
filter_by = {
'mine': False,
'sub': False,
'shared': False,
'group': False,
'org': False,
}
rtype = request.GET.get('type', "")
if not rtype:
# set all to True, no filter applied
filter_by = filter_by.fromkeys(filter_by.iterkeys(), True)
for f in rtype.split(','):
f = f.strip()
filter_by[f] = True
email = request.user.username
if not UserOptions.objects.is_sub_lib_enabled(email):
filter_by['sub'] = False
repos_json = []
if filter_by['mine']:
if is_org_context(request):
org_id = request.user.org.org_id
owned_repos = seafile_api.get_org_owned_repo_list(org_id,
email, ret_corrupted=True)
else:
owned_repos = seafile_api.get_owned_repo_list(email,
ret_corrupted=True)
owned_repos.sort(lambda x, y: cmp(y.last_modify, x.last_modify))
for r in owned_repos:
# do not return virtual repos
if r.is_virtual:
continue
repo = {
"type": "repo",
"id": r.id,
"owner": email,
"name": r.name,
"desc": r.desc,
"mtime": r.last_modify,
"mtime_relative": translate_seahub_time(r.last_modify),
"size": r.size,
"size_formatted": filesizeformat(r.size),
"encrypted": r.encrypted,
"permission": 'rw', # Always have read-write permission to owned repo
"virtual": r.is_virtual,
"root": r.root,
}
if r.encrypted:
repo["enc_version"] = r.enc_version
repo["magic"] = r.magic
repo["random_key"] = r.random_key
repos_json.append(repo)
if filter_by['sub']:
# compose abbrev origin path for display
sub_repos = []
sub_repos = get_virtual_repos_by_owner(request)
for repo in sub_repos:
repo.abbrev_origin_path = get_sub_repo_abbrev_origin_path(
repo.origin_repo_name, repo.origin_path)
sub_repos.sort(lambda x, y: cmp(y.last_modify, x.last_modify))
for r in sub_repos:
# print r._dict
repo = {
"type": "repo",
"id": r.id,
"name": r.name,
"origin_repo_id": r.origin_repo_id,
"origin_path": r.origin_path,
"abbrev_origin_path": r.abbrev_origin_path,
"mtime": r.last_modify,
"mtime_relative": translate_seahub_time(r.last_modify),
"owner": email,
"desc": r.desc,
"size": r.size,
"encrypted": r.encrypted,
"permission": 'rw',
"virtual": r.is_virtual,
"root": r.root,
}
if r.encrypted:
repo["enc_version"] = r.enc_version
repo["magic"] = r.magic
repo["random_key"] = r.random_key
repos_json.append(repo)
if filter_by['shared']:
shared_repos = get_share_in_repo_list(request, -1, -1)
shared_repos.sort(lambda x, y: cmp(y.last_modify, x.last_modify))
for r in shared_repos:
r.password_need = is_passwd_set(r.repo_id, email)
repo = {
"type": "srepo",
"id": r.repo_id,
"owner": r.user,
"name": r.repo_name,
"owner_nickname": email2nickname(r.user),
"desc": r.repo_desc,
"mtime": r.last_modify,
"mtime_relative": translate_seahub_time(r.last_modify),
"size": r.size,
"size_formatted": filesizeformat(r.size),
"encrypted": r.encrypted,
"permission": r.user_perm,
"share_type": r.share_type,
"root": r.root,
}
if r.encrypted:
repo["enc_version"] = r.enc_version
repo["magic"] = r.magic
repo["random_key"] = r.random_key
repos_json.append(repo)
if filter_by['group']:
groups = get_groups_by_user(request)
group_repos = get_group_repos(request, groups)
group_repos.sort(lambda x, y: cmp(y.last_modify, x.last_modify))
for r in group_repos:
repo = {
"type": "grepo",
"id": r.id,
"owner": r.group.group_name,
"groupid": r.group.id,
"name": r.name,
"desc": r.desc,
"mtime": r.last_modify,
"size": r.size,
"encrypted": r.encrypted,
"permission": check_permission(r.id, email),
"root": r.root,
}
if r.encrypted:
repo["enc_version"] = r.enc_version
repo["magic"] = r.magic
repo["random_key"] = r.random_key
repos_json.append(repo)
if filter_by['org'] and request.user.permissions.can_view_org():
public_repos = list_inner_pub_repos(request)
for r in public_repos:
repo = {
"type": "grepo",
"id": r.repo_id,
"name": r.repo_name,
"desc": r.repo_desc,
"owner": "Organization",
"mtime": r.last_modified,
"mtime_relative": translate_seahub_time(r.last_modified),
"size": r.size,
"size_formatted": filesizeformat(r.size),
"encrypted": r.encrypted,
"permission": r.permission,
"share_from": r.user,
"share_type": r.share_type,
"root": r.root,
}
if r.encrypted:
repo["enc_version"] = r.enc_version
repo["magic"] = r.magic
repo["random_key"] = r.random_key
repos_json.append(repo)
response = HttpResponse(json.dumps(repos_json), status=200,
content_type=json_content_type)
response["enable_encrypted_library"] = config.ENABLE_ENCRYPTED_LIBRARY
return response
def post(self, request, format=None):
if not request.user.permissions.can_add_repo():
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create library.')
req_from = request.GET.get('from', "")
if req_from == 'web':
gen_sync_token = False # Do not generate repo sync token
else:
gen_sync_token = True
username = request.user.username
repo_name = request.data.get("name", None)
if not repo_name:
return api_error(status.HTTP_400_BAD_REQUEST,
'Library name is required.')
repo_desc = request.data.get("desc", '')
org_id = -1
if is_org_context(request):
org_id = request.user.org.org_id
repo_id = request.data.get('repo_id', '')
try:
if repo_id:
# client generates magic and random key
repo_id, error = self._create_enc_repo(request, repo_id, repo_name, repo_desc, username, org_id)
else:
repo_id, error = self._create_repo(request, repo_name, repo_desc, username, org_id)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to create library.')
if error is not None:
return error
if not repo_id:
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to create library.')
else:
repo_created.send(sender=None,
org_id=org_id,
creator=username,
repo_id=repo_id,
repo_name=repo_name)
resp = repo_download_info(request, repo_id,
gen_sync_token=gen_sync_token)
# FIXME: according to the HTTP spec, need to return 201 code and
# with a corresponding location header
# resp['Location'] = reverse('api2-repo', args=[repo_id])
return resp
def _create_repo(self, request, repo_name, repo_desc, username, org_id):
passwd = request.data.get("passwd", None)
# to avoid 'Bad magic' error when create repo, passwd should be 'None'
# not an empty string when create unencrypted repo
if not passwd:
passwd = None
if (passwd is not None) and (not config.ENABLE_ENCRYPTED_LIBRARY):
return api_error(status.HTTP_403_FORBIDDEN,
'NOT allow to create encrypted library.')
if org_id > 0:
repo_id = seafile_api.create_org_repo(repo_name, repo_desc,
username, passwd, org_id)
else:
repo_id = seafile_api.create_repo(repo_name, repo_desc,
username, passwd)
return repo_id, None
def _create_enc_repo(self, request, repo_id, repo_name, repo_desc, username, org_id):
if not _REPO_ID_PATTERN.match(repo_id):
return api_error(status.HTTP_400_BAD_REQUEST, 'Repo id must be a valid uuid')
magic = request.data.get('magic', '')
random_key = request.data.get('random_key', '')
try:
enc_version = int(request.data.get('enc_version', 0))
except ValueError:
return None, api_error(status.HTTP_400_BAD_REQUEST,
'Invalid enc_version param.')
if len(magic) != 64 or len(random_key) != 96 or enc_version < 0:
return None, api_error(status.HTTP_400_BAD_REQUEST,
'You must provide magic, random_key and enc_version.')
if org_id > 0:
repo_id = seafile_api.create_org_enc_repo(repo_id, repo_name, repo_desc,
username, magic, random_key, enc_version, org_id)
else:
repo_id = seafile_api.create_enc_repo(
repo_id, repo_name, repo_desc, username,
magic, random_key, enc_version)
return repo_id, None
class PubRepos(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
# List public repos
if not request.user.permissions.can_view_org():
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to view public libraries.')
repos_json = []
public_repos = list_inner_pub_repos(request)
for r in public_repos:
repo = {
"id": r.repo_id,
"name": r.repo_name,
"desc": r.repo_desc,
"owner": r.user,
"owner_nickname": email2nickname(r.user),
"mtime": r.last_modified,
"mtime_relative": translate_seahub_time(r.last_modified),
"size": r.size,
"size_formatted": filesizeformat(r.size),
"encrypted": r.encrypted,
"permission": r.permission,
"root": r.root,
}
if r.encrypted:
repo["enc_version"] = r.enc_version
repo["magic"] = r.magic
repo["random_key"] = r.random_key
repos_json.append(repo)
return Response(repos_json)
def post(self, request, format=None):
# Create public repo
if not request.user.permissions.can_add_repo():
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create library.')
username = request.user.username
repo_name = request.data.get("name", None)
if not repo_name:
return api_error(status.HTTP_400_BAD_REQUEST,
'Library name is required.')
repo_desc = request.data.get("desc", '')
passwd = request.data.get("passwd", None)
# to avoid 'Bad magic' error when create repo, passwd should be 'None'
# not an empty string when create unencrypted repo
if not passwd:
passwd = None
if (passwd is not None) and (not config.ENABLE_ENCRYPTED_LIBRARY):
return api_error(status.HTTP_403_FORBIDDEN,
'NOT allow to create encrypted library.')
permission = request.data.get("permission", 'r')
if permission != 'r' and permission != 'rw':
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid permission')
if is_org_context(request):
org_id = request.user.org.org_id
repo_id = seafile_api.create_org_repo(repo_name, repo_desc,
username, passwd, org_id)
repo = seafile_api.get_repo(repo_id)
seaserv.seafserv_threaded_rpc.set_org_inner_pub_repo(
org_id, repo.id, permission)
else:
repo_id = seafile_api.create_repo(repo_name, repo_desc,
username, passwd)
repo = seafile_api.get_repo(repo_id)
seafile_api.add_inner_pub_repo(repo.id, permission)
pub_repo = {
"id": repo.id,
"name": repo.name,
"desc": repo.desc,
"size": repo.size,
"size_formatted": filesizeformat(repo.size),
"mtime": repo.last_modify,
"mtime_relative": translate_seahub_time(repo.last_modify),
"encrypted": repo.encrypted,
"permission": 'rw', # Always have read-write permission to owned repo
"owner": username,
"owner_nickname": email2nickname(username),
}
return Response(pub_repo, status=201)
def set_repo_password(request, repo, password):
assert password, 'password must not be none'
try:
seafile_api.set_passwd(repo.id, request.user.username, password)
except SearpcError, e:
if e.msg == 'Bad arguments':
return api_error(status.HTTP_400_BAD_REQUEST, e.msg)
elif e.msg == 'Repo is not encrypted':
return api_error(status.HTTP_409_CONFLICT, e.msg)
elif e.msg == 'Incorrect password':
return api_error(status.HTTP_400_BAD_REQUEST, e.msg)
elif e.msg == 'Internal server error':
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
else:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
def check_set_repo_password(request, repo):
if not check_permission(repo.id, request.user.username):
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this library.')
if repo.encrypted:
password = request.REQUEST.get('password', default=None)
if not password:
return api_error(HTTP_440_REPO_PASSWD_REQUIRED,
'Library password is needed.')
return set_repo_password(request, repo, password)
class Repo(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
username = request.user.username
if not check_folder_permission(request, repo_id, '/'):
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this library.')
# check whether user is repo owner
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)
owner = "self" if username == repo_owner else "share"
last_commit = get_commits(repo.id, 0, 1)[0]
repo.latest_modify = last_commit.ctime if last_commit else None
# query repo infomation
repo.size = seafile_api.get_repo_size(repo_id)
current_commit = get_commits(repo_id, 0, 1)[0]
root_id = current_commit.root_id if current_commit else None
repo_json = {
"type":"repo",
"id":repo.id,
"owner":owner,
"name":repo.name,
"desc":repo.desc,
"mtime":repo.latest_modify,
"size":repo.size,
"encrypted":repo.encrypted,
"root":root_id,
"permission": check_permission(repo.id, username),
}
if repo.encrypted:
repo_json["enc_version"] = repo.enc_version
repo_json["magic"] = repo.magic
repo_json["random_key"] = repo.random_key
return Response(repo_json)
def post(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
op = request.GET.get('op', 'setpassword')
if op == 'checkpassword':
magic = request.REQUEST.get('magic', default=None)
if not magic:
return api_error(HTTP_441_REPO_PASSWD_MAGIC_REQUIRED,
'Library password magic is needed.')
if not check_folder_permission(request, repo_id, '/'):
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
try:
seafile_api.check_passwd(repo.id, magic)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
return Response("success")
elif op == 'setpassword':
resp = check_set_repo_password(request, repo)
if resp:
return resp
return Response("success")
elif op == 'rename':
username = request.user.username
repo_name = request.POST.get('repo_name')
repo_desc = request.POST.get('repo_desc')
# 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 api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to rename this library.')
if edit_repo(repo_id, repo_name, repo_desc, username):
return Response("success")
else:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"Unable to rename library")
return Response("unsupported operation")
def delete(self, request, repo_id, format=None):
username = request.user.username
repo = seafile_api.get_repo(repo_id)
if not repo:
return api_error(status.HTTP_400_BAD_REQUEST,
'Library does not exist.')
# 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 api_error(
status.HTTP_403_FORBIDDEN,
'You do not have permission to delete this library.'
)
usernames = seaserv.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=repo_owner,
repo_id=repo_id,
repo_name=repo.name)
return Response('success', status=status.HTTP_200_OK)
class RepoHistory(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
commits_all = get_commits(repo_id, per_page * (current_page - 1),
per_page + 1)
commits = commits_all[:per_page]
if len(commits_all) == per_page + 1:
page_next = True
else:
page_next = False
return HttpResponse(json.dumps({"commits": commits,
"page_next": page_next},
cls=SearpcObjEncoder),
status=200, content_type=json_content_type)
class RepoHistoryLimit(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# 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)
username = request.user.username
# no settings for virtual repo
if repo.is_virtual or username != repo_owner:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try:
keep_days = seafile_api.get_repo_history_limit(repo_id)
return Response({'keep_days': keep_days})
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
def put(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# 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)
username = request.user.username
# no settings for virtual repo
if repo.is_virtual or \
not config.ENABLE_REPO_HISTORY_SETTING or \
username != repo_owner:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# check arg validation
keep_days = request.data.get('keep_days', None)
if not keep_days:
error_msg = 'keep_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
keep_days = int(keep_days)
except ValueError:
error_msg = 'keep_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
# days <= -1, keep full history
# days = 0, not keep history
# days > 0, keep a period of days
res = seafile_api.set_repo_history_limit(repo_id, keep_days)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if res == 0:
new_limit = seafile_api.get_repo_history_limit(repo_id)
return Response({'keep_days': new_limit})
else:
error_msg = 'Failed to set library history limit.'
return api_error(status.HTTP_520_OPERATION_FAILED, error_msg)
class DownloadRepo(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
if not check_folder_permission(request, repo_id, '/'):
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this library.')
return repo_download_info(request, repo_id)
class RepoPublic(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def post(self, request, repo_id, format=None):
"""Set organization library.
"""
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library %s not found.' % repo_id)
if not is_org_repo_creation_allowed(request):
return api_error(status.HTTP_403_FORBIDDEN,
'Permission denied.')
if check_permission(repo_id, request.user.username) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this library.')
try:
seafile_api.add_inner_pub_repo(repo_id, "r")
except:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Unable to make library public')
return HttpResponse(json.dumps({'success': True}), status=200,
content_type=json_content_type)
def delete(self, request, repo_id, format=None):
"""Unset organization library.
"""
username = request.user.username
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
if not request.user.is_staff and \
not seafile_api.is_repo_owner(username, repo_id):
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to unshare library.')
try:
seafile_api.remove_inner_pub_repo(repo_id)
except:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Unable to make library private')
return HttpResponse(json.dumps({'success': True}), status=200,
content_type=json_content_type)
class RepoOwner(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
# check permission
if org_id:
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if request.user.username != repo_owner:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
return HttpResponse(json.dumps({"owner": repo_owner}), status=200,
content_type=json_content_type)
def put(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
# check permission
if org_id:
repo_owner = seafile_api.get_org_repo_owner(repo.id)
else:
repo_owner = seafile_api.get_repo_owner(repo.id)
if request.user.username != repo_owner:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# check new owner validation
new_owner = request.data.get('owner', '').lower()
try:
User.objects.get(email=new_owner)
except User.DoesNotExist:
error_msg = 'User %s not found.' % new_owner
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if org_id:
if not ccnet_api.org_user_exists(org_id, new_owner):
error_msg = _(u'User %s not found in organization.') % new_owner
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# transfer repo
if org_id:
seafile_api.set_org_repo_owner(org_id, repo_id, new_owner)
else:
if ccnet_api.get_orgs_by_user(new_owner):
# can not transfer library to organization user %s.
error_msg = 'Email %s invalid.' % new_owner
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
else:
seafile_api.set_repo_owner(repo_id, new_owner)
return HttpResponse(json.dumps({'success': True}),
content_type=json_content_type)
########## File related
class FileBlockDownloadLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, file_id, block_id, format=None):
parent_dir = request.GET.get('p', '/')
if check_folder_permission(request, repo_id, parent_dir) is None:
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this repo.')
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
token = seafile_api.get_fileserver_access_token(
repo_id, file_id, 'downloadblks', request.user.username)
url = gen_block_get_url(token, block_id)
return Response(url)
class UploadLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
parent_dir = request.GET.get('p', '/')
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this folder.')
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
token = seafile_api.get_fileserver_access_token(
repo_id, 'dummy', 'upload', request.user.username, use_onetime = False)
url = gen_file_upload_url(token, 'upload-api')
return Response(url)
class UpdateLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
parent_dir = request.GET.get('p', '/')
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this folder.')
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
token = seafile_api.get_fileserver_access_token(
repo_id, 'dummy', 'update', request.user.username)
url = gen_file_upload_url(token, 'update-api')
return Response(url)
class UploadBlksLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
parent_dir = request.GET.get('p', '/')
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this folder.')
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
token = seafile_api.get_fileserver_access_token(
repo_id, 'dummy', 'upload-blks-api', request.user.username,
use_onetime = False)
url = gen_file_upload_url(token, 'upload-blks-api')
return Response(url)
def get_blklist_missing(self, repo_id, blks):
if not blks:
return []
blklist = blks.split(',')
try:
return json.loads(seafile_api.check_repo_blocks_missing(
repo_id, json.dumps(blklist)))
except Exception as e:
pass
return blklist
def post(self, request, repo_id, format=None):
parent_dir = request.GET.get('p', '/')
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this folder.')
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
token = seafile_api.get_fileserver_access_token(
repo_id, 'dummy', 'upload', request.user.username,
use_onetime = False)
blksurl = gen_file_upload_url(token, 'upload-raw-blks-api')
commiturl = '%s?commitonly=true&ret-json=true' % gen_file_upload_url(
token, 'upload-blks-api')
blks = request.POST.get('blklist', None)
blklist = self.get_blklist_missing(repo_id, blks)
res = {
'rawblksurl': blksurl,
'commiturl': commiturl,
'blklist': blklist
}
return HttpResponse(json.dumps(res), status=200,
content_type=json_content_type)
class UpdateBlksLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
parent_dir = request.GET.get('p', '/')
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this folder.')
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
token = seafile_api.get_fileserver_access_token(
repo_id, 'dummy', 'update-blks-api', request.user.username,
use_onetime = False)
url = gen_file_upload_url(token, 'update-blks-api')
return Response(url)
def get_dir_recursively(username, repo_id, path, all_dirs):
path_id = seafile_api.get_dir_id_by_path(repo_id, path)
dirs = seafserv_threaded_rpc.list_dir_with_perm(repo_id, path,
path_id, username, -1, -1)
for dirent in dirs:
if stat.S_ISDIR(dirent.mode):
entry = {}
entry["type"] = 'dir'
entry["parent_dir"] = path
entry["id"] = dirent.obj_id
entry["name"] = dirent.obj_name
entry["mtime"] = dirent.mtime
entry["permission"] = dirent.permission
all_dirs.append(entry)
sub_path = posixpath.join(path, dirent.obj_name)
get_dir_recursively(username, repo_id, sub_path, all_dirs)
return all_dirs
def get_dir_entrys_by_id(request, repo, path, dir_id, request_type=None):
""" Get dirents in a dir
if request_type is 'f', only return file list,
if request_type is 'd', only return dir list,
else, return both.
"""
username = request.user.username
try:
dirs = seafserv_threaded_rpc.list_dir_with_perm(repo.id, path, dir_id,
username, -1, -1)
dirs = dirs if dirs else []
except SearpcError, e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to list dir.")
dir_list, file_list = [], []
for dirent in dirs:
dtype = "file"
entry = {}
if stat.S_ISDIR(dirent.mode):
dtype = "dir"
else:
if repo.version == 0:
entry["size"] = get_file_size(repo.store_id, repo.version,
dirent.obj_id)
else:
entry["size"] = dirent.size
if is_pro_version():
entry["is_locked"] = dirent.is_locked
entry["lock_owner"] = dirent.lock_owner
entry["lock_time"] = dirent.lock_time
if username == dirent.lock_owner:
entry["locked_by_me"] = True
else:
entry["locked_by_me"] = False
entry["type"] = dtype
entry["name"] = dirent.obj_name
entry["id"] = dirent.obj_id
entry["mtime"] = dirent.mtime
entry["permission"] = dirent.permission
if dtype == 'dir':
dir_list.append(entry)
else:
file_list.append(entry)
dir_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
file_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
if request_type == 'f':
dentrys = file_list
elif request_type == 'd':
dentrys = dir_list
else:
dentrys = dir_list + file_list
response = HttpResponse(json.dumps(dentrys), status=200,
content_type=json_content_type)
response["oid"] = dir_id
response["dir_perm"] = seafile_api.check_permission_by_path(repo.id, path, username)
return response
def get_shared_link(request, repo_id, path):
l = FileShare.objects.filter(repo_id=repo_id).filter(
username=request.user.username).filter(path=path)
token = None
if len(l) > 0:
fileshare = l[0]
token = fileshare.token
else:
token = gen_token(max_length=10)
fs = FileShare()
fs.username = request.user.username
fs.repo_id = repo_id
fs.path = path
fs.token = token
try:
fs.save()
except IntegrityError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
http_or_https = request.is_secure() and 'https' or 'http'
domain = RequestSite(request).domain
file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain,
settings.SITE_ROOT, token)
return file_shared_link
def get_repo_file(request, repo_id, file_id, file_name, op, use_onetime=True):
if op == 'download':
token = seafile_api.get_fileserver_access_token(repo_id, file_id, op,
request.user.username,
use_onetime)
redirect_url = gen_file_get_url(token, file_name)
response = HttpResponse(json.dumps(redirect_url), status=200,
content_type=json_content_type)
response["oid"] = file_id
return response
if op == 'downloadblks':
blklist = []
encrypted = False
enc_version = 0
if file_id != EMPTY_SHA1:
try:
blks = seafile_api.list_blocks_by_file_id(repo_id, file_id)
blklist = blks.split('\n')
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to get file block list')
blklist = [i for i in blklist if len(i) == 40]
if len(blklist) > 0:
repo = get_repo(repo_id)
encrypted = repo.encrypted
enc_version = repo.enc_version
res = {
'file_id': file_id,
'blklist': blklist,
'encrypted': encrypted,
'enc_version': enc_version,
}
response = HttpResponse(json.dumps(res), status=200,
content_type=json_content_type)
response["oid"] = file_id
return response
if op == 'sharelink':
path = request.GET.get('p', None)
if path is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
file_shared_link = get_shared_link(request, repo_id, path)
return Response(file_shared_link)
def reloaddir(request, repo, parent_dir):
try:
dir_id = seafile_api.get_dir_id_by_path(repo.id, parent_dir)
except SearpcError, e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get dir id by path")
if not dir_id:
return api_error(status.HTTP_404_NOT_FOUND, "Path does not exist")
return get_dir_entrys_by_id(request, repo, parent_dir, dir_id)
def reloaddir_if_necessary (request, repo, parent_dir):
reload_dir = False
s = request.GET.get('reloaddir', None)
if s and s.lower() == 'true':
reload_dir = True
if not reload_dir:
return Response('success')
return reloaddir(request, repo, parent_dir)
# deprecated
class OpDeleteView(APIView):
"""
Delete files.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
def post(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
username = request.user.username
if check_folder_permission(request, repo_id, '/') != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to delete this file.')
if not check_folder_permission(request, repo_id, '/'):
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
parent_dir = request.GET.get('p')
file_names = request.POST.get("file_names")
if not parent_dir or not file_names:
return api_error(status.HTTP_404_NOT_FOUND,
'File or directory not found.')
parent_dir_utf8 = parent_dir.encode('utf-8')
for file_name in file_names.split(':'):
file_name = unquote(file_name.encode('utf-8'))
try:
seafile_api.del_file(repo_id, parent_dir_utf8,
file_name, username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_necessary (request, repo, parent_dir_utf8)
class OpMoveView(APIView):
"""
Move files.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
def post(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
username = request.user.username
parent_dir = request.GET.get('p', '/')
dst_repo = request.POST.get('dst_repo', None)
dst_dir = request.POST.get('dst_dir', None)
file_names = request.POST.get("file_names", None)
if not parent_dir or not file_names or not dst_repo or not dst_dir:
return api_error(status.HTTP_400_BAD_REQUEST,
'Missing argument.')
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to move file in this folder.')
if check_folder_permission(request, dst_repo, dst_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to move file to destination folder.')
if repo_id == dst_repo and parent_dir == dst_dir:
return api_error(status.HTTP_400_BAD_REQUEST,
'The destination directory is the same as the source.')
parent_dir_utf8 = parent_dir.encode('utf-8')
for file_name in file_names.split(':'):
file_name = unquote(file_name.encode('utf-8'))
new_filename = check_filename_with_rename_utf8(dst_repo, dst_dir,
file_name)
try:
seafile_api.move_file(repo_id, parent_dir_utf8, file_name,
dst_repo, dst_dir, new_filename,
username, 0, synchronous=1)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to move file.")
return reloaddir_if_necessary (request, repo, parent_dir_utf8)
class OpCopyView(APIView):
"""
Copy files.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
def post(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
username = request.user.username
parent_dir = request.GET.get('p', '/')
dst_repo = request.POST.get('dst_repo', None)
dst_dir = request.POST.get('dst_dir', None)
file_names = request.POST.get("file_names", None)
if not parent_dir or not file_names or not dst_repo or not dst_dir:
return api_error(status.HTTP_400_BAD_REQUEST,
'Missing argument.')
if check_folder_permission(request, repo_id, parent_dir) is None:
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to copy file of this folder.')
if check_folder_permission(request, dst_repo, dst_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to copy file to destination folder.')
if not get_repo(dst_repo):
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
if seafile_api.get_dir_id_by_path(repo_id, parent_dir) is None or \
seafile_api.get_dir_id_by_path(dst_repo, dst_dir) is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path does not exist.')
parent_dir_utf8 = parent_dir.encode('utf-8')
for file_name in file_names.split(':'):
file_name = unquote(file_name.encode('utf-8'))
new_filename = check_filename_with_rename_utf8(dst_repo, dst_dir,
file_name)
try:
seafile_api.copy_file(repo_id, parent_dir_utf8, file_name,
dst_repo, dst_dir, new_filename,
username, 0, synchronous=1)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to copy file.")
return reloaddir_if_necessary(request, repo, parent_dir_utf8)
class StarredFileView(APIView):
"""
Support uniform interface for starred file operation,
including add/delete/list starred files.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
# list starred files
personal_files = UserStarredFiles.objects.get_starred_files_by_username(
request.user.username)
starred_files = prepare_starred_files(personal_files)
return Response(starred_files)
def post(self, request, format=None):
# add starred file
repo_id = request.POST.get('repo_id', '')
path = request.POST.get('p', '')
if not (repo_id and path):
return api_error(status.HTTP_400_BAD_REQUEST,
'Library ID or path is missing.')
if check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied')
try:
file_id = seafile_api.get_file_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal error')
if not file_id:
return api_error(status.HTTP_404_NOT_FOUND, "File not found")
if path[-1] == '/': # Should not contain '/' at the end of path.
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid file path.')
star_file(request.user.username, repo_id, path, is_dir=False,
org_id=-1)
resp = Response('success', status=status.HTTP_201_CREATED)
resp['Location'] = reverse('starredfiles')
return resp
def delete(self, request, format=None):
# remove starred file
repo_id = request.GET.get('repo_id', '')
path = request.GET.get('p', '')
if not (repo_id and path):
return api_error(status.HTTP_400_BAD_REQUEST,
'Library ID or path is missing.')
if check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied')
try:
file_id = seafile_api.get_file_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal error')
if not file_id:
return api_error(status.HTTP_404_NOT_FOUND, "File not found")
if path[-1] == '/': # Should not contain '/' at the end of path.
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid file path.')
unstar_file(request.user.username, repo_id, path)
return Response('success', status=status.HTTP_200_OK)
class OwaFileView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
""" Get action url and access token when view file through Office Web App
"""
# check args
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
path = request.GET.get('path', None)
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
file_id = seafile_api.get_file_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if not file_id:
error_msg = 'File %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# check permission
if not is_pro_version():
error_msg = 'Office Web App feature only supported in professional edition.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if check_folder_permission(request, repo_id, path) is None:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if repo.encrypted:
error_msg = 'Library encrypted.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not ENABLE_OFFICE_WEB_APP:
error_msg = 'Office Web App feature not enabled.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
filename = os.path.basename(path)
filetype, fileext = get_file_type_and_ext(filename)
if fileext not in OFFICE_WEB_APP_FILE_EXTENSION:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# get wopi dict
from seahub_extra.wopi.utils import get_wopi_dict
username = request.user.username
wopi_dict = get_wopi_dict(username, repo_id, path)
# send stats message
send_file_access_msg(request, repo, path, 'api')
return Response(wopi_dict)
class DevicesView(APIView):
"""List user devices"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
username = request.user.username
user_devices = get_user_devices(username)
return Response(user_devices)
def delete(self, request, format=None):
platform = request.data.get('platform', '')
device_id = request.data.get('device_id', '')
if not platform:
error_msg = 'platform invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if not device_id:
error_msg = 'device_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
do_unlink_device(request.user.username, platform, device_id)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})
class FileView(APIView):
"""
Support uniform interface for file related operations,
including create/delete/rename/view, etc.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
# view file
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
if check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to access this file.')
file_id = None
try:
file_id = seafile_api.get_file_id_by_path(repo_id,
path.encode('utf-8'))
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get file id by path.")
if not file_id:
return api_error(status.HTTP_404_NOT_FOUND, "File not found")
# send stats message
send_file_access_msg(request, repo, path, 'api')
file_name = os.path.basename(path)
op = request.GET.get('op', 'download')
reuse = request.GET.get('reuse', '0')
if reuse not in ('1', '0'):
return api_error(status.HTTP_400_BAD_REQUEST,
"If you want to reuse file server access token for download file, you should set 'reuse' argument as '1'.")
use_onetime = False if reuse == '1' else True
return get_repo_file(request, repo_id, file_id,
file_name, op, use_onetime)
def post(self, request, repo_id, format=None):
# rename, move, copy or create file
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', '')
if not path or path[0] != '/':
return api_error(status.HTTP_400_BAD_REQUEST,
'Path is missing or invalid.')
username = request.user.username
parent_dir = os.path.dirname(path)
operation = request.POST.get('operation', '')
if operation.lower() == 'rename':
if check_folder_permission(request, repo_id, path) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to rename file.')
newname = request.POST.get('newname', '')
if not newname:
return api_error(status.HTTP_400_BAD_REQUEST,
'New name is missing')
newname = newname.encode('utf-8')
if len(newname) > settings.MAX_UPLOAD_FILE_NAME_LEN:
return api_error(status.HTTP_400_BAD_REQUEST, 'New name is too long')
parent_dir_utf8 = parent_dir.encode('utf-8')
oldname = os.path.basename(path)
if oldname == newname:
return api_error(status.HTTP_409_CONFLICT,
'The new name is the same to the old')
newname = check_filename_with_rename_utf8(repo_id, parent_dir,
newname)
try:
seafile_api.rename_file(repo_id, parent_dir, oldname, newname,
username)
except SearpcError,e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to rename file: %s" % e)
if request.GET.get('reloaddir', '').lower() == 'true':
return reloaddir(request, repo, parent_dir_utf8)
else:
resp = Response('success', status=status.HTTP_301_MOVED_PERMANENTLY)
uri = reverse('FileView', args=[repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(parent_dir_utf8) + quote(newname)
return resp
elif operation.lower() == 'move':
if check_folder_permission(request, repo_id, path) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to move file.')
src_dir = os.path.dirname(path)
src_dir_utf8 = src_dir.encode('utf-8')
src_repo_id = repo_id
dst_repo_id = request.POST.get('dst_repo', '')
dst_dir = request.POST.get('dst_dir', '')
dst_dir_utf8 = dst_dir.encode('utf-8')
if dst_dir[-1] != '/': # Append '/' to the end of directory if necessary
dst_dir += '/'
# obj_names = request.POST.get('obj_names', '')
if not (dst_repo_id and dst_dir):
return api_error(status.HTTP_400_BAD_REQUEST, 'Missing arguments.')
if src_repo_id == dst_repo_id and src_dir == dst_dir:
return Response('success', status=status.HTTP_200_OK)
if check_folder_permission(request, dst_repo_id, dst_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to move file.')
filename = os.path.basename(path)
filename_utf8 = filename.encode('utf-8')
new_filename_utf8 = check_filename_with_rename_utf8(dst_repo_id,
dst_dir,
filename)
try:
seafile_api.move_file(src_repo_id, src_dir_utf8,
filename_utf8, dst_repo_id,
dst_dir_utf8, new_filename_utf8,
username, 0, synchronous=1)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
dst_repo = get_repo(dst_repo_id)
if not dst_repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
if request.GET.get('reloaddir', '').lower() == 'true':
return reloaddir(request, dst_repo, dst_dir)
else:
resp = Response('success', status=status.HTTP_301_MOVED_PERMANENTLY)
uri = reverse('FileView', args=[dst_repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(dst_dir_utf8) + quote(new_filename_utf8)
return resp
elif operation.lower() == 'copy':
src_repo_id = repo_id
src_dir = os.path.dirname(path)
src_dir_utf8 = src_dir.encode('utf-8')
dst_repo_id = request.POST.get('dst_repo', '')
dst_dir = request.POST.get('dst_dir', '')
dst_dir_utf8 = dst_dir.encode('utf-8')
if dst_dir[-1] != '/': # Append '/' to the end of directory if necessary
dst_dir += '/'
if not (dst_repo_id and dst_dir):
return api_error(status.HTTP_400_BAD_REQUEST, 'Missing arguments.')
if src_repo_id == dst_repo_id and src_dir == dst_dir:
return Response('success', status=status.HTTP_200_OK)
# check src folder permission
if check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to copy file.')
# check dst folder permission
if check_folder_permission(request, dst_repo_id, dst_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to copy file.')
filename = os.path.basename(path)
filename_utf8 = filename.encode('utf-8')
new_filename_utf8 = check_filename_with_rename_utf8(dst_repo_id,
dst_dir,
filename)
try:
seafile_api.copy_file(src_repo_id, src_dir_utf8,
filename_utf8, dst_repo_id,
dst_dir_utf8, new_filename_utf8,
username, 0, synchronous=1)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
if request.GET.get('reloaddir', '').lower() == 'true':
return reloaddir(request, dst_repo, dst_dir)
else:
resp = Response('success', status=status.HTTP_200_OK)
uri = reverse('FileView', args=[dst_repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(dst_dir_utf8) + quote(new_filename_utf8)
return resp
elif operation.lower() == 'create':
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create file.')
parent_dir_utf8 = parent_dir.encode('utf-8')
new_file_name = os.path.basename(path)
new_file_name_utf8 = check_filename_with_rename_utf8(repo_id,
parent_dir,
new_file_name)
try:
seafile_api.post_empty_file(repo_id, parent_dir,
new_file_name_utf8, username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to create file.')
if request.GET.get('reloaddir', '').lower() == 'true':
return reloaddir(request, repo, parent_dir)
else:
resp = Response('success', status=status.HTTP_201_CREATED)
uri = reverse('FileView', args=[repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(parent_dir_utf8) + \
quote(new_file_name_utf8)
return resp
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation can only be rename, create or move.")
def put(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.data.get('p', '')
file_id = seafile_api.get_file_id_by_path(repo_id, path)
if not path or not file_id:
return api_error(status.HTTP_400_BAD_REQUEST,
'Path is missing or invalid.')
username = request.user.username
# check file access permission
if check_folder_permission(request, repo_id, path) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
operation = request.data.get('operation', '')
if operation.lower() == 'lock':
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
if is_locked:
return api_error(status.HTTP_403_FORBIDDEN, 'File is already locked')
# lock file
expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS)
try:
seafile_api.lock_file(repo_id, path.lstrip('/'), username, expire)
return Response('success', status=status.HTTP_200_OK)
except SearpcError, e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal error')
if operation.lower() == 'unlock':
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
if not is_locked:
return api_error(status.HTTP_403_FORBIDDEN, 'File is not locked')
if not locked_by_me:
return api_error(status.HTTP_403_FORBIDDEN, 'You can not unlock this file')
# unlock file
try:
seafile_api.unlock_file(repo_id, path.lstrip('/'))
return Response('success', status=status.HTTP_200_OK)
except SearpcError, e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal error')
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation can only be lock or unlock")
def delete(self, request, repo_id, format=None):
# delete file
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
parent_dir = os.path.dirname(path)
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
parent_dir_utf8 = os.path.dirname(path).encode('utf-8')
file_name_utf8 = os.path.basename(path).encode('utf-8')
try:
seafile_api.del_file(repo_id, parent_dir_utf8,
file_name_utf8,
request.user.username)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_necessary(request, repo, parent_dir_utf8)
class FileDetailView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = seafile_api.get_repo(repo_id)
if repo is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Library not found.')
path = request.GET.get('p', None)
if path is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
commit_id = request.GET.get('commit_id', None)
if commit_id:
try:
obj_id = seafserv_threaded_rpc.get_file_id_by_commit_and_path(
repo.id, commit_id, path)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Failed to get file id.')
else:
try:
obj_id = seafile_api.get_file_id_by_path(repo_id,
path.encode('utf-8'))
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Failed to get file id.')
if not obj_id:
return api_error(status.HTTP_404_NOT_FOUND, 'File not found.')
# fetch file contributors and latest contributor
try:
# get real path for sub repo
real_path = repo.origin_path + path if repo.origin_path else path
dirent = seafile_api.get_dirent_by_path(repo.store_id, real_path)
if dirent:
latest_contributor, last_modified = dirent.modifier, dirent.mtime
else:
latest_contributor, last_modified = None, 0
except SearpcError as e:
logger.error(e)
latest_contributor, last_modified = None, 0
entry = {}
try:
entry["size"] = get_file_size(repo.store_id, repo.version, obj_id)
except Exception, e:
entry["size"] = 0
entry["type"] = "file"
entry["name"] = os.path.basename(path)
entry["id"] = obj_id
entry["mtime"] = last_modified
return HttpResponse(json.dumps(entry), status=200,
content_type=json_content_type)
class FileRevert(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def put(self, request, repo_id, format=None):
path = request.data.get('p', None)
commit_id = request.data.get('commit_id', None)
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if not commit_id:
error_msg = 'commit_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if not seafile_api.get_repo(repo_id):
error_msg = 'library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_file_id_by_commit_and_path(repo_id, commit_id, path):
error_msg = 'file %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if check_folder_permission(request, repo_id, '/') != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
username = request.user.username
try:
seafile_api.revert_file(repo_id, commit_id, path, username)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})
class FileRevision(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
path = request.GET.get('p', None)
if path is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
file_name = os.path.basename(path)
commit_id = request.GET.get('commit_id', None)
try:
obj_id = seafserv_threaded_rpc.get_file_id_by_commit_and_path(
repo_id, commit_id, path)
except:
return api_error(status.HTTP_404_NOT_FOUND, 'Revision not found.')
return get_repo_file(request, repo_id, obj_id, file_name, 'download')
class FileHistory(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
path = request.GET.get('p', None)
if path is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
try:
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, path,
-1, -1)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, "Internal error")
if not commits:
return api_error(status.HTTP_404_NOT_FOUND, 'File not found.')
return HttpResponse(json.dumps({"commits": commits}, cls=SearpcObjEncoder), status=200, content_type=json_content_type)
class FileSharedLinkView(APIView):
"""
Support uniform interface for file shared link.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def put(self, request, repo_id, format=None):
repo = seaserv.get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, "Library does not exist")
path = request.data.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing')
username = request.user.username
password = request.data.get('password', None)
share_type = request.data.get('share_type', 'download')
if password and len(password) < config.SHARE_LINK_PASSWORD_MIN_LENGTH:
return api_error(status.HTTP_400_BAD_REQUEST, 'Password is too short')
if share_type.lower() == 'download':
if check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied')
expire = request.data.get('expire', None)
if expire:
try:
expire_days = int(expire)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid expiration days')
else:
expire_date = timezone.now() + relativedelta(days=expire_days)
else:
expire_date = None
is_dir = False
if path == '/':
is_dir = True
else:
try:
real_path = repo.origin_path + path if repo.origin_path else path
dirent = seafile_api.get_dirent_by_path(repo.store_id, real_path)
except SearpcError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, "Internal error")
if not dirent:
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid path')
if stat.S_ISDIR(dirent.mode):
is_dir = True
if is_dir:
# generate dir download link
fs = FileShare.objects.get_dir_link_by_path(username, repo_id, path)
if fs is None:
fs = FileShare.objects.create_dir_link(username, repo_id, path,
password, expire_date)
if is_org_context(request):
org_id = request.user.org.org_id
OrgFileShare.objects.set_org_file_share(org_id, fs)
else:
# generate file download link
fs = FileShare.objects.get_file_link_by_path(username, repo_id, path)
if fs is None:
fs = FileShare.objects.create_file_link(username, repo_id, path,
password, expire_date)
if is_org_context(request):
org_id = request.user.org.org_id
OrgFileShare.objects.set_org_file_share(org_id, fs)
token = fs.token
shared_link = gen_shared_link(token, fs.s_type)
elif share_type.lower() == 'upload':
if not seafile_api.get_dir_id_by_path(repo_id, path):
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid path')
if check_folder_permission(request, repo_id, path) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied')
# generate upload link
uls = UploadLinkShare.objects.get_upload_link_by_path(username, repo_id, path)
if uls is None:
uls = UploadLinkShare.objects.create_upload_link_share(
username, repo_id, path, password)
token = uls.token
shared_link = gen_shared_upload_link(token)
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation can only be download or upload.")
resp = Response(status=status.HTTP_201_CREATED)
resp['Location'] = shared_link
return resp
########## Directory related
class DirView(APIView):
"""
Support uniform interface for directory operations, including
create/delete/rename/list, etc.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
# list dir
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
if check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN, 'Forbid to access this folder.')
try:
dir_id = seafile_api.get_dir_id_by_path(repo_id,
path.encode('utf-8'))
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get dir id by path.")
if not dir_id:
return api_error(status.HTTP_404_NOT_FOUND, "Path does not exist")
old_oid = request.GET.get('oid', None)
if old_oid and old_oid == dir_id:
response = HttpResponse(json.dumps("uptodate"), status=200,
content_type=json_content_type)
response["oid"] = dir_id
return response
else:
request_type = request.GET.get('t', None)
if request_type and request_type not in ('f', 'd'):
return api_error(status.HTTP_400_BAD_REQUEST,
"'t'(type) should be 'f' or 'd'.")
if request_type == 'd':
recursive = request.GET.get('recursive', '0')
if recursive not in ('1', '0'):
return api_error(status.HTTP_400_BAD_REQUEST,
"If you want to get recursive dir entries, you should set 'recursive' argument as '1'.")
if recursive == '1':
username = request.user.username
dir_list = get_dir_recursively(username, repo_id, path, [])
dir_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
response = HttpResponse(json.dumps(dir_list), status=200,
content_type=json_content_type)
response["oid"] = dir_id
response["dir_perm"] = seafile_api.check_permission_by_path(repo_id, path, username)
return response
return get_dir_entrys_by_id(request, repo, path, dir_id, request_type)
def post(self, request, repo_id, format=None):
# new dir
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', '')
if not path or path[0] != '/':
return api_error(status.HTTP_400_BAD_REQUEST, "Path is missing.")
if path == '/': # Can not make or rename root dir.
return api_error(status.HTTP_400_BAD_REQUEST, "Path is invalid.")
if path[-1] == '/': # Cut out last '/' if possible.
path = path[:-1]
username = request.user.username
operation = request.POST.get('operation', '')
parent_dir = os.path.dirname(path)
parent_dir_utf8 = parent_dir.encode('utf-8')
if operation.lower() == 'mkdir':
parent_dir = os.path.dirname(path)
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to access this folder.')
create_parents = request.POST.get('create_parents', '').lower() in ('true', '1')
if not create_parents:
# check whether parent dir exists
if not seafile_api.get_dir_id_by_path(repo_id, parent_dir):
return api_error(status.HTTP_400_BAD_REQUEST,
'Parent dir does not exist')
new_dir_name = os.path.basename(path)
new_dir_name_utf8 = check_filename_with_rename_utf8(repo_id,
parent_dir,
new_dir_name)
try:
seafile_api.post_dir(repo_id, parent_dir,
new_dir_name_utf8, username)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to make directory.')
else:
if not is_seafile_pro():
return api_error(status.HTTP_400_BAD_REQUEST,
'Feature not supported.')
try:
seafile_api.mkdir_with_parents(repo_id, '/',
path[1:], username)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to make directory.')
new_dir_name_utf8 = os.path.basename(path).encode('utf-8')
if request.GET.get('reloaddir', '').lower() == 'true':
resp = reloaddir(request, repo, parent_dir)
else:
resp = Response('success', status=status.HTTP_201_CREATED)
uri = reverse('DirView', args=[repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(parent_dir_utf8) + \
quote(new_dir_name_utf8)
return resp
elif operation.lower() == 'rename':
if check_folder_permission(request, repo.id, path) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to access this folder.')
parent_dir = os.path.dirname(path)
old_dir_name = os.path.basename(path)
newname = request.POST.get('newname', '')
if not newname:
return api_error(status.HTTP_400_BAD_REQUEST, "New name is mandatory.")
if newname == old_dir_name:
return Response('success', status=status.HTTP_200_OK)
try:
# rename duplicate name
checked_newname = check_filename_with_rename(
repo_id, parent_dir, newname)
# rename dir
seafile_api.rename_file(repo_id, parent_dir, old_dir_name,
checked_newname, username)
return Response('success', status=status.HTTP_200_OK)
except SearpcError, e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to rename folder.')
# elif operation.lower() == 'move':
# pass
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation not supported.")
def delete(self, request, repo_id, format=None):
# delete dir or file
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
if check_folder_permission(request, repo_id, path) != 'rw':
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to access this folder.')
if path == '/': # Can not delete root path.
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is invalid.')
if path[-1] == '/': # Cut out last '/' if possible.
path = path[:-1]
parent_dir = os.path.dirname(path)
parent_dir_utf8 = os.path.dirname(path).encode('utf-8')
file_name_utf8 = os.path.basename(path).encode('utf-8')
username = request.user.username
try:
seafile_api.del_file(repo_id, parent_dir_utf8,
file_name_utf8, username)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_necessary(request, repo, parent_dir_utf8)
class DirDownloadView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', None)
if path is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
if path[-1] != '/': # Normalize dir path
path += '/'
if len(path) > 1:
dirname = os.path.basename(path.rstrip('/'))
else:
dirname = repo.name
current_commit = get_commits(repo_id, 0, 1)[0]
if not current_commit:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Failed to get current commit of repo %s.' % repo_id)
try:
dir_id = seafile_api.get_dir_id_by_commit_and_path(current_commit.repo_id,
current_commit.id, path)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get dir id by path")
if not dir_id:
return api_error(status.HTTP_404_NOT_FOUND, "Path does not exist")
try:
total_size = seafserv_threaded_rpc.get_dir_size(repo.store_id, repo.version,
dir_id)
except Exception, e:
logger.error(str(e))
return api_error(HTTP_520_OPERATION_FAILED, "Internal error")
if total_size > MAX_DOWNLOAD_DIR_SIZE:
return api_error(status.HTTP_400_BAD_REQUEST,
'Unable to download directory "%s": size is too large.' % dirname)
token = seafile_api.get_fileserver_access_token(repo_id,
dir_id,
'download-dir',
request.user.username)
redirect_url = gen_file_get_url(token, dirname)
return HttpResponse(json.dumps(redirect_url), status=200,
content_type=json_content_type)
class DirRevert(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def put(self, request, repo_id):
path = request.data.get('p', None)
commit_id = request.data.get('commit_id', None)
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if not commit_id:
error_msg = 'commit_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if not seafile_api.get_repo(repo_id):
error_msg = 'library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_dir_id_by_commit_and_path(repo_id, commit_id, path):
error_msg = 'folder %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if check_folder_permission(request, repo_id, '/') != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
username = request.user.username
try:
seafile_api.revert_dir(repo_id, commit_id, path, username)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
return Response({'success': True})
class DirSubRepoView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
# from seahub.views.ajax.py::sub_repo
def get(self, request, repo_id, format=None):
'''
check if a dir has a corresponding sub_repo
if it does not have, create one
'''
result = {}
path = request.GET.get('p')
name = request.GET.get('name')
password = request.GET.get('password', None)
repo = get_repo(repo_id)
if not repo:
result['error'] = 'Library not found.'
return HttpResponse(json.dumps(result), status=404, content_type=json_content_type)
if not (path and name):
result['error'] = 'Argument missing'
return HttpResponse(json.dumps(result), status=400, content_type=json_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=json_content_type)
if sub_repo:
result['sub_repo_id'] = sub_repo.id
else:
if not request.user.permissions.can_add_repo():
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create library.')
# create a sub-lib
try:
# use name as 'repo_name' & 'repo_desc' for sub_repo
if repo.encrypted:
if password:
sub_repo_id = seafile_api.create_virtual_repo(repo_id,
path, name, name, username, password)
else:
result['error'] = 'Password Required.'
return HttpResponse(json.dumps(result), status=403, content_type=json_content_type)
else:
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=json_content_type)
return HttpResponse(json.dumps(result), content_type=json_content_type)
########## Sharing
class SharedRepos(APIView):
"""
List repos that a user share to others/groups/public.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
username = request.user.username
shared_repos = []
shared_repos += list_share_repos(username, 'from_email', -1, -1)
shared_repos += get_group_repos_by_owner(username)
if not CLOUD_MODE:
shared_repos += list_inner_pub_repos_by_owner(username)
return HttpResponse(json.dumps(shared_repos, cls=SearpcObjEncoder),
status=200, content_type=json_content_type)
class BeShared(APIView):
"""
List repos that others/groups share to user.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
username = request.user.username
shared_repos = []
shared_repos += seafile_api.get_share_in_repo_list(username, -1, -1)
joined_groups = get_personal_groups_by_user(username)
for grp in joined_groups:
# Get group repos, and for each group repos...
for r_id in get_group_repoids(grp.id):
# No need to list my own repo
if seafile_api.is_repo_owner(username, r_id):
continue
# 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
cmmts = get_commits(r_id, 0, 1)
last_commit = cmmts[0] if cmmts else None
r.last_modified = last_commit.ctime if last_commit else 0
r._dict['share_type'] = 'group'
r.user = seafile_api.get_repo_owner(r_id)
r.user_perm = check_permission(r_id, username)
shared_repos.append(r)
if not CLOUD_MODE:
shared_repos += seaserv.list_inner_pub_repos(username)
return HttpResponse(json.dumps(shared_repos, cls=SearpcObjEncoder),
status=200, content_type=json_content_type)
class VirtualRepos(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
result = {}
try:
virtual_repos = get_virtual_repos_by_owner(request)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"error:" + e.msg)
result['virtual-repos'] = virtual_repos
return HttpResponse(json.dumps(result, cls=SearpcObjEncoder),
content_type=json_content_type)
class SharedFileView(APIView):
# Anyone should be able to access a Shared File assuming they have the token
throttle_classes = (UserRateThrottle, )
def get(self, request, token, format=None):
assert token is not None # Checked by URLconf
try:
fileshare = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
return api_error(status.HTTP_404_NOT_FOUND, "Token not found")
repo_id = fileshare.repo_id
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, "Library not found")
path = fileshare.path.rstrip('/') # Normalize file path
file_name = os.path.basename(path)
file_id = None
try:
file_id = seafserv_threaded_rpc.get_file_id_by_path(repo_id,
path.encode('utf-8'))
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get file id by path.")
if not file_id:
return api_error(status.HTTP_404_NOT_FOUND, "File not found")
# Increase file shared link view_cnt, this operation should be atomic
fileshare.view_cnt = F('view_cnt') + 1
fileshare.save()
op = request.GET.get('op', 'download')
return get_repo_file(request, repo_id, file_id, file_name, op)
class SharedFileDetailView(APIView):
throttle_classes = (UserRateThrottle, )
def get(self, request, token, format=None):
assert token is not None # Checked by URLconf
try:
fileshare = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
return api_error(status.HTTP_404_NOT_FOUND, "Token not found")
if fileshare.is_encrypted():
password = request.GET.get('password', '')
if not password:
return api_error(status.HTTP_403_FORBIDDEN, "Password is required")
if not check_password(password, fileshare.password):
return api_error(status.HTTP_403_FORBIDDEN, "Invalid Password")
repo_id = fileshare.repo_id
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, "Library not found")
path = fileshare.path.rstrip('/') # Normalize file path
file_name = os.path.basename(path)
file_id = None
try:
file_id = seafserv_threaded_rpc.get_file_id_by_path(repo_id,
path.encode('utf-8'))
commits = seafserv_threaded_rpc.list_file_revisions(repo_id, path,
-1, -1)
c = commits[0]
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get file id by path.")
if not file_id:
return api_error(status.HTTP_404_NOT_FOUND, "File not found")
entry = {}
try:
entry["size"] = get_file_size(repo.store_id, repo.version, file_id)
except Exception as e:
logger.error(e)
entry["size"] = 0
entry["type"] = "file"
entry["name"] = file_name
entry["id"] = file_id
entry["mtime"] = c.ctime
entry["repo_id"] = repo_id
entry["path"] = path
return HttpResponse(json.dumps(entry), status=200,
content_type=json_content_type)
class FileShareEncoder(json.JSONEncoder):
def default(self, obj):
if not isinstance(obj, FileShare):
return None
return {'username':obj.username, 'repo_id':obj.repo_id,
'path':obj.path, 'token':obj.token,
'ctime':obj.ctime, 'view_cnt':obj.view_cnt,
's_type':obj.s_type}
class SharedLinksView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
# from seahub.share.view::list_shared_links
def get(self, request, format=None):
username = request.user.username
fileshares = FileShare.objects.filter(username=username)
p_fileshares = [] # personal file share
for fs in fileshares:
if is_personal_repo(fs.repo_id): # only list files in personal repos
r = seafile_api.get_repo(fs.repo_id)
if not r:
fs.delete()
continue
if fs.s_type == 'f':
if seafile_api.get_file_id_by_path(r.id, fs.path) is None:
fs.delete()
continue
fs.filename = os.path.basename(fs.path)
fs.shared_link = gen_file_share_link(fs.token)
else:
if seafile_api.get_dir_id_by_path(r.id, fs.path) is None:
fs.delete()
continue
fs.filename = os.path.basename(fs.path.rstrip('/'))
fs.shared_link = gen_dir_share_link(fs.token)
fs.repo = r
p_fileshares.append(fs)
return HttpResponse(json.dumps({"fileshares": p_fileshares}, cls=FileShareEncoder), status=200, content_type=json_content_type)
def delete(self, request, format=None):
token = request.GET.get('t', None)
if not token:
return api_error(status.HTTP_400_BAD_REQUEST, 'Token is missing')
username = request.user.username
share = FileShare.objects.filter(token=token).filter(username=username) or \
UploadLinkShare.objects.filter(token=token).filter(username=username)
if not share:
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid token')
share.delete()
return HttpResponse(json.dumps({}), status=200, content_type=json_content_type)
class SharedDirView(APIView):
throttle_classes = (UserRateThrottle, )
def get(self, request, token, format=None):
"""List dirents in dir download shared link
"""
fileshare = FileShare.objects.get_valid_dir_link_by_token(token)
if not fileshare:
return api_error(status.HTTP_400_BAD_REQUEST, "Invalid token")
repo_id = fileshare.repo_id
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_400_BAD_REQUEST, "Invalid token")
if fileshare.is_encrypted():
password = request.GET.get('password', '')
if not password:
return api_error(status.HTTP_403_FORBIDDEN, "Password is required")
if not check_password(password, fileshare.password):
return api_error(status.HTTP_403_FORBIDDEN, "Invalid Password")
req_path = request.GET.get('p', '/')
if req_path[-1] != '/':
req_path += '/'
if req_path == '/':
real_path = fileshare.path
else:
real_path = posixpath.join(fileshare.path, req_path.lstrip('/'))
if real_path[-1] != '/': # Normalize dir path
real_path += '/'
dir_id = seafile_api.get_dir_id_by_path(repo_id, real_path)
if not dir_id:
return api_error(status.HTTP_400_BAD_REQUEST, "Invalid path")
username = fileshare.username
try:
dirs = seafserv_threaded_rpc.list_dir_with_perm(repo_id, real_path, dir_id,
username, -1, -1)
dirs = dirs if dirs else []
except SearpcError, e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED, "Failed to list dir.")
dir_list, file_list = [], []
for dirent in dirs:
dtype = "file"
entry = {}
if stat.S_ISDIR(dirent.mode):
dtype = "dir"
else:
if repo.version == 0:
entry["size"] = get_file_size(repo.store_id, repo.version,
dirent.obj_id)
else:
entry["size"] = dirent.size
entry["type"] = dtype
entry["name"] = dirent.obj_name
entry["id"] = dirent.obj_id
entry["mtime"] = dirent.mtime
if dtype == 'dir':
dir_list.append(entry)
else:
file_list.append(entry)
dir_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
file_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower()))
dentrys = dir_list + file_list
content_type = 'application/json; charset=utf-8'
return HttpResponse(json.dumps(dentrys), status=200, content_type=content_type)
class DefaultRepoView(APIView):
"""
Get user's default library.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
username = request.user.username
repo_id = UserOptions.objects.get_default_repo(username)
if repo_id is None or (get_repo(repo_id) is None):
json = {
'exists': False,
}
return Response(json)
else:
return self.default_repo_info(repo_id)
def default_repo_info(self, repo_id):
repo_json = {
'exists': False,
}
if repo_id is not None:
repo_json['exists'] = True
repo_json['repo_id'] = repo_id
return Response(repo_json)
def post(self, request):
if not request.user.permissions.can_add_repo():
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create library.')
username = request.user.username
repo_id = UserOptions.objects.get_default_repo(username)
if repo_id and (get_repo(repo_id) is not None):
return self.default_repo_info(repo_id)
repo_id = create_default_library(request)
return self.default_repo_info(repo_id)
class SharedRepo(APIView):
"""
Support uniform interface for shared libraries.
"""
authentication_classes = (TokenAuthentication, SessionAuthentication )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def delete(self, request, repo_id, format=None):
"""
Unshare a library.
Repo owner and system admin can perform this operation.
"""
username = request.user.username
if not request.user.is_staff and not seafile_api.is_repo_owner(
username, repo_id):
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to unshare library.')
share_type = request.GET.get('share_type', '')
if not share_type:
return api_error(status.HTTP_400_BAD_REQUEST,
'Share type is required.')
if share_type == 'personal':
user = request.GET.get('user', '')
if not user:
return api_error(status.HTTP_400_BAD_REQUEST,
'User is required.')
if not is_valid_username(user):
return api_error(status.HTTP_400_BAD_REQUEST,
'User is not valid')
remove_share(repo_id, username, user)
elif share_type == 'group':
group_id = request.GET.get('group_id', '')
if not group_id:
return api_error(status.HTTP_400_BAD_REQUEST,
'Group ID is required.')
try:
group_id = int(group_id)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST,
'Group ID is not valid.')
seafile_api.unset_group_repo(repo_id, int(group_id), username)
elif share_type == 'public':
unset_inner_pub_repo(repo_id)
else:
return api_error(status.HTTP_400_BAD_REQUEST,
'Share type can only be personal or group or public.')
return Response('success', status=status.HTTP_200_OK)
def put(self, request, repo_id, format=None):
"""
Share a repo to users/groups/public.
"""
username = request.user.username
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 api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to share library.')
share_type = request.GET.get('share_type')
user = request.GET.get('user')
users = request.GET.get('users')
group_id = request.GET.get('group_id')
permission = request.GET.get('permission')
if permission != 'rw' and permission != "r":
return api_error(status.HTTP_400_BAD_REQUEST,
'Permission need to be rw or r.')
if share_type == 'personal':
from_email = seafile_api.get_repo_owner(repo_id)
shared_users = []
invalid_users = []
notexistent_users = []
notsharable_errors = []
usernames = []
if user:
usernames += user.split(",")
if users:
usernames += users.split(",")
if not user and not users:
return api_error(status.HTTP_400_BAD_REQUEST,
'User or users (comma separated are mandatory) are not provided')
for u in usernames:
if not u:
continue
if not is_valid_username(u):
invalid_users.append(u)
continue
if not is_registered_user(u):
notexistent_users.append(u)
continue
try:
seafile_api.share_repo(repo_id, from_email, u, permission)
shared_users.append(u)
except SearpcError, e:
logger.error(e)
notsharable_errors.append(e)
if invalid_users or notexistent_users or notsharable_errors:
# removing already created share
for s_user in shared_users:
try:
remove_share(repo_id, from_email, s_user)
except SearpcError, e:
# ignoring this error, go to next unsharing
continue
if invalid_users:
return api_error(status.HTTP_400_BAD_REQUEST,
'Some users are not valid, sharing rolled back')
if notexistent_users:
return api_error(status.HTTP_400_BAD_REQUEST,
'Some users are not existent, sharing rolled back')
if notsharable_errors:
# show the first sharing error
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Internal error occurs, sharing rolled back')
elif share_type == 'group':
try:
group_id = int(group_id)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST,
'Group ID must be integer.')
from_email = seafile_api.get_repo_owner(repo_id)
group = get_group(group_id)
if not group:
return api_error(status.HTTP_400_BAD_REQUEST,
'Group does not exist .')
try:
seafile_api.set_group_repo(repo_id, int(group_id),
from_email, permission)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"Searpc Error: " + e.msg)
elif share_type == 'public':
if not CLOUD_MODE:
if not is_org_repo_creation_allowed(request):
return api_error(status.HTTP_403_FORBIDDEN,
'Failed to share library to public: permission denied.')
try:
seafile_api.add_inner_pub_repo(repo_id, permission)
except SearpcError, e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Failed to share library to public.')
else:
if is_org_context(request):
org_id = request.user.org.org_id
try:
seaserv.seafserv_threaded_rpc.set_org_inner_pub_repo(org_id, repo_id, permission)
send_perm_audit_msg('add-repo-perm', username, 'all', repo_id, '/', permission)
except SearpcError, e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Failed to share library to public.')
else:
return api_error(status.HTTP_403_FORBIDDEN,
'Failed to share library to public.')
else:
return api_error(status.HTTP_400_BAD_REQUEST,
'Share type can only be personal or group or public.')
return Response('success', status=status.HTTP_200_OK)
class GroupAndContacts(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
contacts, umsgnum, group_json, gmsgnum, replies, replynum = get_group_and_contacts(request.user.username)
res = {
"groups": group_json,
"contacts": contacts,
"newreplies":replies,
"replynum": replynum,
"umsgnum" : umsgnum,
"gmsgnum" : gmsgnum,
}
return Response(res)
class EventsView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
if not EVENTS_ENABLED:
events = None
return api_error(status.HTTP_404_NOT_FOUND, 'Events not enabled.')
start = request.GET.get('start', '')
if not start:
start = 0
else:
try:
start = int(start)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST, 'Start id must be integer')
email = request.user.username
events_count = 15
if is_org_context(request):
org_id = request.user.org.org_id
events, events_more_offset = get_org_user_events(org_id, email,
start,
events_count)
else:
events, events_more_offset = get_user_events(email, start,
events_count)
events_more = True if len(events) == events_count else False
l = []
for e in events:
d = dict(etype=e.etype)
l.append(d)
if e.etype == 'repo-update':
d['author'] = e.commit.creator_name
d['time'] = e.commit.ctime
d['desc'] = e.commit.desc
d['repo_id'] = e.repo.id
d['repo_name'] = e.repo.name
d['commit_id'] = e.commit.id
d['converted_cmmt_desc'] = translate_commit_desc_escape(convert_cmmt_desc_link(e.commit))
d['more_files'] = e.commit.more_files
d['repo_encrypted'] = e.repo.encrypted
else:
d['repo_id'] = e.repo_id
d['repo_name'] = e.repo_name
if e.etype == 'repo-create':
d['author'] = e.creator
else:
d['author'] = e.repo_owner
epoch = datetime.datetime(1970, 1, 1)
local = utc_to_local(e.timestamp)
time_diff = local - epoch
d['time'] = time_diff.seconds + (time_diff.days * 24 * 3600)
size = request.GET.get('size', 36)
url, is_default, date_uploaded = api_avatar_url(d['author'], size)
d['nick'] = email2nickname(d['author'])
d['name'] = email2nickname(d['author'])
d['avatar'] = avatar(d['author'], size)
d['avatar_url'] = request.build_absolute_uri(url)
d['time_relative'] = translate_seahub_time(utc_to_local(e.timestamp))
d['date'] = utc_to_local(e.timestamp).strftime("%Y-%m-%d")
ret = {
'events': l,
'more': events_more,
'more_offset': events_more_offset,
}
return Response(ret)
class UnseenMessagesCountView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
username = request.user.username
ret = { 'count' : UserNotification.objects.count_unseen_user_notifications(username)
}
return Response(ret)
########## Groups related
class Groups(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
size = request.GET.get('size', 36)
limit = int(request.GET.get('limit', 8))
with_msg = request.GET.get('with_msg', 'true')
# To not broken the old API, we need to make with_msg default
if with_msg == 'true':
group_json, replynum = get_groups(request.user.username)
res = {"groups": group_json, "replynum": replynum}
return Response(res)
else:
groups_json = []
joined_groups = get_personal_groups_by_user(request.user.username)
for g in joined_groups:
if limit <= 0:
break;
group = {
"id": g.id,
"name": g.group_name,
"creator": g.creator_name,
"ctime": g.timestamp,
"avatar": grp_avatar(g.id, int(size)),
}
groups_json.append(group)
limit = limit - 1
return Response(groups_json)
def put(self, request, format=None):
# modified slightly from groups/views.py::group_list
"""
Add a new group.
"""
result = {}
content_type = 'application/json; charset=utf-8'
username = request.user.username
if not request.user.permissions.can_add_group():
return api_error(status.HTTP_403_FORBIDDEN,
'You do not have permission to create group.')
# check plan
num_of_groups = getattr(request.user, 'num_of_groups', -1)
if num_of_groups > 0:
current_groups = len(get_personal_groups_by_user(username))
if current_groups > num_of_groups:
result['error'] = 'You can only create %d groups.' % num_of_groups
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
group_name = request.data.get('group_name', None)
group_name = group_name.strip()
if not validate_group_name(group_name):
result['error'] = 'Failed to rename group, group name can only contain letters, numbers, blank, hyphen or underscore.'
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
# Check whether group name is duplicated.
if request.cloud_mode:
checked_groups = get_personal_groups_by_user(username)
else:
checked_groups = get_personal_groups(-1, -1)
for g in checked_groups:
if g.group_name == group_name:
result['error'] = 'There is already a group with that name.'
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# Group name is valid, create that group.
try:
group_id = ccnet_threaded_rpc.create_group(group_name.encode('utf-8'),
username)
return HttpResponse(json.dumps({'success': True, 'group_id': group_id}),
content_type=content_type)
except SearpcError, e:
result['error'] = e.msg
return HttpResponse(json.dumps(result), status=500,
content_type=content_type)
def delete(self, request, group_id, format=None):
try:
group_id = int(group_id)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST, 'Bad group id format')
group = seaserv.get_group(group_id)
if not group:
return api_error(status.HTTP_404_NOT_FOUND, 'Group not found')
# permission check
username = request.user.username
if not seaserv.check_group_staff(group_id, username):
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to delete group')
# delete group
if is_org_context(request):
org_id = request.user.org.org_id
else:
org_id = None
try:
remove_group_common(group.id, username, org_id=org_id)
except SearpcError as e:
logger.error(e)
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to remove group.')
return Response('success', status=status.HTTP_200_OK)
def post(self, request, group_id, format=None):
group = seaserv.get_group(group_id)
if not group:
return api_error(status.HTTP_404_NOT_FOUND, 'Group not found')
# permission check
username = request.user.username
if not seaserv.check_group_staff(group.id, username):
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to rename group')
operation = request.POST.get('operation', '')
if operation.lower() == 'rename':
newname = request.POST.get('newname', '')
if not newname:
return api_error(status.HTTP_400_BAD_REQUEST,
'New name is missing')
try:
rename_group_with_new_name(request, group.id, newname)
except BadGroupNameError:
return api_error(status.HTTP_400_BAD_REQUEST,
'Group name is not valid.')
except ConflictGroupNameError:
return api_error(status.HTTP_400_BAD_REQUEST,
'There is already a group with that name.')
return Response('success', status=status.HTTP_200_OK)
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation can only be rename.")
class GroupMembers(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
def put(self, request, group_id, format=None):
"""
Add group members.
"""
try:
group_id_int = int(group_id)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid group ID')
group = get_group(group_id_int)
if not group:
return api_error(status.HTTP_404_NOT_FOUND, 'Group not found')
if not is_group_staff(group, request.user):
return api_error(status.HTTP_403_FORBIDDEN, 'Only administrators can add group members')
user_name = request.data.get('user_name', None)
if not is_registered_user(user_name):
return api_error(status.HTTP_400_BAD_REQUEST, 'Not a valid user')
try:
ccnet_threaded_rpc.group_add_member(group.id, request.user.username, user_name)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Unable to add user to group')
return HttpResponse(json.dumps({'success': True}), status=200, content_type=json_content_type)
def delete(self, request, group_id, format=None):
"""
Delete group members.
"""
try:
group_id_int = int(group_id)
except ValueError:
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid group ID')
group = get_group(group_id_int)
if not group:
return api_error(status.HTTP_404_NOT_FOUND, 'Group not found')
if not is_group_staff(group, request.user):
return api_error(status.HTTP_403_FORBIDDEN, 'Only administrators can remove group members')
user_name = request.data.get('user_name', None)
try:
ccnet_threaded_rpc.group_remove_member(group.id, request.user.username, user_name)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Unable to add user to group')
return HttpResponse(json.dumps({'success': True}), status=200, content_type=json_content_type)
class GroupRepos(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
@api_group_check
def post(self, request, group, format=None):
# add group repo
username = request.user.username
repo_name = request.data.get("name", None)
repo_desc = request.data.get("desc", '')
passwd = request.data.get("passwd", None)
# to avoid 'Bad magic' error when create repo, passwd should be 'None'
# not an empty string when create unencrypted repo
if not passwd:
passwd = None
if (passwd is not None) and (not config.ENABLE_ENCRYPTED_LIBRARY):
return api_error(status.HTTP_403_FORBIDDEN,
'NOT allow to create encrypted library.')
permission = request.data.get("permission", 'r')
if permission != 'r' and permission != 'rw':
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid permission')
if is_org_context(request):
org_id = request.user.org.org_id
repo_id = seafile_api.create_org_repo(repo_name, repo_desc,
username, passwd, org_id)
repo = seafile_api.get_repo(repo_id)
seafile_api.add_org_group_repo(repo_id, org_id, group.id,
username, permission)
else:
repo_id = seafile_api.create_repo(repo_name, repo_desc,
username, passwd)
repo = seafile_api.get_repo(repo_id)
seafile_api.set_group_repo(repo.id, group.id, username, permission)
group_repo = {
"id": repo.id,
"name": repo.name,
"desc": repo.desc,
"size": repo.size,
"size_formatted": filesizeformat(repo.size),
"mtime": repo.last_modified,
"mtime_relative": translate_seahub_time(repo.last_modified),
"encrypted": repo.encrypted,
"permission": permission,
"owner": username,
"owner_nickname": email2nickname(username),
"share_from_me": True,
}
return Response(group_repo, status=200)
@api_group_check
def get(self, request, group, format=None):
username = request.user.username
if group.is_pub:
if not request.user.is_staff and not is_group_user(group.id, username):
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
if is_org_context(request):
org_id = request.user.org.org_id
repos = seafile_api.get_org_group_repos(org_id, group.id)
else:
repos = seafile_api.get_repos_by_group(group.id)
repos.sort(lambda x, y: cmp(y.last_modified, x.last_modified))
group.is_staff = is_group_staff(group, request.user)
repos_json = []
for r in repos:
repo = {
"id": r.id,
"name": r.name,
"desc": r.desc,
"size": r.size,
"size_formatted": filesizeformat(r.size),
"mtime": r.last_modified,
"mtime_relative": translate_seahub_time(r.last_modified),
"encrypted": r.encrypted,
"permission": r.permission,
"owner": r.user,
"owner_nickname": email2nickname(r.user),
"share_from_me": True if username == r.user else False,
}
repos_json.append(repo)
req_from = request.GET.get('from', "")
if req_from == 'web':
return Response({"is_staff": group.is_staff, "repos": repos_json})
else:
return Response(repos_json)
class GroupRepo(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
@api_group_check
def delete(self, request, group, repo_id, format=None):
username = request.user.username
group_id = group.id
if not group.is_staff and not seafile_api.is_repo_owner(username, repo_id):
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
if seaserv.is_org_group(group_id):
org_id = seaserv.get_org_id_by_group(group_id)
seaserv.del_org_group_repo(repo_id, org_id, group_id)
else:
seafile_api.unset_group_repo(repo_id, group_id, username)
return HttpResponse(json.dumps({'success': True}), status=200,
content_type=json_content_type)
class UserAvatarView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, user, size, format=None):
url, is_default, date_uploaded = api_avatar_url(user, int(size))
ret = {
"url": request.build_absolute_uri(url),
"is_default": is_default,
"mtime": get_timestamp(date_uploaded) }
return Response(ret)
class GroupAvatarView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, group_id, size, format=None):
url, is_default, date_uploaded = api_grp_avatar_url(group_id, int(size))
ret = {
"url": request.build_absolute_uri(url),
"is_default": is_default,
"mtime": get_timestamp(date_uploaded)}
return Response(ret)
# Html related code
def html_events(request):
if not EVENTS_ENABLED:
events = None
return render_to_response('api2/events.html', {
"events":events,
}, context_instance=RequestContext(request))
email = request.user.username
start = 0
events_count = 15
if is_org_context(request):
org_id = request.user.org.org_id
events, events_more_offset = get_org_user_events(org_id, email,
start, events_count)
else:
events, events_more_offset = get_user_events(email, start,
events_count)
events_more = True if len(events) == events_count else False
event_groups = group_events_data(events)
prepare_events(event_groups)
return render_to_response('api2/events.html', {
"events": events,
"events_more_offset": events_more_offset,
"events_more": events_more,
"event_groups": event_groups,
"events_count": events_count,
}, context_instance=RequestContext(request))
def ajax_events(request):
events_count = 15
username = request.user.username
start = int(request.GET.get('start', 0))
events, start = get_user_events(username, start, events_count)
events_more = True if len(events) == events_count else False
event_groups = group_events_data(events)
prepare_events(event_groups)
ctx = {'event_groups': event_groups}
html = render_to_string("api2/events_body.html", ctx)
return HttpResponse(json.dumps({'html':html, 'events_more':events_more,
'new_start': start}),
content_type=json_content_type)
def html_repo_history_changes(request, repo_id):
changes = {}
repo = get_repo(repo_id)
if not repo:
return HttpResponse(json.dumps({"err": 'Library does not exist'}), status=400, content_type=json_content_type)
if not check_folder_permission(request, repo_id, '/'):
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
if repo.encrypted and not is_passwd_set(repo_id, request.user.username):
return HttpResponse(json.dumps({"err": 'Library is encrypted'}), status=400, content_type=json_content_type)
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return HttpResponse(json.dumps({"err": 'Invalid argument'}), status=400, content_type=json_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 its 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.'
for k in changes:
changes[k] = [f.replace ('a href="/', 'a class="normal" href="api://') for f in changes[k] ]
html = render_to_string('api2/event_details.html', {'changes': changes})
return HttpResponse(json.dumps({"html": html}), content_type=json_content_type)
class AjaxEvents(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
return ajax_events(request)
class EventsHtml(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
return html_events(request)
class RepoHistoryChange(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return HttpResponse(json.dumps({"err": 'Library does not exist'}),
status=400,
content_type=json_content_type)
if not check_folder_permission(request, repo_id, '/'):
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
if repo.encrypted and not is_passwd_set(repo_id, request.user.username):
return HttpResponse(json.dumps({"err": 'Library is encrypted'}),
status=400,
content_type=json_content_type)
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return HttpResponse(json.dumps({"err": 'Invalid argument'}),
status=400,
content_type=json_content_type)
details = get_diff_details(repo_id, '', commit_id)
return HttpResponse(json.dumps(details),
content_type=json_content_type)
class RepoHistoryChangeHtml(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
return html_repo_history_changes(request, repo_id)
# based on views/file.py::office_convert_query_status
class OfficeConvertQueryStatus(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
if not HAS_OFFICE_CONVERTER:
return api_error(status.HTTP_404_NOT_FOUND, 'Office converter not enabled.')
content_type = 'application/json; charset=utf-8'
ret = {'success': False}
file_id = request.GET.get('file_id', '')
if len(file_id) != 40:
ret['error'] = 'invalid param'
else:
try:
d = query_office_convert_status(file_id)
if d.error:
ret['error'] = d.error
else:
ret['success'] = True
ret['status'] = d.status
except Exception, e:
logging.exception('failed to call query_office_convert_status')
ret['error'] = str(e)
return HttpResponse(json.dumps(ret), content_type=content_type)
# based on views/file.py::view_file and views/file.py::handle_document
class OfficeGenerateView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
username = request.user.username
# check arguments
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
path = request.GET.get('p', '/').rstrip('/')
commit_id = request.GET.get('commit_id', None)
if commit_id:
try:
obj_id = seafserv_threaded_rpc.get_file_id_by_commit_and_path(
repo.id, commit_id, path)
except:
return api_error(status.HTTP_404_NOT_FOUND, 'Revision not found.')
else:
try:
obj_id = seafile_api.get_file_id_by_path(repo_id,
path.encode('utf-8'))
except:
return api_error(status.HTTP_404_NOT_FOUND, 'File not found.')
if not obj_id:
return api_error(status.HTTP_404_NOT_FOUND, 'File not found.')
# Check whether user has permission to view file and get file raw path,
# render error page if permission deny.
raw_path, inner_path, user_perm = get_file_view_path_and_perm(request,
repo_id,
obj_id, path)
if not user_perm:
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to view this file.')
u_filename = os.path.basename(path)
filetype, fileext = get_file_type_and_ext(u_filename)
if filetype != DOCUMENT:
return api_error(status.HTTP_400_BAD_REQUEST, 'File is not a convertable document')
ret_dict = {}
if HAS_OFFICE_CONVERTER:
err = prepare_converted_html(inner_path, obj_id, fileext, ret_dict)
# populate return value dict
ret_dict['err'] = err
ret_dict['obj_id'] = obj_id
else:
ret_dict['filetype'] = 'Unknown'
return HttpResponse(json.dumps(ret_dict), status=200, content_type=json_content_type)
class ThumbnailView(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.')
size = request.GET.get('size', None)
if size is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Size is missing.')
try:
size = int(size)
except ValueError as e:
logger.error(e)
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid size.')
path = request.GET.get('p', None)
obj_id = get_file_id_by_path(repo_id, path)
if path is None or obj_id is None:
return api_error(status.HTTP_400_BAD_REQUEST, 'Wrong path.')
if repo.encrypted or not ENABLE_THUMBNAIL or \
check_folder_permission(request, repo_id, path) is None:
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
success, status_code = generate_thumbnail(request, repo_id, size, path)
if success:
thumbnail_dir = os.path.join(THUMBNAIL_ROOT, str(size))
thumbnail_file = os.path.join(thumbnail_dir, obj_id)
try:
with open(thumbnail_file, 'rb') as f:
thumbnail = f.read()
return HttpResponse(thumbnail, 'image/' + THUMBNAIL_EXTENSION)
except IOError as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Failed to get thumbnail.')
else:
if status_code == 400:
return api_error(status.HTTP_400_BAD_REQUEST, "Invalid argument")
if status_code == 403:
return api_error(status.HTTP_403_FORBIDDEN, 'Forbidden')
if status_code == 500:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Failed to generate thumbnail.')
_REPO_ID_PATTERN = re.compile(r'[-0-9a-f]{36}')
class RepoTokensView(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
@json_response
def get(self, request, format=None):
repos_id_str = request.GET.get('repos', None)
if not repos_id_str:
return api_error(status.HTTP_400_BAD_REQUEST, "You must specify libaries ids")
repos_id = [repo_id for repo_id in repos_id_str.split(',') if repo_id]
if any([not _REPO_ID_PATTERN.match(repo_id) for repo_id in repos_id]):
return api_error(status.HTTP_400_BAD_REQUEST, "Libraries ids are invalid")
tokens = {}
for repo_id in repos_id:
repo = seafile_api.get_repo(repo_id)
if not repo:
continue
if not check_folder_permission(request, repo.id, '/'):
continue
tokens[repo_id] = seafile_api.generate_repo_token(repo_id, request.user.username)
return tokens
class OrganizationView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAdminUser, )
throttle_classes = (UserRateThrottle, )
def post(self, request, format=None):
if not CLOUD_MODE or not MULTI_TENANCY:
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied')
username = request.POST.get('username', None)
password = request.POST.get('password', None)
org_name = request.POST.get('org_name', None)
prefix = request.POST.get('prefix', None)
quota = request.POST.get('quota', None)
member_limit = request.POST.get('member_limit', ORG_MEMBER_QUOTA_DEFAULT)
if not org_name or not username or not password or \
not prefix or not quota or not member_limit:
return api_error(status.HTTP_400_BAD_REQUEST, "Missing argument")
if not is_valid_username(username):
return api_error(status.HTTP_400_BAD_REQUEST, "Email is not valid")
try:
quota_mb = int(quota)
except ValueError as e:
logger.error(e)
return api_error(status.HTTP_400_BAD_REQUEST, "Quota is not valid")
try:
User.objects.get(email = username)
user_exist = True
except User.DoesNotExist:
user_exist = False
if user_exist:
return api_error(status.HTTP_400_BAD_REQUEST, "A user with this email already exists")
slug_re = re.compile(r'^[-a-zA-Z0-9_]+$')
if not slug_re.match(prefix):
return api_error(status.HTTP_400_BAD_REQUEST, "URL prefix can only be letters(a-z), numbers, and the underscore character")
if ccnet_threaded_rpc.get_org_by_url_prefix(prefix):
return api_error(status.HTTP_400_BAD_REQUEST, "An organization with this prefix already exists")
try:
User.objects.create_user(username, password, is_staff=False, is_active=True)
create_org(org_name, prefix, username)
new_org = ccnet_threaded_rpc.get_org_by_url_prefix(prefix)
# set member limit
from seahub_extra.organizations.models import OrgMemberQuota
OrgMemberQuota.objects.set_quota(new_org.org_id, member_limit)
# set quota
quota = quota_mb * get_file_size_unit('MB')
seafserv_threaded_rpc.set_org_quota(new_org.org_id, quota)
return Response('success', status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, "Internal error")
class RepoDownloadSharedLinks(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
# check permission
if org_id:
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if request.user.username != repo_owner or repo.is_virtual:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
shared_links = []
fileshares = FileShare.objects.filter(repo_id=repo_id)
for fs in fileshares:
size = None
shared_link = {}
if fs.is_file_share_link():
path = fs.path.rstrip('/') # Normalize file path
if seafile_api.get_file_id_by_path(repo.id, fs.path) is None:
continue
obj_id = seafile_api.get_file_id_by_path(repo_id, path)
size = seafile_api.get_file_size(repo.store_id, repo.version, obj_id)
else:
path = fs.path
if path[-1] != '/': # Normalize dir path
path += '/'
if seafile_api.get_dir_id_by_path(repo.id, fs.path) is None:
continue
shared_link['create_by'] = fs.username
shared_link['creator_name'] = email2nickname(fs.username)
shared_link['create_time'] = datetime_to_isoformat_timestr(fs.ctime)
shared_link['token'] = fs.token
shared_link['path'] = path
shared_link['name'] = os.path.basename(path.rstrip('/')) if path != '/' else '/'
shared_link['view_count'] = fs.view_cnt
shared_link['share_type'] = fs.s_type
shared_link['size'] = size if size else ''
shared_links.append(shared_link)
return Response(shared_links)
class RepoDownloadSharedLink(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def delete(self, request, repo_id, token, format=None):
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
# check permission
if org_id:
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if request.user.username != repo_owner or repo.is_virtual:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try:
link = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
error_msg = 'Link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
link.delete()
result = {'success': True}
return Response(result)
class RepoUploadSharedLinks(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
# check permission
if org_id:
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if request.user.username != repo_owner or repo.is_virtual:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
shared_links = []
fileshares = UploadLinkShare.objects.filter(repo_id=repo_id)
for fs in fileshares:
shared_link = {}
path = fs.path
if path[-1] != '/': # Normalize dir path
path += '/'
if seafile_api.get_dir_id_by_path(repo.id, fs.path) is None:
continue
shared_link['create_by'] = fs.username
shared_link['creator_name'] = email2nickname(fs.username)
shared_link['create_time'] = datetime_to_isoformat_timestr(fs.ctime)
shared_link['token'] = fs.token
shared_link['path'] = path
shared_link['name'] = os.path.basename(path.rstrip('/')) if path != '/' else '/'
shared_link['view_count'] = fs.view_cnt
shared_links.append(shared_link)
return Response(shared_links)
class RepoUploadSharedLink(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def delete(self, request, repo_id, token, format=None):
repo = get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
org_id = None
if is_org_context(request):
org_id = request.user.org.org_id
# check permission
if org_id:
repo_owner = seafile_api.get_org_repo_owner(repo_id)
else:
repo_owner = seafile_api.get_repo_owner(repo_id)
if request.user.username != repo_owner or repo.is_virtual:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try:
link = UploadLinkShare.objects.get(token=token)
except FileShare.DoesNotExist:
error_msg = 'Link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
link.delete()
result = {'success': True}
return Response(result)
def get_repo_user_folder_perm_result(repo_id, path, user):
result = {}
permission = seafile_api.get_folder_user_perm(repo_id, path, user)
if permission:
result['repo_id'] = repo_id
result['user_email'] = user
result['user_name'] = email2nickname(user)
result['folder_path'] = path
result['folder_name'] = path if path == '/' else os.path.basename(path.rstrip('/'))
result['permission'] = permission
return result
class RepoUserFolderPerm(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
@api_repo_setting_permission_check
def get(self, request, repo_id, format=None):
if not is_pro_version():
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
results = []
folder_perms = seafile_api.list_folder_user_perm_by_repo(repo_id)
for perm in folder_perms:
result = {}
result['repo_id'] = perm.repo_id
result['user_email'] = perm.user
result['user_name'] = email2nickname(perm.user)
result['folder_path'] = perm.path
result['folder_name'] = perm.path if perm.path == '/' else os.path.basename(perm.path.rstrip('/'))
result['permission'] = perm.permission
results.append(result)
return Response(results)
@api_repo_user_folder_perm_check
def post(self, request, repo_id, format=None):
if not (is_pro_version() and ENABLE_FOLDER_PERM):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
user = request.data.get('user_email')
path = request.data.get('folder_path')
perm = request.data.get('permission')
path = path.rstrip('/') if path != '/' else path
permission = seafile_api.get_folder_user_perm(repo_id, path, user)
if permission:
error_msg = 'Permission already exists.'
return api_error(status.HTTP_409_CONFLICT, error_msg)
username = request.user.username
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)
result = get_repo_user_folder_perm_result(repo_id, path, user)
return Response(result)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
@api_repo_user_folder_perm_check
def put(self, request, repo_id, format=None):
if not (is_pro_version() and ENABLE_FOLDER_PERM):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
user = request.data.get('user_email')
path = request.data.get('folder_path')
perm = request.data.get('permission')
path = path.rstrip('/') if path != '/' else path
permission = seafile_api.get_folder_user_perm(repo_id, path, user)
if not permission:
error_msg = 'Folder permission not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
username = request.user.username
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)
result = get_repo_user_folder_perm_result(repo_id, path, user)
return Response(result)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
@api_repo_user_folder_perm_check
def delete(self, request, repo_id, format=None):
if not (is_pro_version() and ENABLE_FOLDER_PERM):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
user = request.data.get('user_email')
path = request.data.get('folder_path')
path = path.rstrip('/') if path != '/' else path
permission = seafile_api.get_folder_user_perm(repo_id, path, user)
if not permission:
return Response({'success': True})
username = request.user.username
try:
seafile_api.rm_folder_user_perm(repo_id, path, user)
send_perm_audit_msg('delete-repo-perm', username,
user, repo_id, path, permission)
return Response({'success': True})
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
def get_repo_group_folder_perm_result(repo_id, path, group_id):
result = {}
group = seaserv.get_group(group_id)
permission = seafile_api.get_folder_group_perm(repo_id, path, group_id)
if permission:
result['repo_id'] = repo_id
result['group_id'] = group_id
result['group_name'] = group.group_name
result['folder_path'] = path
result['folder_name'] = path if path == '/' else os.path.basename(path.rstrip('/'))
result['permission'] = permission
return result
class RepoGroupFolderPerm(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
@api_repo_setting_permission_check
def get(self, request, repo_id, format=None):
if not is_pro_version():
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
results = []
group_folder_perms = seafile_api.list_folder_group_perm_by_repo(repo_id)
for perm in group_folder_perms:
result = {}
group = seaserv.get_group(perm.group_id)
result['repo_id'] = perm.repo_id
result['group_id'] = perm.group_id
result['group_name'] = group.group_name
result['folder_path'] = perm.path
result['folder_name'] = perm.path if perm.path == '/' else os.path.basename(perm.path.rstrip('/'))
result['permission'] = perm.permission
results.append(result)
return Response(results)
@api_repo_group_folder_perm_check
def post(self, request, repo_id, format=None):
if not (is_pro_version() and ENABLE_FOLDER_PERM):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
group_id = request.data.get('group_id')
path = request.data.get('folder_path')
perm = request.data.get('permission')
group_id = int(group_id)
path = path.rstrip('/') if path != '/' else path
permission = seafile_api.get_folder_group_perm(repo_id, path, group_id)
if permission:
error_msg = 'Permission already exists.'
return api_error(status.HTTP_409_CONFLICT, error_msg)
username = request.user.username
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)
result = get_repo_group_folder_perm_result(repo_id, path, group_id)
return Response(result)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
@api_repo_group_folder_perm_check
def put(self, request, repo_id, format=None):
if not (is_pro_version() and ENABLE_FOLDER_PERM):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
group_id = request.data.get('group_id')
path = request.data.get('folder_path')
perm = request.data.get('permission')
group_id = int(group_id)
path = path.rstrip('/') if path != '/' else path
permission = seafile_api.get_folder_group_perm(repo_id, path, group_id)
if not permission:
error_msg = 'Folder permission not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
username = request.user.username
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)
result = get_repo_group_folder_perm_result(repo_id, path, group_id)
return Response(result)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
@api_repo_group_folder_perm_check
def delete(self, request, repo_id, format=None):
if not (is_pro_version() and ENABLE_FOLDER_PERM):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
group_id = request.data.get('group_id')
path = request.data.get('folder_path')
group_id = int(group_id)
path = path.rstrip('/') if path != '/' else path
permission = seafile_api.get_folder_group_perm(repo_id, path, group_id)
if not permission:
return Response({'success': True})
username = request.user.username
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, permission)
return Response({'success': True})
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)