1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-10 11:22:09 +00:00

convert markdown and sdoc (#5636)

This commit is contained in:
JoinTyang 2023-09-14 14:36:58 +08:00 committed by GitHub
parent 34a7318725
commit e7d32fd4d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 208 additions and 8 deletions

View File

@ -57,6 +57,7 @@ const propTypes = {
onItemRename: PropTypes.func.isRequired, onItemRename: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onDirentClick: PropTypes.func.isRequired, onDirentClick: PropTypes.func.isRequired,
isAllItemSelected: PropTypes.bool.isRequired, isAllItemSelected: PropTypes.bool.isRequired,
onAllItemSelected: PropTypes.func.isRequired, onAllItemSelected: PropTypes.func.isRequired,
@ -217,6 +218,7 @@ class DirColumnView extends React.Component {
onItemRename={this.props.onItemRename} onItemRename={this.props.onItemRename}
onItemMove={this.props.onItemMove} onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
onDirentClick={this.props.onDirentClick} onDirentClick={this.props.onDirentClick}
updateDirent={this.props.updateDirent} updateDirent={this.props.updateDirent}
isAllItemSelected={this.props.isAllItemSelected} isAllItemSelected={this.props.isAllItemSelected}

View File

@ -19,6 +19,7 @@ const propTypes = {
onItemDelete: PropTypes.func.isRequired, onItemDelete: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onRenameNode: PropTypes.func.isRequired, onRenameNode: PropTypes.func.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired, isGroupOwnedRepo: PropTypes.bool.isRequired,
userPerm: PropTypes.string, userPerm: PropTypes.string,
@ -73,6 +74,7 @@ class DirGridView extends React.Component {
onItemDelete={this.props.onItemDelete} onItemDelete={this.props.onItemDelete}
onItemMove={this.props.onItemMove} onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
isDirentListLoading={this.props.isDirentListLoading} isDirentListLoading={this.props.isDirentListLoading}
updateDirent={this.props.updateDirent} updateDirent={this.props.updateDirent}
onRenameNode={this.props.onRenameNode} onRenameNode={this.props.onRenameNode}

View File

@ -36,6 +36,7 @@ const propTypes = {
selectedDirentList: PropTypes.array.isRequired, selectedDirentList: PropTypes.array.isRequired,
onItemsMove: PropTypes.func.isRequired, onItemsMove: PropTypes.func.isRequired,
onItemsCopy: PropTypes.func.isRequired, onItemsCopy: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onItemsDelete: PropTypes.func.isRequired, onItemsDelete: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func, onFileTagChanged: PropTypes.func,
showDirentDetail: PropTypes.func.isRequired, showDirentDetail: PropTypes.func.isRequired,
@ -96,6 +97,7 @@ class DirListView extends React.Component {
selectedDirentList={this.props.selectedDirentList} selectedDirentList={this.props.selectedDirentList}
onItemsMove={this.props.onItemsMove} onItemsMove={this.props.onItemsMove}
onItemsCopy={this.props.onItemsCopy} onItemsCopy={this.props.onItemsCopy}
onItemConvert={this.props.onItemConvert}
onItemsDelete={this.props.onItemsDelete} onItemsDelete={this.props.onItemsDelete}
onAddFile={this.props.onAddFile} onAddFile={this.props.onAddFile}
onAddFolder={this.props.onAddFolder} onAddFolder={this.props.onAddFolder}

View File

@ -33,6 +33,7 @@ const propTypes = {
onAddFile: PropTypes.func, onAddFile: PropTypes.func,
onItemDelete: PropTypes.func, onItemDelete: PropTypes.func,
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onRenameNode: PropTypes.func.isRequired, onRenameNode: PropTypes.func.isRequired,
onItemClick: PropTypes.func.isRequired, onItemClick: PropTypes.func.isRequired,
@ -115,6 +116,11 @@ class DirentGridView extends React.Component {
this.props.onItemDelete(currentObject); this.props.onItemDelete(currentObject);
}; };
onItemConvert = (currentObject, e, dstType) => {
e.nativeEvent.stopImmediatePropagation(); //for document event
this.props.onItemConvert(currentObject, dstType);
}
onMenuItemClick = (operation, currentObject, event) => { onMenuItemClick = (operation, currentObject, event) => {
hideMenu(); hideMenu();
switch(operation) { switch(operation) {
@ -136,6 +142,12 @@ class DirentGridView extends React.Component {
case 'Copy': case 'Copy':
this.onItemCopyToggle(); this.onItemCopyToggle();
break; break;
case 'Convert to Markdown':
this.onItemConvert(currentObject, event, 'markdown');
break;
case 'Convert to sdoc':
this.onItemConvert(currentObject, event, 'sdoc');
break;
case 'Tags': case 'Tags':
this.onEditFileTagToggle(); this.onEditFileTagToggle();
break; break;

View File

@ -35,6 +35,7 @@ const propTypes = {
onItemRename: PropTypes.func.isRequired, onItemRename: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onDirentClick: PropTypes.func.isRequired, onDirentClick: PropTypes.func.isRequired,
updateDirent: PropTypes.func.isRequired, updateDirent: PropTypes.func.isRequired,
showImagePopup: PropTypes.func.isRequired, showImagePopup: PropTypes.func.isRequired,
@ -268,6 +269,12 @@ class DirentListItem extends React.Component {
case 'Lock': case 'Lock':
this.onLockItem(); this.onLockItem();
break; break;
case 'Convert to Markdown':
this.onItemConvert(event, 'markdown');
break;
case 'Convert to sdoc':
this.onItemConvert(event, 'sdoc');
break;
case 'Mark as draft': case 'Mark as draft':
this.onMarkAsDraft(); this.onMarkAsDraft();
break; break;
@ -299,6 +306,12 @@ class DirentListItem extends React.Component {
} }
}; };
onItemConvert = (e, dstType)=> {
e.preventDefault();
e.nativeEvent.stopImmediatePropagation(); //for document event
this.props.onItemConvert(this.props.dirent, dstType);
}
onEditFileTagToggle = () => { onEditFileTagToggle = () => {
this.setState({ this.setState({
isEditFileTagShow: !this.state.isEditFileTagShow isEditFileTagShow: !this.state.isEditFileTagShow

View File

@ -42,6 +42,7 @@ const propTypes = {
selectedDirentList: PropTypes.array.isRequired, selectedDirentList: PropTypes.array.isRequired,
onItemsMove: PropTypes.func.isRequired, onItemsMove: PropTypes.func.isRequired,
onItemsCopy: PropTypes.func.isRequired, onItemsCopy: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onItemsDelete: PropTypes.func.isRequired, onItemsDelete: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func, onFileTagChanged: PropTypes.func,
enableDirPrivateShare: PropTypes.bool.isRequired, enableDirPrivateShare: PropTypes.bool.isRequired,
@ -620,6 +621,7 @@ class DirentListView extends React.Component {
onItemRename={this.onItemRename} onItemRename={this.onItemRename}
onItemMove={this.props.onItemMove} onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
updateDirent={this.props.updateDirent} updateDirent={this.props.updateDirent}
isItemFreezed={this.state.isItemFreezed} isItemFreezed={this.state.isItemFreezed}
freezeItem={this.freezeItem} freezeItem={this.freezeItem}

View File

@ -69,6 +69,7 @@ const propTypes = {
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onAddFolder: PropTypes.func.isRequired, onAddFolder: PropTypes.func.isRequired,
onAddFile: PropTypes.func.isRequired, onAddFile: PropTypes.func.isRequired,
onItemConvert: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func.isRequired, onFileTagChanged: PropTypes.func.isRequired,
isDirentSelected: PropTypes.bool.isRequired, isDirentSelected: PropTypes.bool.isRequired,
isAllDirentSelected: PropTypes.bool.isRequired, isAllDirentSelected: PropTypes.bool.isRequired,
@ -229,6 +230,7 @@ class LibContentContainer extends React.Component {
onItemRename={this.props.onItemRename} onItemRename={this.props.onItemRename}
onItemMove={this.onItemMove} onItemMove={this.onItemMove}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
onDirentClick={this.onDirentClick} onDirentClick={this.onDirentClick}
updateDirent={this.props.updateDirent} updateDirent={this.props.updateDirent}
isAllItemSelected={this.props.isAllDirentSelected} isAllItemSelected={this.props.isAllDirentSelected}
@ -264,6 +266,7 @@ class LibContentContainer extends React.Component {
onItemDelete={this.props.onItemDelete} onItemDelete={this.props.onItemDelete}
onItemMove={this.onItemMove} onItemMove={this.onItemMove}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
updateDirent={this.props.updateDirent} updateDirent={this.props.updateDirent}
onAddFolder={this.props.onAddFolder} onAddFolder={this.props.onAddFolder}
showDirentDetail={this.props.showDirentDetail} showDirentDetail={this.props.showDirentDetail}
@ -322,6 +325,7 @@ class LibContentContainer extends React.Component {
onItemRename={this.props.onItemRename} onItemRename={this.props.onItemRename}
onItemMove={this.onItemMove} onItemMove={this.onItemMove}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onItemConvert={this.props.onItemConvert}
onDirentClick={this.onDirentClick} onDirentClick={this.onDirentClick}
updateDirent={this.props.updateDirent} updateDirent={this.props.updateDirent}
isAllItemSelected={this.props.isAllDirentSelected} isAllItemSelected={this.props.isAllDirentSelected}

View File

@ -1236,6 +1236,32 @@ class LibContentView extends React.Component {
}); });
}; };
onConvertItem = (dirent, dstType) => {
let path = Utils.joinPath(this.state.path, dirent.name);
let repoID = this.props.repoID;
seafileAPI.convertFile(repoID, path, dstType).then((res) => {
let objName = res.data.obj_name;
let parentDir = res.data.parent_dir;
let file_size = res.data.size;
path = parentDir + '/' + objName;
let name = Utils.getFileName(path);
let parentPath = Utils.getDirName(path);
if (this.state.currentMode === 'column') {
this.updateMoveCopyTreeNode(parentPath);
}
this.loadDirentList(this.state.path);
}).catch((error) => {
let errMessage = Utils.getErrorMsg(error);
if (errMessage === gettext('Error')) {
let name = Utils.getFileName(path);
errMessage = gettext('Renaming {name} failed').replace('{name}', name);
}
toaster.danger(errMessage);
});
}
onDirentClick = (dirent) => { onDirentClick = (dirent) => {
let direntList = this.state.direntList.map(dirent => { let direntList = this.state.direntList.map(dirent => {
dirent.isSelected = false; dirent.isSelected = false;
@ -2025,6 +2051,7 @@ class LibContentView extends React.Component {
onItemRename={this.onMainPanelItemRename} onItemRename={this.onMainPanelItemRename}
onItemMove={this.onMoveItem} onItemMove={this.onMoveItem}
onItemCopy={this.onCopyItem} onItemCopy={this.onCopyItem}
onItemConvert={this.onConvertItem}
onAddFolder={this.onAddFolder} onAddFolder={this.onAddFolder}
onAddFile={this.onAddFile} onAddFile={this.onAddFile}
onFileTagChanged={this.onFileTagChanged} onFileTagChanged={this.onFileTagChanged}

View File

@ -16,6 +16,8 @@ const TextTranslation = {
'OPEN_VIA_CLIENT' : {key : 'Open via Client', value : gettext('Open via Client')}, 'OPEN_VIA_CLIENT' : {key : 'Open via Client', value : gettext('Open via Client')},
'LOCK' : {key : 'Lock', value : gettext('Lock')}, 'LOCK' : {key : 'Lock', value : gettext('Lock')},
'UNLOCK' : {key : 'Unlock', value : gettext('Unlock')}, 'UNLOCK' : {key : 'Unlock', value : gettext('Unlock')},
'CONVERT_TO_MARKDOWN' : {key : 'Convert to Markdown', value : gettext('Convert to Markdown')},
'CONVERT_TO_SDOC' : {key : 'Convert to sdoc', value : gettext('Convert to sdoc')},
'MARK_AS_DRAFT' : {key : 'Mark as draft', value : gettext('Mark as draft')}, 'MARK_AS_DRAFT' : {key : 'Mark as draft', value : gettext('Mark as draft')},
'UNMARK_AS_DRAFT' : {key : 'Unmark as draft', value : gettext('Unmark as draft')}, 'UNMARK_AS_DRAFT' : {key : 'Unmark as draft', value : gettext('Unmark as draft')},
'COMMENT' : {key : 'Comment', value : gettext('Comment')}, 'COMMENT' : {key : 'Comment', value : gettext('Comment')},

View File

@ -1,4 +1,4 @@
import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableOnlyoffice } from './constants'; import { mediaUrl, gettext, serviceURL, siteRoot, isPro, fileAuditEnabled, canGenerateShareLink, canGenerateUploadLink, shareLinkPasswordMinLength, username, folderPermEnabled, onlyofficeConverterExtensions, enableOnlyoffice, enableSeadoc } from './constants';
import TextTranslation from './text-translation'; import TextTranslation from './text-translation';
import React from 'react'; import React from 'react';
import toaster from '../components/toast'; import toaster from '../components/toast';
@ -528,7 +528,7 @@ export const Utils = {
getFileOperationList: function(isRepoOwner, currentRepoInfo, dirent, isContextmenu) { getFileOperationList: function(isRepoOwner, currentRepoInfo, dirent, isContextmenu) {
let list = []; let list = [];
const { SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, TAGS, UNLOCK, LOCK, MARK_AS_DRAFT, UNMARK_AS_DRAFT, const { SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, TAGS, UNLOCK, LOCK, MARK_AS_DRAFT, UNMARK_AS_DRAFT,
HISTORY, ACCESS_LOG, PROPERTIES, OPEN_VIA_CLIENT, ONLYOFFICE_CONVERT } = TextTranslation; HISTORY, ACCESS_LOG, PROPERTIES, OPEN_VIA_CLIENT, ONLYOFFICE_CONVERT, CONVERT_TO_MARKDOWN, CONVERT_TO_SDOC } = TextTranslation;
const permission = dirent.permission; const permission = dirent.permission;
const { isCustomPermission, customPermission } = Utils.getUserPermission(permission); const { isCustomPermission, customPermission } = Utils.getUserPermission(permission);
@ -595,6 +595,19 @@ export const Utils = {
} }
list.push('Divider'); list.push('Divider');
}
if ((permission == 'rw' || permission == 'cloud-edit') && enableSeadoc) {
if (dirent.name.endsWith('.md')) {
list.push(CONVERT_TO_SDOC);
}
if (dirent.name.endsWith('.sdoc')) {
list.push(CONVERT_TO_MARKDOWN);
}
}
if (permission == 'rw') {
if (Utils.isSdocFile(dirent.name)) { if (Utils.isSdocFile(dirent.name)) {
if (dirent.is_sdoc_draft) { if (dirent.is_sdoc_draft) {
list.push(UNMARK_AS_DRAFT); list.push(UNMARK_AS_DRAFT);
@ -602,6 +615,7 @@ export const Utils = {
list.push(MARK_AS_DRAFT); list.push(MARK_AS_DRAFT);
} }
} }
list.push('Divider');
/* /*
if (enableFileComment) { if (enableFileComment) {
list.push(COMMENT); list.push(COMMENT);

View File

@ -5,6 +5,7 @@ import json
import logging import logging
import posixpath import posixpath
import requests import requests
from pathlib import Path
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
@ -20,18 +21,20 @@ from seahub.api2.utils import api_error
from seahub.utils import check_filename_with_rename, is_pro_version, \ from seahub.utils import check_filename_with_rename, is_pro_version, \
gen_inner_file_upload_url, is_valid_dirent_name, normalize_file_path, \ gen_inner_file_upload_url, is_valid_dirent_name, normalize_file_path, \
normalize_dir_path, get_file_type_and_ext normalize_dir_path, get_file_type_and_ext, check_filename_or_rename
from seahub.utils.timeutils import timestamp_to_isoformat_timestr from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.views import check_folder_permission from seahub.views import check_folder_permission
from seahub.utils.file_op import check_file_lock, if_locked_by_online_office 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.views.file import can_preview_file, can_edit_file
from seahub.constants import PERMISSION_READ_WRITE from seahub.constants import PERMISSION_READ_WRITE
from seahub.utils.repo import parse_repo_perm, is_repo_admin, is_repo_owner from seahub.utils.repo import parse_repo_perm, is_repo_admin, is_repo_owner
from seahub.utils.file_types import MARKDOWN, TEXT, SEADOC from seahub.utils.file_types import MARKDOWN, TEXT, SEADOC, MARKDOWN_SUPPORT_CONVERT_TYPES, SDOC_SUPPORT_CONVERT_TYPES
from seahub.tags.models import FileUUIDMap from seahub.tags.models import FileUUIDMap
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocCommentReply from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocCommentReply
from seahub.base.models import FileComment from seahub.base.models import FileComment
from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, OFFICE_TEMPLATE_ROOT from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, OFFICE_TEMPLATE_ROOT
from seahub.api2.endpoints.utils import convert_file
from seahub.seadoc.utils import get_seadoc_file_uuid
from seahub.drafts.models import Draft from seahub.drafts.models import Draft
from seahub.drafts.utils import is_draft_file, get_file_draft from seahub.drafts.utils import is_draft_file, get_file_draft
@ -152,8 +155,8 @@ class FileView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg) return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
operation = operation.lower() operation = operation.lower()
if operation not in ('create', 'rename', 'move', 'copy', 'revert'): if operation not in ('create', 'rename', 'move', 'copy', 'revert', 'convert'):
error_msg = "operation can only be 'create', 'rename', 'move', 'copy' or 'revert'." error_msg = "operation can only be 'create', 'rename', 'move', 'copy', 'convert' or 'revert'."
return api_error(status.HTTP_400_BAD_REQUEST, error_msg) return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check # resource check
@ -523,6 +526,80 @@ class FileView(APIView):
return Response({'success': True}) return Response({'success': True})
if operation == 'convert':
dst_type = request.data.get('dst_type')
extension = Path(path).suffix
if extension not in ['.md', '.sdoc']:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if (extension == '.md' and dst_type not in MARKDOWN_SUPPORT_CONVERT_TYPES) or \
(extension == '.sdoc' and dst_type not in SDOC_SUPPORT_CONVERT_TYPES):
error_msg = 'dst_type invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check
try:
file_id = seafile_api.get_file_id_by_path(repo_id, path)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if not file_id:
error_msg = 'File %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
if parse_repo_perm(check_folder_permission(request, repo_id, parent_dir)).can_edit_on_web is False:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# check file lock
try:
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if is_locked and not locked_by_me:
error_msg = _("File is locked")
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
filename = os.path.basename(path)
if extension == '.md':
src_type = 'markdown'
filename = filename[:-2] + 'sdoc'
elif extension == '.sdoc':
src_type = 'sdoc'
filename = filename[:-4] + 'md'
new_file_name = check_filename_or_rename(repo_id, parent_dir, filename)
new_file_path = posixpath.join(parent_dir, new_file_name)
download_token = seafile_api.get_fileserver_access_token(repo_id, file_id, 'download', username)
obj_id = json.dumps({'parent_dir': parent_dir})
upload_token = seafile_api.get_fileserver_access_token(repo_id, obj_id, 'upload-link', username,
use_onetime=True)
doc_uuid = get_seadoc_file_uuid(repo, path)
try:
resp = convert_file(path, username, doc_uuid, download_token, upload_token, src_type, dst_type)
if resp.status_code == 500:
logger.error('convert file error status: %s body: %s', resp.status_code, resp.text)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
file_info = self.get_file_info(username, repo_id, new_file_path)
return Response(file_info)
def put(self, request, repo_id, format=None): def put(self, request, repo_id, format=None):
""" Currently only support lock, unlock, refresh-lock file. """ Currently only support lock, unlock, refresh-lock file.

View File

@ -4,6 +4,9 @@ import datetime
import time import time
import urllib.request, urllib.parse, urllib.error import urllib.request, urllib.parse, urllib.error
import logging import logging
import requests
import jwt
from urllib.parse import urljoin
from rest_framework import status from rest_framework import status
@ -11,9 +14,9 @@ from seaserv import ccnet_api, seafile_api
from pysearpc import SearpcError from pysearpc import SearpcError
from seahub.api2.utils import api_error from seahub.api2.utils import api_error
from seahub.base.templatetags.seahub_tags import email2nickname, \ from seahub.base.templatetags.seahub_tags import email2nickname, email2contact_email
email2contact_email
from seahub.utils import get_log_events_by_time, is_pro_version, is_org_context from seahub.utils import get_log_events_by_time, is_pro_version, is_org_context
from seahub.settings import SECRET_KEY, FILE_CONVERTER_SERVER_URL
try: try:
from seahub.settings import MULTI_TENANCY from seahub.settings import MULTI_TENANCY
@ -210,3 +213,26 @@ def get_user_quota_usage_and_total(email, org_id=''):
quota_usage = -1 quota_usage = -1
quota_total = -1 quota_total = -1
return quota_usage, quota_total return quota_usage, quota_total
def gen_headers():
payload = {'exp': int(time.time()) + 300, }
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
return {"Authorization": "Token %s" % token}
def convert_file(path, username, doc_uuid, download_token, upload_token, src_type, dst_type):
headers = gen_headers()
params = {
'path': path,
'username': username,
'doc_uuid': doc_uuid,
'download_token': download_token,
'upload_token': upload_token,
'src_type': src_type,
'dst_type': dst_type,
}
url = urljoin(FILE_CONVERTER_SERVER_URL, '/api/v1/file-convert/')
resp = requests.post(url, json=params, headers=headers, timeout=30)
return resp

View File

@ -623,6 +623,8 @@ SHOW_LOGOUT_ICON = False
PRIVACY_POLICY_LINK = '' PRIVACY_POLICY_LINK = ''
TERMS_OF_SERVICE_LINK = '' TERMS_OF_SERVICE_LINK = ''
FILE_CONVERTER_SERVER_URL = 'http://127.0.0.1:8888'
# For security consideration, please set to match the host/domain of your site, e.g., ALLOWED_HOSTS = ['.example.com']. # For security consideration, please set to match the host/domain of your site, e.g., ALLOWED_HOSTS = ['.example.com'].
# Please refer https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts for details. # Please refer https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts for details.
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']

View File

@ -334,6 +334,10 @@ def get_no_duplicate_obj_name(obj_name, exist_obj_names):
i += 1 i += 1
def check_filename_with_rename(repo_id, parent_dir, obj_name): def check_filename_with_rename(repo_id, parent_dir, obj_name):
exist_obj_names = list_obj_names_in_dir(repo_id, parent_dir)
return get_no_duplicate_obj_name(obj_name, exist_obj_names)
def list_obj_names_in_dir(repo_id, parent_dir):
cmmts = seafile_api.get_commit_list(repo_id, 0, 1) cmmts = seafile_api.get_commit_list(repo_id, 0, 1)
latest_commit = cmmts[0] if cmmts else None latest_commit = cmmts[0] if cmmts else None
if not latest_commit: if not latest_commit:
@ -343,6 +347,13 @@ def check_filename_with_rename(repo_id, parent_dir, obj_name):
latest_commit.id, parent_dir) latest_commit.id, parent_dir)
exist_obj_names = [dirent.obj_name for dirent in dirents] exist_obj_names = [dirent.obj_name for dirent in dirents]
return exist_obj_names
def check_filename_or_rename(repo_id, parent_dir, obj_name):
exist_obj_names = list_obj_names_in_dir(repo_id, parent_dir)
if obj_name not in exist_obj_names:
return obj_name
return get_no_duplicate_obj_name(obj_name, exist_obj_names) return get_no_duplicate_obj_name(obj_name, exist_obj_names)
def get_user_repos(username, org_id=None): def get_user_repos(username, org_id=None):

View File

@ -10,3 +10,7 @@ AUDIO = 'Audio'
SPREADSHEET = 'SpreadSheet' SPREADSHEET = 'SpreadSheet'
XMIND = 'XMind' XMIND = 'XMind'
SEADOC = 'SDoc' SEADOC = 'SDoc'
MARKDOWN_SUPPORT_CONVERT_TYPES = ['sdoc']
SDOC_SUPPORT_CONVERT_TYPES = ['markdown']