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:
@@ -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);
|
||||||
|
@@ -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);
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
);
|
);
|
||||||
|
@@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
|
@@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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>
|
||||||
|
@@ -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}
|
||||||
|
@@ -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>
|
||||||
);
|
);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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.
@@ -62,5 +62,5 @@
|
|||||||
<glyph glyph-name="unshared" unicode="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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.
Binary file not shown.
@@ -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 }}"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -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
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user