1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-10 08:55:02 +00:00

Merge branch '6.3' into master

This commit is contained in:
ilearnit 2018-09-28 15:51:34 +08:00
commit dac30d24c5
67 changed files with 31506 additions and 29592 deletions
locale
ar/LC_MESSAGES
bg/LC_MESSAGES
ca/LC_MESSAGES
cs/LC_MESSAGES
de/LC_MESSAGES
el/LC_MESSAGES
en/LC_MESSAGES
es/LC_MESSAGES
es_AR/LC_MESSAGES
es_MX/LC_MESSAGES
fi/LC_MESSAGES
fr/LC_MESSAGES
he/LC_MESSAGES
hu/LC_MESSAGES
is/LC_MESSAGES
it/LC_MESSAGES
ja/LC_MESSAGES
ko/LC_MESSAGES
lt/LC_MESSAGES
lv/LC_MESSAGES
mk/LC_MESSAGES
nl/LC_MESSAGES
pl/LC_MESSAGES
pt_BR/LC_MESSAGES
ru/LC_MESSAGES
sv/LC_MESSAGES
tr/LC_MESSAGES
uk/LC_MESSAGES
vi/LC_MESSAGES
zh_CN/LC_MESSAGES
zh_TW/LC_MESSAGES
media/css
seahub
static/scripts
tests
thirdpart/shibboleth

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@ msgid ""
msgstr ""
"Project-Id-Version: seahub\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-07 10:41+0800\n"
"POT-Creation-Date: 2018-09-17 12:01+0800\n"
"PO-Revision-Date: 2018-09-07 02:46+0000\n"
"Last-Translator: C_Q <helloworld.c@outlook.com>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/haiwen/seahub/language/zh_CN/)\n"
@ -75,14 +75,14 @@ msgstr "提交"
#: frontend/src/components/menu-component/menu-dialog/create-fileforder-dialog.js:91
#: frontend/src/components/menu-component/menu-dialog/rename-dialog.js:71
#: static/scripts/app/views/fileupload.js:21
#: static/scripts/app/views/fileupload.js:22
msgid "Cancel"
msgstr "取消"
#: frontend/src/components/menu-component/menu-dialog/delete-dialog.js:16
#: frontend/src/components/menu-component/node-menu.js:39
#: frontend/src/components/menu-component/node-menu.js:47
#: static/scripts/app/views/fileupload.js:22
#: static/scripts/app/views/fileupload.js:23
#: static/scripts/sysadmin-app/views/device-trusted-ipaddress.js:27
msgid "Delete"
msgstr "删除"
@ -203,23 +203,23 @@ msgid "View-on-Cloud"
msgstr "仅云端只读"
#: static/scripts/app/models/deleted-repo.js:19
#: static/scripts/app/models/repo.js:59 static/scripts/common.js:343
#: static/scripts/app/models/repo.js:59 static/scripts/common.js:344
msgid "Encrypted library"
msgstr "加密资料库"
#: static/scripts/app/models/deleted-repo.js:21
#: static/scripts/app/models/repo.js:61 static/scripts/common.js:347
#: static/scripts/app/models/repo.js:61 static/scripts/common.js:348
#: static/scripts/sysadmin-app/models/trash-repo.js:14
msgid "Read-Write library"
msgstr "可读写资料库"
#: static/scripts/app/models/dirent.js:113 static/scripts/app/views/dir.js:682
#: static/scripts/app/views/dir.js:752
#: static/scripts/app/views/fileupload.js:392
#: static/scripts/app/views/fileupload.js:406
#: static/scripts/app/views/fileupload.js:418
#: static/scripts/app/views/fileupload.js:430
#: static/scripts/app/views/group.js:65 static/scripts/common.js:643
#: static/scripts/app/views/fileupload.js:394
#: static/scripts/app/views/fileupload.js:408
#: static/scripts/app/views/fileupload.js:420
#: static/scripts/app/views/fileupload.js:432
#: static/scripts/app/views/group.js:65 static/scripts/common.js:644
msgid "Just now"
msgstr "刚才"
@ -230,29 +230,29 @@ msgstr "名称为必填项"
#: static/scripts/app/models/repo.js:37
#: static/scripts/app/views/dialogs/dirent-rename.js:65
#: static/scripts/app/views/dir.js:667 static/scripts/app/views/dir.js:727
#: static/scripts/app/views/dirent.js:515
#: static/scripts/app/views/dirent.js:513
#: static/scripts/app/views/group-repo.js:131
#: static/scripts/app/views/repo.js:187
msgid "Name should not include '/'."
msgstr "名字不能包含 /’。"
#: static/scripts/app/models/repo.js:41 static/scripts/app/views/share.js:334
#: static/scripts/app/models/repo.js:41 static/scripts/app/views/share.js:338
msgid "Please enter password"
msgstr "请输入密码"
#: static/scripts/app/models/repo.js:42 static/scripts/app/views/share.js:342
#: static/scripts/app/models/repo.js:42 static/scripts/app/views/share.js:346
msgid "Please enter the password again"
msgstr "请再次输入密码"
#: static/scripts/app/models/repo.js:44 static/scripts/app/views/share.js:338
#: static/scripts/app/models/repo.js:44 static/scripts/app/views/share.js:342
msgid "Password is too short"
msgstr "密码太短"
#: static/scripts/app/models/repo.js:46 static/scripts/app/views/share.js:346
#: static/scripts/app/models/repo.js:46 static/scripts/app/views/share.js:350
msgid "Passwords don't match"
msgstr "两次输入的密码不一致"
#: static/scripts/app/models/repo.js:63 static/scripts/common.js:345
#: static/scripts/app/models/repo.js:63 static/scripts/common.js:346
msgid "Read-Only library"
msgstr "只读资料库"
@ -369,7 +369,7 @@ msgstr "成功拷贝 %(name)s"
#: static/scripts/app/views/dialogs/dirent-rename.js:59
#: static/scripts/app/views/dir.js:662 static/scripts/app/views/dir.js:722
#: static/scripts/app/views/dirent.js:509
#: static/scripts/app/views/dirent.js:507
#: static/scripts/app/views/group-repo.js:125
#: static/scripts/app/views/groups.js:118
#: static/scripts/app/views/invitations.js:65
@ -493,7 +493,7 @@ msgid "Password is required."
msgstr "密码为必填项。"
#: static/scripts/app/views/dir.js:479
#: static/scripts/app/views/notifications.js:63 static/scripts/common.js:467
#: static/scripts/app/views/notifications.js:63 static/scripts/common.js:468
msgid "Please check the network."
msgstr "请检查网络是否已连接。"
@ -617,8 +617,8 @@ msgstr "移动 %(name)s 失败"
msgid "Failed to copy %(name)s"
msgstr "复制 %(name)s 失败"
#: static/scripts/app/views/dirent-details.js:95 static/scripts/common.js:694
#: static/scripts/common.js:765
#: static/scripts/app/views/dirent-details.js:95 static/scripts/common.js:695
#: static/scripts/common.js:766
msgid "No matches"
msgstr "没有匹配项"
@ -632,37 +632,37 @@ msgid "locked by {placeholder}"
msgstr "被 {placeholder} 锁定"
#: static/scripts/app/views/dirent-grid.js:187
#: static/scripts/app/views/dirent.js:416
#: static/scripts/app/views/dirent.js:414
msgid "Successfully deleted %(name)s"
msgstr "删除 %(name)s 成功"
#: static/scripts/app/views/fileupload.js:10
#: static/scripts/app/views/fileupload.js:11
msgid "File is too big"
msgstr "文件太大"
#: static/scripts/app/views/fileupload.js:11
#: static/scripts/app/views/fileupload.js:12
msgid "File is too small"
msgstr "文件太小"
#: static/scripts/app/views/fileupload.js:12
#: static/scripts/app/views/fileupload.js:13
msgid "Filetype not allowed"
msgstr "不支持的文件类型"
#: static/scripts/app/views/fileupload.js:13
#: static/scripts/app/views/fileupload.js:14
msgid "Maximum number of files exceeded"
msgstr "文件太多"
#: static/scripts/app/views/fileupload.js:14
#: static/scripts/app/views/fileupload.js:15
msgid "Uploaded bytes exceed file size"
msgstr "上传大小超过了文件大小"
#: static/scripts/app/views/fileupload.js:15
#: static/scripts/app/views/fileupload.js:16
msgid "Empty file upload result"
msgstr "空文件"
#: static/scripts/app/views/fileupload.js:17
#: static/scripts/app/views/fileupload.js:225
#: static/scripts/app/views/fileupload.js:265
#: static/scripts/app/views/fileupload.js:18
#: static/scripts/app/views/fileupload.js:226
#: static/scripts/app/views/fileupload.js:266
#: static/scripts/app/views/repo-details.js:37
#: static/scripts/sysadmin-app/views/address-book-group-item.js:145
#: static/scripts/sysadmin-app/views/address-book-group-item.js:152
@ -670,43 +670,43 @@ msgstr "空文件"
msgid "Error"
msgstr "错误"
#: static/scripts/app/views/fileupload.js:18
#: static/scripts/app/views/fileupload.js:19
msgid "uploaded"
msgstr "已上传"
#: static/scripts/app/views/fileupload.js:19
#: static/scripts/app/views/fileupload.js:20
msgid "canceled"
msgstr "已取消"
#: static/scripts/app/views/fileupload.js:20
#: static/scripts/app/views/fileupload.js:21
msgid "Start"
msgstr "上传"
#: static/scripts/app/views/fileupload.js:46
#: static/scripts/app/views/fileupload.js:47
msgid "File Uploading..."
msgstr "文件上传中..."
#: static/scripts/app/views/fileupload.js:47
#: static/scripts/app/views/fileupload.js:48
msgid "File Upload complete"
msgstr "文件上传已完成"
#: static/scripts/app/views/fileupload.js:48
#: static/scripts/app/views/fileupload.js:49
msgid "File Upload canceled"
msgstr "文件上传已取消"
#: static/scripts/app/views/fileupload.js:49
#: static/scripts/app/views/fileupload.js:50
msgid "File Upload failed"
msgstr "文件上传失败"
#: static/scripts/app/views/fileupload.js:279
#: static/scripts/app/views/fileupload.js:280
msgid "Replace file {filename}?"
msgstr "覆盖文件 {filename} "
#: static/scripts/app/views/fileupload.js:305
#: static/scripts/app/views/fileupload.js:306
msgid "File is locked"
msgstr "文件已锁定"
#: static/scripts/app/views/fileupload.js:440
#: static/scripts/app/views/fileupload.js:442
msgid "Network error"
msgstr "网络错误"
@ -715,7 +715,7 @@ msgid "Set {placeholder}'s permission"
msgstr "设置 {placeholder} 权限"
#: static/scripts/app/views/folder-perm.js:126
#: static/scripts/app/views/share.js:842
#: static/scripts/app/views/share.js:846
msgid "Select groups"
msgstr "选择群组"
@ -725,7 +725,7 @@ msgstr "编辑失败"
#: static/scripts/app/views/folder-share-item.js:127
#: static/scripts/app/views/folder-share-item.js:188
#: static/scripts/common.js:444
#: static/scripts/common.js:445
#: static/scripts/sysadmin-app/views/dashboard.js:84
msgid "Failed. Please check the network."
msgstr "操作失败。请检查网络是否已连接。"
@ -864,45 +864,45 @@ msgstr "成功更改权限"
msgid "Successfully deleted 1 item"
msgstr "成功删除 1 个条目"
#: static/scripts/app/views/share.js:109
#: static/scripts/app/views/share.js:90
#: static/scripts/sysadmin-app/views/share.js:42
msgid "Share {placeholder}"
msgstr "共享 {placeholder}"
#: static/scripts/app/views/share.js:209
#: static/scripts/app/views/share.js:186
msgid "Expired"
msgstr "已过期"
#: static/scripts/app/views/share.js:287 static/scripts/app/views/share.js:288
#: static/scripts/app/views/share.js:309 static/scripts/app/views/share.js:310
#: static/scripts/app/views/share.js:291 static/scripts/app/views/share.js:292
#: static/scripts/app/views/share.js:313 static/scripts/app/views/share.js:314
msgid "Hide"
msgstr "隐藏"
#: static/scripts/app/views/share.js:305 static/scripts/app/views/share.js:306
#: static/scripts/app/views/share.js:309 static/scripts/app/views/share.js:310
msgid "Show"
msgstr "显示"
#: static/scripts/app/views/share.js:361
#: static/scripts/app/views/share.js:365
msgid "Please enter days."
msgstr "请输入天数。"
#: static/scripts/app/views/share.js:365
#: static/scripts/app/views/share.js:369
msgid "Please enter valid days"
msgstr "请输入有效的天数"
#: static/scripts/app/views/share.js:468
#: static/scripts/app/views/share.js:472
msgid "Share link is copied to the clipboard."
msgstr "共享链接已复制到剪贴板。"
#: static/scripts/app/views/share.js:485
#: static/scripts/app/views/share.js:489
msgid "Please input at least an email."
msgstr "请输入至少一个邮箱。"
#: static/scripts/app/views/share.js:502
#: static/scripts/app/views/share.js:506
msgid "Successfully sent to {placeholder}"
msgstr "成功发送给 {placeholder}"
#: static/scripts/app/views/share.js:506
#: static/scripts/app/views/share.js:510
msgid "Failed to send to {placeholder}"
msgstr "发送给 {placeholder} 失败"
@ -919,34 +919,34 @@ msgstr "成功"
msgid "Successfully unstared {placeholder}"
msgstr "成功取消星标 {placeholder}"
#: static/scripts/common.js:458 static/scripts/common.js:460
#: static/scripts/common.js:459 static/scripts/common.js:461
#: static/scripts/sysadmin-app/views/dashboard.js:79
msgid "Permission error"
msgstr "权限错误"
#: static/scripts/common.js:685
#: static/scripts/common.js:686
#: static/scripts/sysadmin-app/views/address-book-group.js:172
#: static/scripts/sysadmin-app/views/group-members.js:41
msgid "Search users or enter emails and press Enter"
msgstr "搜索用户或输入电子邮件然后按Enter"
#: static/scripts/common.js:693 static/scripts/common.js:764
#: static/scripts/common.js:694 static/scripts/common.js:765
msgid "Please enter 1 or more character"
msgstr "请输入 1 个或更多字符"
#: static/scripts/common.js:695 static/scripts/common.js:766
#: static/scripts/common.js:696 static/scripts/common.js:767
msgid "Searching..."
msgstr "搜索中..."
#: static/scripts/common.js:696 static/scripts/common.js:767
#: static/scripts/common.js:697 static/scripts/common.js:768
msgid "Loading failed"
msgstr "加载失败"
#: static/scripts/common.js:756
#: static/scripts/common.js:757
msgid "Search groups"
msgstr "查找群组"
#: static/scripts/common.js:1091
#: static/scripts/common.js:1092
msgid "Packaging..."
msgstr "正在打包"

