1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-17 07:41:26 +00:00

Wiki mode improve (#2539)

This commit is contained in:
Daniel Pan
2018-11-22 11:26:00 +08:00
committed by GitHub
parent 921089bd01
commit fb5d105e6e
24 changed files with 751 additions and 629 deletions

View File

@@ -77,7 +77,7 @@ class CreateFile extends React.Component {
}); });
} else if (pos === 0 ) { } else if (pos === 0 ) {
this.setState({ this.setState({
childName: '(draft)' + this.state.childname, childName: '(draft)' + this.state.childName,
isDraft: !this.state.isdraft isDraft: !this.state.isdraft
}); });
} else { } else {
@@ -93,10 +93,11 @@ class CreateFile extends React.Component {
} }
componentDidMount() { componentDidMount() {
if (this.props.parentPath === '/') { let parentPath = this.props.parentPath;
this.setState({parentPath: this.props.parentPath}); if (parentPath[parentPath.length - 1] === '/') { // mainPanel
this.setState({parentPath: parentPath});
} else { } else {
this.setState({parentPath: this.props.parentPath + '/'}); this.setState({parentPath: parentPath + '/'}); // sidePanel
} }
this.newInput.focus(); this.newInput.focus();
this.newInput.setSelectionRange(0,0); this.newInput.setSelectionRange(0,0);

View File

@@ -42,10 +42,11 @@ class CreateForder extends React.Component {
} }
componentDidMount() { componentDidMount() {
if (this.props.parentPath === '/') { let parentPath = this.props.parentPath;
this.setState({parentPath: this.props.parentPath}); if (parentPath[parentPath.length - 1] === '/') { // mainPanel
this.setState({parentPath: parentPath});
} else { } else {
this.setState({parentPath: this.props.parentPath + '/'}); this.setState({parentPath: parentPath + '/'}); // sidePanel
} }
this.newInput.focus(); this.newInput.focus();
this.newInput.setSelectionRange(0,0); this.newInput.setSelectionRange(0,0);

View File

@@ -25,7 +25,7 @@ class DetailListView extends React.Component {
getDirentPostion = () => { getDirentPostion = () => {
let { repo, direntPath } = this.props; let { repo, direntPath } = this.props;
let position = repo.repo_name + '/'; let position = repo.repo_name;
if (direntPath !== '/') { if (direntPath !== '/') {
let index = direntPath.lastIndexOf('/'); let index = direntPath.lastIndexOf('/');
let path = direntPath.slice(0, index); let path = direntPath.slice(0, index);
@@ -40,6 +40,10 @@ class DetailListView extends React.Component {
}); });
} }
onFileTagChanged = () => {
this.props.onFileTagChanged(this.props.dirent, this.props.direntPath);
}
render() { render() {
let { direntType, direntDetail, fileTagList } = this.props; let { direntType, direntDetail, fileTagList } = this.props;
let position = this.getDirentPostion(); let position = this.getDirentPostion();
@@ -64,7 +68,7 @@ class DetailListView extends React.Component {
<tr><th width="35%"></th><td width="65%"></td></tr> <tr><th width="35%"></th><td width="65%"></td></tr>
<tr><th>{gettext('Size')}</th><td>{direntDetail.size}</td></tr> <tr><th>{gettext('Size')}</th><td>{direntDetail.size}</td></tr>
<tr><th>{gettext('Position')}</th><td>{position}</td></tr> <tr><th>{gettext('Position')}</th><td>{position}</td></tr>
<tr><th>{gettext('Last Update')}</th><td>{moment(direntDetail.mtime).format('YYYY-MM-DD')}</td></tr> <tr><th>{gettext('Last Update')}</th><td>{moment(direntDetail.last_modified).fromNow()}</td></tr>
<tr className="file-tag-container"><th>{gettext('Tags')}</th> <tr className="file-tag-container"><th>{gettext('Tags')}</th>
<td> <td>
<ul className="file-tag-list"> <ul className="file-tag-list">
@@ -88,7 +92,7 @@ class DetailListView extends React.Component {
fileTagList={fileTagList} fileTagList={fileTagList}
filePath={this.props.direntPath} filePath={this.props.direntPath}
toggleCancel={this.onEditFileTagToggle} toggleCancel={this.onEditFileTagToggle}
onFileTagChanged={this.props.onFileTagChanged} onFileTagChanged={this.onFileTagChanged}
/> />
} }
</Fragment> </Fragment>

View File

@@ -6,10 +6,9 @@ import URLDecorator from '../../utils/url-decorator';
import Toast from '../toast'; import Toast from '../toast';
import DirentMenu from './dirent-menu'; import DirentMenu from './dirent-menu';
import DirentRename from './dirent-rename'; import DirentRename from './dirent-rename';
import FileTag from '../../models/file-tag';
const propTypes = { const propTypes = {
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
isItemFreezed: PropTypes.bool.isRequired, isItemFreezed: PropTypes.bool.isRequired,
dirent: PropTypes.object.isRequired, dirent: PropTypes.object.isRequired,
onItemClick: PropTypes.func.isRequired, onItemClick: PropTypes.func.isRequired,
@@ -22,7 +21,7 @@ const propTypes = {
onDirentItemMove: PropTypes.func.isRequired, onDirentItemMove: PropTypes.func.isRequired,
onDirentItemCopy: PropTypes.func.isRequired, onDirentItemCopy: PropTypes.func.isRequired,
onItemDetails: PropTypes.func.isRequired, onItemDetails: PropTypes.func.isRequired,
updateViewList: PropTypes.func.isRequired, updateDirent: PropTypes.func.isRequired,
currentRepo: PropTypes.object, currentRepo: PropTypes.object,
isRepoOwner: PropTypes.bool, isRepoOwner: PropTypes.bool,
}; };
@@ -36,13 +35,11 @@ class DirentListItem extends React.Component {
highlight: false, highlight: false,
isItemMenuShow: false, isItemMenuShow: false,
menuPosition: {top: 0, left: 0 }, menuPosition: {top: 0, left: 0 },
fileTagList: [],
}; };
} }
componentDidMount() { componentDidMount() {
document.addEventListener('click', this.onItemMenuHide); document.addEventListener('click', this.onItemMenuHide);
this.getFileTag();
} }
componentWillUnmount() { componentWillUnmount() {
@@ -119,11 +116,11 @@ class DirentListItem extends React.Component {
let filePath = this.getDirentPath(dirent); let filePath = this.getDirentPath(dirent);
if (dirent.starred) { if (dirent.starred) {
seafileAPI.unStarFile(repoID, filePath).then(() => { seafileAPI.unStarFile(repoID, filePath).then(() => {
this.props.updateViewList(this.props.filePath); this.props.updateDirent(this.props.dirent, "starred", false);
}); });
} else { } else {
seafileAPI.starFile(repoID, filePath).then(() => { seafileAPI.starFile(repoID, filePath).then(() => {
this.props.updateViewList(this.props.filePath); this.props.updateDirent(this.props.dirent, "starred", true);
}); });
} }
} }
@@ -141,8 +138,7 @@ class DirentListItem extends React.Component {
onItemDelete = (e) => { onItemDelete = (e) => {
e.nativeEvent.stopImmediatePropagation(); //for document event e.nativeEvent.stopImmediatePropagation(); //for document event
let direntPath = this.getDirentPath(this.props.dirent); this.props.onItemDelete(this.props.dirent);
this.props.onItemDelete(direntPath);
} }
onItemMenuItemClick = (operation) => { onItemMenuItemClick = (operation) => {
@@ -215,8 +211,7 @@ class DirentListItem extends React.Component {
return false; return false;
} }
let direntPath = this.getDirentPath(this.props.dirent); this.props.onItemRename(this.props.dirent, newName);
this.props.onItemRename(direntPath, newName);
this.onRenameCancel(); this.onRenameCancel();
} }
@@ -252,7 +247,8 @@ class DirentListItem extends React.Component {
onLockItem = () => { onLockItem = () => {
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.props.dirent);
seafileAPI.lockfile(repoID, filePath).then(() => { seafileAPI.lockfile(repoID, filePath).then(() => {
this.props.updateViewList(this.props.filePath); this.props.updateDirent(this.props.dirent, "is_locked", true);
this.props.updateDirent(this.props.dirent, "locked_by_me", true);
}); });
this.onItemMenuHide(); this.onItemMenuHide();
} }
@@ -260,14 +256,15 @@ class DirentListItem extends React.Component {
onUnlockItem = () => { onUnlockItem = () => {
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.props.dirent);
seafileAPI.unlockfile(repoID, filePath).then(() => { seafileAPI.unlockfile(repoID, filePath).then(() => {
this.props.updateViewList(this.props.filePath); this.props.updateDirent(this.props.dirent, "is_locked", false);
this.props.updateDirent(this.props.dirent, "locked_by_me", false);
}); });
this.onItemMenuHide(); this.onItemMenuHide();
} }
onNewDraft = () => { onNewDraft = () => {
let filePath = this.getDirentPath(this.props.dirent); let filePath = this.getDirentPath(this.props.dirent);
seafileAPI.createDraft(repoID,filePath).then(res => { seafileAPI.createDraft(repoID, filePath).then(res => {
let draft_file_Path = res.data.draft_file_path; let draft_file_Path = res.data.draft_file_path;
let draftId = res.data.id; let draftId = res.data.id;
let url = URLDecorator.getUrl({type: 'draft_view', repoID: repoID, filePath: draft_file_Path, draftId: draftId}); let url = URLDecorator.getUrl({type: 'draft_view', repoID: repoID, filePath: draft_file_Path, draftId: draftId});
@@ -303,26 +300,10 @@ class DirentListItem extends React.Component {
} }
getDirentPath = (dirent) => { getDirentPath = (dirent) => {
let path = this.props.filePath; let path = this.props.path;
return path === '/' ? path + dirent.name : path + '/' + dirent.name; return path === '/' ? path + dirent.name : path + '/' + dirent.name;
} }
getFileTag = () => {
if (this.props.dirent.type === 'file' && this.props.dirent.file_tags!== undefined) {
let FileTgas = this.props.dirent.file_tags;
let fileTagList = [];
FileTgas.forEach(item => {
let fileTag = new FileTag(item)
fileTagList.push(fileTag)
});
this.setState({fileTagList: fileTagList});
}
}
componentWillReceiveProps() {
this.getFileTag();
}
render() { render() {
let { dirent } = this.props; let { dirent } = this.props;
return ( return (
@@ -348,7 +329,7 @@ class DirentListItem extends React.Component {
</td> </td>
<td> <td>
<div className="dirent-item tag-list tag-list-stacked "> <div className="dirent-item tag-list tag-list-stacked ">
{ dirent.type !== 'dir' && this.state.fileTagList.map((fileTag) => { { dirent.type !== 'dir' && dirent.file_tags.map((fileTag) => {
return ( return (
<span className={`file-tag bg-${fileTag.color}`} key={fileTag.id} title={fileTag.name}></span> <span className={`file-tag bg-${fileTag.color}`} key={fileTag.id} title={fileTag.name}></span>
); );

View File

@@ -10,7 +10,7 @@ import MoveDirentDialog from '../dialog/move-dirent-dialog';
import CopyDirentDialog from '../dialog/copy-dirent-dialog'; import CopyDirentDialog from '../dialog/copy-dirent-dialog';
const propTypes = { const propTypes = {
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
direntList: PropTypes.array.isRequired, direntList: PropTypes.array.isRequired,
onItemDelete: PropTypes.func.isRequired, onItemDelete: PropTypes.func.isRequired,
onItemRename: PropTypes.func.isRequired, onItemRename: PropTypes.func.isRequired,
@@ -18,7 +18,7 @@ const propTypes = {
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onItemDetails: PropTypes.func.isRequired, onItemDetails: PropTypes.func.isRequired,
updateViewList: PropTypes.func.isRequired, updateDirent: PropTypes.func.isRequired,
isDirentListLoading: PropTypes.bool.isRequired, isDirentListLoading: PropTypes.bool.isRequired,
isRepoOwner: PropTypes.bool, isRepoOwner: PropTypes.bool,
currentRepo: PropTypes.object, currentRepo: PropTypes.object,
@@ -90,7 +90,7 @@ class DirentListView extends React.Component {
onItemDownload = (dirent, direntPath) => { onItemDownload = (dirent, direntPath) => {
if (dirent.type === 'dir') { if (dirent.type === 'dir') {
this.setState({isProgressDialogShow: true, progress: 0}); this.setState({isProgressDialogShow: true, progress: 0});
editorUtilities.zipDownload(this.props.filePath, dirent.name).then(res => { editorUtilities.zipDownload(this.props.path, dirent.name).then(res => {
this.zip_token = res.data['zip_token']; this.zip_token = res.data['zip_token'];
this.addDownloadAnimation(); this.addDownloadAnimation();
this.interval = setInterval(this.addDownloadAnimation, 1000); this.interval = setInterval(this.addDownloadAnimation, 1000);
@@ -161,14 +161,13 @@ class DirentListView extends React.Component {
<DirentListItem <DirentListItem
key={index} key={index}
dirent={dirent} dirent={dirent}
filePath={this.props.filePath} path={this.props.path}
currentRepo={this.props.currentRepo} currentRepo={this.props.currentRepo}
isRepoOwner={this.props.isRepoOwner} isRepoOwner={this.props.isRepoOwner}
onItemClick={this.props.onItemClick} onItemClick={this.props.onItemClick}
onRenameMenuItemClick={this.onRenameMenuItemClick} onRenameMenuItemClick={this.onRenameMenuItemClick}
onItemDelete={this.props.onItemDelete} onItemDelete={this.props.onItemDelete}
onItemRename={this.props.onItemRename} onItemRename={this.props.onItemRename}
updateViewList={this.props.updateViewList}
isItemFreezed={this.state.isItemFreezed} isItemFreezed={this.state.isItemFreezed}
onFreezedItem={this.onFreezedItem} onFreezedItem={this.onFreezedItem}
onUnfreezedItem={this.onUnfreezedItem} onUnfreezedItem={this.onUnfreezedItem}
@@ -176,6 +175,7 @@ class DirentListView extends React.Component {
onDirentItemMove={this.onDirentItemMove} onDirentItemMove={this.onDirentItemMove}
onDirentItemCopy={this.onDirentItemCopy} onDirentItemCopy={this.onDirentItemCopy}
onItemDetails={this.onItemDetails} onItemDetails={this.onItemDetails}
updateDirent={this.props.updateDirent}
/> />
); );
}) })

View File

@@ -23,7 +23,7 @@ const propTypes = {
minFileSizeErrorCallback: PropTypes.func, minFileSizeErrorCallback: PropTypes.func,
fileTypeErrorCallback: PropTypes.func, fileTypeErrorCallback: PropTypes.func,
dragAndDrop: PropTypes.bool.isRequired, dragAndDrop: PropTypes.bool.isRequired,
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
onFileSuccess: PropTypes.func.isRequired, onFileSuccess: PropTypes.func.isRequired,
}; };
@@ -112,7 +112,7 @@ class FileUploader extends React.Component {
return; // is upload a folder; return; // is upload a folder;
} }
if (enableResumableFileUpload) { if (enableResumableFileUpload) {
seafileAPI.getFileUploadedBytes(repoID, this.props.filePath, file.fileName).then(res => { seafileAPI.getFileUploadedBytes(repoID, this.props.path, file.fileName).then(res => {
let uploadedBytes = res.data.uploadedBytes; let uploadedBytes = res.data.uploadedBytes;
let offset = Math.floor(uploadedBytes / (1024 * 1024)); let offset = Math.floor(uploadedBytes / (1024 * 1024));
file.markChunksCompleted(offset); file.markChunksCompleted(offset);
@@ -122,7 +122,7 @@ class FileUploader extends React.Component {
onFileAdded = (resumableFile, files) => { onFileAdded = (resumableFile, files) => {
//get parent_dir、relative_path //get parent_dir、relative_path
let filePath = this.props.filePath === '/' ? '/' : this.props.filePath + '/'; let path = this.props.path === '/' ? '/' : this.props.path + '/';
let fileName = resumableFile.fileName; let fileName = resumableFile.fileName;
let relativePath = resumableFile.relativePath; let relativePath = resumableFile.relativePath;
let isFile = fileName === relativePath; let isFile = fileName === relativePath;
@@ -131,12 +131,12 @@ class FileUploader extends React.Component {
resumableFile.formData = {}; resumableFile.formData = {};
if (isFile) { if (isFile) {
resumableFile.formData = { resumableFile.formData = {
parent_dir: filePath, parent_dir: path,
}; };
} else { } else {
let relative_path = relativePath.slice(0, relativePath.lastIndexOf('/') + 1); let relative_path = relativePath.slice(0, relativePath.lastIndexOf('/') + 1);
resumableFile.formData = { resumableFile.formData = {
parent_dir: filePath, parent_dir: path,
relative_path: relative_path relative_path: relative_path
}; };
} }
@@ -277,7 +277,7 @@ class FileUploader extends React.Component {
onFileUpload = () => { onFileUpload = () => {
this.uploadInput.removeAttribute('webkitdirectory'); this.uploadInput.removeAttribute('webkitdirectory');
this.uploadInput.click(); this.uploadInput.click();
seafileAPI.getUploadLink(repoID, this.props.filePath).then(res => { seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
this.resumable.opts.target = res.data; this.resumable.opts.target = res.data;
}); });
} }
@@ -285,14 +285,14 @@ class FileUploader extends React.Component {
onFolderUpload = () => { onFolderUpload = () => {
this.uploadInput.setAttribute('webkitdirectory', 'webkitdirectory'); this.uploadInput.setAttribute('webkitdirectory', 'webkitdirectory');
this.uploadInput.click(); this.uploadInput.click();
seafileAPI.getUploadLink(repoID, this.props.filePath).then(res => { seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
this.resumable.opts.target = res.data; this.resumable.opts.target = res.data;
}); });
} }
onDragStart = () => { onDragStart = () => {
this.uploadInput.setAttribute('webkitdirectory', 'webkitdirectory'); this.uploadInput.setAttribute('webkitdirectory', 'webkitdirectory');
seafileAPI.getUploadLink(repoID, this.props.filePath).then(res => { seafileAPI.getUploadLink(repoID, this.props.path).then(res => {
this.resumable.opts.target = res.data; this.resumable.opts.target = res.data;
}); });
} }

View File

@@ -7,7 +7,7 @@ import CreateTagDialog from '../dialog/create-tag-dialog';
import UpdateTagDialog from '../dialog/update-tag-dialog'; import UpdateTagDialog from '../dialog/update-tag-dialog';
const propTypes = { const propTypes = {
filePath: PropTypes.string.isRequired path: PropTypes.string.isRequired
}; };
class PathToolbar extends React.Component { class PathToolbar extends React.Component {
@@ -42,15 +42,13 @@ class PathToolbar extends React.Component {
} }
isMarkdownFile(filePath) { isMarkdownFile(filePath) {
let lastIndex = filePath.lastIndexOf('/'); let name = Utils.getFileName(filePath);
let name = filePath.slice(lastIndex + 1);
return name.indexOf('.md') > -1 ? true : false; return name.indexOf('.md') > -1 ? true : false;
} }
render() { render() {
let isFile = this.isMarkdownFile(this.props.filePath); let isFile = this.isMarkdownFile(this.props.path);
let index = this.props.filePath.lastIndexOf('/'); let name = Utils.getFileName(this.props.path);
let name = this.props.filePath.slice(index + 1);
let trashUrl = siteRoot + 'repo/recycle/' + repoID + '/?referer=' + encodeURIComponent(location.href); let trashUrl = siteRoot + 'repo/recycle/' + repoID + '/?referer=' + encodeURIComponent(location.href);
let historyUrl = siteRoot + 'repo/history/' + repoID + '/?referer=' + encodeURIComponent(location.href); let historyUrl = siteRoot + 'repo/history/' + repoID + '/?referer=' + encodeURIComponent(location.href);
if ( (name === slug || name === '') && !isFile && permission) { if ( (name === slug || name === '') && !isFile && permission) {
@@ -84,14 +82,14 @@ class PathToolbar extends React.Component {
} }
</Fragment> </Fragment>
); );
} else if ( !isFile && permission) { } else if (!isFile && permission) {
return ( return (
<ul className="path-toolbar"> <ul className="path-toolbar">
<li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li> <li className="toolbar-item"><a className="op-link sf2-icon-trash" href={trashUrl} title={gettext('Trash')} aria-label={gettext('Trash')}></a></li>
</ul> </ul>
); );
} else if (permission) { } else if (permission) {
historyUrl = siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + Utils.encodePath(this.props.filePath) + '&referer=' + encodeURIComponent(location.href); historyUrl = siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + Utils.encodePath(this.props.path) + '&referer=' + encodeURIComponent(location.href);
return ( return (
<ul className="path-toolbar"> <ul className="path-toolbar">
<li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li> <li className="toolbar-item"><a className="op-link sf2-icon-history" href={historyUrl} title={gettext('History')} aria-label={gettext('History')}></a></li>

View File

@@ -5,7 +5,7 @@ import { permission } from '../../utils/constants';
const propTypes = { const propTypes = {
isNodeItemFrezee: PropTypes.bool.isRequired, isNodeItemFrezee: PropTypes.bool.isRequired,
currentFilePath: PropTypes.string.isRequired, currentPath: PropTypes.string.isRequired,
paddingLeft: PropTypes.number.isRequired, paddingLeft: PropTypes.number.isRequired,
node: PropTypes.object.isRequired, node: PropTypes.object.isRequired,
treeView: PropTypes.object.isRequired, treeView: PropTypes.object.isRequired,
@@ -31,10 +31,10 @@ class TreeNodeView extends React.Component {
}; };
} }
onClick = (e) => { onClick = () => {
// e.nativeEvent.stopImmediatePropagation(); // e.nativeEvent.stopImmediatePropagation();
let { node } = this.props; let { node } = this.props;
this.props.treeView.onNodeClick(e, node); this.props.treeView.onNodeClick(node);
} }
onMouseEnter = () => { onMouseEnter = () => {
@@ -55,7 +55,7 @@ class TreeNodeView extends React.Component {
handleCollapse = (e) => { handleCollapse = (e) => {
e.stopPropagation(); e.stopPropagation();
this.props.onDirCollapse(e, this.props.node); this.props.onDirCollapse(this.props.node);
} }
onDragStart = (e) => { onDragStart = (e) => {
@@ -121,7 +121,7 @@ class TreeNodeView extends React.Component {
paddingLeft={this.props.paddingLeft} paddingLeft={this.props.paddingLeft}
treeView={this.props.treeView} treeView={this.props.treeView}
isNodeItemFrezee={this.props.isNodeItemFrezee} isNodeItemFrezee={this.props.isNodeItemFrezee}
currentFilePath={this.props.currentFilePath} currentPath={this.props.currentPath}
onDirCollapse={this.props.onDirCollapse} onDirCollapse={this.props.onDirCollapse}
/> />
); );

View File

@@ -6,7 +6,7 @@ import editorUtilities from '../../utils/editor-utilties';
const propTypes = { const propTypes = {
permission: PropTypes.string, permission: PropTypes.string,
isNodeItemFrezee: PropTypes.bool.isRequired, isNodeItemFrezee: PropTypes.bool.isRequired,
currentFilePath: PropTypes.string.isRequired, currentPath: PropTypes.string.isRequired,
treeData: PropTypes.object.isRequired, treeData: PropTypes.object.isRequired,
onShowContextMenu: PropTypes.func.isRequired, onShowContextMenu: PropTypes.func.isRequired,
onNodeClick: PropTypes.func.isRequired, onNodeClick: PropTypes.func.isRequired,
@@ -22,18 +22,14 @@ class TreeView extends React.PureComponent {
*/ */
} }
toggleCollapse = (e, node) => {
this.props.onDirCollapse(e, node);
}
onDragStart = (e, node) => { onDragStart = (e, node) => {
const url = editorUtilities.getFileURL(node); const url = editorUtilities.getFileURL(node);
e.dataTransfer.setData('text/uri-list', url); e.dataTransfer.setData('text/uri-list', url);
e.dataTransfer.setData('text/plain', url); e.dataTransfer.setData('text/plain', url);
} }
onNodeClick = (e, node) => { onNodeClick = (node) => {
this.props.onNodeClick(e, node); this.props.onNodeClick(node);
} }
onShowContextMenu = (e, node) => { onShowContextMenu = (e, node) => {
@@ -53,7 +49,7 @@ class TreeView extends React.PureComponent {
node={this.props.treeData.root} node={this.props.treeData.root}
isNodeItemFrezee={this.props.isNodeItemFrezee} isNodeItemFrezee={this.props.isNodeItemFrezee}
permission={this.props.permission} permission={this.props.permission}
currentFilePath={this.props.currentFilePath} currentPath={this.props.currentPath}
onShowContextMenu={this.props.onShowContextMenu} onShowContextMenu={this.props.onShowContextMenu}
onDirCollapse={this.props.onDirCollapse} onDirCollapse={this.props.onDirCollapse}
/> />

View File

@@ -62,6 +62,25 @@ class Tree {
return false; return false;
} }
deleteNodeByPath(path) {
let node = this.getNodeByPath(path);
this.deleteNode(node);
}
moveNode(node, moveToNode, isDestroy) {
let moveNode = node.clone();
this.addNodeToParent(moveNode, moveToNode);
if (isDestroy) {
this.deleteNode(node);
}
}
moveNodeByPath(path, moveToPath, isDestroy) {
let node = this.getNodeByPath(path);
let moveToNode = this.getNodeByPath(moveToPath);
this.moveNode(node, moveToNode, isDestroy);
}
updateNodeParam(node, param, newValue) { updateNodeParam(node, param, newValue) {
let treeNode = this.findNodeFromTree(node); let treeNode = this.findNodeFromTree(node);
if (treeNode && treeNode[param]) { if (treeNode && treeNode[param]) {
@@ -71,6 +90,11 @@ class Tree {
return false; return false;
} }
updateNodeParamByPath(path, param, newValue) {
let node = this.getNodeByPath(path);
this.updateNodeParam(node, param, newValue);
}
findNode(node) { findNode(node) {
return this.findNodeFromTree(node); return this.findNodeFromTree(node);
} }

View File

@@ -1,5 +1,6 @@
import moment from 'moment'; import moment from 'moment';
import { Utils } from '../utils/utils'; import { Utils } from '../utils/utils';
import FileTag from './file-tag';
class Dirent { class Dirent {
constructor(json) { constructor(json) {
@@ -18,8 +19,18 @@ class Dirent {
this.modifier_name = json.modifier_name; this.modifier_name = json.modifier_name;
this.modifier_email = json.modifier_email; this.modifier_email = json.modifier_email;
this.modifier_contact_email = json.modifier_contact_email; this.modifier_contact_email = json.modifier_contact_email;
this.file_tags = json.file_tags; let file_tags = [];
if (json.file_tags) {
file_tags = json.file_tags.map(item => {
return new FileTag(item);
})
} }
this.file_tags = file_tags;
}
}
isDir() {
return this.type !== 'file';
} }
} }

View File

@@ -4,7 +4,6 @@ import { gettext, repoID, serviceUrl, slug, siteRoot } from '../../utils/constan
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import Repo from '../../models/repo'; import Repo from '../../models/repo';
import Dirent from '../../models/dirent';
import CommonToolbar from '../../components/toolbar/common-toolbar'; import CommonToolbar from '../../components/toolbar/common-toolbar';
import PathToolbar from '../../components/toolbar/path-toolbar'; import PathToolbar from '../../components/toolbar/path-toolbar';
import MarkdownViewer from '../../components/markdown-viewer'; import MarkdownViewer from '../../components/markdown-viewer';
@@ -19,22 +18,27 @@ const propTypes = {
lastModified: PropTypes.string, lastModified: PropTypes.string,
latestContributor: PropTypes.string, latestContributor: PropTypes.string,
permission: PropTypes.string, permission: PropTypes.string,
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
// whether the file or dir corresponding to the path exist
pathExist: PropTypes.bool.isRequired,
isFileLoading: PropTypes.bool.isRequired, isFileLoading: PropTypes.bool.isRequired,
isViewFileState: PropTypes.bool.isRequired, isViewFile: PropTypes.bool.isRequired,
changedNode: PropTypes.object, isDirentListLoading: PropTypes.bool.isRequired,
onMenuClick: PropTypes.func.isRequired, direntList: PropTypes.array.isRequired,
updateDirent: PropTypes.func.isRequired,
onSideNavMenuClick: PropTypes.func.isRequired,
onSearchedClick: PropTypes.func.isRequired, onSearchedClick: PropTypes.func.isRequired,
onMainNavBarClick: PropTypes.func.isRequired, onMainNavBarClick: PropTypes.func.isRequired,
onLinkClick: PropTypes.func.isRequired, onLinkClick: PropTypes.func.isRequired,
onMainItemClick: PropTypes.func.isRequired, onItemClick: PropTypes.func.isRequired,
onMainItemDelete: PropTypes.func.isRequired, onItemDelete: PropTypes.func.isRequired,
onMainItemRename: PropTypes.func.isRequired, onItemRename: PropTypes.func.isRequired,
onMainItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onMainItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onMainAddFile: PropTypes.func.isRequired, onAddFile: PropTypes.func.isRequired,
onMainAddFolder: PropTypes.func.isRequired, onAddFolder: PropTypes.func.isRequired,
switchViewMode: PropTypes.func.isRequired, switchViewMode: PropTypes.func.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
}; };
class MainPanel extends Component { class MainPanel extends Component {
@@ -43,7 +47,6 @@ class MainPanel extends Component {
super(props); super(props);
this.state = { this.state = {
isWikiMode: true, isWikiMode: true,
direntList: [],
newMenuShow: false, newMenuShow: false,
uploadMenuShow: false, uploadMenuShow: false,
showFileDialog: false, showFileDialog: false,
@@ -52,7 +55,6 @@ class MainPanel extends Component {
isDirentDetailShow: false, isDirentDetailShow: false,
currentDirent: null, currentDirent: null,
currentFilePath: '', currentFilePath: '',
isDirentListLoading: true,
currentRepo: null, currentRepo: null,
isRepoOwner: false, isRepoOwner: false,
}; };
@@ -74,40 +76,15 @@ class MainPanel extends Component {
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
let node = nextProps.changedNode; // if (nextProps.path !== this.props.path) {
if (node && node.isDir()) { // this.setState({isDirentDetailShow: false});
let path = node.path; // }
this.updateViewList(path);
}
} }
componentWillUnmount() { componentWillUnmount() {
document.removeEventListener('click', this.hideOperationMenu); document.removeEventListener('click', this.hideOperationMenu);
} }
updateViewList = (filePath) => {
this.setState({isDirentListLoading: true});
seafileAPI.listDir(repoID, filePath).then(res => {
let direntList = [];
res.data.forEach(item => {
let dirent = new Dirent(item);
direntList.push(dirent);
});
this.setState({
direntList: direntList,
isDirentListLoading: false,
});
});
}
onMenuClick = () => {
this.props.onMenuClick();
}
onMainNavBarClick = (e) => {
this.props.onMainNavBarClick(e.target.dataset.path);
}
switchViewMode = (e) => { switchViewMode = (e) => {
e.preventDefault(); e.preventDefault();
if (e.target.id === 'wiki') { if (e.target.id === 'wiki') {
@@ -117,9 +94,17 @@ class MainPanel extends Component {
this.props.switchViewMode(e.target.id); this.props.switchViewMode(e.target.id);
} }
onSideNavMenuClick = () => {
this.props.onSideNavMenuClick();
}
onMainNavBarClick = (e) => {
this.props.onMainNavBarClick(e.target.dataset.path);
}
onEditClick = (e) => { onEditClick = (e) => {
e.preventDefault(); e.preventDefault();
window.location.href= serviceUrl + '/lib/' + repoID + '/file' + this.props.filePath + '?mode=edit'; window.location.href= serviceUrl + '/lib/' + repoID + '/file' + this.props.path + '?mode=edit';
} }
onUploadClick = (e) => { onUploadClick = (e) => {
@@ -184,14 +169,14 @@ class MainPanel extends Component {
this.setState({showFileDialog: !this.state.showFileDialog}); this.setState({showFileDialog: !this.state.showFileDialog});
} }
onMainAddFile = (filePath, isDraft) => { onAddFile = (filePath, isDraft) => {
this.setState({showFileDialog: !this.state.showFileDialog}); this.setState({showFileDialog: !this.state.showFileDialog});
this.props.onMainAddFile(filePath, isDraft); this.props.onAddFile(filePath, isDraft);
} }
onMainAddFolder = (dirPath) => { onAddFolder = (dirPath) => {
this.setState({showFolderDialog: !this.state.showFolderDialog}); this.setState({showFolderDialog: !this.state.showFolderDialog});
this.props.onMainAddFolder(dirPath); this.props.onAddFolder(dirPath);
} }
onItemDetails = (dirent, direntPath) => { onItemDetails = (dirent, direntPath) => {
@@ -206,8 +191,9 @@ class MainPanel extends Component {
this.setState({isDirentDetailShow: false}); this.setState({isDirentDetailShow: false});
} }
onFileTagChanged = () => { onFileTagChanged = (dirent, direntPath) => {
this.updateViewList(this.props.filePath); //todos;
this.props.onFileTagChanged(dirent, direntPath);
} }
uploadFile = (e) => { uploadFile = (e) => {
@@ -225,13 +211,15 @@ class MainPanel extends Component {
} }
render() { render() {
let filePathList = this.props.filePath.split('/'); let path = this.props.path;
path = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
let pathList = path.split('/');
let nodePath = ''; let nodePath = '';
let pathElem = filePathList.map((item, index) => { let pathElem = pathList.map((item, index) => {
if (item === '') { if (item === '') {
return; return;
} }
if (index === (filePathList.length - 1)) { if (index === (pathList.length - 1)) {
return ( return (
<span key={index}><span className="path-split">/</span>{item}</span> <span key={index}><span className="path-split">/</span>{item}</span>
); );
@@ -255,7 +243,7 @@ class MainPanel extends Component {
<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">
<div className="cur-view-toolbar border-left-show"> <div className="cur-view-toolbar border-left-show">
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title={gettext('Side Nav Menu')} onClick={this.onMenuClick}></span> <span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title={gettext('Side Nav Menu')} onClick={this.onSideNavMenuClick}></span>
<div className="file-operation"> <div className="file-operation">
<div className="operation"> <div className="operation">
{ {
@@ -263,7 +251,7 @@ class MainPanel extends Component {
<button className="btn btn-secondary operation-item" title={gettext('Edit File')} onClick={this.onEditClick}>{gettext('Edit')}</button> <button className="btn btn-secondary operation-item" title={gettext('Edit File')} onClick={this.onEditClick}>{gettext('Edit')}</button>
} }
{ {
!this.props.isViewFileState && !this.props.isViewFile &&
<Fragment> <Fragment>
{ {
Utils.isSupportUploadFolder() ? Utils.isSupportUploadFolder() ?
@@ -306,16 +294,19 @@ class MainPanel extends Component {
<div className="path-containter"> <div className="path-containter">
<a href={siteRoot + '#common/'} className="normal">{gettext('Libraries')}</a> <a href={siteRoot + '#common/'} className="normal">{gettext('Libraries')}</a>
<span className="path-split">/</span> <span className="path-split">/</span>
{this.props.filePath === '/' ? {this.props.path === '/' ?
<span>{slug}</span> : <span>{slug}</span> :
<a className="path-link" data-path="/" onClick={this.onMainNavBarClick}>{slug}</a> <a className="path-link" data-path="/" onClick={this.onMainNavBarClick}>{slug}</a>
} }
{pathElem} {pathElem}
</div> </div>
<PathToolbar filePath={this.props.filePath}/> <PathToolbar path={this.props.path}/>
</div> </div>
<div className="cur-view-content"> <div className="cur-view-content">
{ this.props.isViewFileState ? { !this.props.pathExist ?
<div className="message empty-tip err-message"><h2>{gettext('Folder does not exist.')}</h2></div> :
<Fragment>
{ this.props.isViewFile ?
<MarkdownViewer <MarkdownViewer
markdownContent={this.props.content} markdownContent={this.props.content}
latestContributor={this.props.latestContributor} latestContributor={this.props.latestContributor}
@@ -325,28 +316,30 @@ class MainPanel extends Component {
/> : /> :
<Fragment> <Fragment>
<DirentListView <DirentListView
direntList={this.state.direntList} direntList={this.props.direntList}
filePath={this.props.filePath} path={this.props.path}
onItemClick={this.props.onMainItemClick} onItemClick={this.props.onItemClick}
onItemDelete={this.props.onMainItemDelete} onItemDelete={this.props.onItemDelete}
onItemRename={this.props.onMainItemRename} onItemRename={this.props.onItemRename}
onItemMove={this.props.onMainItemMove} onItemMove={this.props.onItemMove}
onItemCopy={this.props.onMainItemCopy} onItemCopy={this.props.onItemCopy}
onItemDetails={this.onItemDetails} onItemDetails={this.onItemDetails}
updateViewList={this.updateViewList} isDirentListLoading={this.props.isDirentListLoading}
isDirentListLoading={this.state.isDirentListLoading} updateDirent={this.props.updateDirent}
currentRepo={this.state.currentRepo} currentRepo={this.state.currentRepo}
isRepoOwner={this.state.isRepoOwner} isRepoOwner={this.state.isRepoOwner}
/> />
<FileUploader <FileUploader
ref={uploader => this.uploader = uploader} ref={uploader => this.uploader = uploader}
dragAndDrop={true} dragAndDrop={true}
filePath={this.props.filePath} path={this.props.path}
onFileSuccess={this.onFileSuccess} onFileSuccess={this.onFileSuccess}
direntList={this.state.direntList} direntList={this.props.direntList}
/> />
</Fragment> </Fragment>
} }
</Fragment>
}
</div> </div>
</div> </div>
{ this.state.isDirentDetailShow && { this.state.isDirentDetailShow &&
@@ -363,16 +356,16 @@ class MainPanel extends Component {
{this.state.showFileDialog && {this.state.showFileDialog &&
<CreateFile <CreateFile
fileType={this.state.createFileType} fileType={this.state.createFileType}
parentPath={this.props.filePath} parentPath={this.props.path}
addFileCancel={this.addFileCancel} addFileCancel={this.addFileCancel}
onAddFile={this.onMainAddFile} onAddFile={this.onAddFile}
/> />
} }
{this.state.showFolderDialog && {this.state.showFolderDialog &&
<CreateFolder <CreateFolder
parentPath={this.props.filePath} parentPath={this.props.path}
addFolderCancel={this.addFolderCancel} addFolderCancel={this.addFolderCancel}
onAddFolder={this.onMainAddFolder} onAddFolder={this.onAddFolder}
/> />
} }
</div> </div>

View File

@@ -10,9 +10,9 @@ import CreateFolder from '../../components/dialog/create-folder-dialog';
import CreateFile from '../../components/dialog/create-file-dialog'; import CreateFile from '../../components/dialog/create-file-dialog';
const propTypes = { const propTypes = {
changedNode: PropTypes.object, currentNode: PropTypes.object,
treeData: PropTypes.object.isRequired, treeData: PropTypes.object.isRequired,
currentFilePath: PropTypes.string.isRequired, currentPath: PropTypes.string.isRequired,
closeSideBar: PropTypes.bool.isRequired, closeSideBar: PropTypes.bool.isRequired,
onCloseSide: PropTypes.func.isRequired, onCloseSide: PropTypes.func.isRequired,
onDirCollapse: PropTypes.func.isRequired, onDirCollapse: PropTypes.func.isRequired,
@@ -62,9 +62,9 @@ class SidePanel extends Component {
}); });
} }
onNodeClick = (e, node) => { onNodeClick = (node) => {
this.setState({currentNode: node}); this.setState({currentNode: node});
this.props.onNodeClick(e, node); this.props.onNodeClick(node);
} }
onShowContextMenu = (e, node) => { onShowContextMenu = (e, node) => {
@@ -149,9 +149,7 @@ class SidePanel extends Component {
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
this.setState({ this.setState({currentNode: nextProps.currentNode});
currentNode: nextProps.changedNode
});
} }
componentWillUnmount() { componentWillUnmount() {
@@ -200,7 +198,7 @@ class SidePanel extends Component {
<div className="wiki-pages-container"> <div className="wiki-pages-container">
{this.props.treeData && {this.props.treeData &&
<TreeView <TreeView
currentFilePath={this.props.currentFilePath} currentPath={this.props.currentPath}
treeData={this.props.treeData} treeData={this.props.treeData}
currentNode={this.state.currentNode} currentNode={this.state.currentNode}
isNodeItemFrezee={this.state.isNodeItemFrezee} isNodeItemFrezee={this.state.isNodeItemFrezee}

View File

@@ -2,14 +2,16 @@ import React, { Component } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import moment from 'moment'; import moment from 'moment';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import { gettext, repoID, serviceUrl, initialFilePath } from './utils/constants'; import { gettext, repoID, serviceUrl, initialPath, isDir } from './utils/constants';
import { seafileAPI } from './utils/seafile-api'; import { seafileAPI } from './utils/seafile-api';
import editorUtilities from './utils/editor-utilties'; import { Utils } from './utils/utils';
import SidePanel from './pages/repo-wiki-mode/side-panel'; import SidePanel from './pages/repo-wiki-mode/side-panel';
import MainPanel from './pages/repo-wiki-mode/main-panel'; import MainPanel from './pages/repo-wiki-mode/main-panel';
import Node from './components/tree-view/node'; import Node from './components/tree-view/node';
import Tree from './components/tree-view/tree'; import Tree from './components/tree-view/tree';
import Toast from './components/toast'; import Toast from './components/toast';
import Dirent from './models/dirent';
import FileTag from './models/file-tag';
import 'seafile-ui'; import 'seafile-ui';
import './assets/css/fa-solid.css'; import './assets/css/fa-solid.css';
import './assets/css/fa-regular.css'; import './assets/css/fa-regular.css';
@@ -24,81 +26,151 @@ class Wiki extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
content: '', path: '',
tree_data: new Tree(), pathExist: true,
treeData: new Tree(),
closeSideBar: false, closeSideBar: false,
filePath: '', currentNode: null,
latestContributor: '', isDirentListLoading: true,
isViewFile: false,
direntList: [],
isFileLoading: true,
content: '',
lastModified: '', lastModified: '',
latestContributor: '',
permission: '', permission: '',
isFileLoading: false,
changedNode: null,
isViewFileState: true
}; };
window.onpopstate = this.onpopstate; window.onpopstate = this.onpopstate;
} }
componentDidMount() { componentDidMount() {
this.initWikiData(initialFilePath); if (isDir === 'None') {
this.setState({pathExist: false});
} else if (isDir === 'True') {
this.showDir(initialPath);
} else if (isDir === 'False') {
this.showFile(initialPath);
} }
initWikiData(filePath){ this.loadSidePanel(initialPath);
this.setState({isFileLoading: true}); }
editorUtilities.listRepoDir().then((files) => {
// construct the tree object
var treeData = new Tree();
treeData.parseListToTree(files);
let node = treeData.getNodeByPath(filePath);
treeData.expandNode(node); deleteItemAjaxCallback(path, isDir) {
if (node.isDir()) { let node = this.state.treeData.getNodeByPath(path);
this.exitViewFileState(treeData, node); this.deleteTreeNode(node);
this.setState({isFileLoading: false}); this.deleteDirent(path);
}
deleteItem(path, isDir) {
if (isDir) {
seafileAPI.deleteDir(repoID, path).then(() => {
this.deleteItemAjaxCallback(path, isDir);
}).catch(() => {
//todos;
});
} else { } else {
this.setState({tree_data: treeData}); seafileAPI.deleteFile(repoID, path).then(() => {
this.initMainPanelData(filePath); this.deleteItemAjaxCallback(path, isDir);
} }).catch(() => {
}, () => { //todos;
/* eslint-disable */
console.log('failed to load files');
/* eslint-enable */
this.setState({
isLoadFailed: true
}); });
}
}
renameItemAjaxCallback(path, isDir, newName) {
let node = this.state.treeData.getNodeByPath(path);
this.renameTreeNode(node, newName);
this.renameDirent(path, newName);
}
renameItem = (path, isDir, newName) => {
//validate task
if (isDir) {
seafileAPI.renameDir(repoID, path, newName).then(() => {
this.renameItemAjaxCallback(path, isDir, newName);
}).catch(() => {
//todos;
});
} else {
seafileAPI.renameFile(repoID, path, newName).then(() => {
this.renameItemAjaxCallback(path, isDir, newName);
}).catch(() => {
//todos;
});
}
}
onAddFile = (filePath, isDraft) => {
//todo validate;
seafileAPI.createFile(repoID, filePath, isDraft).then(res => {
let name = Utils.getFileName(filePath);
let parentPath = Utils.getDirName(filePath);
this.addNodeToTree(name, parentPath, 'file');
if (parentPath === this.state.path && !this.state.isViewFile) {
this.addDirent(name, 'file');
}
}).catch(() => {
//todo;
}); });
} }
initMainPanelData(filePath) { onAddFolder = (dirPath) => {
this.setState({isFileLoading: true}); //validate task
seafileAPI.createDir(repoID, dirPath).then(() => {
let name = Utils.getFileName(dirPath);
let parentPath = Utils.getDirName(dirPath);
seafileAPI.getFileInfo(repoID, filePath).then((res) => { this.addNodeToTree(name, parentPath, 'dir');
let { mtime, permission, last_modifier_name } = res.data; if (parentPath === this.state.path && !this.state.isViewFile) {
this.addDirent(name, 'dir');
}
}).catch(() => {
//return error message
});
}
this.setState({ onMoveItem = (repo, direntPath, moveToDirentPath) => {
latestContributor: last_modifier_name, //just for view list state
lastModified: moment.unix(mtime).fromNow(), let index = direntPath.lastIndexOf('/');
permission: permission, let dirPath = direntPath.slice(0, index + 1);
filePath: filePath, let dirName = direntPath.slice(index + 1);
}); seafileAPI.moveDir(repoID, repo.repo_id,moveToDirentPath, dirPath, dirName).then(() => {
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => { this.moveTreeNode(direntPath, moveToDirentPath, repo);
const downLoadUrl = res.data; this.moveDirent(direntPath);
seafileAPI.getFileContent(downLoadUrl).then((res) => {
this.setState({
content: res.data,
isFileLoading: false
});
});
});
});
let fileUrl = serviceUrl + '/wiki/lib/' + repoID + filePath; let message = gettext('Successfully moved %(name)s.');
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl); message = message.replace('%(name)s', dirName);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to move %(name)s');
message = message.replace('%(name)s', dirName);
Toast.error(message);
});
}
onCopyItem = (repo, direntPath, copyToDirentPath) => {
//just for view list state
let index = direntPath.lastIndexOf('/');
let dirPath = direntPath.slice(0, index + 1);
let dirName = direntPath.slice(index + 1);
seafileAPI.moveDir(repoID, repo.repo_id, copyToDirentPath, dirPath, dirName).then(() => {
this.copyTreeNode(direntPath, copyToDirentPath, repo);
let message = gettext('Successfully copied %(name)s.');
message = message.replace('%(name)s', dirName);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to copy %(name)s');
message = message.replace('%(name)s', dirName);
Toast.error(message);
});
} }
switchViewMode = (mode) => { switchViewMode = (mode) => {
let dirPath; let dirPath;
let tree = this.state.tree_data; let tree = this.state.treeData;
let node = tree.getNodeByPath(this.state.filePath); let node = tree.getNodeByPath(this.state.filePath);
if (node.isDir()) { if (node.isDir()) {
dirPath = this.state.filePath; dirPath = this.state.filePath;
@@ -112,66 +184,159 @@ class Wiki extends Component {
window.location.href = serviceUrl + '/#common/lib/' + repoID + dirPath; window.location.href = serviceUrl + '/#common/lib/' + repoID + dirPath;
} }
loadSidePanel = (filePath) => {
seafileAPI.listDir(repoID, '/',{recursive: true}).then(items => {
const files = items.data.map(item => {
return {
name: item.name,
type: item.type === 'dir' ? 'dir' : 'file',
isExpanded: item.type === 'dir' ? true : false,
parent_path: item.parent_dir,
last_update_time: item.mtime,
permission: item.permission,
size: item.size
};
});
var treeData = new Tree();
treeData.parseListToTree(files);
let node = treeData.getNodeByPath(filePath);
if (node) {
treeData.expandNode(node);
this.setState({treeData: treeData, currentNode: node});
} else {
this.setState({treeData: treeData});
}
}).catch(() => {
/* eslint-disable */
console.log('failed to load files');
/* eslint-enable */
this.setState({isLoadFailed: true});
});
}
showFile = (filePath) => {
this.setState({
path: filePath,
isViewFile: true
});
this.setState({isFileLoading: true});
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
let { mtime, permission, last_modifier_name } = res.data;
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
seafileAPI.getFileContent(res.data).then((res) => {
this.setState({
content: res.data,
permission: permission,
latestContributor: last_modifier_name,
lastModified: moment.unix(mtime).fromNow(),
isFileLoading: false,
});
});
});
});
let fileUrl = serviceUrl + '/wiki/lib/' + repoID + filePath;
window.history.pushState({url: fileUrl, path: filePath}, filePath, fileUrl);
}
showDir = (path) => {
this.loadDirentList(path);
this.setState({
path: path,
isViewFile: false
});
// update location url
let url = serviceUrl + '/wiki/lib/' + repoID + path;
window.history.pushState({ url: url, path: path}, path, url);
}
loadDirentList = (filePath) => {
this.setState({isDirentListLoading: true});
seafileAPI.listDir(repoID, filePath).then(res => {
let direntList = [];
res.data.forEach(item => {
let dirent = new Dirent(item);
direntList.push(dirent);
});
this.setState({
direntList: direntList,
isDirentListLoading: false,
});
});
}
updateDirent = (dirent, paramKey, paramValue) => {
let newDirentList = this.state.direntList.map(item => {
if (item.name === dirent.name) {
item[paramKey] = paramValue;
}
return item;
});
this.setState({direnList: newDirentList});
}
onLinkClick = (event) => { onLinkClick = (event) => {
const url = event.target.href; const url = event.target.href;
if (this.isInternalMarkdownLink(url)) { if (this.isInternalMarkdownLink(url)) {
let path = this.getPathFromInternalMarkdownLink(url); let path = this.getPathFromInternalMarkdownLink(url);
this.initMainPanelData(path); this.showFile(path);
} else if (this.isInternalDirLink(url)) { } else if (this.isInternalDirLink(url)) {
let path = this.getPathFromInternalDirLink(url); let path = this.getPathFromInternalDirLink(url);
this.initWikiData(path); this.showDir(path);
} else { } else {
window.location.href = url; window.location.href = url;
} }
} }
onpopstate = (event) => { onpopstate = (event) => {
if (event.state && event.state.filePath) { if (event.state && event.state.path) {
let path = event.state.filePath; let path = event.state.path;
if (this.isMarkdownFile(path)) { if (this.isMarkdownFile(path)) {
this.initMainPanelData(path); this.showFile(path);
} else { } else {
let changedNode = this.state.tree_data.getNodeByPath(path); let currentNode = this.state.treeData.getNodeByPath(path);
this.exitViewFileState(this.state.tree_data, changedNode); this.showDir(currentNode.path);
} }
} }
} }
onSearchedClick = (item) => { onSearchedClick = (item) => {
//just for file
let path = item.path; let path = item.path;
if (this.state.currentFilePath !== path) { if (this.state.currentFilePath !== path) {
this.initMainPanelData(path); let tree = this.state.treeData.clone();
let tree = this.state.tree_data.clone();
let node = tree.getNodeByPath(path); let node = tree.getNodeByPath(path);
tree.expandNode(node); tree.expandNode(node);
this.enterViewFileState(tree, node, node.path); this.showFile(node.path);
} }
} }
onMainNavBarClick = (nodePath) => { onMainNavBarClick = (nodePath) => {
let tree = this.state.tree_data.clone(); //just for dir
let tree = this.state.treeData.clone();
let node = tree.getNodeByPath(nodePath); let node = tree.getNodeByPath(nodePath);
tree.expandNode(node); tree.expandNode(node);
this.exitViewFileState(tree, node); this.setState({treeData: tree, currentNode: node});
this.showDir(node.path);
// update location url
let fileUrl = serviceUrl + '/wiki/lib/' + repoID + node.path;
window.history.pushState({urlPath: fileUrl, filePath: node.path},node.path, fileUrl);
} }
onMainItemClick = (direntPath) => { onDirentClick = (direntPath) => {
let tree = this.state.tree_data.clone(); let tree = this.state.treeData.clone();
let node = tree.getNodeByPath(direntPath); let node = tree.getNodeByPath(direntPath);
let parentNode = tree.findNodeParentFromTree(node); let parentNode = tree.findNodeParentFromTree(node);
tree.expandNode(parentNode); tree.expandNode(parentNode);
if (node.isMarkdown()) { if (node.isMarkdown()) {
this.initMainPanelData(node.path); this.setState({treeData: tree}); // tree
this.enterViewFileState(tree, node, node.path); this.showFile(direntPath);
} else if (node.isDir()){ } else if (node.isDir()) {
this.exitViewFileState(tree, node); this.setState({treeData: tree, currentNode: node}); //tree
this.showDir(node.path);
} else { } else {
const w=window.open('about:blank'); const w=window.open('about:blank');
const url = serviceUrl + '/lib/' + repoID + '/file' + node.path; const url = serviceUrl + '/lib/' + repoID + '/file' + node.path;
@@ -179,71 +344,101 @@ class Wiki extends Component {
} }
} }
onMainItemDelete = (direntPath) => { onMainPanelItemRename = (dirent, newName) => {
let node = this.state.tree_data.getNodeByPath(direntPath); let path = Utils.joinPath(this.state.path, dirent.name);
this.onDeleteNode(node); this.renameItem(path, dirent.isDir(), newName);
} }
onMainItemRename = (direntPath, newName) => { onMainPanelItemDelete = (dirent) => {
let node = this.state.tree_data.getNodeByPath(direntPath); let path = Utils.joinPath(this.state.path, dirent.name);
this.onRenameNode(node, newName); this.deleteItem(path, dirent.isDir());
} }
onMainItemMove = (repo, direntPath, moveToDirentPath) => { renameDirent = (direntPath, newName) => {
let index = direntPath.lastIndexOf('/'); let parentPath = Utils.getDirName(direntPath);
let dirPath = direntPath.slice(0, index + 1); let newDirentPath = Utils.joinPath(parentPath, newName);
let dirName = direntPath.slice(index + 1); if (direntPath === this.state.path) {
seafileAPI.moveDir(repoID, repo.repo_id, moveToDirentPath, dirPath, dirName).then(() => { // the renamed item is current viewed item
let tree = this.state.tree_data.clone(); // example: direntPath = /A/B/C, state.path = /A/B/C
let moveNode = tree.getNodeByPath(direntPath); this.setState({ path: newDirentPath });
let moveNodeParent = tree.findNodeParentFromTree(moveNode); } else if (Utils.isChildPath(direntPath, this.state.path)) {
if (repoID === repo.repo_id) { // example: direntPath = /A/B/C/D, state.path = /A/B/C
let moveToNode = tree.getNodeByPath(moveToDirentPath); let oldName = Utils.getFileName(direntPath);
tree.addNodeToParent(moveNode, moveToNode); let direntList = this.state.direntList.map(item => {
if (item.name === oldName) {
item.name = newName;
} }
tree.removeNodeFromParent(moveNode, moveNodeParent); return item;
this.exitViewFileState(tree, moveNodeParent);
let message = gettext('Successfully moved %(name)s.');
message = message.replace('%(name)s', dirName);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to move %(name)s');
message = message.replace('%(name)s', dirName);
Toast.error(message);
}); });
this.setState({ direntList: direntList });
} else if (Utils.isAncestorPath(direntPath, this.state.path)) {
// example: direntPath = /A/B, state.path = /A/B/C
let newPath = Utils.renameAncestorPath(this.state.path, direntPath, newDirentPath);
this.setState({ path: newPath });
}
} }
onMainItemCopy = (repo, direntPath, copyToDirentPath) => { deleteDirent(direntPath) {
let index = direntPath.lastIndexOf('/'); let newPath = '';
let dirPath = direntPath.slice(0, index + 1); if (direntPath === this.state.path) {
let dirName = direntPath.slice(index + 1); // The deleted item is current item
seafileAPI.copyDir(repoID, repo.repo_id, copyToDirentPath, dirPath, dirName).then(() => { let parentPath = Utils.getDirName(direntPath);
if (repoID === repo.repo_id) { this.showDir(parentPath);
let tree = this.state.tree_data.clone(); } else if (Utils.isChildPath(direntPath, this.state.path)) {
let copyNode = tree.getNodeByPath(direntPath); // The deleted item is inside current path
let copyToNode = tree.getNodeByPath(copyToDirentPath); let name = Utils.getFileName(direntPath);
tree.addNodeToParent(copyNode, copyToNode); let direntList = this.state.direntList.filter(item => {
this.exitViewFileState(tree, this.state.changedNode); return item.name !== name;
}
let message = gettext('Successfully copied %(name)s.');
message = message.replace('%(name)s', dirName);
Toast.success(message);
}).catch(() => {
let message = gettext('Failed to copy %(name)s');
message = message.replace('%(name)s', dirName);
Toast.error(message);
}); });
this.setState({ direntList: direntList });
} else if (Utils.isAncestorPath(direntPath, this.state.path)) {
// the deleted item is ancester of the current item
let parentPath = Utils.getDirName(direntPath);
this.showDir(parentPath);
}
// else do nothing
} }
onNodeClick = (e, node) => { addDirent = (name, type) => {
if (node instanceof Node && node.isMarkdown()){ let item = this.createDirent(name, type);
let tree = this.state.tree_data.clone(); let direntList = this.state.direntList;
this.initMainPanelData(node.path); if (type === 'dir') {
this.enterViewFileState(tree, node, node.path); direntList.unshift(item);
} else if(node instanceof Node && node.isDir()){ } else {
let tree = this.state.tree_data.clone(); direntList.push(item);
}
this.setState({direntList: direntList});
}
moveDirent = (filePath) => {
let name = filePath.slice(filePath.lastIndexOf('/') + 1);
let direntList = this.state.direntList.filter(item => {
return item.name !== name;
});
this.setState({direntList: direntList});
}
onFileTagChanged = (dirent, direntPath) => {
seafileAPI.listFileTags(repoID, direntPath).then(res => {
let fileTags = res.data.file_tags.map(item => {
return new FileTag(item);
})
this.updateDirent(dirent, 'file_tags', fileTags);
})
}
onTreeNodeClick = (node) => {
if (!this.state.pathExist) {
this.setState({pathExist: true});
}
if (node instanceof Node && node.isMarkdown()) {
let tree = this.state.treeData.clone();
this.setState({treeData: tree});
if (node.path !== this.state.filePath) {
this.showFile(node.path);
}
} else if (node instanceof Node && node.isDir()) {
let tree = this.state.treeData.clone();
if (this.state.filePath === node.path) { if (this.state.filePath === node.path) {
if (node.isExpanded) { if (node.isExpanded) {
tree.collapseNode(node); tree.collapseNode(node);
@@ -251,22 +446,25 @@ class Wiki extends Component {
tree.expandNode(node); tree.expandNode(node);
} }
} }
this.exitViewFileState(tree, node); this.setState({treeData: tree});
if (node.path !== this.state.filePath) {
this.showDir(node.path);
}
} else { } else {
const w=window.open('about:blank'); const w = window.open('about:blank');
const url = serviceUrl + '/lib/' + repoID + '/file' + node.path; const url = serviceUrl + '/lib/' + repoID + '/file' + node.path;
w.location.href = url; w.location.href = url;
} }
} }
onDirCollapse = (e, node) => { onTreeDirCollapse = (node) => {
let tree = this.state.tree_data.clone(); let tree = this.state.treeData.clone();
let findNode = tree.getNodeByPath(node.path); let findNode = tree.getNodeByPath(node.path);
findNode.isExpanded = !findNode.isExpanded; findNode.isExpanded = !findNode.isExpanded;
this.setState({tree_data: tree}); this.setState({treeData: tree});
} }
onMenuClick = () => { onSideNavMenuClick = () => {
this.setState({ this.setState({
closeSideBar: !this.state.closeSideBar, closeSideBar: !this.state.closeSideBar,
}); });
@@ -278,208 +476,55 @@ class Wiki extends Component {
}); });
} }
onAddFolderNode = (dirPath) => { onDeleteTreeNode = (node) => {
editorUtilities.createDir(dirPath).then(res => { this.deleteItem(node.path, node.isDir());
let tree = this.state.tree_data.clone();
let name = this.getFileNameByPath(dirPath);
let index = dirPath.lastIndexOf('/');
let parentPath = dirPath.substring(0, index);
if (!parentPath) {
parentPath = '/';
} }
let node = this.buildNewNode(name, 'dir');
onRenameTreeNode = (node, newName) => {
this.renameItem(node.path, node.isDir(), newName);
}
addNodeToTree = (name, parentPath, type) => {
let tree = this.state.treeData.clone();
let node = this.createTreeNode(name, type);
let parentNode = tree.getNodeByPath(parentPath); let parentNode = tree.getNodeByPath(parentPath);
tree.addNodeToParent(node, parentNode); tree.addNodeToParent(node, parentNode);
if (this.state.isViewFileState) { this.setState({treeData: tree});
tree.expandNode(node);
this.setState({
tree_data: tree,
changedNode: node
});
} else {
this.exitViewFileState(tree, parentNode);
}
});
} }
onAddFileNode = (filePath, isDraft) => { renameTreeNode = (node, newName) => {
editorUtilities.createFile(filePath, isDraft).then(res => { let tree = this.state.treeData.clone();
let tree = this.state.tree_data.clone();
let name = this.getFileNameByPath(filePath);
let index = filePath.lastIndexOf('/');
let parentPath = filePath.substring(0, index);
if (!parentPath) {
parentPath = '/';
}
let node = this.buildNewNode(name, 'file');
let parentNode = tree.getNodeByPath(parentPath);
tree.addNodeToParent(node, parentNode);
if (this.state.isViewFileState) {
tree.expandNode(node);
this.setState({
tree_data: tree,
changedNode: node
});
} else {
this.exitViewFileState(tree, parentNode);
}
});
}
onRenameNode = (node, newName) => {
let tree = this.state.tree_data.clone();
let filePath = node.path;
if (node.isFile()) {
editorUtilities.renameFile(filePath, newName).then(res => {
let cloneNode = node.clone();
tree.updateNodeParam(node, 'name', newName); tree.updateNodeParam(node, 'name', newName);
node.name = newName; this.setState({treeData: tree});
let date = new Date().getTime()/1000;
tree.updateNodeParam(node, 'last_update_time', moment.unix(date).fromNow());
node.last_update_time = moment.unix(date).fromNow();
if (this.state.isViewFileState) {
if (this.isModifyCurrentFile(cloneNode)) {
tree.expandNode(node);
this.setState({
tree_data: tree,
changedNode: node
});
this.initMainPanelData(node.path);
} else {
this.setState({tree_data: tree});
}
} else {
let parentNode = tree.findNodeParentFromTree(node);
this.setState({
tree_data: tree,
changedNode: parentNode
});
}
});
} else if (node.isDir()) {
editorUtilities.renameDir(filePath, newName).then(res => {
let currentFilePath = this.state.filePath;
let currentFileNode = tree.getNodeByPath(currentFilePath);
let nodePath = node.path;
tree.updateNodeParam(node, 'name', newName);
node.name = newName;
let date = new Date().getTime()/1000;
tree.updateNodeParam(node, 'last_update_time', moment.unix(date).fromNow());
node.last_update_time = moment.unix(date).fromNow();
if (this.state.isViewFileState) {
if (currentFilePath.indexOf(nodePath) > -1) {
tree.expandNode(currentFileNode);
this.setState({
tree_data: tree,
changedNode: currentFileNode
});
this.initMainPanelData(currentFileNode.path);
} else {
this.setState({tree_data: tree});
}
} else {
if (nodePath === currentFilePath) { // old node
tree.expandNode(node);
this.exitViewFileState(tree, node);
} else if (node.path.indexOf(currentFilePath) > -1) { // new node
tree.expandNode(currentFileNode);
this.exitViewFileState(tree, currentFileNode);
} else {
this.setState({tree_data: tree});
}
}
});
}
} }
onDeleteNode = (node) => { deleteTreeNode = (node) => {
let filePath = node.path; let tree = this.state.treeData.clone();
if (node.isDir()) {
editorUtilities.deleteDir(filePath).then(() => {
this.deleteNode(node);
});
} else {
editorUtilities.deleteFile(filePath).then(() => {
this.deleteNode(node);
});
}
}
deleteNode = (node) => {
let tree = this.state.tree_data.clone();
let isCurrentFile = false;
if (node.isDir()) {
isCurrentFile = this.isModifyContainsCurrentFile(node);
} else {
isCurrentFile = this.isModifyCurrentFile(node);
}
if (this.state.isViewFileState) {
tree.deleteNode(node); tree.deleteNode(node);
this.setState({treeData: tree});
}
if (isCurrentFile) { moveTreeNode = (nodePath, moveToPath, moveToRepo) => {
let homeNode = this.getHomeNode(tree); let tree = this.state.treeData.clone();
tree.expandNode(homeNode);
this.setState({ if (repoID === moveToRepo.repo_id) {
tree_data: tree, tree.moveNodeByPath(nodePath, moveToPath, true);
changedNode: homeNode
});
this.initMainPanelData(homeNode.path);
} else { } else {
this.setState({tree_data: tree}); tree.deleteNodeByPath(nodePath);
}
} else {
let parentNode = tree.getNodeByPath(this.state.filePath);
let isChild = tree.isNodeChild(parentNode, node);
tree.deleteNode(node);
if (isChild) {
this.exitViewFileState(tree, parentNode);
} else {
this.setState({tree_data: tree});
}
}
} }
this.setState({treeData: tree});
enterViewFileState(newTree, newNode, newPath) {
this.setState({
tree_data: newTree,
changedNode: newNode,
filePath: newPath,
isViewFileState: true
});
} }
exitViewFileState(newTree, newNode) { copyTreeNode = (nodePath, copyToPath) => {
this.setState({ let tree = this.state.treeData.clone();
tree_data: newTree, tree.moveNodeByPath(nodePath, copyToPath, false);
changedNode: newNode, this.setState({treeData: tree});
filePath: newNode.path,
isViewFileState: false
});
let fileUrl = serviceUrl + '/wiki/lib/' + repoID + newNode.path;
window.history.pushState({urlPath: fileUrl, filePath: newNode.path}, newNode.path, fileUrl);
} }
getFileNameByPath(path) { createTreeNode(name, type) {
let index = path.lastIndexOf('/');
if (index === -1) {
return '';
}
return path.slice(index+1);
}
getHomeNode(treeData) {
return treeData.getNodeByPath('/home.md');
}
buildNewNode(name, type) {
let date = new Date().getTime()/1000; let date = new Date().getTime()/1000;
let node = new Node({ let node = new Node({
name : name, name : name,
@@ -492,20 +537,37 @@ class Wiki extends Component {
return node; return node;
} }
isModifyCurrentFile(node) { createDirent(name, type) {
let nodeName = node.name; let data = new Date().getTime()/1000;
let fileName = this.getFileNameByPath(this.state.filePath); let dirent = null;
return nodeName === fileName; if (type === 'dir') {
dirent = new Dirent({
id: '000000000000000000',
name: name,
type: type,
mtime: data,
permission: 'rw',
});
} else {
dirent = new Dirent({
id: '000000000000000000',
name: name,
type: type,
mtime: data,
permission: 'rw',
size: 0,
starred: false,
is_locked: false,
lock_time: '',
lock_owner: null,
locked_by_me: false,
modifier_name: '',
modifier_email: '',
modifier_contact_email: '',
file_tags: []
});
} }
return dirent;
isModifyContainsCurrentFile(node) {
let filePath = this.state.filePath;
let nodePath = node.path;
if (filePath.indexOf(nodePath) > -1) {
return true;
}
return false;
} }
isMarkdownFile(filePath) { isMarkdownFile(filePath) {
@@ -559,39 +621,43 @@ class Wiki extends Component {
return ( return (
<div id="main" className="wiki-main"> <div id="main" className="wiki-main">
<SidePanel <SidePanel
onNodeClick={this.onNodeClick} onNodeClick={this.onTreeNodeClick}
onDirCollapse={this.onTreeDirCollapse}
closeSideBar={this.state.closeSideBar} closeSideBar={this.state.closeSideBar}
onCloseSide ={this.onCloseSide} onCloseSide ={this.onCloseSide}
treeData={this.state.tree_data} treeData={this.state.treeData}
currentFilePath={this.state.filePath} currentPath={this.state.path}
changedNode={this.state.changedNode} currentNode={this.state.currentNode}
onAddFolderNode={this.onAddFolderNode} onAddFolderNode={this.onAddFolder}
onAddFileNode={this.onAddFileNode} onAddFileNode={this.onAddFile}
onRenameNode={this.onRenameNode} onRenameNode={this.onRenameTreeNode}
onDeleteNode={this.onDeleteNode} onDeleteNode={this.onDeleteTreeNode}
onDirCollapse={this.onDirCollapse}
/> />
<MainPanel <MainPanel
content={this.state.content} path={this.state.path}
filePath={this.state.filePath} isViewFile={this.state.isViewFile}
latestContributor={this.state.latestContributor} pathExist={this.state.pathExist}
lastModified={this.state.lastModified} isDirentListLoading={this.state.isDirentListLoading}
permission={this.state.permission}
isViewFileState={this.state.isViewFileState}
changedNode={this.state.changedNode}
isFileLoading={this.state.isFileLoading} isFileLoading={this.state.isFileLoading}
permission={this.state.permission}
content={this.state.content}
lastModified={this.state.lastModified}
latestContributor={this.state.latestContributor}
direntList={this.state.direntList}
switchViewMode={this.switchViewMode} switchViewMode={this.switchViewMode}
updateDirent={this.updateDirent}
onLinkClick={this.onLinkClick} onLinkClick={this.onLinkClick}
onMenuClick={this.onMenuClick} onSideNavMenuClick={this.onSideNavMenuClick}
onSearchedClick={this.onSearchedClick} onSearchedClick={this.onSearchedClick}
onMainNavBarClick={this.onMainNavBarClick} onMainNavBarClick={this.onMainNavBarClick}
onMainItemClick={this.onMainItemClick} onItemClick={this.onDirentClick}
onMainItemDelete={this.onMainItemDelete} onItemDelete={this.onMainPanelItemDelete}
onMainItemRename={this.onMainItemRename} onItemRename={this.onMainPanelItemRename}
onMainItemMove={this.onMainItemMove} onItemMove={this.onMoveItem}
onMainItemCopy={this.onMainItemCopy} onItemCopy={this.onCopyItem}
onMainAddFile={this.onAddFileNode} onAddFile={this.onAddFile}
onMainAddFolder={this.onAddFolderNode} onAddFolder={this.onAddFolder}
onFileTagChanged={this.onFileTagChanged}
/> />
</div> </div>
); );

View File

@@ -27,8 +27,9 @@ export const enableResumableFileUpload = window.app.pageOptions.enableResumableF
export const slug = window.wiki ? window.wiki.config.slug : ''; export const slug = window.wiki ? window.wiki.config.slug : '';
export const repoID = window.wiki ? window.wiki.config.repoId : ''; export const repoID = window.wiki ? window.wiki.config.repoId : '';
export const serviceUrl = window.wiki ? window.wiki.config.serviceUrl : ''; export const serviceUrl = window.wiki ? window.wiki.config.serviceUrl : '';
export const initialFilePath = window.wiki ? window.wiki.config.initial_file_path : ''; export const initialPath = window.wiki ? window.wiki.config.initial_path : '';
export const permission = window.wiki ? window.wiki.config.permission === 'True' : ''; export const permission = window.wiki ? window.wiki.config.permission === 'True' : '';
export const isDir = window.wiki ? window.wiki.config.isDir : '';
// file history // file history
export const PER_PAGE = 25; export const PER_PAGE = 25;

View File

@@ -168,6 +168,40 @@ export const Utils = {
return filePath.slice(lastIndex+1); return filePath.slice(lastIndex+1);
}, },
/*
return dirname of a path.
if path is '/', return '/'.
*/
getDirName: function(path) {
let dir = path.slice(0, path.lastIndexOf('/'));
if (dir === '') {
return '/';
} else {
return dir;
}
},
isChildPath: function(child, parent) {
let p = this.getDirName(child);
return p === parent;
},
isAncestorPath: function(ancestor, path) {
return path.indexOf(ancestor) > -1;
},
renameAncestorPath: function(path, ancestor, newAncestor) {
return newAncestor + '/' + path.replace(ancestor, '');
},
joinPath: function(pathA, pathB) {
if (pathA[pathA.length-1] === '/') {
return pathA + pathB;
} else {
return pathA + '/' + pathB;
}
},
isSupportUploadFolder: function() { isSupportUploadFolder: function() {
return navigator.userAgent.indexOf('Firefox')!=-1 || return navigator.userAgent.indexOf('Firefox')!=-1 ||
navigator.userAgent.indexOf('Chrome') > -1; navigator.userAgent.indexOf('Chrome') > -1;

View File

@@ -893,6 +893,9 @@ table .menu-toggle {
color: #222; color: #222;
font-weight: bold; font-weight: bold;
} }
.err-message h2{
color: red;
}
/* end tip */ /* end tip */
/* begin more component */ /* begin more component */

Binary file not shown.

View File

@@ -62,5 +62,5 @@
<glyph glyph-name="unshared" unicode="&#57397;" d="M427 392l-35 35-136-136-136 136-35-35 136-136-136-136 35-35 136 136 136-136 35 35-136 136z"/> <glyph glyph-name="unshared" unicode="&#57397;" d="M427 392l-35 35-136-136-136 136-35-35 136-136-136-136 35-35 136 136 136-136 35 35-136 136z"/>
<glyph glyph-name="11" unicode="&#57398;" d="M149 64l0 320-128 0 0-320z m22 0l320 0 0 320-320 0z m-150 384l470 0c11 0 21-10 21-21l0-363c0-12-10-21-21-21l-470 0c-11 0-21 9-21 21l0 363c0 11 10 21 21 21z m22-128l85 0 0-21-85 0z m170 0l256 0 0-21-256 0z m-170-43l85 0 0-21-85 0z m0-42l85 0 0-22-85 0z m0-43l85 0 0-21-85 0z"/> <glyph glyph-name="11" unicode="&#57398;" d="M149 64l0 320-128 0 0-320z m22 0l320 0 0 320-320 0z m-150 384l470 0c11 0 21-10 21-21l0-363c0-12-10-21-21-21l-470 0c-11 0-21 9-21 21l0 363c0 11 10 21 21 21z m22-128l85 0 0-21-85 0z m170 0l256 0 0-21-256 0z m-170-43l85 0 0-21-85 0z m0-42l85 0 0-22-85 0z m0-43l85 0 0-21-85 0z"/>
<glyph glyph-name="untitled-font-3-18" unicode="&#57365;" d="M489 325l-63 21c-2 7-5 14-9 21l25 51c3 3 0 7-2 10l-47 46c-2 3-7 3-9 3l-51-26c-7 2-14 7-21 9l-19 54c0 2-4 4-7 4l-63 0c-4 0-7-2-7-4l-18-56c-7-2-14-5-21-9l-51 25c-3 3-7 3-10-2l-46-47c-3 0-3-4 0-7l25-51c-4-7-7-14-9-21l-53-18c-3 0-5-5-5-7l0-66c0-4 2-7 5-7l53-18c2-7 5-14 9-21l-25-51c-3-3 0-7 2-10l47-46c2-3 7-3 9-3l51 26c7-2 14-7 21-9l19-54c0-2 4-4 7-4l65 0c5 0 7 2 7 4l19 54c6 2 13 4 20 9l52-26c2-2 7 0 9 3l47 46c2 3 2 7 2 10l-26 51c3 7 7 14 10 21l53 18c2 0 5 5 5 7l2 66c0 2 0 2-2 4z m-72-81c-3 0-5-3-5-5-2-9-7-16-12-25-2-3-2-5 0-7l24-49-38-38-49 24c-2 2-4 0-7 0-9-5-16-10-25-12-2 0-5-2-5-5l-16-51-56 0-16 51c0 3-3 5-5 5-9 2-16 7-25 12-3 2-5 2-7 0l-49-24-38 38 24 49c2 2 0 4 0 7-5 9-10 16-12 25 0 2-2 5-5 5l-51 16 0 56 51 16c3 0 5 3 5 5 2 9 7 16 12 25 2 3 2 5 0 7l-24 49 38 38 49-24c2-2 4 0 7 0 9 5 16 10 25 12 2 0 5 2 5 5l16 51 54 0 16-51c0-3 2-5 5-5 9-2 16-7 25-12 2-2 5-2 7 0l49 24 37-38-23-49c-2-2 0-4 0-7 5-9 9-16 12-25 0-2 2-5 4-5l56-16 0-54m-184 135c-86 24-163-53-142-142 10-37 42-69 79-79 87-23 163 54 142 142-9 40-39 70-79 79z m-4-202c-73-21-140 46-119 119 7 32 35 60 67 67 75 21 142-46 121-121-9-30-37-58-69-65z m0 0"/> <glyph glyph-name="untitled-font-3-18" unicode="&#57365;" d="M489 325l-63 21c-2 7-5 14-9 21l25 51c3 3 0 7-2 10l-47 46c-2 3-7 3-9 3l-51-26c-7 2-14 7-21 9l-19 54c0 2-4 4-7 4l-63 0c-4 0-7-2-7-4l-18-56c-7-2-14-5-21-9l-51 25c-3 3-7 3-10-2l-46-47c-3 0-3-4 0-7l25-51c-4-7-7-14-9-21l-53-18c-3 0-5-5-5-7l0-66c0-4 2-7 5-7l53-18c2-7 5-14 9-21l-25-51c-3-3 0-7 2-10l47-46c2-3 7-3 9-3l51 26c7-2 14-7 21-9l19-54c0-2 4-4 7-4l65 0c5 0 7 2 7 4l19 54c6 2 13 4 20 9l52-26c2-2 7 0 9 3l47 46c2 3 2 7 2 10l-26 51c3 7 7 14 10 21l53 18c2 0 5 5 5 7l2 66c0 2 0 2-2 4z m-72-81c-3 0-5-3-5-5-2-9-7-16-12-25-2-3-2-5 0-7l24-49-38-38-49 24c-2 2-4 0-7 0-9-5-16-10-25-12-2 0-5-2-5-5l-16-51-56 0-16 51c0 3-3 5-5 5-9 2-16 7-25 12-3 2-5 2-7 0l-49-24-38 38 24 49c2 2 0 4 0 7-5 9-10 16-12 25 0 2-2 5-5 5l-51 16 0 56 51 16c3 0 5 3 5 5 2 9 7 16 12 25 2 3 2 5 0 7l-24 49 38 38 49-24c2-2 4 0 7 0 9 5 16 10 25 12 2 0 5 2 5 5l16 51 54 0 16-51c0-3 2-5 5-5 9-2 16-7 25-12 2-2 5-2 7 0l49 24 37-38-23-49c-2-2 0-4 0-7 5-9 9-16 12-25 0-2 2-5 4-5l56-16 0-54m-184 135c-86 24-163-53-142-142 10-37 42-69 79-79 87-23 163 54 142 142-9 40-39 70-79 79z m-4-202c-73-21-140 46-119 119 7 32 35 60 67 67 75 21 142-46 121-121-9-30-37-58-69-65z m0 0"/>
<glyph glyph-name="tag" unicode="&#57399;" d="M278 78l150 151-200 201-146 6c-1 0-1 0-1 0-6 0-11-5-10-11l6-146z m-229 346c-1 18 13 33 31 34 1 0 2 0 3 0l154-7 222-222-181-181-222 222z m123-89c-12-13-33-13-45 0-13 12-13 33 0 45 12 13 33 13 45 0 12-12 12-33 0-45z m-15 15c4 4 4 11 0 15-4 4-11 4-15 0-4-4-4-11 0-15 4-4 11-4 15 0z"/> <glyph glyph-name="tag111" unicode="&#57399;" d="M278 100l150 151-200 200-146 7c-1 0-1 0-1 0-6-1-11-6-10-11l6-147z m-229 346c-1 17 13 32 31 33 1 0 2 0 3 0l154-7 222-221-181-181-222 221z m123-90c-12-12-33-12-45 0-13 13-13 33 0 45 12 13 33 13 45 0 12-12 12-32 0-45z m-15 15c4 4 4 11 0 15-4 5-11 5-15 0-4-4-4-11 0-15 4-4 11-4 15 0z"/>
</font></defs></svg> </font></defs></svg>

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

View File

@@ -10,8 +10,9 @@
config: { config: {
repoId: "{{ repo_id }}", repoId: "{{ repo_id }}",
serviceUrl: "{{ service_url}}", serviceUrl: "{{ service_url}}",
initial_file_path: "{{ file_path }}", initial_path: "{{ initial_path }}",
slug: "{{ repo_name }}", slug: "{{ repo_name }}",
isDir: "{{ is_dir }}",
permission: "{{ permission }}" permission: "{{ permission }}"
} }
}; };

View File

@@ -159,6 +159,15 @@ def view_lib_as_wiki(request, repo_id, path):
repo = seafile_api.get_repo(repo_id) repo = seafile_api.get_repo(repo_id)
is_dir = None
file_id = seafile_api.get_file_id_by_path(repo.id, path)
if file_id:
is_dir = False
dir_id = seafile_api.get_dir_id_by_path(repo.id, path)
if dir_id:
is_dir = True
user_perm = check_folder_permission(request, repo.id, '/') user_perm = check_folder_permission(request, repo.id, '/')
if user_perm is None: if user_perm is None:
return render_error(request, _(u'Permission denied')) return render_error(request, _(u'Permission denied'))
@@ -171,7 +180,8 @@ def view_lib_as_wiki(request, repo_id, path):
return render(request, 'view_lib_as_wiki.html', { return render(request, 'view_lib_as_wiki.html', {
'repo_id': repo_id, 'repo_id': repo_id,
'service_url': get_service_url().rstrip('/'), 'service_url': get_service_url().rstrip('/'),
'file_path': path, 'initial_path': path,
'is_dir': is_dir,
'repo_name': repo.name, 'repo_name': repo.name,
'permission': user_can_write 'permission': user_can_write
}) })