1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-17 15:53:28 +00:00

Wiki module improve (#2901)

This commit is contained in:
杨顺强
2019-01-29 11:03:43 +08:00
committed by Daniel Pan
parent d9a2f6ded9
commit 3c2b89dc22
7 changed files with 155 additions and 31 deletions

View File

@@ -641,7 +641,7 @@
}, },
"axios": { "axios": {
"version": "0.18.0", "version": "0.18.0",
"resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
"requires": { "requires": {
"follow-redirects": "^1.3.0", "follow-redirects": "^1.3.0",
@@ -5282,7 +5282,7 @@
}, },
"git-up": { "git-up": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "http://registry.npmjs.org/git-up/-/git-up-1.2.1.tgz", "resolved": "https://registry.npmjs.org/git-up/-/git-up-1.2.1.tgz",
"integrity": "sha1-JkSAoAax2EJhrB/gmjpRacV+oZ0=", "integrity": "sha1-JkSAoAax2EJhrB/gmjpRacV+oZ0=",
"requires": { "requires": {
"is-ssh": "^1.0.0", "is-ssh": "^1.0.0",
@@ -5291,7 +5291,7 @@
}, },
"git-url-parse": { "git-url-parse": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "http://registry.npmjs.org/git-url-parse/-/git-url-parse-5.0.1.tgz", "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-5.0.1.tgz",
"integrity": "sha1-/j15xnRq4FBIz6UIyB553du6OEM=", "integrity": "sha1-/j15xnRq4FBIz6UIyB553du6OEM=",
"requires": { "requires": {
"git-up": "^1.0.0" "git-up": "^1.0.0"
@@ -7844,7 +7844,7 @@
}, },
"node-status-codes": { "node-status-codes": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "http://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz",
"integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8=" "integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8="
}, },
"noop6": { "noop6": {
@@ -8151,7 +8151,7 @@
}, },
"package.json": { "package.json": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "http://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz", "resolved": "https://registry.npmjs.org/package.json/-/package.json-2.0.1.tgz",
"integrity": "sha1-+IYFnSpJ7QduZIg2ldc7K0bSHW0=", "integrity": "sha1-+IYFnSpJ7QduZIg2ldc7K0bSHW0=",
"requires": { "requires": {
"git-package-json": "^1.4.0", "git-package-json": "^1.4.0",
@@ -8161,7 +8161,7 @@
"dependencies": { "dependencies": {
"got": { "got": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz", "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
"integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=", "integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
"requires": { "requires": {
"create-error-class": "^3.0.1", "create-error-class": "^3.0.1",
@@ -8183,7 +8183,7 @@
}, },
"package-json": { "package-json": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "http://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz",
"integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=", "integrity": "sha1-DRW9Z9HLvduyyiIv8u24a8sxqLs=",
"requires": { "requires": {
"got": "^5.0.0", "got": "^5.0.0",

View File

@@ -1,17 +1,114 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import MarkdownViewer from '@seafile/seafile-editor/dist/viewer/markdown-viewer'; import MarkdownViewer from '@seafile/seafile-editor/dist/viewer/markdown-viewer';
import { repoID, slug, serviceURL, isPublicWiki } from '../utils/constants';
import { Utils } from '../utils/utils';
const viewerPropTypes = { const viewerPropTypes = {
indexContent: PropTypes.string.isRequired, indexContent: PropTypes.string.isRequired,
onLinkClick: PropTypes.func.isRequired, onLinkClick: PropTypes.func.isRequired,
}; };
const contentClass = 'wiki-page-content';
class IndexContentViewer extends React.Component { class IndexContentViewer extends React.Component {
constructor(props) {
super(props);
this.links = [];
}
componentDidMount() {
// Bind event when first loaded
this.links = document.querySelectorAll(`.${contentClass} a`);
this.links.forEach(link => {
link.addEventListener('click', this.onLinkClick);
});
}
componentWillReceiveProps() {
// Unbound event when updating
this.links.forEach(link => {
link.removeEventListener('click', this.onLinkClick);
});
}
componentDidUpdate() {
// Update completed, rebind event
this.links = document.querySelectorAll(`.${contentClass} a`);
this.links.forEach(link => {
link.addEventListener('click', this.onLinkClick);
});
}
componentWillUnmount() {
// Rebinding events when the component is destroyed
this.links.forEach(link => {
link.removeEventListener('click', this.onLinkClick);
});
}
onLinkClick = (event) => { onLinkClick = (event) => {
event.preventDefault(); event.preventDefault();
this.props.onLinkClick(event); event.stopPropagation();
let link = '';
if (event.target.tagName !== 'A') {
let target = event.target.parentNode;
while (target.tagName !== 'A') {
target = target.parentNode;
}
link = target.href;
} else {
link = event.target.href;
}
this.props.onLinkClick(link);
}
changeInlineNode = (item) => {
if (item.object == 'inline') {
let url;
// change image url
if (item.type == 'image' && isPublicWiki) {
url = item.data.src;
const re = new RegExp(serviceURL + '/lib/' + repoID +'/file.*raw=1');
// different repo
if (!re.test(url)) {
return;
}
// get image path
let index = url.indexOf('/file');
let index2 = url.indexOf('?');
const imagePath = url.substring(index + 5, index2);
// replace url
item.data.src = serviceURL + '/view-image-via-public-wiki/?slug=' + slug + '&path=' + imagePath;
}
else if (item.type == 'link') {
url = item.data.href;
// change file url
if (Utils.isInternalMarkdownLink(url, repoID)) {
let path = Utils.getPathFromInternalMarkdownLink(url, repoID);
// replace url
item.data.href = serviceURL + '/wikis/' + slug + path;
}
// change dir url
else if (Utils.isInternalDirLink(url, repoID)) {
let path = Utils.getPathFromInternalDirLink(url, repoID, slug);
// replace url
item.data.href = serviceURL + '/wikis/' + slug + path;
}
}
}
return item;
}
modifyValueBeforeRender = (value) => {
let nodes = value.document.nodes;
let newNodes = Utils.changeMarkdownNodes(nodes, this.changeInlineNode);
value.document.nodes = newNodes;
return value;
} }
onContentRendered = () => { onContentRendered = () => {
@@ -20,10 +117,11 @@ class IndexContentViewer extends React.Component {
render() { render() {
return ( return (
<div className="markdown-content"> <div className={contentClass}>
<MarkdownViewer <MarkdownViewer
markdownContent={this.props.indexContent} markdownContent={this.props.indexContent}
onContentRendered={this.props.onContentRendered} onContentRendered={this.props.onContentRendered}
modifyValueBeforeRender={this.modifyValueBeforeRender}
/> />
</div> </div>
); );

View File

@@ -56,7 +56,7 @@ class WikiDirListItem extends React.Component {
<a href={href} onClick={this.onDirentClick}>{dirent.name}</a> <a href={href} onClick={this.onDirentClick}>{dirent.name}</a>
</td> </td>
<td>{dirent.size}</td> <td>{dirent.size}</td>
<td title={dirent.last_update_time}>{dirent.last_update_time}</td> <td title={dirent.mtime_relative}>{dirent.mtime_relative}</td>
</tr> </tr>
); );
} }

View File

@@ -31,7 +31,8 @@ class MainPanel extends Component {
onEditClick = (e) => { onEditClick = (e) => {
e.preventDefault(); e.preventDefault();
window.location.href= siteRoot + 'lib/' + repoID + '/file' + this.props.filePath + '?mode=edit'; let url = siteRoot + 'lib/' + repoID + '/file' + this.props.path + '?mode=edit';
window.open(url);
} }
onMainNavBarClick = (e) => { onMainNavBarClick = (e) => {
@@ -69,6 +70,7 @@ class MainPanel extends Component {
render() { render() {
const errMessage = (<div className="message empty-tip err-message"><h2>{gettext('Folder does not exist.')}</h2></div>);
return ( return (
<div className="main-panel wiki-main-panel o-hidden"> <div className="main-panel wiki-main-panel o-hidden">
<div className="main-panel-top panel-top"> <div className="main-panel-top panel-top">
@@ -102,8 +104,9 @@ class MainPanel extends Component {
</div> </div>
</div> </div>
<div className="cur-view-content"> <div className="cur-view-content">
{this.props.isDataLoading && <Loading />} {!this.props.pathExist && errMessage}
{(!this.props.isDataLoading && this.props.isViewFile) && ( {this.props.pathExist && this.props.isDataLoading && <Loading />}
{(this.props.pathExist && !this.props.isDataLoading && this.props.isViewFile) && (
<WikiMarkdownViewer <WikiMarkdownViewer
markdownContent={this.props.content} markdownContent={this.props.content}
isFileLoading={this.props.isDataLoading} isFileLoading={this.props.isDataLoading}

View File

@@ -1,6 +1,6 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { gettext, siteRoot, repoID } from '../../utils/constants'; import { gettext, siteRoot, repoID, slug } from '../../utils/constants';
import Logo from '../../components/logo'; import Logo from '../../components/logo';
import Loading from '../../components/loading'; import Loading from '../../components/loading';
import TreeView from '../../components/tree-view/tree-view'; import TreeView from '../../components/tree-view/tree-view';
@@ -27,21 +27,11 @@ class SidePanel extends Component {
this.isNodeMenuShow = false; this.isNodeMenuShow = false;
} }
onEditClick = (e) => {
e.preventDefault();
let indexNode = this.props.indexNode
window.location.href= siteRoot + 'lib/' + repoID + '/file' + indexNode.path + '?mode=edit';
}
renderIndexView = () => { renderIndexView = () => {
let indexNode = this.props.indexNode;
return ( return (
<Fragment> <Fragment>
<h3 className="wiki-pages-heading"> <h3 className="wiki-pages-heading">
{gettext('Contents')} {gettext('Contents')}
{indexNode.object.permission === 'rw' &&
<button className="btn btn-secondary operation-item index-edit" title="Edit Index" onClick={this.onEditClick}>{gettext('Edit')}</button>
}
</h3> </h3>
<div className="wiki-pages-container"> <div className="wiki-pages-container">
<IndexContentViewer <IndexContentViewer
@@ -83,6 +73,7 @@ class SidePanel extends Component {
{this.props.isTreeDataLoading && <Loading /> } {this.props.isTreeDataLoading && <Loading /> }
{!this.props.isTreeDataLoading && this.props.indexNode && this.renderIndexView() } {!this.props.isTreeDataLoading && this.props.indexNode && this.renderIndexView() }
{!this.props.isTreeDataLoading && !this.props.indexNode && this.renderTreeView() } {!this.props.isTreeDataLoading && !this.props.indexNode && this.renderTreeView() }
<div className="text-left p-2"><a href={siteRoot + 'library/' + repoID + "/" + slug} className="text-dark text-decoration-underline">{gettext('Go to Library')}</a></div>
</div> </div>
</div> </div>
); );

View File

@@ -50,11 +50,17 @@ class Wiki extends Component {
this.loadWikiData(initialPath); this.loadWikiData(initialPath);
} }
loadWikiData = () => { loadWikiData = (initialPath) => {
this.loadSidePanel(initialPath); this.loadSidePanel(initialPath);
if (isDir === 'None') { if (isDir === 'None') {
if (initialPath === '/home.md') {
this.showDir('/');
} else {
this.setState({pathExist: false}); this.setState({pathExist: false});
let fileUrl = siteRoot + 'wikis/' + slug + initialPath;
window.history.pushState({url: fileUrl, path: initialPath}, initialPath, fileUrl);
}
} else if (isDir === 'True') { } else if (isDir === 'True') {
this.showDir(initialPath); this.showDir(initialPath);
} else if (isDir === 'False') { } else if (isDir === 'False') {
@@ -66,9 +72,10 @@ class Wiki extends Component {
if (initialPath === this.homePath || isDir === 'None') { if (initialPath === this.homePath || isDir === 'None') {
seafileAPI.listDir(repoID, '/').then(res => { seafileAPI.listDir(repoID, '/').then(res => {
let tree = this.state.treeData; let tree = this.state.treeData;
this.addResponseListToNode(res.data.dirent_list, tree.root); this.addFirstResponseListToNode(res.data.dirent_list, tree.root);
let indexNode = tree.getNodeByPath(this.indexPath); let indexNode = tree.getNodeByPath(this.indexPath);
if (indexNode) { let homeNode = tree.getNodeByPath(this.homePath);
if (homeNode && indexNode) {
seafileAPI.getFileDownloadLink(repoID, indexNode.path).then(res => { seafileAPI.getFileDownloadLink(repoID, indexNode.path).then(res => {
seafileAPI.getFileContent(res.data).then(res => { seafileAPI.getFileContent(res.data).then(res => {
this.setState({ this.setState({
@@ -202,10 +209,10 @@ class Wiki extends Component {
const url = link; const url = link;
if (Utils.isWikiInternalMarkdownLink(url, slug)) { if (Utils.isWikiInternalMarkdownLink(url, slug)) {
let path = Utils.getPathFromWikiInternalMarkdownLink(url, slug); let path = Utils.getPathFromWikiInternalMarkdownLink(url, slug);
this.initMainPanelData(path); this.showFile(path);
} else if (Utils.isWikiInternalDirLink(url, slug)) { } else if (Utils.isWikiInternalDirLink(url, slug)) {
let path = Utils.getPathFromWikiInternalDirLink(url, slug); let path = Utils.getPathFromWikiInternalDirLink(url, slug);
this.initWikiData(path); this.showDir(path);
} else { } else {
window.location.href = url; window.location.href = url;
} }
@@ -364,6 +371,27 @@ class Wiki extends Component {
} }
} }
addFirstResponseListToNode = (list, node) => {
node.isLoaded = true;
node.isExpanded = true;
let direntList = list.map(item => {
return new Dirent(item);
});
direntList = direntList.filter(item => {
if (item.type === 'dir') {
let name = item.name.toLowerCase();
return name !== 'drafts' && name !== 'images' && name !== 'downloads';
}
return true;
});
direntList = Utils.sortDirents(direntList, 'name', 'asc');
let nodeList = direntList.map(object => {
return new TreeNode({object});
});
node.addChildren(nodeList);
}
addResponseListToNode = (list, node) => { addResponseListToNode = (list, node) => {
node.isLoaded = true; node.isLoaded = true;
node.isExpanded = true; node.isExpanded = true;

View File

@@ -149,6 +149,10 @@ ul,ol,li {
cursor: pointer; cursor: pointer;
} }
.text-decoration-underline {
text-decoration: underline !important;
}
.sf-dropdown-toggle { .sf-dropdown-toggle {
margin-left: 0.5rem; margin-left: 0.5rem;
vertical-align: middle; vertical-align: middle;