File diff suppressed because it is too large Load Diff

View File

@ -2674,18 +2674,6 @@ button.sf-dropdown-toggle:focus {
display:inline;
margin-right:5px;
}
.audit-item .audit-select-hidden {
position:absolute;
background:#fff;
padding:6px 1px;
border:1px solid #eee;
border-radius:5px;
z-index:10;
}
.audit-select-hidden li a {
display:block;
padding:0 12px;
}
.file-choose-form {
width:500px;
padding:10px 20px;
@ -3086,12 +3074,23 @@ button.sf-dropdown-toggle:focus {
padding:0;
}
#file-view-tip {
height:130px;
min-height:130px;
padding:30px 10px 10px;
width:950px;
background:#fff;
text-align:center;
}
#file-view-tip .open-via-client { /* for the text link */
display:block;
margin-top:25px;
color:#333;
font-weight:normal;
text-decoration:underline;
}
#file-view-op .open-via-client { /* for the button */
padding-top:6px;
padding-bottom:4px;
}
@media (max-width:991.98px) {
#file-view-tip {
width: 80%;
@ -3789,25 +3788,21 @@ button.sf-dropdown-toggle:focus {
}
/* file audit*/
.audit-show-select {
font-weight: normal;
font-size: 13px;
padding-right: 2px;
}
#audit-unselect-op div {
display: inline-block;
height: 20px;
border: 1px solid #ccc;
.audit-unselect-item {
display:inline-block;
border:1px solid #ccc;
border-radius:2px;
padding:1px 8px;
background: #f2f2f2;
cursor: pointer;
font-size:14px;
}
#audit-unselect-op span {
margin-right: 5px;
.audit-unselect-item:hover {
border-color:#a9a9a9;
}
#audit-unselect-op a {
margin: 0 8px;
text-decoration: none;
font-size: 14px;
.audit-select-user,
.audit-select-repo {
font-size:13px;
}
/* multi repo operation */
.repo-op .op-link {

View File

@ -279,7 +279,7 @@ class AdminLibrary(APIView):
return Response({'success': True})
def put(self, request, repo_id, format=None):
""" transfer a library
""" transfer a library, rename a library
Permission checking:
1. only admin can perform this action.
@ -289,100 +289,113 @@ class AdminLibrary(APIView):
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
new_owner = request.data.get('owner', None)
if not new_owner:
error_msg = 'owner invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
new_owner_obj = 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 not new_owner_obj.permissions.can_add_repo():
error_msg = 'Transfer failed: role of %s is %s, can not add library.' % \
(new_owner, new_owner_obj.role)
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if MULTI_TENANCY:
new_repo_name = request.data.get('name', None)
if new_repo_name:
try:
if seafile_api.get_org_id_by_repo_id(repo_id) > 0:
error_msg = 'Can not transfer organization library.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if ccnet_api.get_orgs_by_user(new_owner):
error_msg = 'Can not transfer library to organization user %s' % new_owner
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
res = seafile_api.edit_repo(repo_id, new_repo_name, '', None)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
repo_owner = seafile_api.get_repo_owner(repo_id)
if res == -1:
e = 'Admin rename failed: ID of library is %s, edit_repo api called failed.' % \
repo_id
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
# get repo shared to user/group list
shared_users = seafile_api.list_repo_shared_to(
repo_owner, repo_id)
shared_groups = seafile_api.list_repo_shared_group_by_user(
repo_owner, repo_id)
new_owner = request.data.get('owner', None)
if new_owner:
try:
new_owner_obj = 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)
# get all pub repos
pub_repos = []
if not request.cloud_mode:
pub_repos = seafile_api.list_inner_pub_repos_by_owner(repo_owner)
if not new_owner_obj.permissions.can_add_repo():
error_msg = 'Transfer failed: role of %s is %s, can not add library.' % \
(new_owner, new_owner_obj.role)
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# transfer repo
seafile_api.set_repo_owner(repo_id, new_owner)
if MULTI_TENANCY:
try:
if seafile_api.get_org_id_by_repo_id(repo_id) > 0:
error_msg = 'Can not transfer organization library.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# reshare repo to user
for shared_user in shared_users:
shared_username = shared_user.user
if ccnet_api.get_orgs_by_user(new_owner):
error_msg = 'Can not transfer library to organization user %s' % new_owner
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if new_owner == shared_username:
continue
repo_owner = seafile_api.get_repo_owner(repo_id)
seafile_api.share_repo(repo_id, new_owner,
shared_username, shared_user.perm)
# get repo shared to user/group list
shared_users = seafile_api.list_repo_shared_to(
repo_owner, repo_id)
shared_groups = seafile_api.list_repo_shared_group_by_user(
repo_owner, repo_id)
# reshare repo to group
for shared_group in shared_groups:
shared_group_id = shared_group.group_id
# get all pub repos
pub_repos = []
if not request.cloud_mode:
pub_repos = seafile_api.list_inner_pub_repos_by_owner(repo_owner)
if not is_group_member(shared_group_id, new_owner):
continue
# transfer repo
seafile_api.set_repo_owner(repo_id, new_owner)
seafile_api.set_group_repo(repo_id, shared_group_id,
new_owner, shared_group.perm)
# reshare repo to user
for shared_user in shared_users:
shared_username = shared_user.user
# reshare repo to links
try:
UploadLinkShare.objects.filter(username=repo_owner, repo_id=repo_id).update(username=new_owner)
FileShare.objects.filter(username=repo_owner, repo_id=repo_id).update(username=new_owner)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if new_owner == shared_username:
continue
# check if current repo is pub-repo
# if YES, reshare current repo to public
for pub_repo in pub_repos:
if repo_id != pub_repo.id:
continue
seafile_api.share_repo(repo_id, new_owner,
shared_username, shared_user.perm)
seafile_api.add_inner_pub_repo(repo_id, pub_repo.permission)
# reshare repo to group
for shared_group in shared_groups:
shared_group_id = shared_group.group_id
break
if not is_group_member(shared_group_id, new_owner):
continue
# send admin operation log signal
admin_op_detail = {
"id": repo_id,
"name": repo.name,
"from": repo_owner,
"to": new_owner,
}
admin_operation.send(sender=None, admin_name=request.user.username,
operation=REPO_TRANSFER, detail=admin_op_detail)
seafile_api.set_group_repo(repo_id, shared_group_id,
new_owner, shared_group.perm)
# reshare repo to links
try:
UploadLinkShare.objects.filter(username=repo_owner, repo_id=repo_id).update(username=new_owner)
FileShare.objects.filter(username=repo_owner, repo_id=repo_id).update(username=new_owner)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
# check if current repo is pub-repo
# if YES, reshare current repo to public
for pub_repo in pub_repos:
if repo_id != pub_repo.id:
continue
seafile_api.add_inner_pub_repo(repo_id, pub_repo.permission)
break
# send admin operation log signal
admin_op_detail = {
"id": repo_id,
"name": repo.name,
"from": repo_owner,
"to": new_owner,
}
admin_operation.send(sender=None, admin_name=request.user.username,
operation=REPO_TRANSFER, detail=admin_op_detail)
repo = seafile_api.get_repo(repo_id)
repo_info = get_repo_info(repo)

View File

@ -22,6 +22,8 @@ from seahub.utils import check_filename_with_rename, is_pro_version, \
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.views import check_folder_permission
from seahub.utils.file_op import check_file_lock, if_locked_by_online_office
from seahub.views.file import can_preview_file, can_edit_file
from seahub.constants import PERMISSION_READ_WRITE
from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, \
FILE_LOCK_EXPIRATION_DAYS, OFFICE_TEMPLATE_ROOT
@ -44,7 +46,13 @@ class FileView(APIView):
def get_file_info(self, username, repo_id, file_path):
repo = seafile_api.get_repo(repo_id)
file_obj = seafile_api.get_dirent_by_path(repo_id, file_path)
file_name = file_obj.obj_name
file_size = file_obj.size
can_preview, error_msg = can_preview_file(file_name, file_size, repo)
can_edit, error_msg = can_edit_file(file_name, file_size, repo)
try:
is_locked, locked_by_me = check_file_lock(repo_id, file_path, username)
@ -56,11 +64,13 @@ class FileView(APIView):
'type': 'file',
'repo_id': repo_id,
'parent_dir': os.path.dirname(file_path),
'obj_name': file_obj.obj_name,
'obj_name': file_name,
'obj_id': file_obj.obj_id,
'size': file_obj.size,
'size': file_size,
'mtime': timestamp_to_isoformat_timestr(file_obj.mtime),
'is_locked': is_locked,
'can_preview': can_preview,
'can_edit': can_edit,
}
return file_info
@ -156,7 +166,7 @@ class FileView(APIView):
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -244,7 +254,7 @@ class FileView(APIView):
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -315,12 +325,12 @@ class FileView(APIView):
# permission check for source file
src_repo_id = repo_id
src_dir = os.path.dirname(path)
if check_folder_permission(request, src_repo_id, src_dir) != 'rw':
if check_folder_permission(request, src_repo_id, src_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# permission check for dst dir
if check_folder_permission(request, dst_repo_id, dst_dir) != 'rw':
if check_folder_permission(request, dst_repo_id, dst_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -401,7 +411,7 @@ class FileView(APIView):
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# permission check for dst dir
if check_folder_permission(request, dst_repo_id, dst_dir) != 'rw':
if check_folder_permission(request, dst_repo_id, dst_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -427,7 +437,7 @@ class FileView(APIView):
if seafile_api.get_file_id_by_path(repo_id, path):
# file exists in repo
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -445,7 +455,7 @@ class FileView(APIView):
else:
# file NOT exists in repo
if check_folder_permission(request, repo_id, '/') != 'rw':
if check_folder_permission(request, repo_id, '/') != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -499,7 +509,7 @@ class FileView(APIView):
# permission check
parent_dir = os.path.dirname(path)
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
@ -535,10 +545,7 @@ class FileView(APIView):
error_msg = _("File is not locked.")
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# file can only be locked by normal user or OnlineOffice
is_repo_owner = seafile_api.is_repo_owner(username, repo_id)
if locked_by_me or \
(locked_by_online_office and is_repo_owner):
if locked_by_me or locked_by_online_office:
# unlock file
try:
seafile_api.unlock_file(repo_id, path)
@ -600,7 +607,7 @@ class FileView(APIView):
parent_dir = os.path.dirname(path)
username = request.user.username
if check_folder_permission(request, repo_id, parent_dir) != 'rw':
if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

View File

@ -35,8 +35,9 @@ from seahub.utils.rpc import SeafileAPI
from seahub.share.signals import share_repo_to_user_successful, share_repo_to_group_successful
from seahub.share.utils import share_dir_to_user, share_dir_to_group, update_user_dir_permission, \
check_user_share_out_permission, update_group_dir_permission, \
check_group_share_out_permission
check_group_share_out_permission, check_user_share_in_permission
from seahub.constants import PERMISSION_READ, PERMISSION_READ_WRITE
from seahub.views import check_folder_permission
from seahub.settings import ENABLE_STORAGE_CLASSES, STORAGE_CLASS_MAPPING_POLICY
@ -1313,3 +1314,43 @@ class GroupOwnedLibraryGroupShare(APIView):
repo_id, path, permission)
return Response({'success': True})
class GroupOwnedLibraryUserShareInLibrary(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, IsProVersion)
throttle_classes = (UserRateThrottle,)
@add_org_context
def delete(self, request, repo_id, org_id, format=None):
""" User delete a repo shared to him/her.
"""
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)
if not check_folder_permission(request, repo_id, '/'):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
username = request.user.username
repo_owner = get_repo_owner(request, repo_id)
try:
if org_id:
is_org = True
seafile_api.org_remove_share(org_id, repo_id, repo_owner, username)
else:
is_org = False
seafile_api.remove_share(repo_id, repo_owner, username)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
permission = check_user_share_in_permission(repo_id, username, is_org)
send_perm_audit_msg('delete-repo-perm', repo_owner, username,
repo_id, '/', permission)
return Response({'success': True})

View File

@ -704,6 +704,11 @@ class Repos(APIView):
if q and q.lower() not in r.name.lower():
continue
library_group_name = ''
if '@seafile_group' in r.user:
library_group_id = get_group_id_by_repo_owner(r.user)
library_group_name= group_id_to_name(library_group_id)
r.password_need = is_passwd_set(r.repo_id, email)
repo = {
"type": "srepo",
@ -726,6 +731,7 @@ class Repos(APIView):
"root": '',
"head_commit_id": r.head_cmmt_id,
"version": r.version,
"group_name": library_group_name,
}
if r.repo_id in repos_with_admin_share_to:

View File

@ -9,15 +9,6 @@ class BaseConfig(AppConfig):
def ready(self):
super(BaseConfig, self).ready()
# check memcache is available
cache.set('test_cache', 'worked')
if cache.get('test_cache') != 'worked':
print '''
Warning: Cache is not working, please check memcached is running if you are using
memcached backend, otherwise, please check permission of cache directory on your
file system.
'''
# check table `base_filecomment` is ok
from seahub.base.models import FileComment
try:

View File

@ -47,22 +47,30 @@ DEFAULT_ENABLED_ROLE_PERMISSIONS = {
},
}
_default_role_perms = DEFAULT_ENABLED_ROLE_PERMISSIONS.copy()
role_permissions = DEFAULT_ENABLED_ROLE_PERMISSIONS.copy()
try:
_default_role_perms.update(settings.ENABLED_ROLE_PERMISSIONS) # merge outter dict
role_permissions.update(settings.ENABLED_ROLE_PERMISSIONS) # merge outter dict
except AttributeError:
pass # ignore error if ENABLED_ROLE_PERMISSONS is not set in settings.py
def get_enabled_role_permissions():
for role, perms in _default_role_perms.iteritems():
permissions = {}
for role, perms in role_permissions.iteritems():
default_permissions = DEFAULT_ENABLED_ROLE_PERMISSIONS[DEFAULT_USER]
# check role permission syntax
for k in perms.keys():
if k not in DEFAULT_ENABLED_ROLE_PERMISSIONS[DEFAULT_USER].keys():
if k not in default_permissions.keys():
logger.warn('"%s" is not valid permission, please review the ENABLED_ROLE_PERMISSIONS setting.' % k)
assert False, '"%s" is not valid permission, please review the ENABLED_ROLE_PERMISSIONS setting.' % k
return _default_role_perms
all_false_permission = {}
for permission in default_permissions.keys():
all_false_permission[permission] = False
all_false_permission.update(perms)
permissions[role] = all_false_permission
return permissions
ENABLED_ROLE_PERMISSIONS = get_enabled_role_permissions()
@ -107,16 +115,16 @@ DEFAULT_ENABLED_ADMIN_ROLE_PERMISSIONS = {
},
}
_default_admin_role_permissions = DEFAULT_ENABLED_ADMIN_ROLE_PERMISSIONS.copy()
admin_role_permissions = DEFAULT_ENABLED_ADMIN_ROLE_PERMISSIONS.copy()
try:
_default_admin_role_permissions.update(settings.ENABLED_ADMIN_ROLE_PERMISSIONS) # merge outter dict
admin_role_permissions.update(settings.ENABLED_ADMIN_ROLE_PERMISSIONS) # merge outter dict
except AttributeError:
pass # ignore error if ENABLED_ADMIN_ROLE_PERMISSIONS is not set in settings.py
def get_enabled_admin_role_permissions():
permissions = {}
for role, perms in _default_admin_role_permissions.iteritems():
for role, perms in admin_role_permissions.iteritems():
# check admin role permission syntax
default_admin_permissions = DEFAULT_ENABLED_ADMIN_ROLE_PERMISSIONS[DEFAULT_ADMIN]
for k in perms.keys():

