2019-01-15 11:58:25 +00:00
|
|
|
import React from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
2023-12-08 03:20:12 +00:00
|
|
|
import { EXTERNAL_EVENTS, EventBus, MarkdownViewer } from '@seafile/seafile-editor';
|
|
|
|
import { gettext, isPublicWiki, mediaUrl, repoID, serviceURL, sharedToken, slug } from '../utils/constants';
|
2019-01-15 11:58:25 +00:00
|
|
|
import Loading from './loading';
|
2019-07-16 02:01:09 +00:00
|
|
|
import { Utils } from '../utils/utils';
|
2019-01-15 11:58:25 +00:00
|
|
|
|
|
|
|
const propTypes = {
|
|
|
|
children: PropTypes.object,
|
|
|
|
isFileLoading: PropTypes.bool.isRequired,
|
|
|
|
markdownContent: PropTypes.string.isRequired,
|
|
|
|
latestContributor: PropTypes.string.isRequired,
|
|
|
|
lastModified: PropTypes.string.isRequired,
|
2019-01-24 07:41:01 +00:00
|
|
|
onLinkClick: PropTypes.func.isRequired,
|
2019-02-20 06:43:55 +00:00
|
|
|
isWiki: PropTypes.bool,
|
|
|
|
isTOCShow: PropTypes.bool,
|
2019-04-08 05:32:46 +00:00
|
|
|
// for dir-column-file component(import repoID is undefined)
|
|
|
|
repoID: PropTypes.string,
|
|
|
|
path: PropTypes.string,
|
2019-01-15 11:58:25 +00:00
|
|
|
};
|
|
|
|
|
2019-05-28 09:53:51 +00:00
|
|
|
const contentClass = 'wiki-page-content';
|
2019-01-15 11:58:25 +00:00
|
|
|
|
|
|
|
class WikiMarkdownViewer extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2023-12-08 03:20:12 +00:00
|
|
|
this.scrollRef = React.createRef();
|
2019-01-15 11:58:25 +00:00
|
|
|
}
|
|
|
|
|
2019-01-22 07:58:28 +00:00
|
|
|
componentDidMount() {
|
2023-12-08 03:20:12 +00:00
|
|
|
const eventBus = EventBus.getInstance();
|
|
|
|
this.unsubscribeLinkClick = eventBus.subscribe(EXTERNAL_EVENTS.ON_LINK_CLICK, this.onLinkClick);
|
2019-01-22 07:58:28 +00:00
|
|
|
}
|
2019-01-15 11:58:25 +00:00
|
|
|
componentWillUnmount() {
|
2023-12-08 03:20:12 +00:00
|
|
|
this.unsubscribeLinkClick();
|
2019-01-15 11:58:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onLinkClick = (event) => {
|
2020-11-02 05:56:35 +00:00
|
|
|
event.preventDefault();
|
2019-01-22 07:58:28 +00:00
|
|
|
event.stopPropagation();
|
2019-01-15 11:58:25 +00:00
|
|
|
let link = '';
|
2023-12-08 03:20:12 +00:00
|
|
|
let target = event.target;
|
|
|
|
while (!target.dataset || !target.dataset.url) {
|
|
|
|
target = target.parentNode;
|
2019-01-15 11:58:25 +00:00
|
|
|
}
|
2023-12-08 03:20:12 +00:00
|
|
|
if (!target) return;
|
|
|
|
link = target.dataset.url;
|
2019-01-15 11:58:25 +00:00
|
|
|
this.props.onLinkClick(link);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-01-15 11:58:25 +00:00
|
|
|
|
2019-01-24 07:41:01 +00:00
|
|
|
changeInlineNode = (item) => {
|
2021-04-27 03:22:55 +00:00
|
|
|
let url, imagePath;
|
|
|
|
|
2021-04-27 03:28:12 +00:00
|
|
|
if (item.type == 'image' && isPublicWiki) { // change image url
|
2021-04-27 03:22:55 +00:00
|
|
|
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;
|
2020-11-02 05:56:35 +00:00
|
|
|
}
|
2021-04-27 03:22:55 +00:00
|
|
|
item.data.src = serviceURL + '/view-image-via-public-wiki/?slug=' + slug + '&path=' + imagePath;
|
2021-04-27 03:28:12 +00:00
|
|
|
} else if (item.type == 'link') { // change link url
|
2023-12-08 03:20:12 +00:00
|
|
|
url = item.url;
|
2021-04-27 03:22:55 +00:00
|
|
|
if (Utils.isInternalFileLink(url, repoID)) { // change file url
|
|
|
|
if (Utils.isInternalMarkdownLink(url, repoID)) {
|
|
|
|
let path = Utils.getPathFromInternalMarkdownLink(url, repoID);
|
2019-01-24 07:41:01 +00:00
|
|
|
// replace url
|
2023-12-08 03:20:12 +00:00
|
|
|
item.url = serviceURL + '/published/' + slug + path;
|
2021-04-27 03:22:55 +00:00
|
|
|
} else {
|
2023-12-08 03:20:12 +00:00
|
|
|
item.url = url.replace(/(.*)lib\/([-0-9a-f]{36})\/file(.*)/g, (match, p1, p2, p3) => {
|
2021-04-27 03:22:55 +00:00
|
|
|
return `${p1}d/${sharedToken}/files/?p=${p3}&dl=1`;
|
|
|
|
});
|
2020-11-02 05:56:35 +00:00
|
|
|
}
|
2021-04-27 03:22:55 +00:00
|
|
|
} else if (Utils.isInternalDirLink(url, repoID)) { // change dir url
|
|
|
|
let path = Utils.getPathFromInternalDirLink(url, repoID);
|
|
|
|
// replace url
|
2023-12-08 03:20:12 +00:00
|
|
|
item.url = serviceURL + '/published/' + slug + path;
|
2019-01-24 07:41:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return item;
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-01-24 07:41:01 +00:00
|
|
|
|
|
|
|
modifyValueBeforeRender = (value) => {
|
2020-06-30 13:10:47 +00:00
|
|
|
let newNodes = Utils.changeMarkdownNodes(value, this.changeInlineNode);
|
|
|
|
return newNodes;
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-01-24 07:41:01 +00:00
|
|
|
|
|
|
|
renderMarkdown = () => {
|
2023-12-08 03:20:12 +00:00
|
|
|
const { isTOCShow = true, isWiki, markdownContent } = this.props;
|
|
|
|
const props = {
|
|
|
|
isShowOutline: isTOCShow,
|
|
|
|
mathJaxSource: `${mediaUrl}js/mathjax/tex-svg.js`,
|
|
|
|
value: markdownContent,
|
|
|
|
scrollRef: this.scrollRef,
|
|
|
|
...(isWiki && {beforeRenderCallback: this.modifyValueBeforeRender})
|
|
|
|
};
|
2019-01-24 07:41:01 +00:00
|
|
|
|
2023-12-08 03:20:12 +00:00
|
|
|
return <MarkdownViewer {...props} />;
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2019-01-24 07:41:01 +00:00
|
|
|
|
2019-01-15 11:58:25 +00:00
|
|
|
render() {
|
|
|
|
if (this.props.isFileLoading) {
|
2019-01-31 09:37:02 +00:00
|
|
|
return <Loading />;
|
2019-01-15 11:58:25 +00:00
|
|
|
}
|
2019-05-29 03:48:00 +00:00
|
|
|
// In dir-column-file repoID is one of props, width is 100%; In wiki-viewer repoID is not props, width isn't 100%
|
2019-06-04 09:59:54 +00:00
|
|
|
let contentClassName = `${this.props.repoID ? contentClass + ' w-100' : contentClass}`;
|
2019-01-15 11:58:25 +00:00
|
|
|
return (
|
2023-12-08 03:20:12 +00:00
|
|
|
<div ref={this.scrollRef} className="wiki-page-container">
|
2019-05-29 03:48:00 +00:00
|
|
|
<div className={contentClassName}>
|
2019-01-15 11:58:25 +00:00
|
|
|
{this.props.children}
|
2019-01-24 07:41:01 +00:00
|
|
|
{this.renderMarkdown()}
|
2019-01-15 11:58:25 +00:00
|
|
|
<p id="wiki-page-last-modified">{gettext('Last modified by')} {this.props.latestContributor}, <span>{this.props.lastModified}</span></p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-24 07:41:01 +00:00
|
|
|
const defaultProps = {
|
|
|
|
isWiki: false,
|
2019-01-31 09:37:02 +00:00
|
|
|
};
|
2019-01-24 07:41:01 +00:00
|
|
|
|
2019-01-15 11:58:25 +00:00
|
|
|
WikiMarkdownViewer.propTypes = propTypes;
|
2019-01-24 07:41:01 +00:00
|
|
|
MarkdownViewer.defaultProps = defaultProps;
|
2019-01-15 11:58:25 +00:00
|
|
|
|
|
|
|
export default WikiMarkdownViewer;
|