1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-16 23:29:49 +00:00

Wiki mode improve (#2539)

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

View File

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

View File

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

View File

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

View File

@@ -6,10 +6,9 @@ import URLDecorator from '../../utils/url-decorator';
import Toast from '../toast'; import Toast from '../toast';
import DirentMenu from './dirent-menu'; import DirentMenu from './dirent-menu';
import DirentRename from './dirent-rename'; import DirentRename from './dirent-rename';
import FileTag from '../../models/file-tag';
const propTypes = { const propTypes = {
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
isItemFreezed: PropTypes.bool.isRequired, isItemFreezed: PropTypes.bool.isRequired,
dirent: PropTypes.object.isRequired, dirent: PropTypes.object.isRequired,
onItemClick: PropTypes.func.isRequired, onItemClick: PropTypes.func.isRequired,
@@ -22,7 +21,7 @@ const propTypes = {
onDirentItemMove: PropTypes.func.isRequired, onDirentItemMove: PropTypes.func.isRequired,
onDirentItemCopy: PropTypes.func.isRequired, onDirentItemCopy: PropTypes.func.isRequired,
onItemDetails: PropTypes.func.isRequired, onItemDetails: PropTypes.func.isRequired,
updateViewList: PropTypes.func.isRequired, updateDirent: PropTypes.func.isRequired,
currentRepo: PropTypes.object, currentRepo: PropTypes.object,
isRepoOwner: PropTypes.bool, isRepoOwner: PropTypes.bool,
}; };
@@ -36,15 +35,13 @@ 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() {
document.removeEventListener('click', this.onItemMenuHide); document.removeEventListener('click', this.onItemMenuHide);
} }
@@ -113,21 +110,21 @@ class DirentListItem extends React.Component {
onItemSelected = () => { onItemSelected = () => {
//todos; //todos;
} }
onItemStarred = () => { onItemStarred = () => {
let dirent = this.props.dirent; let dirent = this.props.dirent;
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);
}); });
} }
} }
onItemClick = () => { onItemClick = () => {
let direntPath = this.getDirentPath(this.props.dirent); let direntPath = this.getDirentPath(this.props.dirent);
this.props.onItemClick(direntPath); this.props.onItemClick(direntPath);
@@ -141,13 +138,12 @@ 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) => {
switch(operation) { switch(operation) {
case 'Rename': case 'Rename':
this.onRenameMenuItemClick(); this.onRenameMenuItemClick();
break; break;
case 'Move': case 'Move':
@@ -202,21 +198,20 @@ class DirentListItem extends React.Component {
this.onRenameCancel(); this.onRenameCancel();
return false; return false;
} }
if (!newName) { if (!newName) {
let errMessage = 'It is required.'; let errMessage = 'It is required.';
Toast.error(gettext(errMessage)); Toast.error(gettext(errMessage));
return false; return false;
} }
if (newName.indexOf('/') > -1) { if (newName.indexOf('/') > -1) {
let errMessage = 'Name should not include "/".'; let errMessage = 'Name should not include "/".';
Toast.error(gettext(errMessage)); Toast.error(gettext(errMessage));
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();
} }
@@ -226,7 +221,7 @@ class DirentListItem extends React.Component {
}); });
this.props.onUnfreezedItem(); this.props.onUnfreezedItem();
} }
onDirentItemMove = () => { onDirentItemMove = () => {
let direntPath = this.getDirentPath(this.props.dirent); let direntPath = this.getDirentPath(this.props.dirent);
this.props.onDirentItemMove(this.props.dirent, direntPath); this.props.onDirentItemMove(this.props.dirent, direntPath);
@@ -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>
); );
@@ -357,7 +338,7 @@ class DirentListItem extends React.Component {
</td> </td>
<td className="operation"> <td className="operation">
{ {
this.state.isOperationShow && this.state.isOperationShow &&
<div className="operations"> <div className="operations">
<ul className="operation-group"> <ul className="operation-group">
<li className="operation-group-item"> <li className="operation-group-item">
@@ -374,8 +355,8 @@ class DirentListItem extends React.Component {
</li> </li>
</ul> </ul>
{ {
this.state.isItemMenuShow && this.state.isItemMenuShow &&
<DirentMenu <DirentMenu
dirent={this.props.dirent} dirent={this.props.dirent}
menuPosition={this.state.menuPosition} menuPosition={this.state.menuPosition}
onMenuItemClick={this.onItemMenuItemClick} onMenuItemClick={this.onItemMenuItemClick}

View File

@@ -10,7 +10,7 @@ import MoveDirentDialog from '../dialog/move-dirent-dialog';
import CopyDirentDialog from '../dialog/copy-dirent-dialog'; import CopyDirentDialog from '../dialog/copy-dirent-dialog';
const propTypes = { const propTypes = {
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
direntList: PropTypes.array.isRequired, direntList: PropTypes.array.isRequired,
onItemDelete: PropTypes.func.isRequired, onItemDelete: PropTypes.func.isRequired,
onItemRename: PropTypes.func.isRequired, onItemRename: PropTypes.func.isRequired,
@@ -18,7 +18,7 @@ const propTypes = {
onItemMove: PropTypes.func.isRequired, onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired, onItemCopy: PropTypes.func.isRequired,
onItemDetails: PropTypes.func.isRequired, onItemDetails: PropTypes.func.isRequired,
updateViewList: PropTypes.func.isRequired, updateDirent: PropTypes.func.isRequired,
isDirentListLoading: PropTypes.bool.isRequired, isDirentListLoading: PropTypes.bool.isRequired,
isRepoOwner: PropTypes.bool, isRepoOwner: PropTypes.bool,
currentRepo: PropTypes.object, currentRepo: PropTypes.object,
@@ -42,7 +42,7 @@ class DirentListView extends React.Component {
onFreezedItem = () => { onFreezedItem = () => {
this.setState({isItemFreezed: true}); this.setState({isItemFreezed: true});
} }
onUnfreezedItem = () => { onUnfreezedItem = () => {
this.setState({isItemFreezed: false}); this.setState({isItemFreezed: false});
} }
@@ -70,15 +70,15 @@ class DirentListView extends React.Component {
onItemMove = (repo, direntPath, moveToDirentPath) => { onItemMove = (repo, direntPath, moveToDirentPath) => {
this.props.onItemMove(repo, direntPath, moveToDirentPath); this.props.onItemMove(repo, direntPath, moveToDirentPath);
} }
onCancelMove = () => { onCancelMove = () => {
this.setState({isMoveDialogShow: false}); this.setState({isMoveDialogShow: false});
} }
onItemCopy = (repo, direntPath, copyToDirentPath) => { onItemCopy = (repo, direntPath, copyToDirentPath) => {
this.props.onItemCopy(repo, direntPath, copyToDirentPath); this.props.onItemCopy(repo, direntPath, copyToDirentPath);
} }
onCancelCopy = () => { onCancelCopy = () => {
this.setState({isCopyDialogShow: false}); this.setState({isCopyDialogShow: false});
} }
@@ -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,30 +175,31 @@ 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}
/> />
); );
}) })
} }
</tbody> </tbody>
</table> </table>
{this.state.isProgressDialogShow && {this.state.isProgressDialogShow &&
<ZipDownloadDialog progress={this.state.progress} onCancelDownload={this.onCancelDownload} <ZipDownloadDialog progress={this.state.progress} onCancelDownload={this.onCancelDownload}
/> />
} }
{this.state.isMoveDialogShow && {this.state.isMoveDialogShow &&
<MoveDirentDialog <MoveDirentDialog
dirent={this.state.currentDirent} dirent={this.state.currentDirent}
direntPath={this.state.direntPath} direntPath={this.state.direntPath}
onItemMove={this.props.onItemMove} onItemMove={this.props.onItemMove}
onCancelMove={this.onCancelMove} onCancelMove={this.onCancelMove}
/> />
} }
{this.state.isCopyDialogShow && {this.state.isCopyDialogShow &&
<CopyDirentDialog <CopyDirentDialog
dirent={this.state.currentDirent} dirent={this.state.currentDirent}
direntPath={this.state.direntPath} direntPath={this.state.direntPath}
onItemCopy={this.props.onItemCopy} onItemCopy={this.props.onItemCopy}
onCancelCopy={this.onCancelCopy} onCancelCopy={this.onCancelCopy}
/> />
} }
</Fragment> </Fragment>

View File

@@ -23,7 +23,7 @@ const propTypes = {
minFileSizeErrorCallback: PropTypes.func, minFileSizeErrorCallback: PropTypes.func,
fileTypeErrorCallback: PropTypes.func, fileTypeErrorCallback: PropTypes.func,
dragAndDrop: PropTypes.bool.isRequired, dragAndDrop: PropTypes.bool.isRequired,
filePath: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
onFileSuccess: PropTypes.func.isRequired, onFileSuccess: PropTypes.func.isRequired,
}; };
@@ -71,7 +71,7 @@ class FileUploader extends React.Component {
bindCallbackHandler = () => { bindCallbackHandler = () => {
let {maxFilesErrorCallback, minFileSizeErrorCallback, maxFileSizeErrorCallback, fileTypeErrorCallback } = this.props; let {maxFilesErrorCallback, minFileSizeErrorCallback, maxFileSizeErrorCallback, fileTypeErrorCallback } = this.props;
if (maxFilesErrorCallback) { if (maxFilesErrorCallback) {
this.resumable.opts.maxFilesErrorCallback = this.props.maxFilesErrorCallback; this.resumable.opts.maxFilesErrorCallback = this.props.maxFilesErrorCallback;
} }
@@ -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
}; };
} }
@@ -168,7 +168,7 @@ class FileUploader extends React.Component {
} }
filesAddedComplete = (resumable, files) => { filesAddedComplete = (resumable, files) => {
// single file uploading can check repetition, because custom dialog conn't prevent program execution; // single file uploading can check repetition, because custom dialog conn't prevent program execution;
} }
setUploadFileList = (files) => { setUploadFileList = (files) => {
@@ -198,7 +198,7 @@ class FileUploader extends React.Component {
} }
return item; return item;
}); });
this.setState({uploadFileList: uploadFileList}); this.setState({uploadFileList: uploadFileList});
} }
@@ -216,7 +216,7 @@ class FileUploader extends React.Component {
} }
onComplete = () => { onComplete = () => {
} }
onPause = () => [ onPause = () => [
@@ -238,7 +238,7 @@ class FileUploader extends React.Component {
onCancel = () => { onCancel = () => {
} }
setHeaders = (resumableFile, resumable) => { setHeaders = (resumableFile, resumable) => {
let offset = resumable.offset; let offset = resumable.offset;
let chunkSize = resumable.getOpt('chunkSize'); let chunkSize = resumable.getOpt('chunkSize');
@@ -249,13 +249,13 @@ class FileUploader extends React.Component {
if (fileSize - resumable.endByte < chunkSize && !resumable.getOpt('forceChunkSize')) { if (fileSize - resumable.endByte < chunkSize && !resumable.getOpt('forceChunkSize')) {
endByte = fileSize; endByte = fileSize;
} }
let headers = { let headers = {
'Accept': 'application/json; text/javascript, */*; q=0.01', 'Accept': 'application/json; text/javascript, */*; q=0.01',
'Content-Disposition': 'attachment; filename="' + encodeURI(resumableFile.fileName) + '"', 'Content-Disposition': 'attachment; filename="' + encodeURI(resumableFile.fileName) + '"',
'Content-Range': 'bytes ' + startByte + '-' + endByte + '/' + fileSize, 'Content-Range': 'bytes ' + startByte + '-' + endByte + '/' + fileSize,
}; };
return headers; return headers;
} }
@@ -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;
}); });
} }
@@ -328,13 +328,13 @@ class FileUploader extends React.Component {
// this.setState({isUploadRemindDialogShow: false}); // this.setState({isUploadRemindDialogShow: false});
this.setUploadFileList(this.resumable.files); this.setUploadFileList(this.resumable.files);
this.resumable.upload(); this.resumable.upload();
} }
uploadFile = () => { uploadFile = () => {
// this.setState({isUploadRemindDialogShow: false}); // this.setState({isUploadRemindDialogShow: false});
this.setUploadFileList(this.resumable.files); this.setUploadFileList(this.resumable.files);
this.resumable.upload(); this.resumable.upload();
} }
@@ -352,7 +352,7 @@ class FileUploader extends React.Component {
</div> </div>
{ {
this.state.isUploadProgressDialogShow && this.state.isUploadProgressDialogShow &&
<UploadProgressDialog <UploadProgressDialog
uploadFileList={this.state.uploadFileList} uploadFileList={this.state.uploadFileList}
totalProgress={this.state.totalProgress} totalProgress={this.state.totalProgress}
onMinimizeUploadDialog={this.onMinimizeUploadDialog} onMinimizeUploadDialog={this.onMinimizeUploadDialog}
@@ -362,7 +362,7 @@ class FileUploader extends React.Component {
} }
{ {
this.state.isUploadRemindDialogShow && this.state.isUploadRemindDialogShow &&
<UploadRemindDialog <UploadRemindDialog
currentResumableFile={this.state.currentResumableFile} currentResumableFile={this.state.currentResumableFile}
replaceRepetitionFile={this.replaceRepetitionFile} replaceRepetitionFile={this.replaceRepetitionFile}
uploadFile={this.uploadFile} uploadFile={this.uploadFile}

View File

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

View File

@@ -16,7 +16,7 @@ class Node {
return node; return node;
} }
constructor({name, type, size, last_update_time, permission, parent_path, isExpanded, children}) { constructor({name, type, size, last_update_time, permission, parent_path, isExpanded, children}) {
this.name = name; this.name = name;
this.type = type; this.type = type;
@@ -28,7 +28,7 @@ class Node {
this.children = children ? children : []; this.children = children ? children : [];
this.parent = null; this.parent = null;
} }
clone() { clone() {
var n = new Node({ var n = new Node({
name: this.name, name: this.name,
@@ -39,10 +39,10 @@ class Node {
parent_path: this.parent_path, parent_path: this.parent_path,
isExpanded: this.isExpanded isExpanded: this.isExpanded
}); });
n.children = this.children.map(child => { n.children = this.children.map(child => {
var newChild = child.clone(); var newChild = child.clone();
newChild.parent = n; newChild.parent = n;
return newChild; return newChild;
}); });
return n; return n;
} }
@@ -122,4 +122,4 @@ class Node {
} }
export default Node; export default Node;

View File

@@ -5,7 +5,7 @@ import { permission } from '../../utils/constants';
const propTypes = { const propTypes = {
isNodeItemFrezee: PropTypes.bool.isRequired, isNodeItemFrezee: PropTypes.bool.isRequired,
currentFilePath: PropTypes.string.isRequired, currentPath: PropTypes.string.isRequired,
paddingLeft: PropTypes.number.isRequired, paddingLeft: PropTypes.number.isRequired,
node: PropTypes.object.isRequired, node: PropTypes.object.isRequired,
treeView: PropTypes.object.isRequired, treeView: PropTypes.object.isRequired,
@@ -31,10 +31,10 @@ class TreeNodeView extends React.Component {
}; };
} }
onClick = (e) => { onClick = () => {
// e.nativeEvent.stopImmediatePropagation(); // e.nativeEvent.stopImmediatePropagation();
let { node } = this.props; let { node } = this.props;
this.props.treeView.onNodeClick(e, node); this.props.treeView.onNodeClick(node);
} }
onMouseEnter = () => { onMouseEnter = () => {
@@ -55,7 +55,7 @@ class TreeNodeView extends React.Component {
handleCollapse = (e) => { handleCollapse = (e) => {
e.stopPropagation(); e.stopPropagation();
this.props.onDirCollapse(e, this.props.node); this.props.onDirCollapse(this.props.node);
} }
onDragStart = (e) => { onDragStart = (e) => {
@@ -121,7 +121,7 @@ class TreeNodeView extends React.Component {
paddingLeft={this.props.paddingLeft} paddingLeft={this.props.paddingLeft}
treeView={this.props.treeView} treeView={this.props.treeView}
isNodeItemFrezee={this.props.isNodeItemFrezee} isNodeItemFrezee={this.props.isNodeItemFrezee}
currentFilePath={this.props.currentFilePath} currentPath={this.props.currentPath}
onDirCollapse={this.props.onDirCollapse} onDirCollapse={this.props.onDirCollapse}
/> />
); );
@@ -138,7 +138,7 @@ class TreeNodeView extends React.Component {
let isShow = (this.props.node.path === this.props.currentFilePath); let isShow = (this.props.node.path === this.props.currentFilePath);
return ( return (
<div className="right-icon"> <div className="right-icon">
<MenuControl <MenuControl
isShow={this.state.isMenuIconShow || isShow} isShow={this.state.isMenuIconShow || isShow}
onClick={this.onMenuControlClick} onClick={this.onMenuControlClick}
/> />
@@ -186,11 +186,11 @@ class TreeNodeView extends React.Component {
return ( return (
<div type={type} className="tree-node" style={styles}> <div type={type} className="tree-node" style={styles}>
<div <div
onMouseLeave={this.onMouseLeave} onMouseLeave={this.onMouseLeave}
onMouseEnter={this.onMouseEnter} onMouseEnter={this.onMouseEnter}
onClick={this.onClick} onClick={this.onClick}
type={type} type={type}
className={`tree-node-inner text-nowrap ${hlClass} ${node.name === '/'? 'hide': ''}`} className={`tree-node-inner text-nowrap ${hlClass} ${node.name === '/'? 'hide': ''}`}
> >
<div className="tree-node-text" type={type} draggable="true" onDragStart={this.onDragStart}>{node.name}</div> <div className="tree-node-text" type={type} draggable="true" onDragStart={this.onDragStart}>{node.name}</div>

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
import moment from 'moment'; import moment from 'moment';
import { Utils } from '../utils/utils'; import { Utils } from '../utils/utils';
import FileTag from './file-tag';
class Dirent { class Dirent {
constructor(json) { constructor(json) {
@@ -18,10 +19,20 @@ 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';
}
} }
export default Dirent; export default Dirent;

View File

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

View File

@@ -10,9 +10,9 @@ import CreateFolder from '../../components/dialog/create-folder-dialog';
import CreateFile from '../../components/dialog/create-file-dialog'; import CreateFile from '../../components/dialog/create-file-dialog';
const propTypes = { const propTypes = {
changedNode: PropTypes.object, currentNode: PropTypes.object,
treeData: PropTypes.object.isRequired, treeData: PropTypes.object.isRequired,
currentFilePath: PropTypes.string.isRequired, currentPath: PropTypes.string.isRequired,
closeSideBar: PropTypes.bool.isRequired, closeSideBar: PropTypes.bool.isRequired,
onCloseSide: PropTypes.func.isRequired, onCloseSide: PropTypes.func.isRequired,
onDirCollapse: PropTypes.func.isRequired, onDirCollapse: PropTypes.func.isRequired,
@@ -62,9 +62,9 @@ class SidePanel extends Component {
}); });
} }
onNodeClick = (e, node) => { onNodeClick = (node) => {
this.setState({currentNode: node}); this.setState({currentNode: node});
this.props.onNodeClick(e, node); this.props.onNodeClick(node);
} }
onShowContextMenu = (e, node) => { onShowContextMenu = (e, node) => {
@@ -111,12 +111,12 @@ class SidePanel extends Component {
this.setState({showFolder: !this.state.showFolder}); this.setState({showFolder: !this.state.showFolder});
this.onHideContextMenu(); this.onHideContextMenu();
} }
toggleRename = () => { toggleRename = () => {
this.setState({showRename: !this.state.showRename}); this.setState({showRename: !this.state.showRename});
this.onHideContextMenu(); this.onHideContextMenu();
} }
toggleDelete = () => { toggleDelete = () => {
this.setState({showDelete: !this.state.showDelete}); this.setState({showDelete: !this.state.showDelete});
this.onHideContextMenu(); this.onHideContextMenu();
@@ -126,18 +126,18 @@ class SidePanel extends Component {
this.setState({showFolder: !this.state.showFolder}); this.setState({showFolder: !this.state.showFolder});
this.props.onAddFolderNode(dirPath); this.props.onAddFolderNode(dirPath);
} }
onAddFileNode = (filePath, isDraft) => { onAddFileNode = (filePath, isDraft) => {
this.setState({showFile: !this.state.showFile}); this.setState({showFile: !this.state.showFile});
this.props.onAddFileNode(filePath, isDraft); this.props.onAddFileNode(filePath, isDraft);
} }
onRenameNode = (newName) => { onRenameNode = (newName) => {
this.setState({showRename: !this.state.showRename}); this.setState({showRename: !this.state.showRename});
let node = this.state.currentNode; let node = this.state.currentNode;
this.props.onRenameNode(node, newName); this.props.onRenameNode(node, newName);
} }
onDeleteNode = () => { onDeleteNode = () => {
this.setState({showDelete: !this.state.showDelete}); this.setState({showDelete: !this.state.showDelete});
let node = this.state.currentNode; let node = this.state.currentNode;
@@ -149,9 +149,7 @@ class SidePanel extends Component {
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
this.setState({ this.setState({currentNode: nextProps.currentNode});
currentNode: nextProps.changedNode
});
} }
componentWillUnmount() { componentWillUnmount() {
@@ -184,23 +182,23 @@ class SidePanel extends Component {
<a title="Close" aria-label="Close" onClick={this.closeSide} className="sf2-icon-x1 sf-popover-close side-panel-close op-icon d-md-none "></a> <a title="Close" aria-label="Close" onClick={this.closeSide} className="sf2-icon-x1 sf-popover-close side-panel-close op-icon d-md-none "></a>
</div> </div>
<div id="side-nav" className="wiki-side-nav" role="navigation"> <div id="side-nav" className="wiki-side-nav" role="navigation">
<h3 <h3
className="wiki-pages-heading" className="wiki-pages-heading"
onMouseEnter={this.onMouseEnter} onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave} onMouseLeave={this.onMouseLeave}
> >
{gettext('Files')} {gettext('Files')}
<div className="heading-icon"> <div className="heading-icon">
<MenuControl <MenuControl
isShow={this.state.isMenuIconShow} isShow={this.state.isMenuIconShow}
onClick={this.onHeadingMenuClick} onClick={this.onHeadingMenuClick}
/> />
</div> </div>
</h3> </h3>
<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}
@@ -209,8 +207,8 @@ class SidePanel extends Component {
onDirCollapse={this.props.onDirCollapse} onDirCollapse={this.props.onDirCollapse}
/> />
} }
{this.state.isShowMenu && {this.state.isShowMenu &&
<NodeMenu <NodeMenu
menuPosition={this.state.menuPosition} menuPosition={this.state.menuPosition}
currentNode={this.state.currentNode} currentNode={this.state.currentNode}
toggleAddFile={this.toggleAddFile} toggleAddFile={this.toggleAddFile}
@@ -220,13 +218,13 @@ class SidePanel extends Component {
/> />
} }
{this.state.showDelete && {this.state.showDelete &&
<Delete <Delete
currentNode={this.state.currentNode} currentNode={this.state.currentNode}
handleSubmit={this.onDeleteNode} handleSubmit={this.onDeleteNode}
toggleCancel={this.deleteCancel} toggleCancel={this.deleteCancel}
/> />
} }
{this.state.showFile && {this.state.showFile &&
<CreateFile <CreateFile
fileType={'.md'} fileType={'.md'}
parentPath={this.state.currentNode.path} parentPath={this.state.currentNode.path}
@@ -235,17 +233,17 @@ class SidePanel extends Component {
/> />
} }
{this.state.showFolder && {this.state.showFolder &&
<CreateFolder <CreateFolder
parentPath={this.state.currentNode.path} parentPath={this.state.currentNode.path}
onAddFolder={this.onAddFolderNode} onAddFolder={this.onAddFolderNode}
addFolderCancel={this.addFolderCancel} addFolderCancel={this.addFolderCancel}
/> />
} }
{this.state.showRename && {this.state.showRename &&
<Rename <Rename
currentNode={this.state.currentNode} currentNode={this.state.currentNode}
onRename={this.onRenameNode} onRename={this.onRenameNode}
toggleCancel={this.renameCancel} toggleCancel={this.renameCancel}
/> />
} }
</div> </div>

View File

@@ -2,14 +2,16 @@ import React, { Component } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import moment from 'moment'; import moment from 'moment';
import cookie from 'react-cookies'; import cookie from 'react-cookies';
import { gettext, repoID, serviceUrl, initialFilePath } from './utils/constants'; import { gettext, repoID, serviceUrl, initialPath, isDir } from './utils/constants';
import { seafileAPI } from './utils/seafile-api'; import { seafileAPI } from './utils/seafile-api';
import editorUtilities from './utils/editor-utilties'; import { Utils } from './utils/utils';
import SidePanel from './pages/repo-wiki-mode/side-panel'; import SidePanel from './pages/repo-wiki-mode/side-panel';
import MainPanel from './pages/repo-wiki-mode/main-panel'; import MainPanel from './pages/repo-wiki-mode/main-panel';
import Node from './components/tree-view/node'; import Node from './components/tree-view/node';
import Tree from './components/tree-view/tree'; import Tree from './components/tree-view/tree';
import Toast from './components/toast'; import Toast from './components/toast';
import Dirent from './models/dirent';
import FileTag from './models/file-tag';
import 'seafile-ui'; import 'seafile-ui';
import './assets/css/fa-solid.css'; import './assets/css/fa-solid.css';
import './assets/css/fa-regular.css'; import './assets/css/fa-regular.css';
@@ -24,81 +26,151 @@ class Wiki extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
content: '', path: '',
tree_data: new Tree(), pathExist: true,
treeData: new Tree(),
closeSideBar: false, closeSideBar: false,
filePath: '', currentNode: null,
latestContributor: '', isDirentListLoading: true,
isViewFile: false,
direntList: [],
isFileLoading: true,
content: '',
lastModified: '', lastModified: '',
latestContributor: '',
permission: '', permission: '',
isFileLoading: false,
changedNode: null,
isViewFileState: true
}; };
window.onpopstate = this.onpopstate; window.onpopstate = this.onpopstate;
} }
componentDidMount() { componentDidMount() {
this.initWikiData(initialFilePath); if (isDir === 'None') {
this.setState({pathExist: false});
} else if (isDir === 'True') {
this.showDir(initialPath);
} else if (isDir === 'False') {
this.showFile(initialPath);
}
this.loadSidePanel(initialPath);
} }
initWikiData(filePath){
this.setState({isFileLoading: true});
editorUtilities.listRepoDir().then((files) => {
// construct the tree object
var treeData = new Tree();
treeData.parseListToTree(files);
let node = treeData.getNodeByPath(filePath); deleteItemAjaxCallback(path, isDir) {
treeData.expandNode(node); let node = this.state.treeData.getNodeByPath(path);
if (node.isDir()) { this.deleteTreeNode(node);
this.exitViewFileState(treeData, node); this.deleteDirent(path);
this.setState({isFileLoading: false}); }
} else {
this.setState({tree_data: treeData}); deleteItem(path, isDir) {
this.initMainPanelData(filePath); if (isDir) {
seafileAPI.deleteDir(repoID, path).then(() => {
this.deleteItemAjaxCallback(path, isDir);
}).catch(() => {
//todos;
});
} else {
seafileAPI.deleteFile(repoID, path).then(() => {
this.deleteItemAjaxCallback(path, isDir);
}).catch(() => {
//todos;
});
}
}
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(() => {
/* eslint-disable */ //todo;
console.log('failed to load files');
/* eslint-enable */
this.setState({
isLoadFailed: true
});
}); });
} }
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');
this.setState({ }
latestContributor: last_modifier_name, }).catch(() => {
lastModified: moment.unix(mtime).fromNow(), //return error message
permission: permission,
filePath: filePath,
});
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
const downLoadUrl = res.data;
seafileAPI.getFileContent(downLoadUrl).then((res) => {
this.setState({
content: res.data,
isFileLoading: false
});
});
});
}); });
}
let fileUrl = serviceUrl + '/wiki/lib/' + repoID + filePath; onMoveItem = (repo, direntPath, moveToDirentPath) => {
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl); //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,moveToDirentPath, dirPath, dirName).then(() => {
this.moveTreeNode(direntPath, moveToDirentPath, repo);
this.moveDirent(direntPath);
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);
});
}
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) => {
let node = this.state.tree_data.getNodeByPath(direntPath);
this.onRenameNode(node, newName);
} }
onMainItemMove = (repo, direntPath, moveToDirentPath) => { onMainPanelItemDelete = (dirent) => {
let index = direntPath.lastIndexOf('/'); let path = Utils.joinPath(this.state.path, dirent.name);
let dirPath = direntPath.slice(0, index + 1); this.deleteItem(path, dirent.isDir());
let dirName = direntPath.slice(index + 1); }
seafileAPI.moveDir(repoID, repo.repo_id, moveToDirentPath, dirPath, dirName).then(() => {
let tree = this.state.tree_data.clone();
let moveNode = tree.getNodeByPath(direntPath);
let moveNodeParent = tree.findNodeParentFromTree(moveNode);
if (repoID === repo.repo_id) {
let moveToNode = tree.getNodeByPath(moveToDirentPath);
tree.addNodeToParent(moveNode, moveToNode);
}
tree.removeNodeFromParent(moveNode, moveNodeParent);
this.exitViewFileState(tree, moveNodeParent); renameDirent = (direntPath, newName) => {
let message = gettext('Successfully moved %(name)s.'); let parentPath = Utils.getDirName(direntPath);
message = message.replace('%(name)s', dirName); let newDirentPath = Utils.joinPath(parentPath, newName);
Toast.success(message); if (direntPath === this.state.path) {
}).catch(() => { // the renamed item is current viewed item
let message = gettext('Failed to move %(name)s'); // example: direntPath = /A/B/C, state.path = /A/B/C
message = message.replace('%(name)s', dirName); this.setState({ path: newDirentPath });
Toast.error(message); } else if (Utils.isChildPath(direntPath, this.state.path)) {
// example: direntPath = /A/B/C/D, state.path = /A/B/C
let oldName = Utils.getFileName(direntPath);
let direntList = this.state.direntList.map(item => {
if (item.name === oldName) {
item.name = newName;
}
return item;
});
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 });
}
}
deleteDirent(direntPath) {
let newPath = '';
if (direntPath === this.state.path) {
// The deleted item is current item
let parentPath = Utils.getDirName(direntPath);
this.showDir(parentPath);
} else if (Utils.isChildPath(direntPath, this.state.path)) {
// The deleted item is inside current path
let name = Utils.getFileName(direntPath);
let direntList = this.state.direntList.filter(item => {
return item.name !== name;
});
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
}
addDirent = (name, type) => {
let item = this.createDirent(name, type);
let direntList = this.state.direntList;
if (type === 'dir') {
direntList.unshift(item);
} else {
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});
} }
onMainItemCopy = (repo, direntPath, copyToDirentPath) => { onFileTagChanged = (dirent, direntPath) => {
let index = direntPath.lastIndexOf('/'); seafileAPI.listFileTags(repoID, direntPath).then(res => {
let dirPath = direntPath.slice(0, index + 1); let fileTags = res.data.file_tags.map(item => {
let dirName = direntPath.slice(index + 1); return new FileTag(item);
seafileAPI.copyDir(repoID, repo.repo_id, copyToDirentPath, dirPath, dirName).then(() => { })
if (repoID === repo.repo_id) { this.updateDirent(dirent, 'file_tags', fileTags);
let tree = this.state.tree_data.clone(); })
let copyNode = tree.getNodeByPath(direntPath); }
let copyToNode = tree.getNodeByPath(copyToDirentPath);
tree.addNodeToParent(copyNode, copyToNode); onTreeNodeClick = (node) => {
this.exitViewFileState(tree, this.state.changedNode); 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 message = gettext('Successfully copied %(name)s.'); let tree = this.state.treeData.clone();
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);
});
}
onNodeClick = (e, node) => {
if (node instanceof Node && node.isMarkdown()){
let tree = this.state.tree_data.clone();
this.initMainPanelData(node.path);
this.enterViewFileState(tree, node, node.path);
} else if(node instanceof Node && node.isDir()){
let tree = this.state.tree_data.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,234 +476,98 @@ 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');
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);
}
});
} }
onAddFileNode = (filePath, isDraft) => { onRenameTreeNode = (node, newName) => {
editorUtilities.createFile(filePath, isDraft).then(res => { this.renameItem(node.path, node.isDir(), newName);
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) => { addNodeToTree = (name, parentPath, type) => {
let tree = this.state.tree_data.clone(); let tree = this.state.treeData.clone();
let filePath = node.path;
if (node.isFile()) {
editorUtilities.renameFile(filePath, newName).then(res => {
let cloneNode = node.clone();
tree.updateNodeParam(node, 'name', newName); let node = this.createTreeNode(name, type);
node.name = newName; let parentNode = tree.getNodeByPath(parentPath);
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) { tree.addNodeToParent(node, parentNode);
if (this.isModifyCurrentFile(cloneNode)) { this.setState({treeData: tree});
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) => { renameTreeNode = (node, newName) => {
let filePath = node.path; let tree = this.state.treeData.clone();
if (node.isDir()) { tree.updateNodeParam(node, 'name', newName);
editorUtilities.deleteDir(filePath).then(() => { this.setState({treeData: tree});
this.deleteNode(node); }
});
deleteTreeNode = (node) => {
let tree = this.state.treeData.clone();
tree.deleteNode(node);
this.setState({treeData: tree});
}
moveTreeNode = (nodePath, moveToPath, moveToRepo) => {
let tree = this.state.treeData.clone();
if (repoID === moveToRepo.repo_id) {
tree.moveNodeByPath(nodePath, moveToPath, true);
} else { } else {
editorUtilities.deleteFile(filePath).then(() => { tree.deleteNodeByPath(nodePath);
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) { this.setState({treeData: tree});
tree.deleteNode(node);
if (isCurrentFile) {
let homeNode = this.getHomeNode(tree);
tree.expandNode(homeNode);
this.setState({
tree_data: tree,
changedNode: homeNode
});
this.initMainPanelData(homeNode.path);
} else {
this.setState({tree_data: tree});
}
} 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});
}
}
} }
copyTreeNode = (nodePath, copyToPath) => {
enterViewFileState(newTree, newNode, newPath) { let tree = this.state.treeData.clone();
this.setState({ tree.moveNodeByPath(nodePath, copyToPath, false);
tree_data: newTree, this.setState({treeData: tree});
changedNode: newNode,
filePath: newPath,
isViewFileState: true
});
}
exitViewFileState(newTree, newNode) {
this.setState({
tree_data: newTree,
changedNode: newNode,
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,
type: type, type: type,
size: '0', size: '0',
last_update_time: moment.unix(date).fromNow(), last_update_time: moment.unix(date).fromNow(),
isExpanded: false, isExpanded: false,
children: [] children: []
}); });
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',
isModifyContainsCurrentFile(node) { name: name,
let filePath = this.state.filePath; type: type,
let nodePath = node.path; mtime: data,
permission: 'rw',
if (filePath.indexOf(nodePath) > -1) { });
return true; } 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 false; return dirent;
} }
isMarkdownFile(filePath) { isMarkdownFile(filePath) {
@@ -559,39 +621,43 @@ class Wiki extends Component {
return ( return (
<div id="main" className="wiki-main"> <div id="main" className="wiki-main">
<SidePanel <SidePanel
onNodeClick={this.onNodeClick} onNodeClick={this.onTreeNodeClick}
onDirCollapse={this.onTreeDirCollapse}
closeSideBar={this.state.closeSideBar} closeSideBar={this.state.closeSideBar}
onCloseSide ={this.onCloseSide} onCloseSide ={this.onCloseSide}
treeData={this.state.tree_data} treeData={this.state.treeData}
currentFilePath={this.state.filePath} currentPath={this.state.path}
changedNode={this.state.changedNode} currentNode={this.state.currentNode}
onAddFolderNode={this.onAddFolderNode} onAddFolderNode={this.onAddFolder}
onAddFileNode={this.onAddFileNode} onAddFileNode={this.onAddFile}
onRenameNode={this.onRenameNode} onRenameNode={this.onRenameTreeNode}
onDeleteNode={this.onDeleteNode} onDeleteNode={this.onDeleteTreeNode}
onDirCollapse={this.onDirCollapse}
/> />
<MainPanel <MainPanel
content={this.state.content} path={this.state.path}
filePath={this.state.filePath} isViewFile={this.state.isViewFile}
latestContributor={this.state.latestContributor} pathExist={this.state.pathExist}
lastModified={this.state.lastModified} isDirentListLoading={this.state.isDirentListLoading}
permission={this.state.permission}
isViewFileState={this.state.isViewFileState}
changedNode={this.state.changedNode}
isFileLoading={this.state.isFileLoading} isFileLoading={this.state.isFileLoading}
permission={this.state.permission}
content={this.state.content}
lastModified={this.state.lastModified}
latestContributor={this.state.latestContributor}
direntList={this.state.direntList}
switchViewMode={this.switchViewMode} switchViewMode={this.switchViewMode}
updateDirent={this.updateDirent}
onLinkClick={this.onLinkClick} onLinkClick={this.onLinkClick}
onMenuClick={this.onMenuClick} onSideNavMenuClick={this.onSideNavMenuClick}
onSearchedClick={this.onSearchedClick} onSearchedClick={this.onSearchedClick}
onMainNavBarClick={this.onMainNavBarClick} onMainNavBarClick={this.onMainNavBarClick}
onMainItemClick={this.onMainItemClick} onItemClick={this.onDirentClick}
onMainItemDelete={this.onMainItemDelete} onItemDelete={this.onMainPanelItemDelete}
onMainItemRename={this.onMainItemRename} onItemRename={this.onMainPanelItemRename}
onMainItemMove={this.onMainItemMove} onItemMove={this.onMoveItem}
onMainItemCopy={this.onMainItemCopy} onItemCopy={this.onCopyItem}
onMainAddFile={this.onAddFileNode} onAddFile={this.onAddFile}
onMainAddFolder={this.onAddFolderNode} onAddFolder={this.onAddFolder}
onFileTagChanged={this.onFileTagChanged}
/> />
</div> </div>
); );

View File

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

View File

@@ -12,12 +12,12 @@ export const Utils = {
bytesToSize: function(bytes) { bytesToSize: function(bytes) {
if (typeof(bytes) == 'undefined') return ' '; if (typeof(bytes) == 'undefined') return ' ';
if(bytes < 0) return '--'; if(bytes < 0) return '--';
const sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; const sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
if (bytes === 0) return bytes + ' ' + sizes[0]; if (bytes === 0) return bytes + ' ' + sizes[0];
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)), 10); const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)), 10);
if (i === 0) return bytes + ' ' + sizes[i]; if (i === 0) return bytes + ' ' + sizes[i];
return (bytes / (1000 ** i)).toFixed(1) + ' ' + sizes[i]; return (bytes / (1000 ** i)).toFixed(1) + ' ' + sizes[i];
@@ -168,6 +168,40 @@ export const Utils = {
return filePath.slice(lastIndex+1); return filePath.slice(lastIndex+1);
}, },
/*
return dirname of a path.
if path is '/', return '/'.
*/
getDirName: function(path) {
let dir = path.slice(0, path.lastIndexOf('/'));
if (dir === '') {
return '/';
} else {
return dir;
}
},
isChildPath: function(child, parent) {
let p = this.getDirName(child);
return p === parent;
},
isAncestorPath: function(ancestor, path) {
return path.indexOf(ancestor) > -1;
},
renameAncestorPath: function(path, ancestor, newAncestor) {
return newAncestor + '/' + path.replace(ancestor, '');
},
joinPath: function(pathA, pathB) {
if (pathA[pathA.length-1] === '/') {
return pathA + pathB;
} else {
return pathA + '/' + pathB;
}
},
isSupportUploadFolder: function() { isSupportUploadFolder: function() {
return navigator.userAgent.indexOf('Firefox')!=-1 || return navigator.userAgent.indexOf('Firefox')!=-1 ||
navigator.userAgent.indexOf('Chrome') > -1; navigator.userAgent.indexOf('Chrome') > -1;

View File

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

Binary file not shown.

View File

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

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

View File

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

View File

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