View File

@ -313,6 +313,9 @@ REPO_PASSWORD_MIN_LENGTH = 8
# token length for the share link
SHARE_LINK_TOKEN_LENGTH = 20
# if limit only authenticated user can view preview share link
SHARE_LINK_LOGIN_REQUIRED = False
# min/max expire days for a share link
SHARE_LINK_EXPIRE_DAYS_MIN = 0 # 0 means no limit
SHARE_LINK_EXPIRE_DAYS_MAX = 0 # 0 means no limit
@ -378,7 +381,6 @@ ENABLE_FILE_COMMENT = True
# File preview
FILE_PREVIEW_MAX_SIZE = 30 * 1024 * 1024
OFFICE_PREVIEW_MAX_SIZE = 2 * 1024 * 1024
FILE_ENCODING_LIST = ['auto', 'utf-8', 'gbk', 'ISO-8859-1', 'ISO-8859-5']
FILE_ENCODING_TRY_LIST = ['utf-8', 'gbk']
HIGHLIGHT_KEYWORD = False # If True, highlight the keywords in the file when the visit is via clicking a link in 'search result' page.

View File

@ -5,7 +5,7 @@ from django.http import Http404
from django.shortcuts import render
from seahub.share.models import FileShare, UploadLinkShare
from seahub.utils import normalize_cache_key, is_pro_version
from seahub.utils import normalize_cache_key, is_pro_version, redirect_to_login
def share_link_audit(func):
def _decorated(request, token, *args, **kwargs):
@ -56,3 +56,14 @@ def share_link_audit(func):
assert False, 'TODO'
return _decorated
def share_link_login_required(func):
def _decorated(request, *args, **kwargs):
if not request.user.is_authenticated() \
and settings.SHARE_LINK_LOGIN_REQUIRED:
return redirect_to_login(request)
else:
return func(request, *args, **kwargs)
return _decorated

