1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-15 06:44:16 +00:00

Add sdoc viewer (#5867)

* add seafile sdoc viewer

* optimize code

* optimize code

* update sdoc-editor version

* optimize code
This commit is contained in:
杨顺强
2023-12-28 15:00:26 +08:00
committed by GitHub
parent d9923452ef
commit 7b18222eab
9 changed files with 226 additions and 12 deletions

View File

@@ -12,7 +12,7 @@
"@gatsbyjs/reach-router": "1.3.9",
"@seafile/react-image-lightbox": "2.0.2",
"@seafile/resumablejs": "1.1.16",
"@seafile/sdoc-editor": "0.4.4",
"@seafile/sdoc-editor": "0.4.6",
"@seafile/seafile-calendar": "0.0.12",
"@seafile/seafile-editor": "1.0.16",
"@uiw/codemirror-extensions-langs": "^4.19.4",
@@ -4607,9 +4607,9 @@
"integrity": "sha512-8rBbmAEuuwOAGHYGCtEzpx+bxAcGS+V30otMmhRe7bPAdh4E57RWgCa8x7pkzHGFlY1t5d+ILz1gojvPVMYQig=="
},
"node_modules/@seafile/sdoc-editor": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.4.4.tgz",
"integrity": "sha512-S4Z8SZ+P7sSSv6QIgrAM69UZkXLRS+PKaRpIuT1eG/zFJH6VMKfZ/8MrT7jiwxcjUst/Z0UHnuw9YA8M40nu1Q==",
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.4.6.tgz",
"integrity": "sha512-FmSG9n5WOxJO/oglJcKl2gezE2C18krLp60LAYaixRevdKwyR8JZvKth/3o5dUWTSAeBOUPnVGNAwHzhacbfMw==",
"dependencies": {
"@seafile/react-image-lightbox": "2.0.4",
"@seafile/slate": "0.91.8",
@@ -31643,9 +31643,9 @@
"integrity": "sha512-8rBbmAEuuwOAGHYGCtEzpx+bxAcGS+V30otMmhRe7bPAdh4E57RWgCa8x7pkzHGFlY1t5d+ILz1gojvPVMYQig=="
},
"@seafile/sdoc-editor": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.4.4.tgz",
"integrity": "sha512-S4Z8SZ+P7sSSv6QIgrAM69UZkXLRS+PKaRpIuT1eG/zFJH6VMKfZ/8MrT7jiwxcjUst/Z0UHnuw9YA8M40nu1Q==",
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/@seafile/sdoc-editor/-/sdoc-editor-0.4.6.tgz",
"integrity": "sha512-FmSG9n5WOxJO/oglJcKl2gezE2C18krLp60LAYaixRevdKwyR8JZvKth/3o5dUWTSAeBOUPnVGNAwHzhacbfMw==",
"requires": {
"@seafile/react-image-lightbox": "2.0.4",
"@seafile/slate": "0.91.8",

View File

@@ -7,7 +7,7 @@
"@gatsbyjs/reach-router": "1.3.9",
"@seafile/react-image-lightbox": "2.0.2",
"@seafile/resumablejs": "1.1.16",
"@seafile/sdoc-editor": "0.4.4",
"@seafile/sdoc-editor": "0.4.6",
"@seafile/seafile-calendar": "0.0.12",
"@seafile/seafile-editor": "1.0.16",
"@uiw/codemirror-extensions-langs": "^4.19.4",

View File

@@ -0,0 +1,162 @@
import React from 'react';
import PropTypes from 'prop-types';
import { WikiViewer } from '@seafile/sdoc-editor';
import { appAvatarURL, assetsUrl, gettext, name, repoID, serviceURL, sharedToken, siteRoot, slug, username } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import Loading from '../loading';
import './style.css';
const propTypes = {
isWiki: PropTypes.bool,
path: PropTypes.string,
repoID: PropTypes.string,
isTOCShow: PropTypes.bool,
children: PropTypes.object,
isFileLoading: PropTypes.bool.isRequired,
containerClassName: PropTypes.string,
markdownContent: PropTypes.string.isRequired,
latestContributor: PropTypes.string.isRequired,
lastModified: PropTypes.string.isRequired,
onLinkClick: PropTypes.func.isRequired,
};
class SdocWikiPageViewer extends React.Component {
constructor(props) {
super(props);
this.scrollRef = React.createRef();
}
componentDidMount() {
// const eventBus = EventBus.getInstance();
// this.unsubscribeLinkClick = eventBus.subscribe(EXTERNAL_EVENTS.ON_LINK_CLICK, this.onLinkClick);
}
componentWillUnmount() {
// this.unsubscribeLinkClick();
}
onLinkClick = (event) => {
event.preventDefault();
event.stopPropagation();
let link = '';
let target = event.target;
while (!target.dataset || !target.dataset.url) {
target = target.parentNode;
}
if (!target) return;
link = target.dataset.url;
this.props.onLinkClick(link);
};
changeInlineNode = (item) => {
const { repoID } = this.props;
let url, imagePath;
// isPublicWiki: in the old version, only public wiki need replace image url
if (item.type == 'image') { // change image url
url = item.data.src;
const re = new RegExp(serviceURL + '/lib/' + repoID +'/file.*raw=1');
// different repo
if (re.test(url)) {
// get image path
let index = url.indexOf('/file');
let index2 = url.indexOf('?');
imagePath = url.substring(index + 5, index2);
} else if (/^\.\.\/*/.test(url) || /^\.\/*/.test(url)) {
const path = this.props.path;
const originalPath = path.slice(0, path.lastIndexOf('/')) + '/' + url;
imagePath = Utils.pathNormalize(originalPath);
} else {
return;
}
item.data.src = serviceURL + '/view-image-via-public-wiki/?slug=' + slug + '&path=' + imagePath;
} else if (item.type == 'link') { // change link url
url = item.url;
if (Utils.isInternalFileLink(url, repoID)) { // change file url
if (Utils.isInternalMarkdownLink(url, repoID)) {
let path = Utils.getPathFromInternalMarkdownLink(url, repoID);
// replace url
item.url = serviceURL + '/published/' + slug + path;
} else {
item.url = url.replace(/(.*)lib\/([-0-9a-f]{36})\/file(.*)/g, (match, p1, p2, p3) => {
return `${p1}d/${sharedToken}/files/?p=${p3}&dl=1`;
});
}
} else if (Utils.isInternalDirLink(url, repoID)) { // change dir url
let path = Utils.getPathFromInternalDirLink(url, repoID);
// replace url
item.url = serviceURL + '/published/' + slug + path;
}
}
return item;
};
modifyValueBeforeRender = (value) => {
let newNodes = Utils.changeMarkdownNodes(value, this.changeInlineNode);
return newNodes;
};
renderMarkdown = () => {
let { isTOCShow, markdownContent } = this.props;
if (!markdownContent) return null;
let document = JSON.parse(markdownContent);
// if (isWiki) {
// const newChildren = this.modifyValueBeforeRender(document.children);
// document.children = newChildren;
// }
if (!window.seafile) {
window.seafile = {
serviceUrl: serviceURL,
username: username,
name: name,
avatarURL: appAvatarURL,
repoID: repoID,
siteRoot: siteRoot,
assetsUrl: assetsUrl,
};
}
const props = {
document: document,
showOutline: isTOCShow,
scrollRef: this.scrollRef,
};
return <WikiViewer {...props} />;
};
render() {
if (this.props.isFileLoading) {
return <Loading />;
}
const { isWiki, containerClassName = '' } = this.props;
const containerClass = `wiki-page-container ${containerClassName}`;
// In dir-column-file width is 100%;
// In wiki-viewer width isn't 100%
const contentClassName = `wiki-page-content ${!isWiki ? + 'w-100' : ''}`;
return (
<div ref={this.scrollRef} className={containerClass}>
<div className={contentClassName}>
{this.props.children}
{this.renderMarkdown()}
<p id="wiki-page-last-modified">{gettext('Last modified by')} {this.props.latestContributor}, <span>{this.props.lastModified}</span></p>
</div>
</div>
);
}
}
const defaultProps = {
isWiki: false,
isTOCShow: true,
};
SdocWikiPageViewer.propTypes = propTypes;
SdocWikiPageViewer.defaultProps = defaultProps;
export default SdocWikiPageViewer;

View File

@@ -0,0 +1,25 @@
.wiki-page-container .sdoc-wiki-scroll-container {
background-color: #fff !important;
padding: 0px !important;
overflow: inherit;
}
.wiki-page-container .sdoc-wiki-scroll-container .sdoc-article-container {
margin: 0 auto !important;
}
.wiki-page-container .sdoc-article-container .article .sdoc-header-title {
margin-top: 0;
}
.wiki-page-container .sdoc-article-container .article {
margin: 0;
padding: 0 10px;
max-width: none;
border: none;
}
.wiki-page-container .sdoc-wiki-outline-container {
top: 79px;
width: 200px !important;
}

View File

@@ -71,12 +71,17 @@ class Wiki extends Component {
}
// load dir list
initialPath = isDir === 'None' ? '/' : initialPath;
initialPath = (isDir === 'None' || Utils.isSdocFile(initialPath)) ? '/' : initialPath;
this.loadNodeAndParentsByPath(initialPath);
};
loadWikiData = (initialPath) => {
this.pythonWrapper = document.getElementById('wiki-file-content');
if (isDir === 'False' && Utils.isSdocFile(initialPath)) {
this.showDir('/');
return;
}
if (isDir === 'False') {
// this.showFile(initialPath);
this.setState({path: initialPath});
@@ -400,7 +405,7 @@ class Wiki extends Component {
if (node.object.isDir()) { // isDir
this.showDir(node.path);
} else {
if (Utils.isMarkdownFile(node.path)) {
if (Utils.isMarkdownFile(node.path) || Utils.isSdocFile(node.path)) {
if (node.path !== this.state.path) {
this.showFile(node.path);
}

View File

@@ -8,6 +8,7 @@ import { Utils } from '../../utils/utils';
import Search from '../../components/search/search';
import Notification from '../../components/common/notification';
import Account from '../../components/common/account';
import SdocWikiPageViewer from '../../components/sdoc-wiki-page-viewer';
const propTypes = {
path: PropTypes.string.isRequired,
@@ -120,7 +121,7 @@ class MainPanel extends Component {
<div className={`cur-view-content ${isViewingFile ? 'o-hidden' : ''}`}>
{!this.props.pathExist && errMessage}
{this.props.pathExist && this.props.isDataLoading && <Loading />}
{isViewingFile && (
{isViewingFile && Utils.isMarkdownFile(this.props.path) && (
<SeafileMarkdownViewer
isWiki={true}
path={this.props.path}
@@ -132,6 +133,18 @@ class MainPanel extends Component {
onLinkClick={this.props.onLinkClick}
/>
)}
{isViewingFile && Utils.isSdocFile(this.props.path) && (
<SdocWikiPageViewer
isWiki={true}
path={this.props.path}
repoID={repoID}
markdownContent={this.props.content}
isFileLoading={this.props.isDataLoading}
lastModified = {this.props.lastModified}
latestContributor={this.props.latestContributor}
onLinkClick={this.props.onLinkClick}
/>
)}
{(!this.props.isDataLoading && !this.props.isViewFile) && (
<WikiDirListView
path={this.props.path}

View File

@@ -119,6 +119,7 @@ export const isPublicWiki = window.wiki ? window.wiki.config.isPublicWiki === 'T
export const sharedToken = window.wiki ? window.wiki.config.sharedToken : '';
export const sharedType = window.wiki ? window.wiki.config.sharedType : '';
export const hasIndex = window.wiki ? window.wiki.config.hasIndex : '';
export const assetsUrl = window.wiki ? window.wiki.config.assetsUrl : '';
// file history
export const PER_PAGE = 25;

View File

@@ -48,6 +48,7 @@
isPublicWiki: "{{ is_public_wiki }}",
isDir: "{{ is_dir }}",
hasIndex: {% if has_index %} true {% else %} false {% endif %},
assetsUrl: "{{ assets_url }}"
}
};
</script>

View File

@@ -25,7 +25,8 @@ from seahub.views import check_folder_permission
from seahub.utils import get_file_type_and_ext, render_permission_error, \
gen_inner_file_get_url, render_error, get_service_url
from seahub.views.file import send_file_access_msg
from seahub.utils.file_types import IMAGE, MARKDOWN
from seahub.utils.file_types import IMAGE, MARKDOWN, SEADOC
from seahub.seadoc.utils import get_seadoc_file_uuid
# Get an instance of a logger
logger = logging.getLogger(__name__)
@@ -197,6 +198,7 @@ def slug(request, slug, file_path="home.md"):
outlines = []
latest_contributor = ''
last_modified = 0
assets_url = ''
if is_dir is False and file_type == MARKDOWN:
send_file_access_msg(request, repo, file_path, 'web')
@@ -230,6 +232,10 @@ def slug(request, slug, file_path="home.md"):
except Exception as e:
logger.warning(e)
if is_dir is False and file_type == SEADOC:
file_uuid = get_seadoc_file_uuid(repo, file_path)
assets_url = '/api/v2.1/seadoc/download-image/' + file_uuid
last_modified = datetime.fromtimestamp(last_modified)
return render(request, "wiki/wiki.html", {
@@ -252,6 +258,7 @@ def slug(request, slug, file_path="home.md"):
"is_public_wiki": is_public_wiki,
"is_dir": is_dir,
"has_index": has_index,
"assets_url": assets_url,
})