1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-04-27 02:51:00 +00:00
seahub/seahub/views/__init__.py

2085 lines
72 KiB
Python

# encoding: utf-8
import hashlib
import os
import stat
import json
import mimetypes
import urllib2
import logging
from math import ceil
import posixpath
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.http import HttpResponse, HttpResponseBadRequest, Http404, \
HttpResponseRedirect
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.utils import timezone
from django.utils.http import urlquote
from django.utils.html import escape
from django.utils.translation import ugettext as _
from django.views.decorators.http import condition
import seaserv
from seaserv import get_repo, get_commits, is_valid_filename, \
seafserv_threaded_rpc, seafserv_rpc, is_repo_owner, check_permission, \
is_passwd_set, get_file_size, get_group, get_session_info, get_commit, \
MAX_DOWNLOAD_DIR_SIZE, send_message, ccnet_threaded_rpc, \
get_personal_groups_by_user, seafile_api
from pysearpc import SearpcError
from seahub.avatar.util import get_avatar_file_storage
from seahub.auth.decorators import login_required, login_required_ajax
from seahub.auth import login as auth_login
from seahub.auth import get_backends
from seahub.base.accounts import User
from seahub.base.decorators import user_mods_check, require_POST
from seahub.base.models import UserStarredFiles, ClientLoginToken
from seahub.contacts.models import Contact
from seahub.options.models import UserOptions, CryptoOptionNotSetError
from seahub.profile.models import Profile
from seahub.share.models import FileShare, PrivateFileDirShare, \
UploadLinkShare
from seahub.forms import RepoPassowrdForm
from seahub.utils import render_permission_error, render_error, list_to_string, \
get_fileserver_root, gen_shared_upload_link, is_org_context, \
gen_dir_share_link, gen_file_share_link, get_repo_last_modify, \
calculate_repos_last_modify, get_file_type_and_ext, get_user_repos, \
EMPTY_SHA1, normalize_file_path, gen_file_upload_url, \
get_file_revision_id_size, get_ccnet_server_addr_port, \
gen_file_get_url, string2list, MAX_INT, IS_EMAIL_CONFIGURED, \
EVENTS_ENABLED, get_user_events, get_org_user_events, show_delete_days, \
TRAFFIC_STATS_ENABLED, get_user_traffic_stat, new_merge_with_no_conflict, \
user_traffic_over_limit, send_perm_audit_msg, get_origin_repo_info, \
is_org_context, get_max_upload_file_size, is_pro_version
from seahub.utils.paginator import get_page_range
from seahub.utils.star import get_dir_starred_files
from seahub.utils.timeutils import utc_to_local
from seahub.views.modules import MOD_PERSONAL_WIKI, enable_mod_for_user, \
disable_mod_for_user
from seahub.utils.devices import get_user_devices, do_unlink_device
import seahub.settings as settings
from seahub.settings import FILE_PREVIEW_MAX_SIZE, INIT_PASSWD, USE_PDFJS, \
FILE_ENCODING_LIST, FILE_ENCODING_TRY_LIST, AVATAR_FILE_STORAGE, \
SEND_EMAIL_ON_ADDING_SYSTEM_MEMBER, SEND_EMAIL_ON_RESETTING_USER_PASSWD, \
ENABLE_SUB_LIBRARY, ENABLE_FOLDER_PERM
from constance import config
# Get an instance of a logger
logger = logging.getLogger(__name__)
def validate_owner(request, repo_id):
"""
Check whether user in the request owns the repo.
"""
ret = is_repo_owner(request.user.username, repo_id)
return True if ret else False
def is_registered_user(email):
"""
Check whether user is registerd.
"""
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
return True if user else False
_default_repo_id = None
def get_system_default_repo_id():
global _default_repo_id
if not _default_repo_id:
try:
_default_repo_id = seaserv.seafserv_threaded_rpc.get_system_default_repo_id()
except SearpcError as e:
logger.error(e)
return _default_repo_id
def check_folder_permission(request, repo_id, path):
"""Check repo/folder access permission of a user, always return 'rw'
when repo is system repo and user is admin.
Arguments:
- `request`:
- `repo_id`:
- `path`:
"""
username = request.user.username
if request.user.is_staff and get_system_default_repo_id() == repo_id:
return 'rw'
return seafile_api.check_permission_by_path(repo_id, path, username)
def check_file_permission(request, repo_id, path):
"""Check file access permission of a user, always return 'rw'
when repo is system repo and user is admin.
Arguments:
- `request`:
- `repo_id`:
- `path`:
"""
username = request.user.username
if get_system_default_repo_id() == repo_id and request.user.is_staff:
return 'rw'
return seafile_api.check_permission_by_path(repo_id, path, username)
def check_file_lock(repo_id, file_path, username):
""" check if file is locked to current user
according to returned value of seafile_api.check_file_lock:
0: not locked
1: locked by other
2: locked by me
-1: error
return (is_locked, locked_by_me)
"""
try:
return_value = seafile_api.check_file_lock(repo_id,
file_path.lstrip('/'), username)
except SearpcError as e:
logger.error(e)
return (None, None)
if return_value == 0:
return (False, False)
elif return_value == 1:
return (True , False)
elif return_value == 2:
return (True, True)
else:
return (None, None)
def check_repo_access_permission(repo_id, user):
"""Check repo access permission of a user, always return 'rw' when repo is
system repo and user is admin.
Arguments:
- `repo_id`:
- `user`:
"""
if user.is_staff and get_system_default_repo_id() == repo_id:
return 'rw'
else:
return seafile_api.check_repo_access_permission(repo_id, user.username)
def get_file_access_permission(repo_id, path, username):
"""Check user has permission to view the file.
1. check whether this file is private shared.
2. if failed, check whether the parent of this directory is private shared.
"""
pfs = PrivateFileDirShare.objects.get_private_share_in_file(username,
repo_id, path)
if pfs is None:
dirs = PrivateFileDirShare.objects.list_private_share_in_dirs_by_user_and_repo(username, repo_id)
for e in dirs:
if path.startswith(e.path):
return e.permission
return None
else:
return pfs.permission
def gen_path_link(path, repo_name):
"""
Generate navigate paths and links in repo page.
"""
if path and path[-1] != '/':
path += '/'
paths = []
links = []
if path and path != '/':
paths = path[1:-1].split('/')
i = 1
for name in paths:
link = '/' + '/'.join(paths[:i])
i = i + 1
links.append(link)
if repo_name:
paths.insert(0, repo_name)
links.insert(0, '/')
zipped = zip(paths, links)
return zipped
def get_file_download_link(repo_id, obj_id, path):
"""Generate file download link.
Arguments:
- `repo_id`:
- `obj_id`:
- `filename`:
"""
return reverse('download_file', args=[repo_id, obj_id]) + '?p=' + \
urlquote(path)
def get_repo_dirents_with_perm(request, repo, commit, path, offset=-1, limit=-1):
"""List repo dirents with perm based on commit id and path.
Use ``offset`` and ``limit`` to do paginating.
Returns: A tupple of (file_list, dir_list, dirent_more)
TODO: Some unrelated parts(file sharing, stars, modified info, etc) need
to be pulled out to multiple functions.
"""
if get_system_default_repo_id() == repo.id:
return get_repo_dirents(request, repo, commit, path, offset, limit)
dir_list = []
file_list = []
dirent_more = False
username = request.user.username
if commit.root_id == EMPTY_SHA1:
return ([], [], False) if limit == -1 else ([], [], False)
else:
try:
dir_id = seafile_api.get_dir_id_by_path(repo.id, path)
if not dir_id:
return ([], [], False)
dirs = seafserv_threaded_rpc.list_dir_with_perm(repo.id, path,
dir_id, username,
offset, limit)
except SearpcError as e:
logger.error(e)
return ([], [], False)
if limit != -1 and limit == len(dirs):
dirent_more = True
starred_files = get_dir_starred_files(username, repo.id, path)
fileshares = FileShare.objects.filter(repo_id=repo.id).filter(username=username)
uploadlinks = UploadLinkShare.objects.filter(repo_id=repo.id).filter(username=username)
view_dir_base = reverse('repo', args=[repo.id])
dl_dir_base = reverse('repo_download_dir', args=[repo.id])
view_file_base = reverse('repo_view_file', args=[repo.id])
file_history_base = reverse('file_revisions', args=[repo.id])
for dirent in dirs:
dirent.last_modified = dirent.mtime
dirent.sharelink = ''
dirent.uploadlink = ''
if stat.S_ISDIR(dirent.props.mode):
dpath = posixpath.join(path, dirent.obj_name)
if dpath[-1] != '/':
dpath += '/'
for share in fileshares:
if dpath == share.path:
dirent.sharelink = gen_dir_share_link(share.token)
dirent.sharetoken = share.token
break
for link in uploadlinks:
if dpath == link.path:
dirent.uploadlink = gen_shared_upload_link(link.token)
dirent.uploadtoken = link.token
break
p_dpath = posixpath.join(path, dirent.obj_name)
dirent.view_link = view_dir_base + '?p=' + urlquote(p_dpath)
dirent.dl_link = dl_dir_base + '?p=' + urlquote(p_dpath)
dir_list.append(dirent)
else:
file_list.append(dirent)
if repo.version == 0:
dirent.file_size = get_file_size(repo.store_id, repo.version, dirent.obj_id)
else:
dirent.file_size = dirent.size
dirent.starred = False
fpath = posixpath.join(path, dirent.obj_name)
p_fpath = posixpath.join(path, dirent.obj_name)
dirent.view_link = view_file_base + '?p=' + urlquote(p_fpath)
dirent.dl_link = get_file_download_link(repo.id, dirent.obj_id,
p_fpath)
dirent.history_link = file_history_base + '?p=' + urlquote(p_fpath)
if fpath in starred_files:
dirent.starred = True
for share in fileshares:
if fpath == share.path:
dirent.sharelink = gen_file_share_link(share.token)
dirent.sharetoken = share.token
break
return (file_list, dir_list, dirent_more)
def get_repo_dirents(request, repo, commit, path, offset=-1, limit=-1):
"""List repo dirents based on commit id and path. Use ``offset`` and
``limit`` to do paginating.
Returns: A tupple of (file_list, dir_list, dirent_more)
TODO: Some unrelated parts(file sharing, stars, modified info, etc) need
to be pulled out to multiple functions.
"""
dir_list = []
file_list = []
dirent_more = False
if commit.root_id == EMPTY_SHA1:
return ([], [], False) if limit == -1 else ([], [], False)
else:
try:
dirs = seafile_api.list_dir_by_commit_and_path(commit.repo_id,
commit.id, path,
offset, limit)
if not dirs:
return ([], [], False)
except SearpcError as e:
logger.error(e)
return ([], [], False)
if limit != -1 and limit == len(dirs):
dirent_more = True
username = request.user.username
starred_files = get_dir_starred_files(username, repo.id, path)
fileshares = FileShare.objects.filter(repo_id=repo.id).filter(username=username)
uploadlinks = UploadLinkShare.objects.filter(repo_id=repo.id).filter(username=username)
view_dir_base = reverse('repo', args=[repo.id])
dl_dir_base = reverse('repo_download_dir', args=[repo.id])
file_history_base = reverse('file_revisions', args=[repo.id])
for dirent in dirs:
dirent.last_modified = dirent.mtime
dirent.sharelink = ''
dirent.uploadlink = ''
if stat.S_ISDIR(dirent.props.mode):
dpath = posixpath.join(path, dirent.obj_name)
if dpath[-1] != '/':
dpath += '/'
for share in fileshares:
if dpath == share.path:
dirent.sharelink = gen_dir_share_link(share.token)
dirent.sharetoken = share.token
break
for link in uploadlinks:
if dpath == link.path:
dirent.uploadlink = gen_shared_upload_link(link.token)
dirent.uploadtoken = link.token
break
p_dpath = posixpath.join(path, dirent.obj_name)
dirent.view_link = view_dir_base + '?p=' + urlquote(p_dpath)
dirent.dl_link = dl_dir_base + '?p=' + urlquote(p_dpath)
dir_list.append(dirent)
else:
file_list.append(dirent)
if repo.version == 0:
dirent.file_size = get_file_size(repo.store_id, repo.version, dirent.obj_id)
else:
dirent.file_size = dirent.size
dirent.starred = False
fpath = posixpath.join(path, dirent.obj_name)
p_fpath = posixpath.join(path, dirent.obj_name)
dirent.view_link = reverse('view_lib_file', args=[repo.id, urlquote(p_fpath)])
dirent.dl_link = get_file_download_link(repo.id, dirent.obj_id,
p_fpath)
dirent.history_link = file_history_base + '?p=' + urlquote(p_fpath)
if fpath in starred_files:
dirent.starred = True
for share in fileshares:
if fpath == share.path:
dirent.sharelink = gen_file_share_link(share.token)
dirent.sharetoken = share.token
break
return (file_list, dir_list, dirent_more)
def get_unencry_rw_repos_by_user(request):
"""Get all unencrypted repos the user can read and write.
"""
username = request.user.username
def has_repo(repos, repo):
for r in repos:
if repo.id == r.id:
return True
return False
org_id = request.user.org.org_id if is_org_context(request) else None
owned_repos, shared_repos, groups_repos, public_repos = get_user_repos(
username, org_id=org_id)
accessible_repos = []
for r in owned_repos:
if not has_repo(accessible_repos, r) and not r.encrypted:
accessible_repos.append(r)
for r in shared_repos + groups_repos + public_repos:
if not has_repo(accessible_repos, r) and not r.encrypted:
if seafile_api.check_repo_access_permission(r.id, username) == 'rw':
accessible_repos.append(r)
return accessible_repos
def render_recycle_root(request, repo_id):
repo = get_repo(repo_id)
if not repo:
raise Http404
days = show_delete_days(request)
try:
deleted_entries = seafserv_threaded_rpc.get_deleted(repo_id, days)
except SearpcError as e:
logger.error(e)
messages.error(request, _('Internal server error'))
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
return HttpResponseRedirect(next)
dir_list = []
file_list = []
for dirent in deleted_entries:
if stat.S_ISDIR(dirent.mode):
dir_list.append(dirent)
else:
file_list.append(dirent)
# Entries sort by deletion time in descending order.
dir_list.sort(lambda x, y : cmp(y.delete_time,
x.delete_time))
file_list.sort(lambda x, y : cmp(y.delete_time,
x.delete_time))
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)
is_repo_owner = True if repo_owner == username else False
enable_clean = False
if is_repo_owner:
enable_clean = True
return render_to_response('repo_recycle_view.html', {
'show_recycle_root': True,
'repo': repo,
'dir_list': dir_list,
'file_list': file_list,
'days': days,
'enable_clean': enable_clean,
}, context_instance=RequestContext(request))
def render_recycle_dir(request, repo_id, commit_id):
basedir = request.GET.get('base', '')
path = request.GET.get('p', '')
if not basedir or not path:
return render_recycle_root(request, repo_id)
if basedir[0] != '/':
basedir = '/' + basedir
if path[-1] != '/':
path += '/'
repo = get_repo(repo_id)
if not repo:
raise Http404
try:
commit = seafserv_threaded_rpc.get_commit(repo.id, repo.version, commit_id)
except SearpcError as e:
logger.error(e)
messages.error(request, _('Internal server error'))
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
return HttpResponseRedirect(next)
if not commit:
raise Http404
zipped = gen_path_link(path, '')
file_list, dir_list, dirent_more = get_repo_dirents(request, repo, commit,
basedir + path)
days = show_delete_days(request)
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)
is_repo_owner = True if repo_owner == username else False
enable_clean = False
if is_repo_owner:
enable_clean = True
return render_to_response('repo_recycle_view.html', {
'show_recycle_root': False,
'repo': repo,
'zipped': zipped,
'dir_list': dir_list,
'file_list': file_list,
'commit_id': commit_id,
'basedir': basedir,
'path': path,
'days': days,
'enable_clean': enable_clean,
}, context_instance=RequestContext(request))
def render_dir_recycle_root(request, repo_id, dir_path):
repo = get_repo(repo_id)
if not repo:
raise Http404
days = show_delete_days(request)
try:
deleted_entries = seafserv_threaded_rpc.get_deleted(repo_id,
days,
dir_path)
except SearpcError as e:
logger.error(e)
messages.error(request, _('Internal server error'))
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
return HttpResponseRedirect(next)
dir_list = []
file_list = []
for dirent in deleted_entries:
if stat.S_ISDIR(dirent.mode):
dir_list.append(dirent)
else:
file_list.append(dirent)
# Entries sort by deletion time in descending order.
dir_list.sort(lambda x, y : cmp(y.delete_time,
x.delete_time))
file_list.sort(lambda x, y : cmp(y.delete_time,
x.delete_time))
return render_to_response('dir_recycle_view.html', {
'show_recycle_root': True,
'repo': repo,
'dir_list': dir_list,
'file_list': file_list,
'days': days,
'dir_name': os.path.basename(dir_path.rstrip('/')),
'dir_path': dir_path,
}, context_instance=RequestContext(request))
def render_dir_recycle_dir(request, repo_id, commit_id, dir_path):
basedir = request.GET.get('base', '')
path = request.GET.get('p', '')
if not basedir or not path:
return render_dir_recycle_root(request, repo_id, dir_path)
if basedir[0] != '/':
basedir = '/' + basedir
if path[-1] != '/':
path += '/'
repo = get_repo(repo_id)
if not repo:
raise Http404
try :
commit = seafserv_threaded_rpc.get_commit(repo.id, repo.version, commit_id)
except SearpcError as e:
logger.error(e)
messages.error(request, _('Internal server error'))
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
return HttpResponseRedirect(next)
if not commit:
raise Http404
zipped = gen_path_link(path, '')
file_list, dir_list, dirent_more = get_repo_dirents(request, repo, commit,
basedir + path)
days = show_delete_days(request)
return render_to_response('dir_recycle_view.html', {
'show_recycle_root': False,
'repo': repo,
'zipped': zipped,
'dir_list': dir_list,
'file_list': file_list,
'commit_id': commit_id,
'basedir': basedir,
'path': path,
'days': days,
'dir_name': os.path.basename(dir_path.rstrip('/')),
'dir_path': dir_path,
}, context_instance=RequestContext(request))
@login_required
def repo_recycle_view(request, repo_id):
if check_folder_permission(request, repo_id, '/') != 'rw':
return render_permission_error(request, _(u'Unable to view recycle page'))
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return render_recycle_root(request, repo_id)
else:
return render_recycle_dir(request, repo_id, commit_id)
@login_required
def dir_recycle_view(request, repo_id):
dir_path = request.GET.get('dir_path', '')
if check_folder_permission(request, repo_id, dir_path) != 'rw':
return render_permission_error(request, _(u'Unable to view recycle page'))
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return render_dir_recycle_root(request, repo_id, dir_path)
else:
return render_dir_recycle_dir(request, repo_id, commit_id, dir_path)
@login_required
def repo_online_gc(request, repo_id):
if request.method != 'POST':
raise Http404
repo = get_repo(repo_id)
if not repo:
raise Http404
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
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)
is_repo_owner = True if repo_owner == username else False
if not is_repo_owner:
messages.error(request, _('Permission denied'))
return HttpResponseRedirect(next)
day = int(request.POST.get('day'))
try:
seafile_api.clean_up_repo_history(repo.id, day)
except SearpcError as e:
logger.error(e)
messages.error(request, _('Internal server error'))
return HttpResponseRedirect(next)
return HttpResponseRedirect(next)
def can_access_repo_setting(request, repo_id, username):
repo = seafile_api.get_repo(repo_id)
if not repo:
return (False, None)
# no settings for virtual repo
if ENABLE_SUB_LIBRARY and repo.is_virtual:
return (False, None)
# 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 (False, None)
return (True, repo)
@login_required
def repo_basic_info(request, repo_id):
"""List and change library basic info.
"""
username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access:
raise Http404
history_limit = seaserv.get_repo_history_limit(repo.id)
full_history_checked = no_history_checked = partial_history_checked = False
if history_limit > 0:
partial_history_checked = True
elif history_limit == 0:
no_history_checked = True
else:
full_history_checked = True
full_history_enabled = no_history_enabled = partial_history_enabled = True
days_enabled = True
if not config.ENABLE_REPO_HISTORY_SETTING:
full_history_enabled = no_history_enabled = partial_history_enabled = False
days_enabled = False
if history_limit <= 0:
days_enabled = False
return render_to_response('repo_basic_info.html', {
'repo': repo,
'history_limit': history_limit if history_limit > 0 else '',
'full_history_checked': full_history_checked,
'no_history_checked': no_history_checked,
'partial_history_checked': partial_history_checked,
'full_history_enabled': full_history_enabled,
'no_history_enabled': no_history_enabled,
'partial_history_enabled': partial_history_enabled,
'days_enabled': days_enabled,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request))
@login_required
def repo_transfer_owner(request, repo_id):
"""Show transfer repo owner page.
"""
username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access:
raise Http404
return render_to_response('repo_transfer_owner.html', {
'repo': repo,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request))
def repo_transfer_success(request, repo_id):
repo = get_repo(repo_id)
if not repo:
raise Http404
return render_to_response('repo_transfer_success.html', {
'repo': repo,
}, context_instance=RequestContext(request))
@login_required
def repo_change_password(request, repo_id):
"""Show change library password page.
"""
username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access:
raise Http404
return render_to_response('repo_change_password.html', {
'repo': repo,
'repo_password_min_length': config.REPO_PASSWORD_MIN_LENGTH,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request))
@login_required
def repo_shared_link(request, repo_id):
"""List and change library shared links.
"""
username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access:
raise Http404
# download links
fileshares = FileShare.objects.filter(repo_id=repo_id)
p_fileshares = []
for fs in fileshares:
if fs.is_file_share_link():
if seafile_api.get_file_id_by_path(repo.id, fs.path) is None:
fs.delete()
continue
fs.filename = os.path.basename(fs.path)
fs.shared_link = gen_file_share_link(fs.token)
path = fs.path.rstrip('/') # Normalize file path
obj_id = seafile_api.get_file_id_by_path(repo.id, path)
fs.filesize = seafile_api.get_file_size(repo.store_id, repo.version,
obj_id)
else:
if seafile_api.get_dir_id_by_path(repo.id, fs.path) is None:
fs.delete()
continue
if fs.path != '/':
fs.filename = os.path.basename(fs.path.rstrip('/'))
else:
fs.filename = fs.path
fs.shared_link = gen_dir_share_link(fs.token)
path = fs.path
if path[-1] != '/': # Normalize dir path
path += '/'
#get dir size
dir_id = seafserv_threaded_rpc.get_dirid_by_path(
repo.id, repo.head_cmmt_id, path)
fs.filesize = seafserv_threaded_rpc.get_dir_size(repo.store_id,
repo.version, dir_id)
p_fileshares.append(fs)
# upload links
uploadlinks = UploadLinkShare.objects.filter(repo_id=repo_id)
p_uploadlinks = []
for link in uploadlinks:
if seafile_api.get_dir_id_by_path(repo.id, link.path) is None:
link.delete()
continue
if link.path != '/':
link.dir_name = os.path.basename(link.path.rstrip('/'))
else:
link.dir_name = link.path
link.shared_link = gen_shared_upload_link(link.token)
p_uploadlinks.append(link)
return render_to_response('repo_shared_link.html', {
'repo': repo,
'fileshares': p_fileshares,
'uploadlinks': p_uploadlinks,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request))
@login_required
def repo_share_manage(request, repo_id):
"""Manage share of this library.
"""
username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access:
raise Http404
# sharing management
repo_share_user = []
repo_share_group = []
if is_org_context(request):
org_id = request.user.org.org_id
repo_share_user = seafile_api.get_org_share_out_repo_list(org_id, username, -1, -1)
repo_share_group = seafserv_threaded_rpc.get_org_group_repos_by_owner(org_id, username)
else:
repo_share_user = seafile_api.get_share_out_repo_list(username, -1, -1)
repo_share_group = seaserv.get_group_repos_by_owner(username)
repo_share_user = filter(lambda i: i.repo_id == repo_id, repo_share_user)
repo_share_group = filter(lambda i: i.repo_id == repo_id, repo_share_group)
for share in repo_share_group:
share.group_name = get_group(share.group_id).group_name
return render_to_response('repo_share_manage.html', {
'repo': repo,
'repo_share_user': repo_share_user,
'repo_share_group': repo_share_group,
'ENABLE_FOLDER_PERM': ENABLE_FOLDER_PERM,
}, context_instance=RequestContext(request))
@login_required
def repo_folder_perm(request, repo_id):
"""Manage folder permmission of this library.
"""
username = request.user.username
can_access, repo = can_access_repo_setting(request, repo_id, username)
if not can_access or not ENABLE_FOLDER_PERM:
raise Http404
def not_need_delete(perm):
repo_id = perm.repo_id
path = perm.path
group_id = perm.group_id if hasattr(perm, 'group_id') else None
email = perm.user if hasattr(perm, 'user') else None
repo = get_repo(repo_id)
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
if group_id is not None:
# is a group folder perm
group = get_group(group_id)
if repo is None or dir_id is None or group is None:
seafile_api.rm_folder_group_perm(repo_id, path, group_id)
return False
if email is not None:
# is a user folder perm
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
if repo is None or dir_id is None or user is None:
seafile_api.rm_folder_user_perm(repo_id, path, email)
return False
return True
# for user folder permission
user_folder_perms = seafile_api.list_folder_user_perm_by_repo(repo_id)
user_folder_perms = filter(lambda x: not_need_delete(x), user_folder_perms)
user_folder_perms.reverse()
for folder_perm in user_folder_perms:
folder_path = folder_perm.path
folder_perm.folder_link = reverse('repo', args=[repo_id]) + '?p=' + urlquote(folder_path)
if folder_path == '/':
folder_perm.folder_name = _(u'Root Directory')
else:
folder_perm.folder_name = os.path.basename(folder_path)
# for group folder permission
group_folder_perms = seafile_api.list_folder_group_perm_by_repo(repo_id)
group_folder_perms = filter(lambda x: not_need_delete(x), group_folder_perms)
group_folder_perms.reverse()
for folder_perm in group_folder_perms:
folder_path = folder_perm.path
folder_perm.folder_link = reverse('repo', args=[repo_id]) + '?p=' + urlquote(folder_path)
if folder_path == '/':
folder_perm.folder_name = _(u'Root Directory')
else:
folder_perm.folder_name = os.path.basename(folder_path)
folder_perm.group_name = get_group(folder_perm.group_id).group_name
# contacts that already registered
sys_contacts = []
contacts = Contact.objects.get_contacts_by_user(username)
for contact in contacts:
try:
user = User.objects.get(email = contact.contact_email)
except User.DoesNotExist:
user = None
if user is not None:
sys_contacts.append(contact.contact_email)
return render_to_response('repo_folder_perm.html', {
'repo': repo,
'user_folder_perms': user_folder_perms,
'group_folder_perms': group_folder_perms,
'contacts': sys_contacts,
}, context_instance=RequestContext(request))
def upload_error_msg (code):
err_msg = _(u'Internal Server Error')
if (code == 0):
err_msg = _(u'Filename contains invalid character')
elif (code == 1):
err_msg = _(u'Duplicated filename')
elif (code == 2):
err_msg = _(u'File does not exist')
elif (code == 3):
err_msg = _(u'File size surpasses the limit')
elif (code == 4):
err_msg = _(u'The space of owner is used up, upload failed')
elif (code == 5):
err_msg = _(u'An error occurs during file transfer')
return err_msg
def upload_file_error(request, repo_id):
if request.method == 'GET':
repo = get_repo(repo_id)
if not repo:
raise Http404
parent_dir = request.GET.get('p')
filename = request.GET.get('fn', '')
err = request.GET.get('err')
if not parent_dir or not err:
return render_error(request, _(u'Invalid url'))
zipped = gen_path_link (parent_dir, repo.name)
code = int(err)
err_msg = upload_error_msg(code)
return render_to_response('upload_file_error.html', {
'repo': repo,
'zipped': zipped,
'filename': filename,
'err_msg': err_msg,
}, context_instance=RequestContext(request))
def update_file_error(request, repo_id):
if request.method == 'GET':
repo = get_repo(repo_id)
if not repo:
raise Http404
target_file = request.GET.get('p')
err = request.GET.get('err')
if not target_file or not err:
return render_error(request, _(u'Invalid url'))
zipped = gen_path_link (target_file, repo.name)
code = int(err)
err_msg = upload_error_msg(code)
return render_to_response('update_file_error.html', {
'repo': repo,
'zipped': zipped,
'err_msg': err_msg,
}, context_instance=RequestContext(request))
@login_required
def repo_history(request, repo_id):
"""
List library modification histories.
"""
user_perm = check_repo_access_permission(repo_id, request.user)
if not user_perm:
return render_permission_error(request, _(u'Unable to view library modification'))
repo = get_repo(repo_id)
if not repo:
raise Http404
username = request.user.username
try:
server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set.
server_crypto = False
password_set = False
if repo.props.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)):
try:
ret = seafserv_rpc.is_passwd_set(repo_id, username)
if ret == 1:
password_set = True
except SearpcError, e:
return render_error(request, e.msg)
if not password_set:
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
commits_all = get_commits(repo_id, per_page * (current_page -1),
per_page + 1)
commits = commits_all[:per_page]
for c in commits:
c.show = False if new_merge_with_no_conflict(c) else True
if len(commits_all) == per_page + 1:
page_next = True
else:
page_next = False
return render_to_response('repo_history.html', {
"repo": repo,
"commits": commits,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
'user_perm': user_perm,
}, context_instance=RequestContext(request))
@login_required
@require_POST
def repo_revert_history(request, repo_id):
next = request.META.get('HTTP_REFERER', None)
if not next:
next = settings.SITE_ROOT
repo = get_repo(repo_id)
if not repo:
messages.error(request, _("Library does not exist"))
return HttpResponseRedirect(next)
# perm check
perm = check_repo_access_permission(repo.id, request.user)
username = request.user.username
repo_owner = seafile_api.get_repo_owner(repo.id)
if perm is None or repo_owner != username:
messages.error(request, _("Permission denied"))
return HttpResponseRedirect(next)
try:
server_crypto = UserOptions.objects.is_server_crypto(username)
except CryptoOptionNotSetError:
# Assume server_crypto is ``False`` if this option is not set.
server_crypto = False
password_set = False
if repo.props.encrypted and \
(repo.enc_version == 1 or (repo.enc_version == 2 and server_crypto)):
try:
ret = seafserv_rpc.is_passwd_set(repo_id, username)
if ret == 1:
password_set = True
except SearpcError, e:
return render_error(request, e.msg)
if not password_set:
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
commit_id = request.GET.get('commit_id', '')
if not commit_id:
return render_error(request, _(u'Please specify history ID'))
try:
seafserv_threaded_rpc.revert_on_server(repo_id, commit_id, request.user.username)
except SearpcError, e:
if e.msg == 'Bad arguments':
return render_error(request, _(u'Invalid arguments'))
elif e.msg == 'No such repo':
return render_error(request, _(u'Library does not exist'))
elif e.msg == "Commit doesn't exist":
return render_error(request, _(u'History you specified does not exist'))
else:
return render_error(request, _(u'Unknown error'))
return HttpResponseRedirect(next)
def fpath_to_link(repo_id, path, is_dir=False):
"""Translate file path of a repo to its view link"""
if is_dir:
href = reverse("view_common_lib_dir", args=[repo_id, urllib2.quote(path.encode('utf-8')).strip('/')])
else:
if not path.startswith('/'):
p = '/' + path
href = reverse("view_lib_file", args=[repo_id, urllib2.quote(p.encode('utf-8'))])
return '<a href="%s">%s</a>' % (href, escape(path))
def get_diff(repo_id, arg1, arg2):
lists = {'new': [], 'removed': [], 'renamed': [], 'modified': [],
'newdir': [], 'deldir': []}
diff_result = seafserv_threaded_rpc.get_diff(repo_id, arg1, arg2)
if not diff_result:
return lists
for d in diff_result:
if d.status == "add":
lists['new'].append(fpath_to_link(repo_id, d.name))
elif d.status == "del":
lists['removed'].append(escape(d.name))
elif d.status == "mov":
lists['renamed'].append(escape(d.name) + " ==> " + fpath_to_link(repo_id, d.new_name))
elif d.status == "mod":
lists['modified'].append(fpath_to_link(repo_id, d.name))
elif d.status == "newdir":
lists['newdir'].append(fpath_to_link(repo_id, d.name, is_dir=True))
elif d.status == "deldir":
lists['deldir'].append(escape(d.name))
return lists
def create_default_library(request):
"""Create a default library for user.
Arguments:
- `username`:
"""
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
default_repo = seafile_api.create_org_repo(name=_("My Library"),
desc=_("My Library"),
username=username,
passwd=None,
org_id=org_id)
else:
default_repo = seafile_api.create_repo(name=_("My Library"),
desc=_("My Library"),
username=username,
passwd=None)
sys_repo_id = get_system_default_repo_id()
if sys_repo_id is None:
return
try:
dirents = seafile_api.list_dir_by_path(sys_repo_id, '/')
for e in dirents:
obj_name = e.obj_name
seafile_api.copy_file(sys_repo_id, '/', obj_name,
default_repo, '/', obj_name, username, 0)
except SearpcError as e:
logger.error(e)
return
UserOptions.objects.set_default_repo(username, default_repo)
return default_repo
def get_owned_repo_list(request):
"""List owned repos.
"""
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
return seafile_api.get_org_owned_repo_list(org_id, username)
else:
return seafile_api.get_owned_repo_list(username)
def get_virtual_repos_by_owner(request):
"""List virtual repos.
Arguments:
- `request`:
"""
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
return seaserv.seafserv_threaded_rpc.get_org_virtual_repos_by_owner(
org_id, username)
else:
return seafile_api.get_virtual_repos_by_owner(username)
@login_required
@user_mods_check
def myhome(request):
return HttpResponseRedirect(reverse('libraries'))
@login_required
@user_mods_check
def libraries(request):
"""
New URL to replace myhome
"""
username = request.user.username
# options
if request.cloud_mode and request.user.org is None:
allow_public_share = False
else:
allow_public_share = True
sub_lib_enabled = UserOptions.objects.is_sub_lib_enabled(username)
max_upload_file_size = get_max_upload_file_size()
guide_enabled = UserOptions.objects.is_user_guide_enabled(username)
if guide_enabled:
if request.user.permissions.can_add_repo():
create_default_library(request)
# only show guide once
UserOptions.objects.disable_user_guide(username)
folder_perm_enabled = True if is_pro_version() and ENABLE_FOLDER_PERM else False
return render_to_response('libraries.html', {
"allow_public_share": allow_public_share,
"guide_enabled": guide_enabled,
"sub_lib_enabled": sub_lib_enabled,
'enable_upload_folder': settings.ENABLE_UPLOAD_FOLDER,
'enable_resumable_fileupload': settings.ENABLE_RESUMABLE_FILEUPLOAD,
'enable_thumbnail': settings.ENABLE_THUMBNAIL,
'enable_encrypted_library': config.ENABLE_ENCRYPTED_LIBRARY,
'max_upload_file_size': max_upload_file_size,
'folder_perm_enabled': folder_perm_enabled,
'is_pro': True if is_pro_version() else False,
}, context_instance=RequestContext(request))
@login_required
@user_mods_check
def starred(request):
"""List starred files.
Arguments:
- `request`:
"""
username = request.user.username
starred_files = UserStarredFiles.objects.get_starred_files_by_username(
username)
return render_to_response('starred.html', {
"starred_files": starred_files,
}, context_instance=RequestContext(request))
@login_required
@user_mods_check
def devices(request):
"""List user devices"""
username = request.user.username
user_devices = get_user_devices(username)
return render_to_response('devices.html', {
"devices": user_devices,
}, context_instance=RequestContext(request))
@login_required_ajax
@require_POST
def unlink_device(request):
content_type = 'application/json; charset=utf-8'
platform = request.POST.get('platform', '')
device_id = request.POST.get('device_id', '')
if not platform or not device_id:
return HttpResponseBadRequest(json.dumps({'error': _(u'Argument missing')}),
content_type=content_type)
try:
do_unlink_device(request.user.username, platform, device_id)
except:
return HttpResponse(json.dumps({'error': _(u'Internal server error')}),
status=500, content_type=content_type)
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
@login_required
@require_POST
def unsetinnerpub(request, repo_id):
"""Unshare repos in organization or in share admin page.
Only system admin, organization admin or repo owner can perform this op.
"""
repo = get_repo(repo_id)
perm = request.GET.get('permission', None)
if perm is None:
return render_error(request, _(u'Argument is not valid'))
if not repo:
messages.error(request, _('Failed to unshare the library, as it does not exist.'))
return HttpResponseRedirect(reverse('share_admin'))
# permission check
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
repo_owner = seafile_api.get_org_repo_owner(repo.id)
is_repo_owner = True if repo_owner == username else False
if not (request.user.org.is_staff or is_repo_owner):
raise Http404
else:
repo_owner = seafile_api.get_repo_owner(repo.id)
is_repo_owner = True if repo_owner == username else False
if not (request.user.is_staff or is_repo_owner):
raise Http404
try:
if is_org_context(request):
org_id = request.user.org.org_id
seaserv.seafserv_threaded_rpc.unset_org_inner_pub_repo(org_id,
repo.id)
else:
seaserv.unset_inner_pub_repo(repo.id)
origin_repo_id, origin_path = get_origin_repo_info(repo.id)
if origin_repo_id is not None:
perm_repo_id = origin_repo_id
perm_path = origin_path
else:
perm_repo_id = repo.id
perm_path = '/'
send_perm_audit_msg('delete-repo-perm', username, 'all',
perm_repo_id, perm_path, perm)
messages.success(request, _('Unshare "%s" successfully.') % repo.name)
except SearpcError:
messages.error(request, _('Failed to unshare "%s".') % repo.name)
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
return HttpResponseRedirect(next)
# @login_required
# def ownerhome(request, owner_name):
# owned_repos = []
# quota_usage = 0
# owned_repos = seafserv_threaded_rpc.list_owned_repos(owner_name)
# quota_usage = seafserv_threaded_rpc.get_user_quota_usage(owner_name)
# user_dict = user_info(request, owner_name)
# return render_to_response('ownerhome.html', {
# "owned_repos": owned_repos,
# "quota_usage": quota_usage,
# "owner": owner_name,
# "user_dict": user_dict,
# }, context_instance=RequestContext(request))
@login_required
def repo_set_access_property(request, repo_id):
ap = request.GET.get('ap', '')
seafserv_threaded_rpc.repo_set_access_property(repo_id, ap)
return HttpResponseRedirect(reverse('repo', args=[repo_id]))
@login_required
def file_upload_progress_page(request):
'''
As iframe in repo_upload_file.html, for solving problem in chrome.
'''
uuid = request.GET.get('uuid', '')
fileserver_root = get_fileserver_root()
upload_progress_con_id = request.GET.get('upload_progress_con_id', '')
return render_to_response('file_upload_progress_page.html', {
'uuid': uuid,
'fileserver_root': fileserver_root,
'upload_progress_con_id': upload_progress_con_id,
}, context_instance=RequestContext(request))
@login_required
def validate_filename(request):
repo_id = request.GET.get('repo_id')
filename = request.GET.get('filename')
if not (repo_id and filename):
return render_error(request)
result = {'ret':'yes'}
try:
ret = is_valid_filename(filename)
except SearpcError:
result['ret'] = 'error'
else:
result['ret'] = 'yes' if ret == 1 else 'no'
content_type = 'application/json; charset=utf-8'
return HttpResponse(json.dumps(result), content_type=content_type)
def render_file_revisions (request, repo_id):
"""List all history versions of a file."""
days_str = request.GET.get('days', '')
try:
days = int(days_str)
except ValueError:
days = 7
path = request.GET.get('p', '/')
if path[-1] == '/':
path = path[:-1]
u_filename = os.path.basename(path)
if not path:
return render_error(request)
repo = get_repo(repo_id)
if not repo:
error_msg = _(u"Library does not exist")
return render_error(request, error_msg)
filetype = get_file_type_and_ext(u_filename)[0].lower()
if filetype == 'text' or filetype == 'markdown':
can_compare = True
else:
can_compare = False
try:
commits = seafile_api.get_file_revisions(repo_id, path, -1, -1, days)
except SearpcError, e:
logger.error(e.msg)
return render_error(request, e.msg)
if not commits:
return render_error(request, _(u'No revisions found'))
# Check whether user is repo owner
if validate_owner(request, repo_id):
is_owner = True
else:
is_owner = False
cur_path = path
for commit in commits:
commit.path = cur_path
if commit.rev_renamed_old_path:
cur_path = '/' + commit.rev_renamed_old_path
zipped = gen_path_link(path, repo.name)
can_revert_file = True
username = request.user.username
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
if seafile_api.check_permission_by_path(repo_id, path, username) != 'rw' or \
(is_locked and not locked_by_me):
can_revert_file = False
return render_to_response('file_revisions.html', {
'repo': repo,
'path': path,
'u_filename': u_filename,
'zipped': zipped,
'commits': commits,
'is_owner': is_owner,
'can_compare': can_compare,
'can_revert_file': can_revert_file,
'days': days,
}, context_instance=RequestContext(request))
@login_required
@require_POST
def repo_revert_file(request, repo_id):
repo = get_repo(repo_id)
if not repo:
raise Http404
commit_id = request.GET.get('commit')
path = request.GET.get('p')
from_page = request.GET.get('from')
if not (commit_id and path and from_page):
return render_error(request, _(u"Invalid arguments"))
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
username = request.user.username
# perm check
if check_folder_permission(request, repo.id, path) != 'rw':
messages.error(request, _("Permission denied"))
return HttpResponseRedirect(next)
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
if (is_locked, locked_by_me) == (None, None):
messages.error(request, _("Check file lock error"))
return HttpResponseRedirect(next)
if is_locked and not locked_by_me:
messages.error(request, _("File is locked"))
return HttpResponseRedirect(next)
try:
ret = seafile_api.revert_file(repo_id, commit_id, path, username)
except Exception as e:
logger.error(e)
messages.error(request, _('Failed to restore, please try again later.'))
return HttpResponseRedirect(next)
else:
if from_page == 'repo_history':
# When revert file from repo history, we redirect to repo history
url = reverse('repo', args=[repo_id]) + u'?commit_id=%s&history=y' % commit_id
elif from_page == 'recycle':
# When revert from repo recycle page, redirect to recycle page.
url = reverse('repo_recycle_view', args=[repo_id])
elif from_page == 'dir_recycle':
# When revert from dir recycle page, redirect to recycle page.
dir_path = request.GET.get('dir_path', '')
url = next if not dir_path else reverse('dir_recycle_view', args=[repo_id]) + '?dir_path=' + urlquote(dir_path)
else:
# When revert file from file history, we reload page
url = next
if ret == 1:
root_url = reverse('view_common_lib_dir', args=[repo_id, '/'])
msg = _(u'Successfully revert %(path)s to <a href="%(root)s">root directory.</a>') % {"path": escape(path.lstrip('/')), "root": root_url}
messages.success(request, msg, extra_tags='safe')
else:
file_view_url = reverse('view_lib_file', args=[repo_id, urllib2.quote(path.encode('utf-8'))])
msg = _(u'Successfully revert <a href="%(url)s">%(path)s</a>') % {"url": file_view_url, "path": escape(path.lstrip('/'))}
messages.success(request, msg, extra_tags='safe')
return HttpResponseRedirect(url)
@login_required
@require_POST
def repo_revert_dir(request, repo_id):
repo = get_repo(repo_id)
if not repo:
raise Http404
commit_id = request.GET.get('commit')
path = request.GET.get('p')
from_page = request.GET.get('from')
if not (commit_id and path and from_page):
return render_error(request, _(u"Invalid arguments"))
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
# perm check
if check_folder_permission(request, repo.id, path) != 'rw':
messages.error(request, _("Permission denied"))
return HttpResponseRedirect(next)
try:
ret = seafile_api.revert_dir(repo_id, commit_id, path, request.user.username)
except Exception as e:
logger.error(e)
messages.error(request, _('Failed to restore, please try again later.'))
return HttpResponseRedirect(next)
else:
if from_page == 'repo_history':
# When revert file from repo history, we redirect to repo history
url = reverse('repo', args=[repo_id]) + u'?commit_id=%s&history=y' % commit_id
elif from_page == 'recycle':
# When revert from repo recycle page, redirect to recycle page.
url = reverse('repo_recycle_view', args=[repo_id])
elif from_page == 'dir_recycle':
# When revert from dir recycle page, redirect to dir recycle page.
dir_path = request.GET.get('dir_path', '')
url = next if not dir_path else reverse('dir_recycle_view', args=[repo_id]) + '?dir_path=' + urlquote(dir_path)
else:
# When revert file from file history, we redirect to parent dir of this file
parent_dir = os.path.dirname(path)
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urllib2.quote(parent_dir.encode('utf-8')))
if ret == 1:
root_url = reverse('repo', args=[repo_id]) + u'?p=/'
msg = _(u'Successfully revert %(path)s to <a href="%(url)s">root directory.</a>') % {"path": escape(path.lstrip('/')), "url": root_url}
messages.success(request, msg, extra_tags='safe')
else:
dir_view_url = reverse('repo', args=[repo_id]) + u'?p=' + urllib2.quote(path.encode('utf-8'))
msg = _(u'Successfully revert <a href="%(url)s">%(path)s</a>') % {"url": dir_view_url, "path": escape(path.lstrip('/'))}
messages.success(request, msg, extra_tags='safe')
return HttpResponseRedirect(url)
@login_required
def file_revisions(request, repo_id):
"""List file revisions in file version history page.
"""
repo = get_repo(repo_id)
if not repo:
raise Http404
# perm check
if check_repo_access_permission(repo.id, request.user) is None:
raise Http404
return render_file_revisions(request, repo_id)
def demo(request):
"""
Login as demo account.
"""
user = User.objects.get(email=settings.CLOUD_DEMO_USER)
for backend in get_backends():
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
auth_login(request, user)
redirect_to = settings.SITE_ROOT
return HttpResponseRedirect(redirect_to)
def list_inner_pub_repos(request):
"""List inner pub repos.
"""
username = request.user.username
if is_org_context(request):
org_id = request.user.org.org_id
return seaserv.list_org_inner_pub_repos(org_id, username)
if not request.cloud_mode:
return seaserv.list_inner_pub_repos(username)
return []
@login_required
def pubrepo(request):
"""
Show public libraries.
"""
if not request.user.permissions.can_view_org():
raise Http404
username = request.user.username
if request.cloud_mode and request.user.org is not None:
org_id = request.user.org.org_id
public_repos = seaserv.list_org_inner_pub_repos(org_id, username)
for r in public_repos:
if r.user == username:
r.share_from_me = True
return render_to_response('organizations/pubrepo.html', {
'public_repos': public_repos,
'create_shared_repo': True,
}, context_instance=RequestContext(request))
if not request.cloud_mode:
public_repos = seaserv.list_inner_pub_repos(username)
for r in public_repos:
if r.user == username:
r.share_from_me = True
return render_to_response('pubrepo.html', {
'public_repos': public_repos,
'create_shared_repo': True,
}, context_instance=RequestContext(request))
raise Http404
def get_pub_users(request, start, limit):
if is_org_context(request):
url_prefix = request.user.org.url_prefix
users_plus_one = seaserv.get_org_users_by_url_prefix(url_prefix,
start, limit)
elif request.cloud_mode:
raise Http404 # no pubuser in cloud mode
else:
users_plus_one = seaserv.get_emailusers('DB', start,
limit, is_active=True)
return users_plus_one
def count_pub_users(request):
if is_org_context(request):
url_prefix = request.user.org.url_prefix
# TODO: need a new api to count org users.
org_users = seaserv.get_org_users_by_url_prefix(url_prefix, -1, -1)
return len(org_users)
elif request.cloud_mode:
return 0
else:
return seaserv.ccnet_threaded_rpc.count_emailusers('DB')
@login_required
def pubuser(request):
"""
Show public users in database or ldap depending on request arg ``ldap``.
"""
if not request.user.permissions.can_view_org():
raise Http404
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
except ValueError:
current_page = 1
per_page = 20 # show 20 users per-page
# Show LDAP users or Database users.
have_ldap_user = False
if len(seaserv.get_emailusers('LDAPImport', 0, 1, is_active=True)) > 0:
have_ldap_user = True
try:
ldap = True if int(request.GET.get('ldap', 0)) == 1 else False
except ValueError:
ldap = False
if ldap and have_ldap_user:
# return ldap imported active users
users_plus_one = seaserv.get_emailusers('LDAPImport',
per_page * (current_page - 1),
per_page + 1,
is_active=True)
else:
users_plus_one = get_pub_users(request, per_page * (current_page - 1),
per_page + 1)
has_prev = False if current_page == 1 else True
has_next = True if len(users_plus_one) == per_page + 1 else False
if ldap and have_ldap_user:
# return the number of ldap imported active users
emailusers_count = seaserv.ccnet_threaded_rpc.count_emailusers('LDAP')
else:
emailusers_count = count_pub_users(request)
num_pages = int(ceil(emailusers_count / float(per_page)))
page_range = get_page_range(current_page, num_pages)
show_paginator = True if len(page_range) > 1 else False
users = users_plus_one[:per_page]
return render_to_response('pubuser.html', {
'users': users,
'current_page': current_page,
'has_prev': has_prev,
'has_next': has_next,
'page_range': page_range,
'show_paginator': show_paginator,
'have_ldap_user': have_ldap_user,
'ldap': ldap,
}, context_instance=RequestContext(request))
@login_required_ajax
def repo_set_password(request):
content_type = 'application/json; charset=utf-8'
form = RepoPassowrdForm(request.POST)
if form.is_valid():
return HttpResponse(json.dumps({'success': True}), content_type=content_type)
else:
return HttpResponse(json.dumps({'error': str(form.errors.values()[0])}),
status=400, content_type=content_type)
def i18n(request):
"""
Set client language preference, lasts for one month
"""
from django.conf import settings
next = request.META.get('HTTP_REFERER', settings.SITE_ROOT)
lang = request.GET.get('lang', settings.LANGUAGE_CODE)
if lang not in [e[0] for e in settings.LANGUAGES]:
# language code is not supported, use default.
lang = settings.LANGUAGE_CODE
# set language code to user profile if user is logged in
if not request.user.is_anonymous():
p = Profile.objects.get_profile_by_user(request.user.username)
if p is not None:
# update exist record
p.set_lang_code(lang)
else:
# add new record
Profile.objects.add_or_update(request.user.username, '', '', lang)
# set language code to client
res = HttpResponseRedirect(next)
res.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang, max_age=30*24*60*60)
return res
@login_required
def repo_download_dir(request, repo_id):
repo = get_repo(repo_id)
if not repo:
return render_error(request, _(u'Library does not exist'))
path = request.GET.get('p', '/')
if path[-1] != '/': # Normalize dir path
path += '/'
if not seafile_api.get_dir_id_by_path(repo.id, path):
return render_error(request, _('"%s" does not exist.') % path)
if len(path) > 1:
dirname = os.path.basename(path.rstrip('/')) # Here use `rstrip` to cut out last '/' in path
else:
dirname = repo.name
allow_download = True if check_repo_access_permission(
repo_id, request.user) else False
if allow_download:
dir_id = seafserv_threaded_rpc.get_dirid_by_path (repo.id,
repo.head_cmmt_id,
path.encode('utf-8'))
try:
total_size = seafserv_threaded_rpc.get_dir_size(repo.store_id, repo.version,
dir_id)
except Exception, e:
logger.error(str(e))
return render_error(request, _(u'Internal Error'))
if total_size > MAX_DOWNLOAD_DIR_SIZE:
return render_error(request, _(u'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)
else:
return render_error(request, _(u'Unable to download "%s"') % dirname )
url = gen_file_get_url(token, dirname)
return redirect(url)
@login_required
@user_mods_check
def activities(request):
if not EVENTS_ENABLED:
raise Http404
events_count = 15
username = request.user.username
start = int(request.GET.get('start', 0))
if is_org_context(request):
org_id = request.user.org.org_id
events, start = get_org_user_events(org_id, username, start, events_count)
else:
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)
return render_to_response('activities.html', {
'event_groups': event_groups,
'events_more': events_more,
'new_start': start,
}, context_instance=RequestContext(request))
def group_events_data(events):
"""
Group events according to the date.
"""
event_groups = []
for e in events:
e.time = utc_to_local(e.timestamp)
e.date = e.time.strftime("%Y-%m-%d")
if e.etype == 'repo-update':
e.author = e.commit.creator_name
elif e.etype == 'repo-create':
e.author = e.creator
else:
e.author = e.repo_owner
if len(event_groups) == 0 or \
len(event_groups) > 0 and e.date != event_groups[-1]['date']:
event_group = {}
event_group['date'] = e.date
event_group['events'] = [e]
event_groups.append(event_group)
else:
event_groups[-1]['events'].append(e)
return event_groups
@login_required
def convert_cmmt_desc_link(request):
"""Return user to file/directory page based on the changes in commit.
"""
repo_id = request.GET.get('repo_id')
cmmt_id = request.GET.get('cmmt_id')
name = request.GET.get('nm')
repo = get_repo(repo_id)
if not repo:
raise Http404
# perm check
if check_repo_access_permission(repo_id, request.user) is None:
raise Http404
diff_result = seafserv_threaded_rpc.get_diff(repo_id, '', cmmt_id)
if not diff_result:
raise Http404
for d in diff_result:
if name not in d.name:
# skip to next diff_result if file/folder user clicked does not
# match the diff_result
continue
if d.status == 'add' or d.status == 'mod': # Add or modify file
return HttpResponseRedirect(
reverse('view_lib_file', args=[repo_id, '/' + urlquote(d.name)]))
elif d.status == 'mov': # Move or Rename file
return HttpResponseRedirect(
reverse('view_lib_file', args=[repo_id, '/' + urlquote(d.new_name)]))
elif d.status == 'newdir':
return HttpResponseRedirect(
reverse('view_common_lib_dir', args=[repo_id, urlquote(d.name).strip('/')]))
else:
continue
# Shoud never reach here.
logger.warn('OUT OF CONTROL!')
logger.warn('repo_id: %s, cmmt_id: %s, name: %s' % (repo_id, cmmt_id, name))
for d in diff_result:
logger.warn('diff_result: %s' % (d.__dict__))
raise Http404
@login_required
def toggle_modules(request):
"""Enable or disable modules.
"""
if request.method != 'POST':
raise Http404
referer = request.META.get('HTTP_REFERER', None)
next = settings.SITE_ROOT if referer is None else referer
username = request.user.username
personal_wiki = request.POST.get('personal_wiki', 'off')
if personal_wiki == 'on':
enable_mod_for_user(username, MOD_PERSONAL_WIKI)
messages.success(request, _('Successfully enable "Personal Wiki".'))
else:
disable_mod_for_user(username, MOD_PERSONAL_WIKI)
if referer.find('wiki') > 0:
next = reverse('myhome')
messages.success(request, _('Successfully disable "Personal Wiki".'))
return HttpResponseRedirect(next)
storage = get_avatar_file_storage()
def latest_entry(request, filename):
try:
return storage.modified_time(filename)
except Exception as e:
logger.error(e)
return None
@condition(last_modified_func=latest_entry)
def image_view(request, filename):
if AVATAR_FILE_STORAGE is None:
raise Http404
# read file from cache, if hit
filename_md5 = hashlib.md5(filename).hexdigest()
cache_key = 'image_view__%s' % filename_md5
file_content = cache.get(cache_key)
if file_content is None:
# otherwise, read file from database and update cache
image_file = storage.open(filename, 'rb')
if not image_file:
raise Http404
file_content = image_file.read()
cache.set(cache_key, file_content, 365 * 24 * 60 * 60)
# Prepare response
content_type, content_encoding = mimetypes.guess_type(filename)
response = HttpResponse(content=file_content, content_type=content_type)
response['Content-Disposition'] = 'inline; filename=%s' % filename
if content_encoding:
response['Content-Encoding'] = content_encoding
return response
def shib_login(request):
return HttpResponseRedirect(request.GET.get("next",reverse('myhome')))
def underscore_template(request, template):
"""Serve underscore template through Django, mainly for I18n.
Arguments:
- `request`:
- `template`:
"""
if not template.startswith('js'): # light security check
raise Http404
return render_to_response(template, {},
context_instance=RequestContext(request))
def fake_view(request, **kwargs):
"""
Used for 'view_common_lib_dir' and 'view_group' url
As the two urls aboved starts with '#',
http request will not access this function
"""
pass
def client_token_login(request):
"""Login from desktop client with a generated token.
"""
tokenstr = request.GET.get('token', '')
user = None
if len(tokenstr) == 32:
try:
username = ClientLoginToken.objects.get_username(tokenstr)
except ClientLoginToken.DoesNotExist:
pass
else:
try:
user = User.objects.get(email=username)
for backend in get_backends():
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
except User.DoesNotExist:
pass
if user:
if request.user.is_authenticated() and request.user.username == user.username:
pass
else:
request.client_token_login = True
auth_login(request, user)
return HttpResponseRedirect(request.GET.get("next", reverse('libraries')))