View File

@ -1246,18 +1246,14 @@
<input type="radio" name="permission" value="preview_download" checked="checked" class="vam" />
<span class="vam">{% trans "Preview and download" %}</span>
</label><br />
<% if (show_link_preview_only_perm_option) { %>
<label class="radio-item">
<label id="share-link-preview-only-radio" class="radio-item">
<input type="radio" name="permission" value="preview_only" class="vam" />
<span class="vam">{% trans "Preview only" %}</span>
</label><br />
<% } %>
<% if (show_link_edit_perm_option) { %>
<label class="radio-item">
<label id="share-link-edit-download-radio" class="radio-item hide">
<input type="radio" name="permission" value="edit_download" class="vam" />
<span class="vam">{% trans "Edit on cloud and download" %}</span>
</label>
<% } %>
</div>
<% } %>
@ -1439,7 +1435,7 @@
</td>
<td><%= size_formatted %></td>
<td><%= mtime_relative %></td>
<td><span title="<%- owner_nickname %>"><%- owner_nickname %></span></td>
<td><span title="<%- owner_name_shown %>"><%- owner_name_shown %></span></td>
</script>
<script type="text/template" id="shared-repo-mobile-tmpl">
<td>
@ -1447,7 +1443,7 @@
</td>
<td>
<a href="#shared-libs/lib/<%= id %>" class="normal"><%- name %></a><br />
<span class="repo-meta-info" title="<%- owner %>"><%- owner_nickname %></span>
<span class="repo-meta-info" title="<%- owner_name_shown %>"><%- owner_name_shown %></span>
<span class="repo-meta-info"><%= size_formatted %></span>
<span class="repo-meta-info"><%= mtime_relative %></span>
</td>

View File

