1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-06-24 22:22:38 +00:00
seahub/api2/views.py
2013-03-02 16:59:26 +08:00

964 lines
35 KiB
Python

# encoding: utf-8
import os
import stat
import simplejson as json
from urllib2 import unquote, quote
import seahub.settings as settings
from rest_framework import parsers
from rest_framework import status
from rest_framework import renderers
from rest_framework.permissions import IsAuthenticated
from rest_framework.reverse import reverse
from rest_framework.response import Response
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
from rest_framework.views import APIView
from django.contrib.sites.models import RequestSite
from django.http import HttpResponse
from models import Token
from authentication import TokenAuthentication
from permissions import IsRepoWritable, IsRepoAccessible, IsRepoOwner
from serializers import AuthTokenSerializer
from base.accounts import User
from share.models import FileShare
from seahub.views import access_to_repo, validate_owner
from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
check_filename_with_rename, get_starred_files, get_ccnetapplet_root, \
get_ccnet_server_addr_port, star_file, unstar_file, string2list
try:
from seahub.settings import CLOUD_MODE
except ImportError:
CLOUD_MODE = False
from pysearpc import SearpcError, SearpcObjEncoder
from seaserv import seafserv_rpc, seafserv_threaded_rpc, server_repo_size, \
get_personal_groups_by_user, get_session_info, get_repo_token_nonnull, \
get_group_repos, get_repo, check_permission, get_commits, is_passwd_set,\
list_personal_repos_by_owner, list_personal_shared_repos, check_quota, \
list_share_repos, get_group_repos_by_owner, list_inner_pub_repos_by_owner,\
remove_share, unshare_group_repo, unset_inner_pub_repo, get_user_quota, \
get_user_share_usage, get_user_quota_usage, CALC_SHARE_USAGE
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_520_OPERATION_FAILED = 520
class Ping(APIView):
"""
Returns a simple `pong` message when client calls `api2/ping/`.
For example:
curl http://127.0.0.1:8000/api2/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,)
def get(self, request, format=None):
return Response('pong')
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,)
model = Token
def post(self, request):
serializer = AuthTokenSerializer(data=request.DATA)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'].username)
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def api_error(code, msg):
err_resp = {'error_msg': msg}
return Response(err_resp, status=code)
class Account(APIView):
"""
Show account info.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
info = {}
email = request.user.username
info['email'] = email
info['total'] = get_user_quota(email)
if CALC_SHARE_USAGE:
my_usage = get_user_quota_usage(email)
share_usage = get_user_share_usage(email)
info['usage'] = my_usage + share_usage
else:
info['usage'] = get_user_quota_usage(email)
return Response(info)
def calculate_repo_info(repo_list, username):
"""
Get some info for repo.
"""
for repo in repo_list:
commit = get_commits(repo.id, 0, 1)[0]
if not commit:
continue
repo.latest_modify = commit.ctime
repo.root = commit.root_id
repo.size = server_repo_size(repo.id)
class Repos(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
email = request.user.username
repos_json = []
owned_repos = list_personal_repos_by_owner(email)
calculate_repo_info(owned_repos, email)
owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
for r in owned_repos:
repo = {
"type":"repo",
"id":r.id,
"owner":email,
"name":r.name,
"desc":r.desc,
"mtime":r.latest_modify,
"root":r.root,
"size":r.size,
"encrypted":r.encrypted,
"permission": 'rw', # Always have read-write permission to owned repo
}
repos_json.append(repo)
shared_repos = list_personal_shared_repos(email, 'to_email', -1, -1)
for r in shared_repos:
commit = get_commits(r.repo_id, 0, 1)[0]
if not commit:
continue
r.latest_modify = commit.ctime
r.root = commit.root_id
r.size = server_repo_size(r.repo_id)
r.password_need = is_passwd_set(r.repo_id, email)
r.permission = check_permission(r.repo_id, email)
repo = {
"type":"srepo",
"id":r.repo_id,
"owner":r.user,
"name":r.repo_name,
"desc":r.repo_desc,
"mtime":r.latest_modify,
"root":r.root,
"size":r.size,
"encrypted":r.encrypted,
"permission": r.permission,
}
repos_json.append(repo)
groups = get_personal_groups_by_user(email)
for group in groups:
g_repos = get_group_repos(group.id, email)
calculate_repo_info (g_repos, email)
g_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
for r in g_repos:
repo = {
"type":"grepo",
"id":r.id,
"owner":group.group_name,
"name":r.name,
"desc":r.desc,
"mtime":r.latest_modify,
"root":r.root,
"size":r.size,
"encrypted":r.encrypted,
"permission": check_permission(r.id, email),
}
repos_json.append(repo)
return Response(repos_json)
def can_access_repo(request, repo_id):
if not check_permission(repo_id, request.user.username):
return False
return True
def set_repo_password(request, repo, password):
assert password, 'password must not be none'
try:
seafserv_threaded_rpc.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_repo_access_permission(request, repo):
if not can_access_repo(request, repo.id):
return api_error(status.HTTP_403_FORBIDDEN, 'Forbid to access this repo.')
password_set = False
if repo.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo.id, request.user.username)
if ret == 1:
password_set = True
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
if not password_set:
password = request.REQUEST.get('password', default=None)
if not password:
return api_error(HTTP_440_REPO_PASSWD_REQUIRED,
'Repo password is needed.')
return set_repo_password(request, repo, password)
class Repo(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoAccessible, )
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, 'Repo not found.')
# check whether user is repo owner
if validate_owner(request, repo_id):
owner = "self"
else:
owner = "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 = seafserv_threaded_rpc.server_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,
}
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, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
op = request.GET.get('op', 'setpassword')
if op == 'setpassword':
return Response("success")
return Response("unsupported operation")
class DownloadRepo(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoAccessible, )
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, 'Repo not found.')
# generate download url for client
ccnet_applet_root = get_ccnetapplet_root()
relay_id = get_session_info().id
addr, port = get_ccnet_server_addr_port ()
email = request.user.username
token = get_repo_token_nonnull(repo_id, request.user.username)
repo_name = repo.name
enc = 1 if repo.encrypted else ''
magic = repo.magic if repo.encrypted else ''
info_json = {
'relay_id': relay_id,
'relay_addr': addr,
'relay_port': port,
'email': email,
'token': token,
'repo_id': repo_id,
'repo_name': repo_name,
'encrypted': enc,
'magic': magic,
}
return Response(info_json)
class UploadLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if check_permission(repo_id, request.user.username) == 'rw':
token = seafserv_rpc.web_get_access_token(repo_id,
'dummy',
'upload',
request.user.username)
else:
return api_error(status.HTTP_403_FORBIDDEN, "Can not access repo")
if check_quota(repo_id) < 0:
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
upload_url = gen_file_upload_url(token, 'upload-api')
return Response(upload_url)
def get_file_size (id):
size = seafserv_threaded_rpc.get_file_size(id)
return size if size else 0
def get_dir_entrys_by_id(request, dir_id):
try:
dirs = seafserv_threaded_rpc.list_dir(dir_id)
except SearpcError, 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:
try:
entry["size"] = get_file_size(dirent.obj_id)
except Exception, e:
entry["size"]=0
entry["type"]=dtype
entry["name"]=dirent.obj_name
entry["id"]=dirent.obj_id
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
response = HttpResponse(json.dumps(dentrys), status=200,
content_type=json_content_type)
response["oid"] = dir_id
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_err(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 Response(file_shared_link)
def get_repo_file(request, repo_id, file_id, file_name, op):
if op == 'download':
token = seafserv_rpc.web_get_access_token(repo_id, file_id,
op, request.user.username)
redirect_url = gen_file_get_url(token, file_name)
#return Response(redirect_url)
response = HttpResponse(json.dumps(redirect_url), status=200,
content_type=json_content_type)
response["oid"] = file_id
return response
if op == 'sharelink':
path = request.GET.get('p', None)
assert path, 'path must be passed in the url'
return get_shared_link(request, repo_id, path)
def reloaddir(request, repo_id, parent_dir):
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 = seafserv_threaded_rpc.get_dirid_by_path(current_commit.id,
parent_dir.encode('utf-8'))
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")
return get_dir_entrys_by_id(request, dir_id)
def reloaddir_if_neccessary (request, repo_id, 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_id, parent_dir)
# deprecated
class OpDeleteView(APIView):
"""
Delete a file.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoWritable, )
def post(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
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.')
for file_name in file_names.split(':'):
file_name = unquote(file_name.encode('utf-8'))
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir,
file_name, request.user.username)
except SearpcError,e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_neccessary (request, repo_id, parent_dir)
def append_starred_files(array, files):
for f in files:
sfile = {'org' : f.org_id,
'repo' : f.repo.id,
'path' : f.path,
'mtime' : f.last_modified,
'dir' : f.is_dir,
'size' : f.size
}
array.append(sfile)
def api_starred_files(request):
starred_files = []
personal_files = get_starred_files(request.user.username, -1)
append_starred_files (starred_files, personal_files)
return Response(starred_files)
class StarredFileView(APIView):
"""
Support uniform interface for starred file operation,
including add/delete/list starred files.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
# list starred files
return api_starred_files(request)
def post(self, request, format=None):
# add starred file
repo_id = request.POST.get('repo_id', '')
path = unquote(request.POST.get('p', '').encode('utf-8'))
if not (repo_id and path):
return api_error(status.HTTP_400_BAD_REQUEST,
'Repo_id or path is missing.')
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,
'Repo_id or path is missing.')
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 FileView(APIView):
"""
Support uniform interface for file related operations,
including create/delete/rename/view, etc.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoWritable, )
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, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
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")
return get_repo_file(request, repo_id, file_id, file_name, 'download')
def post(self, request, repo_id, format=None):
# rename or move file
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', '')
if not path or path[0] != '/':
return api_error(status.HTTP_400_BAD_REQUEST,
'Path is missing or invalid.')
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,
'Newname is missing')
newname = unquote(newname.encode('utf-8'))
if len(newname) > settings.MAX_UPLOAD_FILE_NAME_LEN:
return api_error(status.HTTP_400_BAD_REQUEST, 'Newname too long')
parent_dir = os.path.dirname(path)
parent_dir_utf8 = parent_dir.encode('utf-8')
oldname = os.path.basename(path)
oldname_utf8 = oldname.encode('utf-8')
if oldname == newname:
return api_error(status.HTTP_409_CONFLICT,
'The new name is the same to the old')
newname = check_filename_with_rename(repo_id, parent_dir, newname)
newname_utf8 = newname.encode('utf-8')
try:
seafserv_threaded_rpc.rename_file (repo_id, parent_dir_utf8,
oldname_utf8, newname,
request.user.username)
except SearpcError,e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to rename file: %s" % e)
if request.GET.get('reloaddir', '').lower() == 'true':
reloaddir(request, repo_id, parent_dir)
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_utf8)
return resp
elif operation.lower() == 'move':
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)
# names = obj_names.split(':')
# names = map(lambda x: unquote(x).decode('utf-8'), names)
# if dst_dir.startswith(src_dir):
# for obj_name in names:
# if dst_dir.startswith('/'.join([src_dir, obj_name])):
# return api_error(status.HTTP_409_CONFLICT,
# 'Can not move a dirctory to its subdir')
filename = os.path.basename(path)
filename_utf8 = filename.encode('utf-8')
new_filename = check_filename_with_rename(dst_repo_id, dst_dir,
filename)
new_filename_utf8 = new_filename.encode('utf-8')
try:
seafserv_threaded_rpc.move_file(src_repo_id, src_dir_utf8,
filename_utf8, dst_repo_id,
dst_dir_utf8, new_filename_utf8,
request.user.username)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
if request.GET.get('reloaddir', '').lower() == 'true':
reloaddir(request, dst_repo_id, dst_dir)
else:
resp = Response('success', status=status.HTTP_301_MOVED_PERMANENTLY)
uri = reverse('FileView', args=[repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(dst_dir_utf8) + quote(new_filename_utf8)
return resp
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation can only be rename or move.")
def put(self, request, repo_id, format=None):
# update file
# TODO
pass
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, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
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)
parent_dir_utf8 = os.path.dirname(path).encode('utf-8')
file_name_utf8 = os.path.basename(path).encode('utf-8')
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir_utf8,
file_name_utf8,
request.user.username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_neccessary(request, repo_id, parent_dir)
class FileSharedLinkView(APIView):
"""
Support uniform interface for file shared link.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
throttle_classes = (UserRateThrottle, )
def put(self, request, repo_id, format=None):
# generate file shared link
path = unquote(request.DATA.get('p', '').encode('utf-8'))
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
if path[-1] == '/':
path = path[:-1]
l = FileShare.objects.filter(repo_id=repo_id).filter(
username=request.user.username).filter(path=path)
if len(l) > 0:
fileshare = l[0]
token = fileshare.token
else:
token = gen_token(max_length=10)
fs = FileShare()
fs.username = request.user.username
fs.repo_id = repo_id
fs.path = path
fs.token = token
try:
fs.save()
except IntegrityError, e:
return api_err(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)
resp = Response(status=status.HTTP_201_CREATED)
resp['Location'] = file_shared_link
return resp
class DirView(APIView):
"""
Support uniform interface for directory operations, including
create/delete/rename/list, etc.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoWritable, )
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, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
try:
dir_id = seafserv_threaded_rpc.get_dir_id_by_path(repo_id,
path.encode('utf-8'))
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")
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:
return get_dir_entrys_by_id(request, dir_id)
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, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
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 root dir.
return api_error(status.HTTP_400_BAD_REQUEST, "Path is invalid.")
if path[-1] == '/': # Cut out last '/' if possible.
path = path[:-1]
operation = request.POST.get('operation', '')
if operation.lower() == 'mkdir':
parent_dir = os.path.dirname(path)
parent_dir_utf8 = parent_dir.encode('utf-8')
new_dir_name = os.path.basename(path)
new_dir_name = check_filename_with_rename(repo_id, parent_dir,
new_dir_name)
new_dir_name_utf8 = new_dir_name.encode('utf-8')
try:
seafserv_threaded_rpc.post_dir(repo_id, parent_dir,
new_dir_name,
request.user.username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to make directory.')
if request.GET.get('reloaddir', '').lower() == 'true':
resp = reloaddir(request, repo_id, 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':
# pass
# 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, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
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')
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir_utf8,
file_name_utf8,
request.user.username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_neccessary(request, repo_id, parent_dir)
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 SharedRepo(APIView):
"""
Support uniform interface for shared libraries.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoOwner)
throttle_classes = (UserRateThrottle, )
def delete(self, request, repo_id, format=None):
"""
Unshare a library. Only repo owner can perform this operation.
"""
share_type = request.GET.get('share_type', '')
user = request.GET.get('user', '')
group_id = request.GET.get('group_id', '')
if not (share_type and user and group_id):
return api_error(status.HTTP_400_BAD_REQUEST,
'share_type and user and group_id is required.')
if share_type == 'personal':
remove_share(repo_id, request.user.username, user)
elif share_type == 'group':
unshare_group_repo(repo_id, group_id, user)
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)