mirror of
https://github.com/haiwen/seahub.git
synced 2025-04-28 03:10:45 +00:00
Merge 083382bc57
into 371dd4a057
This commit is contained in:
commit
5e932206c0
@ -106,6 +106,13 @@ class SidePanel extends PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
exportPage = async (fromPageConfig) => {
|
||||
const { pageId, exportType } = fromPageConfig;
|
||||
const serviceUrl = window.app.config.serviceURL;
|
||||
let exportPageUrl = serviceUrl + '/api/v2.1/wiki2/' + wikiId + '/page/' + pageId + '/export/?exportType=' + exportType;
|
||||
window.location.href = exportPageUrl;
|
||||
};
|
||||
|
||||
addPage = (page, parent_id, successCallback, errorCallback, jumpToNewPage = true) => {
|
||||
const { config } = this.props;
|
||||
const navigation = config.navigation;
|
||||
@ -160,6 +167,7 @@ class SidePanel extends PureComponent {
|
||||
updateWikiConfig={this.props.updateWikiConfig}
|
||||
onAddNewPage={this.onAddNewPage}
|
||||
duplicatePage={this.duplicatePage}
|
||||
exportPage={this.exportPage}
|
||||
getCurrentPageId={this.props.getCurrentPageId}
|
||||
addPageInside={this.addPageInside}
|
||||
toggleTrashDialog={this.toggleTrashDialog}
|
||||
|
@ -17,6 +17,7 @@ export default class PageDropdownMenu extends Component {
|
||||
toggleInsertSiblingPage: PropTypes.func,
|
||||
duplicatePage: PropTypes.func,
|
||||
onDeletePage: PropTypes.func,
|
||||
exportPage: PropTypes.func,
|
||||
isOnlyOnePage: PropTypes.bool,
|
||||
};
|
||||
|
||||
@ -61,10 +62,24 @@ export default class PageDropdownMenu extends Component {
|
||||
this.props.duplicatePage({ from_page_id: page.id }, () => {}, this.duplicatePageFailure);
|
||||
};
|
||||
|
||||
exportPageToSdoc = () => {
|
||||
const { page } = this.props;
|
||||
this.props.exportPage({ pageId: page.id, exportType: 'sdoc' }, () => {}, this.exportPageFailure);
|
||||
};
|
||||
|
||||
exportPageToMarkdown = () => {
|
||||
const { page } = this.props;
|
||||
this.props.exportPage({ pageId: page.id, exportType: 'markdown' }, () => {}, this.exportPageFailure);
|
||||
};
|
||||
|
||||
duplicatePageFailure = () => {
|
||||
toaster.danger(gettext('Failed to duplicate page'));
|
||||
};
|
||||
|
||||
exportPageFailure = () => {
|
||||
toaster.danger(gettext('Failed to export page'));
|
||||
};
|
||||
|
||||
handleCopyLink = () => {
|
||||
const { page } = this.props;
|
||||
const wikiLink = getWikPageLink(page.id);
|
||||
@ -119,6 +134,14 @@ export default class PageDropdownMenu extends Component {
|
||||
<i className="sf3-font sf3-font-copy1" aria-hidden="true" />
|
||||
<span className="item-text">{gettext('Duplicate page')}</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this.exportPageToSdoc}>
|
||||
<i className="sf3-font sf3-font-copy1" aria-hidden="true" />
|
||||
<span className="item-text">{gettext('Export as sdoc')}</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this.exportPageToMarkdown}>
|
||||
<i className="sf3-font sf3-font-copy1" aria-hidden="true" />
|
||||
<span className="item-text">{gettext('Export as Markdown')}</span>
|
||||
</DropdownItem>
|
||||
{(isOnlyOnePage || pagesLength === 1) ? '' : (
|
||||
<DropdownItem onClick={this.onDeletePage}>
|
||||
<i className="sf3-font sf3-font-delete1" aria-hidden="true" />
|
||||
|
@ -261,6 +261,7 @@ class PageItem extends Component {
|
||||
toggleNameEditor={this.toggleNameEditor}
|
||||
duplicatePage={this.props.duplicatePage}
|
||||
onDeletePage={this.props.onDeletePage.bind(this, page.id)}
|
||||
exportPage={this.props.exportPage}
|
||||
toggleInsertSiblingPage={this.toggleInsertSiblingPage}
|
||||
/>
|
||||
}
|
||||
@ -350,6 +351,7 @@ PageItem.propTypes = {
|
||||
connectDragPreview: PropTypes.func,
|
||||
connectDropTarget: PropTypes.func,
|
||||
duplicatePage: PropTypes.func,
|
||||
exportPage: PropTypes.func,
|
||||
setCurrentPage: PropTypes.func,
|
||||
onUpdatePage: PropTypes.func,
|
||||
onDeletePage: PropTypes.func,
|
||||
|
@ -85,6 +85,7 @@ class WikiNav extends Component {
|
||||
pages={pages}
|
||||
pageIndex={index}
|
||||
duplicatePage={this.props.duplicatePage}
|
||||
exportPage={this.props.exportPage}
|
||||
setCurrentPage={this.props.setCurrentPage}
|
||||
onUpdatePage={this.props.onUpdatePage}
|
||||
onDeletePage={this.props.onDeletePage}
|
||||
|
@ -288,6 +288,22 @@ def sdoc_export_to_docx(path, username, doc_uuid, download_url,
|
||||
return resp
|
||||
|
||||
|
||||
def sdoc_export_to_md(path, doc_uuid, download_url,
|
||||
src_type, dst_type):
|
||||
headers = convert_file_gen_headers()
|
||||
params = {
|
||||
'path': path,
|
||||
'doc_uuid': doc_uuid,
|
||||
'download_url': download_url,
|
||||
'src_type': src_type,
|
||||
'dst_type': dst_type,
|
||||
}
|
||||
url = FILE_CONVERTER_SERVER_URL.rstrip('/') + '/api/v1/sdoc-export-to-md/'
|
||||
resp = requests.post(url, json=params, headers=headers, timeout=30)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
def format_date(start, end):
|
||||
start_struct_time = datetime.datetime.strptime(start, "%Y-%m-%d")
|
||||
start_timestamp = time.mktime(start_struct_time.timetuple())
|
||||
|
@ -7,8 +7,10 @@ import posixpath
|
||||
import datetime
|
||||
import uuid
|
||||
import re
|
||||
import requests
|
||||
from copy import deepcopy
|
||||
from constance import config
|
||||
from urllib.parse import quote
|
||||
|
||||
from rest_framework import status
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
@ -18,11 +20,13 @@ from rest_framework.views import APIView
|
||||
from seaserv import seafile_api
|
||||
from pysearpc import SearpcError
|
||||
from django.utils.translation import gettext as _
|
||||
from django.http import HttpResponse
|
||||
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
from seahub.api2.endpoints.utils import sdoc_export_to_md
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.utils import api_error, is_wiki_repo
|
||||
from seahub.utils import HAS_FILE_SEARCH, HAS_FILE_SEASEARCH, get_service_url
|
||||
from seahub.utils import HAS_FILE_SEARCH, HAS_FILE_SEASEARCH
|
||||
if HAS_FILE_SEARCH or HAS_FILE_SEASEARCH:
|
||||
from seahub.search.utils import search_wikis, ai_search_wikis
|
||||
from seahub.utils.db_api import SeafileDB
|
||||
@ -35,7 +39,7 @@ from seahub.wiki2.utils import is_valid_wiki_name, get_wiki_config, WIKI_PAGES_D
|
||||
delete_page, move_nav, revert_nav, get_sub_ids_by_page_id, get_parent_id_stack, add_convert_wiki_task
|
||||
|
||||
from seahub.utils import is_org_context, get_user_repos, is_pro_version, is_valid_dirent_name, \
|
||||
get_no_duplicate_obj_name, HAS_FILE_SEARCH, HAS_FILE_SEASEARCH
|
||||
get_no_duplicate_obj_name, HAS_FILE_SEARCH, HAS_FILE_SEASEARCH, gen_file_get_url, get_service_url
|
||||
|
||||
from seahub.views import check_folder_permission
|
||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||
@ -56,6 +60,7 @@ from seahub.constants import PERMISSION_READ_WRITE
|
||||
from seaserv import ccnet_api
|
||||
from seahub.share.utils import is_repo_admin
|
||||
|
||||
|
||||
HTTP_520_OPERATION_FAILED = 520
|
||||
|
||||
|
||||
@ -1564,3 +1569,71 @@ class WikiConvertView(APIView):
|
||||
|
||||
return Response({"task_id": task_id})
|
||||
|
||||
|
||||
class WikiPageExport(APIView):
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request, wiki_id, page_id):
|
||||
types = ['sdoc', 'markdown']
|
||||
export_type = request.GET.get('exportType')
|
||||
if export_type not in types:
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid export type')
|
||||
|
||||
# resource check
|
||||
wiki = Wiki.objects.get(wiki_id=wiki_id)
|
||||
if not wiki:
|
||||
error_msg = "Wiki not found."
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
repo_id = wiki.repo_id
|
||||
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)
|
||||
|
||||
username = request.user.username
|
||||
wiki_config = get_wiki_config(repo_id, username)
|
||||
navigation = wiki_config.get('navigation', [])
|
||||
pages = wiki_config.get('pages', [])
|
||||
id_set = get_all_wiki_ids(navigation)
|
||||
if page_id not in id_set:
|
||||
error_msg = "Page not found."
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
permission = check_wiki_permission(wiki, username)
|
||||
if permission != 'rw':
|
||||
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
|
||||
page_path = ''
|
||||
page_name = ''
|
||||
doc_uuid = ''
|
||||
for page in pages:
|
||||
if page_id == page.get('id'):
|
||||
page_path = page.get('path')
|
||||
page_name = page.get('name')
|
||||
doc_uuid = page.get('docUuid')
|
||||
break
|
||||
try:
|
||||
file_id = seafile_api.get_file_id_by_path(repo_id, page_path)
|
||||
filename = os.path.basename(page_path)
|
||||
download_token = seafile_api.get_fileserver_access_token(repo_id, file_id, 'download', username)
|
||||
download_url = gen_file_get_url(download_token, filename)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
|
||||
|
||||
response = HttpResponse(content_type='application/octet-stream')
|
||||
if export_type == 'markdown':
|
||||
resp_with_md_file = sdoc_export_to_md(page_path, doc_uuid, download_url, 'sdoc', 'md')
|
||||
new_filename = f'{page_name}.md'
|
||||
encoded_filename = quote(new_filename.encode('utf-8'))
|
||||
response.write(resp_with_md_file.content)
|
||||
elif export_type == 'sdoc':
|
||||
sdoc_content = requests.get(download_url).content
|
||||
new_filename = f'{page_name}.sdoc'
|
||||
encoded_filename = quote(new_filename)
|
||||
response.write(sdoc_content)
|
||||
|
||||
response['Content-Disposition'] = 'attachment;filename*=utf-8''%s;filename="%s"' % (encoded_filename, encoded_filename)
|
||||
|
||||
return response
|
||||
|
@ -215,7 +215,7 @@ from seahub.ocm.settings import OCM_ENDPOINT
|
||||
from seahub.wiki2.views import wiki_view, wiki_publish_view, wiki_history_view
|
||||
from seahub.api2.endpoints.wiki2 import Wikis2View, Wiki2View, Wiki2ConfigView, Wiki2PagesView, Wiki2PageView, \
|
||||
Wiki2DuplicatePageView, WikiPageTrashView, Wiki2PublishView, Wiki2PublishConfigView, Wiki2PublishPageView, \
|
||||
WikiSearch, WikiConvertView
|
||||
WikiSearch, WikiConvertView, WikiPageExport
|
||||
from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView
|
||||
from seahub.api2.endpoints.user_list import UserListView
|
||||
from seahub.api2.endpoints.seahub_io import SeahubIOStatus
|
||||
@ -579,6 +579,7 @@ urlpatterns = [
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/publish/config/$', Wiki2PublishConfigView.as_view(), name='api-v2.1-wiki2-publish-config'),
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/pages/$', Wiki2PagesView.as_view(), name='api-v2.1-wiki2-pages'),
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/page/(?P<page_id>[-0-9a-zA-Z]{4})/$', Wiki2PageView.as_view(), name='api-v2.1-wiki2-page'),
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/page/(?P<page_id>[-0-9a-zA-Z]{4})/export/$', WikiPageExport.as_view(), name='api-v2.1-wiki2'),
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/publish/page/(?P<page_id>[-0-9a-zA-Z]{4})/$', Wiki2PublishPageView.as_view(), name='api-v2.1-wiki2-page'),
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/duplicate-page/$', Wiki2DuplicatePageView.as_view(), name='api-v2.1-wiki2-duplicate-page'),
|
||||
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/trash/', WikiPageTrashView.as_view(), name='api-v2.1-wiki2-trash'),
|
||||
|
Loading…
Reference in New Issue
Block a user