@ -41,7 +41,7 @@ html,body {
<span>{% trans "Deleted Libraries" %}</span>
</div>
<div class="cur-view-main-con">
<p class="tip">{% trans "Tip: libraries deleted 30 days ago will be cleaned automatically."%}</p>
<p class="tip">{% blocktrans %}Tip: libraries deleted {{ trash_repos_expire_days }} days ago will be cleaned automatically.{% endblocktrans %}</p>
<table class="my-deleted-repos-table hide">
<thead></thead>
<tbody></tbody>

View File

@ -40,14 +40,16 @@ For details please refer to 'snippets/file_content_js.html'.
<img src="{{raw_path}}" alt="{{ file_name }}" id="svg-view" class="hide" />
</div>
{% endif %}
{% if filetype == 'SpreadSheet' %}
{% include 'snippets/spreadsheet_convert_html.html' %}
{% endif %}
{% else %}
<div id="file-view-tip">
{% if err != 'invalid extension' %}
<p class="error">{{ err }}</p>
{% if err != 'File preview unsupported' %}
<p class="error">{{ err }}</p>
{% else %}
<p>{% trans "Online view is not applicable to this file format" %}</p>
{% endif %}
</div>
{% endif %}

View File

@ -67,11 +67,18 @@
<div id="file-view" class="flex-1 ov-auto {% block file_view_extra_class %}{% endblock %}">
{% if err %}
<div id="file-view-tip">
{% if err != 'invalid extension' %}
{% if err != 'File preview unsupported' %}
<p class="error">{{ err }}</p>
{% else %}
{% else %}
<p>{% trans "Online view is not applicable to this file format" %}</p>
{% endif %}
{% endif %}
<a href="?dl=1" class="sf-btn-link big-btn-link">{% trans "Download" %}</a>
{% if file_perm == 'rw' %}
{% if filetype == 'Document' or filetype == 'SpreadSheet' %}
<a class="open-via-client" href="seafile://openfile?repo_id={{repo.id}}&path={{path|urlencode}}">{% trans "Open via Client" %}</a>
<p class="tip">{% trans "Please install the desktop client to open file via client." %}</p>
{% endif %}
{% endif %}
</div>
{% else %}
{% block file_view %}{% endblock %}
@ -132,6 +139,12 @@
<script type="text/template" id="editor-tools-tmpl">
<a class="sf-btn-group-btn sf-btn-group-btn-first sf-btn-link op-icon sf2-icon-folder" href="{{ SITE_ROOT }}#common/lib/{{ repo.id }}/{{ parent_dir|strip_slash }}" title="{% trans "Open parent folder" %}"></a>
{% if not err and file_perm == 'rw' %}
{% if filetype == 'Document' or filetype == 'SpreadSheet' %}
<a class="sf-btn-group-btn sf-btn-link op-icon sf2-icon-monitor open-via-client" href="seafile://openfile?repo_id={{repo.id}}&path={{path|urlencode}}" title="{% trans "Open via Client" %}"></a>
{% endif %}
{% endif %}
{% if can_lock_unlock_file %}
{% if not file_locked %}
<button id="lock-file" class="sf-btn-group-btn op-icon sf2-icon-lock" title="{% trans "Lock" %}"></button>
@ -225,12 +238,6 @@
<script type="text/javascript" src="{{ MEDIA_URL }}js/findAndReplaceDOMText.js"></script>
{% endif %}
<script type="text/javascript">
// download
$(function() {
var dld_url = $('#download').attr('href');
$('#file-view-tip').append('<a href="' + dld_url + '" class="sf-btn-link big-btn-link">' + "{% trans "Download" %}" + '</a>');
});
// share link
{% include "snippets/shared_link_js.html" %}

View File

@ -11,8 +11,8 @@ from seahub.views.sso import *
from seahub.views.file import view_history_file, view_trash_file,\
view_snapshot_file, file_edit, view_shared_file, view_file_via_shared_dir,\
text_diff, view_raw_file, view_raw_shared_file, \
download_file, view_lib_file, file_access, view_lib_file_via_smart_link
text_diff, view_raw_file, download_file, view_lib_file, \
file_access, view_lib_file_via_smart_link
from seahub.views.repo import repo_history_view, view_shared_dir, \
view_shared_upload_link, view_lib_as_wiki
from notifications.views import notification_list
@ -28,7 +28,7 @@ from seahub.api2.endpoints.group_libraries import GroupLibraries, GroupLibrary
from seahub.api2.endpoints.group_owned_libraries import GroupOwnedLibraries, \
GroupOwnedLibrary, GroupOwnedLibraryUserFolderPermission, \
GroupOwnedLibraryGroupFolderPermission, GroupOwnedLibraryUserShare, \
GroupOwnedLibraryGroupShare
GroupOwnedLibraryGroupShare, GroupOwnedLibraryUserShareInLibrary
from seahub.api2.endpoints.address_book.groups import AddressBookGroupsSubGroups
from seahub.api2.endpoints.address_book.members import AddressBookGroupsSearchMember
@ -170,7 +170,6 @@ urlpatterns = [
### share/upload link ###
url(r'^f/(?P<token>[a-f0-9]+)/$', view_shared_file, name='view_shared_file'),
url(r'^f/(?P<token>[a-f0-9]+)/raw/(?P<obj_id>[0-9a-f]{40})/(?P<file_name>.*)', view_raw_shared_file, name='view_raw_shared_file'),
url(r'^d/(?P<token>[a-f0-9]+)/$', view_shared_dir, name='view_shared_dir'),
url(r'^d/(?P<token>[a-f0-9]+)/files/$', view_file_via_shared_dir, name='view_file_via_shared_dir'),
url(r'^u/d/(?P<token>[a-f0-9]+)/$', view_shared_upload_link, name='view_shared_upload_link'),
@ -244,6 +243,7 @@ urlpatterns = [
url(r'^api/v2.1/group-owned-libraries/(?P<repo_id>[-0-9a-f]{36})/group-folder-permission/$', GroupOwnedLibraryGroupFolderPermission.as_view(), name='api-v2.1-group-owned-library-group-folder-permission'),
url(r'^api/v2.1/group-owned-libraries/(?P<repo_id>[-0-9a-f]{36})/user-share/$', GroupOwnedLibraryUserShare.as_view(), name='api-v2.1-group-owned-library-user-share'),
url(r'^api/v2.1/group-owned-libraries/(?P<repo_id>[-0-9a-f]{36})/group-share/$', GroupOwnedLibraryGroupShare.as_view(), name='api-v2.1-group-owned-library-group-share'),
url(r'^api/v2.1/group-owned-libraries/user-share-in-libraries/(?P<repo_id>[-0-9-a-f]{36})/$', GroupOwnedLibraryUserShareInLibrary.as_view(), name='api-v2.1-group-owned-library-user-share-in-library'),
## user::shared-folders
url(r'^api/v2.1/shared-folders/$', SharedFolders.as_view(), name='api-v2.1-shared-folders'),

View File

@ -31,6 +31,7 @@ from django.utils.http import urlquote
from django.utils.html import escape
from django.views.static import serve as django_static_serve
from seahub.auth import REDIRECT_FIELD_NAME
from seahub.api2.models import Token, TokenV2
import seahub.settings
from seahub.settings import SITE_NAME, MEDIA_URL, LOGO_PATH, \
@ -954,7 +955,7 @@ def redirect_to_login(request):
from django.conf import settings
login_url = settings.LOGIN_URL
path = urlquote(request.get_full_path())
tup = login_url, redirect_field_name, path
tup = login_url, REDIRECT_FIELD_NAME, path
return HttpResponseRedirect('%s?%s=%s' % tup)
def mkstemp():
@ -1052,6 +1053,7 @@ if EVENTS_CONFIG_FILE:
HAS_OFFICE_CONVERTER = check_office_converter_enabled()
OFFICE_PREVIEW_MAX_SIZE = 2 * 1024 * 1024
if HAS_OFFICE_CONVERTER:
OFFICE_HTML_DIR = get_office_converter_html_dir()

View File

@ -710,6 +710,12 @@ def libraries(request):
joined_groups_exclude_address_book = [item for item in joined_groups if
item.parent_group_id == 0]
try:
expire_days = seafile_api.get_server_config_int('library_trash', 'expire_days')
except Exception as e:
logger.error(e)
expire_days = -1
return render(request, 'libraries.html', {
"allow_public_share": allow_public_share,
"guide_enabled": guide_enabled,
@ -742,6 +748,7 @@ def libraries(request):
'share_link_expire_days_max': SHARE_LINK_EXPIRE_DAYS_MAX,
'enable_office_web_app': ENABLE_OFFICE_WEB_APP,
'enable_onlyoffice': ENABLE_ONLYOFFICE,
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
})
@login_required

View File

@ -12,7 +12,6 @@ import StringIO
from django.core.urlresolvers import reverse
from django.http import HttpResponse, Http404
from django.template.loader import render_to_string
from django.shortcuts import render
from django.utils.http import urlquote
from django.utils.html import escape
from django.utils.translation import ugettext as _
@ -21,7 +20,7 @@ from django.template.defaultfilters import filesizeformat
import seaserv
from seaserv import seafile_api, is_passwd_set, ccnet_api, \
seafserv_threaded_rpc, ccnet_threaded_rpc
seafserv_threaded_rpc
from pysearpc import SearpcError
from seahub.auth.decorators import login_required_ajax
@ -30,11 +29,10 @@ from seahub.forms import RepoRenameDirentForm
from seahub.options.models import UserOptions, CryptoOptionNotSetError
from seahub.notifications.models import UserNotification
from seahub.notifications.views import add_notice_from_info
from seahub.share.models import UploadLinkShare, ExtraSharePermission, ExtraGroupsSharePermission
from seahub.share.models import UploadLinkShare
from seahub.signals import upload_file_successful
from seahub.views import get_unencry_rw_repos_by_user, \
get_diff, check_folder_permission
from seahub.views.file import can_preview_file
from seahub.group.utils import is_group_member, is_group_admin_or_owner, \
get_group_member_info
import seahub.settings as settings
@ -43,8 +41,7 @@ from seahub.settings import ENABLE_THUMBNAIL, THUMBNAIL_ROOT, \
from seahub.utils import check_filename_with_rename, EMPTY_SHA1, \
gen_block_get_url, TRAFFIC_STATS_ENABLED, get_user_traffic_stat,\
new_merge_with_no_conflict, get_commit_before_new_merge, \
get_repo_last_modify, gen_file_upload_url, is_org_context, \
get_file_type_and_ext, is_pro_version, normalize_dir_path, \
gen_file_upload_url, is_org_context, is_pro_version, normalize_dir_path, \
FILEEXT_TYPE_MAP
from seahub.utils.star import get_dir_starred_files
from seahub.utils.file_types import IMAGE, VIDEO
@ -56,7 +53,7 @@ from seahub.thumbnail.utils import get_thumbnail_src
from seahub.share.utils import is_repo_admin
from seahub.base.templatetags.seahub_tags import translate_seahub_time, \
email2nickname, tsstr_sec
from seahub.constants import PERMISSION_ADMIN
from seahub.constants import PERMISSION_READ_WRITE
from seahub.constants import HASH_URLS
# Get an instance of a logger
@ -322,9 +319,6 @@ def list_lib_dir(request, repo_id):
if fpath in starred_files:
dirent.starred = True
can_preview, err_msg = can_preview_file(dirent.obj_name, file_size, repo)
dirent.can_preview = can_preview
file_list.append(dirent)
if is_org_context(request):
@ -394,7 +388,6 @@ def list_lib_dir(request, repo_id):
f_['file_size'] = filesizeformat(f.file_size)
f_['obj_id'] = f.obj_id
f_['perm'] = f.permission # perm for file in current dir
f_['can_preview'] = f.can_preview # if user can preview current file
if not repo.encrypted and ENABLE_THUMBNAIL:
# used for providing a way to determine
@ -430,7 +423,7 @@ def list_lib_dir(request, repo_id):
f_['locked_by_me'] = True
if f.lock_owner == ONLINE_OFFICE_LOCK_OWNER and \
repo_owner == username:
user_perm == PERMISSION_READ_WRITE:
f_['locked_by_me'] = True
dirent_list.append(f_)
@ -854,8 +847,10 @@ def cp_dirents(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_file_n
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=403, content_type=content_type)
dst_path = normalize_dir_path(dst_path)
for obj_name in obj_dir_names:
src_dir = posixpath.join(src_path, obj_name)
src_dir = normalize_dir_path(src_dir)
if dst_path.startswith(src_dir):
error_msg = _(u'Can not copy directory %(src)s to its subdirectory %(des)s') \
% {'src': escape(src_dir), 'des': escape(dst_path)}
@ -1196,9 +1191,8 @@ def get_file_upload_url_ul(request, token):
return HttpResponse(json.dumps({"error": _("Permission denied")}),
status=403, content_type=content_type)
username = request.user.username or request.session.get('anonymous_email') or ''
args = [repo_id, json.dumps({'anonymous_user': username}), 'upload-link', '']
dir_id = seafile_api.get_dir_id_by_path(uls.repo_id, uls.path)
args = [repo_id, dir_id, 'upload-link', shared_by]
kwargs = {
'use_onetime': False,
}

View File

@ -37,7 +37,6 @@ from seaserv import get_repo, send_message, get_commits, \
seafserv_threaded_rpc
from pysearpc import SearpcError
from seahub.auth import REDIRECT_FIELD_NAME
from seahub.tags.models import FileUUIDMap
from seahub.wopi.utils import get_wopi_dict
from seahub.onlyoffice.utils import get_onlyoffice_dict
@ -45,7 +44,7 @@ from seahub.auth.decorators import login_required
from seahub.base.decorators import repo_passwd_set_required
from seahub.base.accounts import ANONYMOUS_EMAIL
from seahub.share.models import FileShare, check_share_link_common
from seahub.share.decorators import share_link_audit
from seahub.share.decorators import share_link_audit, share_link_login_required
from seahub.wiki.utils import get_wiki_dirent
from seahub.wiki.models import WikiDoesNotExist, WikiPageMissing
from seahub.utils import render_error, is_org_context, \
@ -54,8 +53,8 @@ from seahub.utils import render_error, is_org_context, \
mkstemp, EMPTY_SHA1, HtmlDiff, gen_inner_file_get_url, \
user_traffic_over_limit, get_file_audit_events_by_path, \
generate_file_audit_event_type, FILE_AUDIT_ENABLED, \
get_conf_text_ext, HAS_OFFICE_CONVERTER, FILEEXT_TYPE_MAP, \
normalize_file_path, get_service_url
get_conf_text_ext, HAS_OFFICE_CONVERTER, PREVIEW_FILEEXT, \
normalize_file_path, get_service_url, OFFICE_PREVIEW_MAX_SIZE
from seahub.utils.ip import get_remote_ip
from seahub.utils.timeutils import utc_to_local
@ -76,7 +75,7 @@ from seahub.constants import HASH_URLS
if HAS_OFFICE_CONVERTER:
from seahub.utils import (
query_office_convert_status, add_office_convert_task,
prepare_converted_html, OFFICE_PREVIEW_MAX_SIZE, get_office_converted_page
prepare_converted_html, get_office_converted_page
)
import seahub.settings as settings
@ -105,11 +104,18 @@ except ImportError:
try:
from seahub.settings import ENABLE_ONLYOFFICE
from seahub.onlyoffice.settings import ONLYOFFICE_FILE_EXTENSION, \
ONLYOFFICE_EDIT_FILE_EXTENSION
except ImportError:
ENABLE_ONLYOFFICE = False
try:
from seahub.onlyoffice.settings import ONLYOFFICE_FILE_EXTENSION
except ImportError:
ONLYOFFICE_FILE_EXTENSION = ()
try:
from seahub.onlyoffice.settings import ONLYOFFICE_EDIT_FILE_EXTENSION
except ImportError:
ONLYOFFICE_EDIT_FILE_EXTENSION = ()
# Get an instance of a logger
logger = logging.getLogger(__name__)
@ -301,56 +307,82 @@ def convert_md_link(file_content, repo_id, username):
return re.sub(r'\[\[(.+?)\]\]|(`.+?`)', repl, file_content)
def file_size_exceeds_preview_limit(file_size, file_type):
"""Check whether file size exceeds the preview limit base on different
type of file.
def can_preview_file(file_name, file_size, repo):
"""Check whether Seafile supports view file.
Returns (True, None) if Yes, otherwise (False, error_msg).
"""
if file_type in (DOCUMENT, ) and HAS_OFFICE_CONVERTER:
if file_size > OFFICE_PREVIEW_MAX_SIZE:
err = _(u'File size surpasses %s, can not be opened online.') % \
filesizeformat(OFFICE_PREVIEW_MAX_SIZE)
return True, err
else:
return False, ''
elif file_type in (VIDEO, AUDIO):
return False, ''
else:
if file_size > FILE_PREVIEW_MAX_SIZE:
err = _(u'File size surpasses %s, can not be opened online.') % \
filesizeformat(FILE_PREVIEW_MAX_SIZE)
return True, err
else:
return False, ''
def can_preview_file(file_name, file_size, repo=None):
"""Check whether a file can be viewed online.
Returns (True, None) if file can be viewed online, otherwise
(False, erro_msg).
filetype, fileext = get_file_type_and_ext(file_name)
# Seafile defines 9 kinds of filetype:
# TEXT, MARKDOWN, IMAGE, DOCUMENT, SPREADSHEET, VIDEO, AUDIO, PDF, SVG
if filetype in (TEXT, MARKDOWN, IMAGE) or fileext in get_conf_text_ext():
if file_size > FILE_PREVIEW_MAX_SIZE:
error_msg = _(u'File size surpasses %s, can not be opened online.') % \
filesizeformat(FILE_PREVIEW_MAX_SIZE)
return False, error_msg
elif filetype in (DOCUMENT, SPREADSHEET):
if repo.encrypted:
error_msg = _(u'The library is encrypted, can not open file online.')
return False, error_msg
if not HAS_OFFICE_CONVERTER and \
not ENABLE_OFFICE_WEB_APP and \
not ENABLE_ONLYOFFICE:
error_msg = "File preview unsupported"
return False, error_msg
# priority of view office file is:
# OOS > OnlyOffice > Seafile integrated
if ENABLE_OFFICE_WEB_APP:
if fileext not in OFFICE_WEB_APP_FILE_EXTENSION:
error_msg = "File preview unsupported"
return False, error_msg
elif ENABLE_ONLYOFFICE:
if fileext not in ONLYOFFICE_FILE_EXTENSION:
error_msg = "File preview unsupported"
return False, error_msg
else:
# HAS_OFFICE_CONVERTER
if file_size > OFFICE_PREVIEW_MAX_SIZE:
error_msg = _(u'File size surpasses %s, can not be opened online.') % \
filesizeformat(OFFICE_PREVIEW_MAX_SIZE)
return False, error_msg
else:
# NOT depends on Seafile settings
if filetype not in PREVIEW_FILEEXT.keys():
error_msg = "File preview unsupported"
return False, error_msg
return True, ''
def can_edit_file(file_name, file_size, repo):
"""Check whether Seafile supports edit file.
Returns (True, None) if Yes, otherwise (False, error_msg).
"""
can_preview, err_msg = can_preview_file(file_name, file_size, repo)
if not can_preview:
return False, err_msg
file_type, file_ext = get_file_type_and_ext(file_name)
if file_type in (TEXT, MARKDOWN) or file_ext in get_conf_text_ext():
return True, ''
if ENABLE_OFFICE_WEB_APP and file_ext in OFFICE_WEB_APP_FILE_EXTENSION:
return (True, None)
if file_type in (DOCUMENT, SPREADSHEET):
if ENABLE_OFFICE_WEB_APP_EDIT and \
file_ext in OFFICE_WEB_APP_EDIT_FILE_EXTENSION:
return True, ''
if ENABLE_ONLYOFFICE and file_ext in ONLYOFFICE_FILE_EXTENSION:
return (True, None)
if ENABLE_ONLYOFFICE and file_ext in ONLYOFFICE_EDIT_FILE_EXTENSION:
return True, ''
if repo and repo.encrypted and (file_type in (DOCUMENT, SPREADSHEET)):
return (False, _(u'The library is encrypted, can not open file online.'))
if file_ext in FILEEXT_TYPE_MAP or file_ext in get_conf_text_ext(): # check file extension
exceeds_limit, err_msg = file_size_exceeds_preview_limit(file_size,
file_type)
if exceeds_limit:
return (False, err_msg)
else:
return (True, None)
else:
# TODO: may need a better way instead of return string, and compare
# that string in templates
return (False, "invalid extension")
return False, 'File edit unsupported'
def send_file_access_msg_when_preview(request, repo, path, access_from):
""" send file access msg when user preview file from web
@ -696,7 +728,7 @@ def view_lib_file(request, repo_id, path):
return_dict['err'] = _(u'Error when prepare OnlyOffice file preview page.')
if not HAS_OFFICE_CONVERTER:
return_dict['err'] = "invalid extension"
return_dict['err'] = "File preview unsupported"
return render(request, 'view_file_base.html', return_dict)
if file_size > OFFICE_PREVIEW_MAX_SIZE:
@ -713,7 +745,7 @@ def view_lib_file(request, repo_id, path):
# render file preview page
return render(request, template, return_dict)
else:
return_dict['err'] = "invalid extension"
return_dict['err'] = "File preview unsupported"
return render(request, 'view_file_base.html', return_dict)
def view_history_file_common(request, repo_id, ret_dict):
@ -884,7 +916,6 @@ def _download_file_from_share_link(request, fileshare):
next = request.META.get('HTTP_REFERER', settings.SITE_ROOT)
username = request.user.username
shared_by = fileshare.username
repo = get_repo(fileshare.repo_id)
if not repo:
raise Http404
@ -914,7 +945,7 @@ def _download_file_from_share_link(request, fileshare):
send_file_access_msg(request, repo, real_path, 'share-link')
dl_token = seafile_api.get_fileserver_access_token(repo.id,
obj_id, 'download-link', username, use_onetime=False)
obj_id, 'download-link', fileshare.username, use_onetime=False)
if not dl_token:
messages.error(request, _(u'Unable to download file.'))
@ -922,12 +953,14 @@ def _download_file_from_share_link(request, fileshare):
return HttpResponseRedirect(gen_file_get_url(dl_token, filename))
@share_link_audit
@share_link_login_required
def view_shared_file(request, fileshare):
"""
View file via shared link.
Download share file if `dl` in request param.
View raw share file if `raw` in request param.
"""
token = fileshare.token
# check if share link is encrypted
@ -952,16 +985,6 @@ def view_shared_file(request, fileshare):
if not seafile_api.check_permission_by_path(repo_id, '/', shared_by):
return render_error(request, _(u'Permission denied'))
# get share link permission
can_download = fileshare.get_permissions()['can_download']
can_edit = fileshare.get_permissions()['can_edit']
if can_edit and not request.user.is_authenticated():
login_url = settings.LOGIN_URL
path = urlquote(request.get_full_path())
tup = login_url, REDIRECT_FIELD_NAME, path
return HttpResponseRedirect('%s?%s=%s' % tup)
# Increase file shared link view_cnt, this operation should be atomic
fileshare.view_cnt = F('view_cnt') + 1
fileshare.save()
@ -970,6 +993,10 @@ def view_shared_file(request, fileshare):
file_size = seafile_api.get_file_size(repo.store_id, repo.version, obj_id)
send_file_access_msg(request, repo, path, 'share-link')
# get share link permission
can_download = fileshare.get_permissions()['can_download']
can_edit = fileshare.get_permissions()['can_edit']
# download shared file
if request.GET.get('dl', '') == '1':
if can_download is False:
@ -1082,68 +1109,10 @@ def view_shared_file(request, fileshare):
'enable_watermark': ENABLE_WATERMARK,
})
def view_raw_shared_file(request, token, obj_id, file_name):
"""Returns raw content of a shared file.
Arguments:
- `request`:
- `token`:
- `obj_id`:
- `file_name`:
"""
fileshare = FileShare.objects.get_valid_file_link_by_token(token)
if fileshare is None:
raise Http404
password_check_passed, err_msg = check_share_link_common(request, fileshare)
if not password_check_passed:
d = {'token': token, 'err_msg': err_msg}
if fileshare.is_file_share_link():
d['view_name'] = 'view_shared_file'
else:
d['view_name'] = 'view_shared_dir'
return render(request, 'share_access_validation.html', d)
repo_id = fileshare.repo_id
repo = get_repo(repo_id)
if not repo:
raise Http404
# Normalize file path based on file or dir share link
req_path = request.GET.get('p', '').rstrip('/')
if req_path:
file_path = posixpath.join(fileshare.path, req_path.lstrip('/'))
else:
if fileshare.is_file_share_link():
file_path = fileshare.path.rstrip('/')
else:
file_path = fileshare.path.rstrip('/') + '/' + file_name
real_obj_id = seafile_api.get_file_id_by_path(repo_id, file_path)
if not real_obj_id:
raise Http404
if real_obj_id != obj_id: # perm check
raise Http404
if not seafile_api.check_permission_by_path(repo_id, '/',
fileshare.username):
return render_error(request, _(u'Permission denied'))
filename = os.path.basename(file_path)
username = request.user.username
token = seafile_api.get_fileserver_access_token(repo_id,
real_obj_id, 'view', username, use_onetime=False)
if not token:
raise Http404
outer_url = gen_file_get_url(token, filename)
return HttpResponseRedirect(outer_url)
@share_link_audit
@share_link_login_required
def view_file_via_shared_dir(request, fileshare):
token = fileshare.token
# argument check

View File

@ -4,7 +4,6 @@ import os
import posixpath
import logging
from django.core.urlresolvers import reverse
from django.db.models import F
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render
@ -16,7 +15,7 @@ from seaserv import seafile_api
from seahub.auth.decorators import login_required
from seahub.options.models import UserOptions, CryptoOptionNotSetError
from seahub.share.decorators import share_link_audit
from seahub.share.decorators import share_link_audit, share_link_login_required
from seahub.share.models import FileShare, UploadLinkShare, \
check_share_link_common
from seahub.views import gen_path_link, get_repo_dirents, \
@ -179,7 +178,9 @@ def view_lib_as_wiki(request, repo_id, path):
########## shared dir/uploadlink
@share_link_audit
@share_link_login_required
def view_shared_dir(request, fileshare):
token = fileshare.token
password_check_passed, err_msg = check_share_link_common(request, fileshare)

View File

@ -201,12 +201,10 @@ define([
share: function() {
var dir = this.dir,
can_preview = this.model.get('can_preview'),
obj_name = this.model.get('obj_name'),
dirent_path = this.model.getPath();
var options = {
'can_preview': can_preview,
'is_repo_owner': dir.is_repo_owner,
'is_virtual': dir.is_virtual,
'user_perm': this.model.get('perm'),

View File

@ -373,11 +373,9 @@ define([
this.hideMobileMenu();
var dir = this.dir,
obj_name = this.model.get('obj_name'),
can_preview = this.model.get('can_preview'),
dirent_path = Common.pathJoin([dir.path, obj_name]);
var options = {
'can_preview': can_preview,
'is_repo_owner': dir.is_repo_owner,
'is_virtual': dir.is_virtual,
'user_perm': this.model.get('perm'),

View File

@ -21,9 +21,12 @@ define([
'click .rm-invitation': 'removeInvitation'
},
removeInvitation: function() {
removeInvitation: function(e) {
var _this = this;
var $el = $(e.currentTarget);
$el.hide(); // hide the icon to avoid being clicked repeatedly
$.ajax({
url: Common.getUrl({
'name': 'invitation',
@ -37,6 +40,7 @@ define([
Common.feedback(gettext("Successfully deleted 1 item."), 'success');
},
error: function(xhr) {
$el.show();
Common.ajaxErrorHandler(xhr);
}
});

View File

@ -30,7 +30,6 @@ define([
this.dirent_path = options.dirent_path;
this.obj_name = options.obj_name;
this.is_dir = options.is_dir;
this.can_preview = options.can_preview;
// share to user/group
var enable_dir_private_share = false;
@ -87,24 +86,6 @@ define([
show_admin_perm_option = true;
}
// show 'can edit' perm option for download link or not
var show_link_edit_perm_option = false;
var show_link_preview_only_perm_option = true;
var file_ext = '';
if (!this.is_dir && this.obj_name.lastIndexOf('.') != -1) {
file_ext = this.obj_name.substr(this.obj_name.lastIndexOf('.') + 1)
.toLowerCase();
}
if (this.user_perm == 'rw' && !this.is_dir &&
(app.pageOptions.enable_office_web_app ||
app.pageOptions.enable_onlyoffice) &&
(file_ext == 'docx' || file_ext == 'xlsx' || file_ext == 'pptx')) {
show_link_edit_perm_option = true;
}
if (!this.is_dir && !this.can_preview) {
show_link_preview_only_perm_option = false;
}
this.$el.html(this.template({
title: gettext("Share {placeholder}")
.replace('{placeholder}', '<span class="op-target ellipsis ellipsis-op-target" title="' + Common.HTMLescape(this.obj_name) + '">' + Common.HTMLescape(this.obj_name) + '</span>'),
@ -113,10 +94,6 @@ define([
enable_dir_private_share: this.enable_dir_private_share,
show_admin_perm_option: show_admin_perm_option,
show_link_edit_perm_option: show_link_edit_perm_option,
show_link_preview_only_perm_option: show_link_preview_only_perm_option,
user_perm: this.user_perm,
repo_id: this.repo_id,
repo_encrypted: this.repo_encrypted,
@ -269,6 +246,33 @@ define([
$loadingTip.hide();
}
});
if (!this.is_dir) {
// check if can preview/edit file
$.ajax({
url: Common.getUrl({name: 'get_file_info', repo_id: this.repo_id}),
data: {
'p': this.dirent_path
},
cache: false,
dataType: 'json',
success: function(data) {
// show 'can preview/edit' perm option for download link or not
if (!data.can_preview) {
_this.$('#share-link-preview-only-radio').addClass('hide');
}
var file_ext = '';
if (_this.obj_name.lastIndexOf('.') != -1) {
file_ext = _this.obj_name.substr(_this.obj_name.lastIndexOf('.') + 1)
.toLowerCase();
}
if ((file_ext == 'docx' || file_ext == 'xlsx' || file_ext == 'pptx') && data.can_edit) {
_this.$('#share-link-edit-download-radio').removeClass('hide');
}
}
});
}
},
generateRandomPassword: function(e, form) {

View File

@ -52,9 +52,21 @@ define([
}
};
var url,
repo_id = this.model.get('id'),
owner = this.model.get('owner');
if (owner.indexOf('@seafile_group') == -1) {
url = Common.getUrl({name: 'beshared_repo', repo_id: repo_id})
+ "?share_type=personal&from=" + encodeURIComponent(owner);
} else {
url = Common.getUrl({
name: 'group-owned-library-user-share-in-library',
repo_id: repo_id
});
}
$.ajax({
url: Common.getUrl({name: 'beshared_repo', repo_id: this.model.get('id')})
+ "?share_type=personal&from=" + encodeURIComponent(this.model.get('owner')),
url: url,
type: 'DELETE',
beforeSend: Common.prepareCSRFToken,
dataType: 'json',
@ -69,9 +81,15 @@ define([
var icon_size = Common.isHiDPI() ? 48 : 24;
var icon_url = this.model.getIconUrl(icon_size);
var tmpl = $(window).width() >= 768 ? this.template : this.mobileTemplate;
var owner_name_shown = obj.owner_name;
if (obj.owner.indexOf('@seafile_group') != -1) {
owner_name_shown = obj.group_name;
}
_.extend(obj, {
'icon_url': icon_url,
'icon_title': this.model.getIconTitle()
'icon_title': this.model.getIconTitle(),
'owner_name_shown': owner_name_shown
});
this.$el.html(tmpl(obj));
return this;

View File

@ -77,6 +77,7 @@ define([
case 'list_lib_dir': return siteRoot + 'ajax/lib/' + options.repo_id + '/dir/';
case 'del_dir': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/dir/';
case 'del_file': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/';
case 'get_file_info': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/';
case 'download_dir_zip_url': return fileServerRoot + 'zip/' + options.zip_token;
case 'zip_task': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/zip-task/';
case 'query_zip_progress': return siteRoot + 'api/v2.1/query-zip-progress/';
@ -127,6 +128,7 @@ define([
case 'group-owned-library-user-folder-permission': return siteRoot + 'api/v2.1/group-owned-libraries/' + options.repo_id + '/user-folder-permission/';
case 'group-owned-library-group-folder-permission': return siteRoot + 'api/v2.1/group-owned-libraries/' + options.repo_id + '/group-folder-permission/';
case 'group-owned-library-user-share-in-library': return siteRoot + 'api/v2.1/group-owned-libraries/user-share-in-libraries/' + options.repo_id + '/';
// Share admin
case 'share_admin_repos': return siteRoot + 'api/v2.1/shared-repos/';

View File

@ -171,11 +171,6 @@ class AdminLibraryTest(BaseTestCase):
self.login_as(self.admin)
# invalid new owner
data = 'invalid_new_owner=%s' % self.admin_name
resp = self.client.put(self.library_url, data, 'application/x-www-form-urlencoded')
self.assertEqual(400, resp.status_code)
# new owner not exist
data = 'owner=invalid@email.com'
resp = self.client.put(self.library_url, data, 'application/x-www-form-urlencoded')

View File

@ -10,6 +10,9 @@ except ImportError:
class AdminOrgStatsTrafficTest(BaseTestCase):
def test_get(self):
if not LOCAL_PRO_DEV_ENV:
return
self.login_as(self.admin)
url = reverse('api-v2.1-admin-org-stats-traffic', args=[1])

View File

@ -29,7 +29,7 @@ class ShibbolethRemoteUserMiddlewareTest(BaseTestCase):
self.middleware = ShibbolethRemoteUserMiddleware()
self.factory = RequestFactory()
# Create an instance of a GET request.
self.request = self.factory.get('/foo/')
self.request = self.factory.get('/sso/')
self.request.user = self.user
self.request.user.is_authenticated = lambda: False

View File

@ -4,6 +4,7 @@ from django.core.urlresolvers import reverse
from django.test import override_settings
from seahub.share.models import UploadLinkShare
from seahub.utils import EMPTY_SHA1
from seahub.test_utils import BaseTestCase
@ -27,8 +28,8 @@ class GetFileUploadUrlULTest(BaseTestCase):
self.login_as(self.user)
resp = self.client.get(self.url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
mock_get_fileserver_access_token.assert_called_with(
self.repo.id, '{"anonymous_user": "%s"}' % self.user.username,
'upload', '', use_onetime=False)
self.repo.id, EMPTY_SHA1,
'upload-link', self.user.username, use_onetime=False)
json_resp = json.loads(resp.content)
assert 'test_token' in json_resp['url']
@ -39,8 +40,8 @@ class GetFileUploadUrlULTest(BaseTestCase):
resp = self.client.get(self.url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
mock_get_fileserver_access_token.assert_called_with(
self.repo.id, '{"anonymous_user": ""}',
'upload', '', use_onetime=False)
self.repo.id, EMPTY_SHA1,
'upload-link', self.user.username, use_onetime=False)
json_resp = json.loads(resp.content)
assert 'test_token' in json_resp['url']
@ -54,8 +55,8 @@ class GetFileUploadUrlULTest(BaseTestCase):
session.save()
resp = self.client.get(self.url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
mock_get_fileserver_access_token.assert_called_with(
self.repo.id, '{"anonymous_user": "anonymous@email.com"}',
'upload', '', use_onetime=False)
self.repo.id, EMPTY_SHA1,
'upload-link', self.user.username, use_onetime=False)
json_resp = json.loads(resp.content)
assert 'test_token' in json_resp['url']
@ -69,7 +70,7 @@ class GetFileUploadUrlULTest(BaseTestCase):
resp = self.client.get(self.url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
mock_get_fileserver_access_token.assert_called_with(
self.repo.id, '{"anonymous_user": ""}',
'upload', '', use_onetime=False, check_virus=True)
self.repo.id, EMPTY_SHA1,
'upload-link', self.user.username, use_onetime=False, check_virus=True)
json_resp = json.loads(resp.content)
assert 'test_token' in json_resp['url']

View File

@ -0,0 +1,216 @@
from mock import patch
from seaserv import seafile_api
from seahub.views.file import can_edit_file
from seahub.test_utils import BaseTestCase
from seahub.settings import FILE_PREVIEW_MAX_SIZE
from seahub.utils import OFFICE_PREVIEW_MAX_SIZE
OFFICE_WEB_APP_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx')
OFFICE_WEB_APP_EDIT_FILE_EXTENSION = ('docx', 'pptx', 'xlsx')
ONLYOFFICE_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx')
ONLYOFFICE_EDIT_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx')
class CanEditFileTest(BaseTestCase):
def setUp(self):
self.file_size = 1
self.exceeded_file_size = FILE_PREVIEW_MAX_SIZE + 1
self.office_file_size = 1
self.exceeded_office_file_size = OFFICE_PREVIEW_MAX_SIZE + 1
self.encrypted_repo_id = seafile_api.create_repo('encrypted-repo',
'', self.user.username, 'password')
self.encrypted_repo = seafile_api.get_repo(self.encrypted_repo_id)
def tearDown(self):
self.remove_repo(self.repo.id)
self.remove_repo(self.encrypted_repo.id)
def can_edit_in_normal_repo_normal_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.office_file_size
else:
file_size = self.file_size
can_edit, error_msg = can_edit_file(file_name, file_size,
self.repo)
return can_edit
def can_edit_in_encrypted_repo_normal_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.office_file_size
else:
file_size = self.file_size
can_edit, error_msg = can_edit_file(file_name, file_size,
self.encrypted_repo)
return can_edit
def can_edit_in_normal_repo_exceeded_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.exceeded_office_file_size
else:
file_size = self.exceeded_file_size
can_edit, error_msg = can_edit_file(file_name, file_size,
self.repo)
return can_edit
def can_edit_in_encrypted_repo_exceeded_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.exceeded_office_file_size
else:
file_size = self.exceeded_file_size
can_edit, error_msg = can_edit_file(file_name, file_size,
self.encrypted_repo)
return can_edit
def test_iso(self):
file_name = '123.iso'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
def test_pdf(self):
file_name = '123.pdf'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
def test_jpg(self):
file_name = '123.jpg'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
def test_txt(self):
file_name = '123.txt'
assert self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
def test_md(self):
file_name = '123.md'
assert self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
def test_doc(self):
file_name = '123.doc'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.HAS_OFFICE_CONVERTER', True)
def test_doc_has_office_converter(self):
file_name = '123.doc'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_ONLYOFFICE', True)
@patch('seahub.views.file.ONLYOFFICE_FILE_EXTENSION',
ONLYOFFICE_FILE_EXTENSION)
@patch('seahub.views.file.ONLYOFFICE_EDIT_FILE_EXTENSION',
ONLYOFFICE_EDIT_FILE_EXTENSION)
def test_doc_enable_onlyoffice(self):
file_name = '123.doc'
assert self.can_edit_in_normal_repo_normal_size(file_name)
assert self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_OFFICE_WEB_APP', True)
@patch('seahub.views.file.OFFICE_WEB_APP_FILE_EXTENSION',
OFFICE_WEB_APP_FILE_EXTENSION)
@patch('seahub.views.file.ENABLE_OFFICE_WEB_APP_EDIT', True)
@patch('seahub.views.file.OFFICE_WEB_APP_EDIT_FILE_EXTENSION',
OFFICE_WEB_APP_EDIT_FILE_EXTENSION)
def test_doc_enable_office_web_app(self):
file_name = '123.doc'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
def test_docx(self):
file_name = '123.docx'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.HAS_OFFICE_CONVERTER', True)
def test_docx_has_office_converter(self):
file_name = '123.docx'
assert not self.can_edit_in_normal_repo_normal_size(file_name)
assert not self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_ONLYOFFICE', True)
@patch('seahub.views.file.ONLYOFFICE_FILE_EXTENSION',
ONLYOFFICE_FILE_EXTENSION)
@patch('seahub.views.file.ONLYOFFICE_EDIT_FILE_EXTENSION',
ONLYOFFICE_EDIT_FILE_EXTENSION)
def test_docx_enable_onlyoffice(self):
file_name = '123.docx'
assert self.can_edit_in_normal_repo_normal_size(file_name)
assert self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_OFFICE_WEB_APP', True)
@patch('seahub.views.file.OFFICE_WEB_APP_FILE_EXTENSION',
OFFICE_WEB_APP_FILE_EXTENSION)
@patch('seahub.views.file.ENABLE_OFFICE_WEB_APP_EDIT', True)
@patch('seahub.views.file.OFFICE_WEB_APP_EDIT_FILE_EXTENSION',
OFFICE_WEB_APP_EDIT_FILE_EXTENSION)
def test_docx_enable_office_web_app(self):
file_name = '123.docx'
assert self.can_edit_in_normal_repo_normal_size(file_name)
assert self.can_edit_in_normal_repo_exceeded_size(file_name)
assert not self.can_edit_in_encrypted_repo_normal_size(file_name)
assert not self.can_edit_in_encrypted_repo_exceeded_size(file_name)

View File

@ -0,0 +1,203 @@
from mock import patch
from seaserv import seafile_api
from seahub.views.file import can_preview_file
from seahub.test_utils import BaseTestCase
from seahub.settings import FILE_PREVIEW_MAX_SIZE
from seahub.utils import OFFICE_PREVIEW_MAX_SIZE
OFFICE_WEB_APP_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx')
ONLYOFFICE_FILE_EXTENSION = ('doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx')
class CanPreviewFileTest(BaseTestCase):
def setUp(self):
self.file_size = 1
self.exceeded_file_size = FILE_PREVIEW_MAX_SIZE + 1
self.office_file_size = 1
self.exceeded_office_file_size = OFFICE_PREVIEW_MAX_SIZE + 1
self.encrypted_repo_id = seafile_api.create_repo('encrypted-repo',
'', self.user.username, 'password')
self.encrypted_repo = seafile_api.get_repo(self.encrypted_repo_id)
def tearDown(self):
self.remove_repo(self.repo.id)
self.remove_repo(self.encrypted_repo.id)
def can_preview_in_normal_repo_normal_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.office_file_size
else:
file_size = self.file_size
can_preview, error_msg = can_preview_file(file_name, file_size,
self.repo)
return can_preview
def can_preview_in_encrypted_repo_normal_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.office_file_size
else:
file_size = self.file_size
can_preview, error_msg = can_preview_file(file_name, file_size,
self.encrypted_repo)
return can_preview
def can_preview_in_normal_repo_exceeded_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.exceeded_office_file_size
else:
file_size = self.exceeded_file_size
can_preview, error_msg = can_preview_file(file_name, file_size,
self.repo)
return can_preview
def can_preview_in_encrypted_repo_exceeded_size(self, file_name):
if file_name.endswith('.doc') or file_name.endswith('.docx'):
file_size = self.exceeded_office_file_size
else:
file_size = self.exceeded_file_size
can_preview, error_msg = can_preview_file(file_name, file_size,
self.encrypted_repo)
return can_preview
def test_iso(self):
file_name = '123.iso'
assert not self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
def test_pdf(self):
file_name = '123.pdf'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert self.can_preview_in_normal_repo_exceeded_size(file_name)
assert self.can_preview_in_encrypted_repo_normal_size(file_name)
assert self.can_preview_in_encrypted_repo_exceeded_size(file_name)
def test_jpg(self):
file_name = '123.jpg'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
def test_txt(self):
file_name = '123.txt'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
def test_md(self):
file_name = '123.md'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
def test_doc(self):
file_name = '123.doc'
assert not self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.HAS_OFFICE_CONVERTER', True)
def test_doc_has_office_converter(self):
file_name = '123.doc'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_ONLYOFFICE', True)
@patch('seahub.views.file.ONLYOFFICE_FILE_EXTENSION',
ONLYOFFICE_FILE_EXTENSION)
def test_doc_enable_onlyoffice(self):
file_name = '123.doc'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_OFFICE_WEB_APP', True)
@patch('seahub.views.file.OFFICE_WEB_APP_FILE_EXTENSION',
OFFICE_WEB_APP_FILE_EXTENSION)
def test_doc_enable_office_web_app(self):
file_name = '123.doc'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
def test_docx(self):
file_name = '123.docx'
assert not self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.HAS_OFFICE_CONVERTER', True)
def test_docx_has_office_converter(self):
file_name = '123.docx'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert not self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_ONLYOFFICE', True)
@patch('seahub.views.file.ONLYOFFICE_FILE_EXTENSION',
ONLYOFFICE_FILE_EXTENSION)
def test_docx_enable_onlyoffice(self):
file_name = '123.docx'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)
@patch('seahub.views.file.ENABLE_OFFICE_WEB_APP', True)
@patch('seahub.views.file.OFFICE_WEB_APP_FILE_EXTENSION',
OFFICE_WEB_APP_FILE_EXTENSION)
def test_docx_enable_office_web_app(self):
file_name = '123.docx'
assert self.can_preview_in_normal_repo_normal_size(file_name)
assert self.can_preview_in_normal_repo_exceeded_size(file_name)
assert not self.can_preview_in_encrypted_repo_normal_size(file_name)
assert not self.can_preview_in_encrypted_repo_exceeded_size(file_name)

View File

@ -56,7 +56,7 @@ class ViewLibFileTest(BaseTestCase):
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
self.assertTemplateUsed(resp, 'view_file_base.html')
assert resp.context['err'] == 'invalid extension'
assert resp.context['err'] == 'File preview unsupported'
@patch('seahub.views.file.FILE_PREVIEW_MAX_SIZE', -1)
def test_file_size_exceeds_limit(self):

View File

@ -1,88 +0,0 @@
import os
from django.core.urlresolvers import reverse
from django.test import TestCase
from seaserv import seafile_api
from seahub.share.models import FileShare
from seahub.test_utils import Fixtures
class RawSharedFileTest(TestCase, Fixtures):
def setUp(self):
share_file_info = {
'username': 'test@test.com',
'repo_id': self.repo.id,
'path': '/',
'password': None,
'expire_date': None,
}
self.fs = FileShare.objects.create_dir_link(**share_file_info)
self.file_id = seafile_api.get_file_id_by_path(self.repo.id, self.file)
self.filename= os.path.basename(self.file)
def tearDown(self):
self.remove_repo()
def test_can_get_fileserver_url(self):
resp = self.client.get(
reverse('view_raw_shared_file', args=[self.fs.token,
self.file_id, self.filename])
)
self.assertEqual(302, resp.status_code)
self.assertRegexpMatches(resp['Location'],
r'http(.*)/files/[-0-9a-f]{36}/%s' % self.filename)
class EncryptRawSharedFileTest(TestCase, Fixtures):
def setUp(self):
share_file_info = {
'username': 'test@test.com',
'repo_id': self.repo.id,
'path': '/',
'password': '12345678',
'expire_date': None,
}
self.fs = FileShare.objects.create_dir_link(**share_file_info)
self.file_id = seafile_api.get_file_id_by_path(self.repo.id, self.file)
self.filename= os.path.basename(self.file)
def tearDown(self):
self.remove_repo()
def test_can_decrypt(self):
resp = self.client.post(
reverse('view_raw_shared_file', args=[self.fs.token,
self.file_id, self.filename]), {'password': '12345678'}
)
self.assertEqual(302, resp.status_code)
self.assertRegexpMatches(resp['Location'],
r'http(.*)/files/[-0-9a-f]{36}/%s' % self.filename)
def test_wrong_password(self):
resp = self.client.post(
reverse('view_raw_shared_file', args=[self.fs.token,
self.file_id, self.filename]), {'password': '1234567'}
)
self.assertEqual(200, resp.status_code)
self.assertTemplateUsed(resp, 'share_access_validation.html')
self.assertContains(resp, 'Please enter a correct password')
def test_no_password(self):
resp = self.client.get(
reverse('view_raw_shared_file', args=[self.fs.token,
self.file_id, self.filename])
)
self.assertEqual(200, resp.status_code)
self.assertTemplateUsed(resp, 'share_access_validation.html')
resp = self.client.post(
reverse('view_raw_shared_file', args=[self.fs.token,
self.file_id, self.filename])
)
self.assertEqual(200, resp.status_code)
self.assertTemplateUsed(resp, 'share_access_validation.html')

View File

@ -51,6 +51,9 @@ class ShibbolethRemoteUserBackend(RemoteUserBackend):
email=username, is_active=self.activate_after_creation)
if user and self.activate_after_creation is False:
notify_admins_on_activate_request(user.email)
# Do not send follwing registration finished email (if any)
# which will cause confusion.
return user
if user and settings.NOTIFY_ADMIN_AFTER_REGISTRATION is True:
notify_admins_on_register_complete(user.email)
else:

View File

@ -31,6 +31,9 @@ class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
super(ShibbolethRemoteUserMiddleware, self).__init__(*a, **kw)
def process_request(self, request):
if request.path.rstrip('/') != settings.SITE_ROOT + 'sso':
return
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, 'user'):
raise ImproperlyConfigured(