diff --git a/frontend/src/components/dialog/copy-dirent-dialog.js b/frontend/src/components/dialog/copy-dirent-dialog.js index 61c6b1af55..501321109a 100644 --- a/frontend/src/components/dialog/copy-dirent-dialog.js +++ b/frontend/src/components/dialog/copy-dirent-dialog.js @@ -124,7 +124,7 @@ class CopyDirent extends React.Component { return; } - this.props.onItemCopy(repo, direntPath, selectedPath); + this.props.onItemCopy(repo, this.props.dirent, selectedPath); this.toggle(); } diff --git a/frontend/src/components/dialog/move-dirent-dialog.js b/frontend/src/components/dialog/move-dirent-dialog.js index 10cfc50529..afd6032dae 100644 --- a/frontend/src/components/dialog/move-dirent-dialog.js +++ b/frontend/src/components/dialog/move-dirent-dialog.js @@ -124,7 +124,7 @@ class MoveDirent extends React.Component { return; } - this.props.onItemMove(repo, direntPath, selectedPath); + this.props.onItemMove(repo, this.props.dirent, selectedPath); this.toggle(); } diff --git a/frontend/src/components/dir-view/dir-panel.js b/frontend/src/components/dir-view/dir-panel.js new file mode 100644 index 0000000000..cc7ce5a04a --- /dev/null +++ b/frontend/src/components/dir-view/dir-panel.js @@ -0,0 +1,208 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import cookie from 'react-cookies'; +import { gettext, siteRoot } from '../../utils/constants'; +import { seafileAPI } from '../../utils/seafile-api'; +import CommonToolbar from '../toolbar/common-toolbar'; +import ViewModeToolbar from '../toolbar/view-mode-toolbar'; +import DirOperationToolBar from '../toolbar/dir-operation-toolbar'; +import MutipleDirOperationToolbar from '../toolbar/mutilple-dir-operation-toolbar'; +import CurDirPath from '../cur-dir-path'; +import DirentListView from '../dirent-list-view/dirent-list-view'; +import DirentDetail from '../dirent-detail/dirent-details'; +import FileUploader from '../file-uploader/file-uploader'; + +const propTypes = { + currentRepo: PropTypes.object, + path: PropTypes.string.isRequired, + repoID: PropTypes.string.isRequired, + repoName: PropTypes.string.isRequired, + pathExist: PropTypes.bool.isRequired, + permission: PropTypes.bool.isRequired, + isDirentListLoading: PropTypes.bool.isRequired, + isDirentSelected: PropTypes.bool.isRequired, + isAllDirentSelected: PropTypes.bool.isRequired, + direntList: PropTypes.array.isRequired, + selectedDirentList: PropTypes.array.isRequired, + onItemClick: PropTypes.func.isRequired, + onAddFile: PropTypes.func.isRequired, + onAddFolder: PropTypes.func.isRequired, + onItemMove: PropTypes.func.isRequired, + onItemCopy: PropTypes.func.isRequired, + onItemRename: PropTypes.func.isRequired, + onItemDelete: PropTypes.func.isRequired, + onItemSelected: PropTypes.func.isRequired, + onItemsCopy: PropTypes.func.isRequired, + onItemsMove: PropTypes.func.isRequired, + onItemsDelete: PropTypes.func.isRequired, + onAllItemSelected: PropTypes.func.isRequired, + onFileTagChanged: PropTypes.func.isRequired, + onMenuClick: PropTypes.func.isRequired, + onPathClick: PropTypes.func.isRequired, + updateDirent: PropTypes.func.isRequired, + onSearchedClick: PropTypes.func.isRequired, + onFileUploadSuccess: PropTypes.func.isRequired, +}; + +class DirPanel extends React.Component { + + constructor(props) { + super(props); + this.state = { + currentDirent: null, + currentMode: 'list', + isDirentDetailShow: false, + isRepoOwner: true, + }; + } + + componentDidMount() { + let currentRepo = this.props.currentRepo; + if (currentRepo) { + seafileAPI.getAccountInfo().then(res => { + let user_email = res.data.email; + let isRepoOwner = currentRepo.owner_email === user_email; + this.setState({isRepoOwner: isRepoOwner}); + }); + } + } + + onItemDetails = (dirent) => { + this.setState({ + currentDirent: dirent, + isDirentDetailShow: true, + }); + } + + onItemDetailsClose = () => { + this.setState({isDirentDetailShow: false}); + } + + onUploadFile = (e) => { + e.nativeEvent.stopImmediatePropagation(); + this.uploader.onFileUpload(); + } + + onUploadFolder = (e) => { + e.nativeEvent.stopImmediatePropagation(); + this.uploader.onFolderUpload(); + } + + switchViewMode = (mode) => { + let { path, repoID } = this.props; + if (mode === this.state.currentMode) { + return; + } + if (mode === 'wiki') { + var url = siteRoot + 'wiki/lib/' + repoID + path; + window.location = url; + } + cookie.save('view_mode', mode, { path: '/' }); + + this.setState({currentMode: mode}); + } + + render() { + const ErrMessage = (

{gettext('Folder does not exist.')}

); + + return ( +
+
+
+ +
+ {this.props.isDirentSelected ? + : + + } +
+ +
+ +
+
+
+
+ +
+
+ {!this.props.pathExist ? + ErrMessage : + + + this.uploader = uploader} + path={this.props.path} + repoID={this.props.repoID} + direntList={this.props.direntList} + onFileUploadSuccess={this.props.onFileUploadSuccess} + /> + + } +
+
+ {this.state.isDirentDetailShow && ( +
+ +
+ )} +
+
+ ); + } +} + +DirPanel.propTypes = propTypes; + +export default DirPanel; diff --git a/frontend/src/components/dir-view/dir-view.js b/frontend/src/components/dir-view/dir-view.js new file mode 100644 index 0000000000..5ab9b85f2c --- /dev/null +++ b/frontend/src/components/dir-view/dir-view.js @@ -0,0 +1,465 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { siteRoot } from '../../utils/constants'; +import { seafileAPI } from '../../utils/seafile-api'; +import { Utils } from '../../utils/utils'; +import { gettext } from '../../utils/constants'; +import Toast from '../toast'; +import DirPanel from './dir-panel'; +import Dirent from '../../models/dirent'; +import FileTag from '../../models/file-tag'; +import Repo from '../../models/repo'; + +const propTypes = { + onMenuClick: PropTypes.func.isRequired, +}; + +class DirView extends React.Component { + + constructor(props) { + super(props); + + this.state = { + path: '/', + pathExit: true, + repoName: '', + repoID: '', + permission: true, + isDirentSelected: false, + isAllDirentSelected: false, + isDirentListLoading: true, + currentRepo: null, + direntList: [], + selectedDirentList: [], + }; + window.onpopstate = this.onpopstate; + } + + onpopstate = (event) => { + if (event.state && event.state.path) { + this.updateDirentList(event.state.path); + this.setState({path: event.state.path}); + } + } + + componentDidMount() { + let repoID = this.props.repoID; + let index = location.href.indexOf(repoID) + repoID.length; + let path = decodeURIComponent(location.href.slice(index)); + seafileAPI.getRepoInfo(repoID).then(res => { + let repo = new Repo(res.data); + this.setState({ + repoID: repo.repo_id, + repoName: repo.repo_name, + permission: repo.permission === 'rw', + currentRepo: repo, + }); + this.updateDirentList(path); + this.setState({path: path}); + }); + } + + updateDirentList = (filePath) => { + let repoID = this.state.repoID; + this.setState({isDirentListLoading: true}); + seafileAPI.listDir(repoID, filePath).then(res => { + let direntList = res.data.map(item => { + return new Dirent(item); + }); + this.setState({ + isDirentListLoading: false, + direntList: direntList, + }); + }).catch(() => { + this.setState({pathExist: false}); + }); + } + + onItemClick = (dirent) => { + this.resetSelected(); + let direntPath = Utils.joinPath(this.state.path, dirent.name); + if (dirent.isDir()) { + this.updateDirentList(direntPath); + this.setState({path: direntPath}); + + let fileUrl = siteRoot + 'librarys/' + this.state.repoID + direntPath; + window.history.pushState({url: fileUrl, path: direntPath}, direntPath, fileUrl); + } else { + const w=window.open('about:blank'); + const url = siteRoot + 'lib/' + this.state.repoID + '/file' + direntPath; + w.location.href = url; + } + } + + onAddFolder = (dirPath) => { + let repoID = this.state.repoID; + seafileAPI.createDir(repoID, dirPath).then(() => { + let name = Utils.getFileName(dirPath); + let dirent = this.createDirent(name, 'dir'); + let direntList = this.addItem(dirent, 'dir'); + this.setState({direntList: direntList}); + }); + } + + onAddFile = (filePath, isDraft) => { + let repoID = this.state.repoID; + seafileAPI.createDir(repoID, filePath).then(() => { + let name = Utils.getFileName(filePath); + let dirent = this.createDirent(name, 'file'); + let direntList = this.addItem(dirent, 'file'); + this.setState({direntList: direntList}); + }); + } + + onItemDelete = (dirent) => { + let repoID = this.state.repoID; + let direntPath = Utils.joinPath(this.state.path, dirent.name); + if (dirent.isDir()) { + seafileAPI.deleteDir(repoID, direntPath).then(() => { + let direntList = this.deleteItem(dirent); + this.setState({direntList: direntList}); + }).catch(() => { + // todo + }) + } else { + seafileAPI.deleteFile(repoID, direntPath).then(() => { + let direntList = this.deleteItem(dirent); + this.setState({direntList: direntList}); + }).catch(() => { + // todo + }) + } + } + + onItemRename = (dirent, newName) => { + let repoID = this.state.repoID; + let direntPath = Utils.joinPath(this.state.path, dirent.name); + if (dirent.isDir()) { + seafileAPI.renameDir(repoID, direntPath, newName).then(() => { + let direntList = this.renameItem(dirent, newName); + this.setState({direntList: direntList}); + }).catch(() => { + //todo + }); + } else { + seafileAPI.renameFile(repoID, direntPath, newName).then(() => { + let direntList = this.renameItem(dirent, newName); + this.setState({direntList: direntList}); + }).catch(() => { + //todo + }); + } + } + + onItemMove = (destRepo, dirent, moveToDirentPath) => { + let dirName = dirent.name; + let repoID = this.state.repoID; + seafileAPI.moveDir(repoID, destRepo.repo_id, moveToDirentPath, this.state.path, dirName).then(() => { + + let direntList = this.deleteItem(dirent); + this.setState(direntList); + + 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); + }); + } + + onItemCopy = (destRepo, dirent, copyToDirentPath) => { + let dirName = dirent.name; + let repoID = this.state.repoID; + seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(() => { + 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); + }); + } + + onItemSelected = (dirent) => { + let direntList = this.state.direntList.map(item => { + if (item.name === dirent.name) { + item.isSelected = !item.isSelected; + } + return item; + }); + let selectedDirentList = direntList.filter(item => { + return item.isSelected; + }); + + if (selectedDirentList.length) { + this.setState({isDirentSelected: true}); + if (selectedDirentList.length === direntList.length) { + this.setState({ + isAllDirentSelected: true, + direntList: direntList, + selectedDirentList: selectedDirentList, + }); + } else { + this.setState({ + isAllDirentSelected: false, + direntList: direntList, + selectedDirentList: selectedDirentList + }); + } + } else { + this.setState({ + isDirentSelected: false, + isAllDirentSelected: false, + direntList: direntList, + selectedDirentList: [] + }); + } + } + + onItemsMove = (destRepo, destDirentPath) => { + let dirNames = this.getSelectedDirentNames(); + let repoID = this.state.repoID; + seafileAPI.moveDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(() => { + let direntList = this.deleteItems(dirNames); + this.setState({direntList: direntList}); + let message = gettext('Successfully moved %(name)s.'); + message = message.replace('%(name)s', dirNames); + Toast.success(message); + }).catch(() => { + let message = gettext('Failed to move %(name)s'); + message = message.replace('%(name)s', dirNames); + Toast.error(message); + }); + } + + onItemsCopy = (destRepo, destDirentPath) => { + let dirNames = this.getSelectedDirentNames(); + let repoID = this.state.repoID; + seafileAPI.copyDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(() => { + let message = gettext('Successfully copied %(name)s.'); + message = message.replace('%(name)s', dirNames); + Toast.success(message); + }).catch(() => { + let message = gettext('Failed to copy %(name)s'); + message = message.replace('%(name)s', dirNames); + Toast.error(message); + }); + } + + onItemsDelete = () => { + let dirNames = this.getSelectedDirentNames(); + let repoID = this.state.repoID; + seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => { + let direntList = this.deleteItems(dirNames); + this.setState({direntList: direntList}); + }); + } + + onAllItemSelected = () => { + if (this.state.isAllDirentSelected) { + let direntList = this.state.direntList.map(item => { + item.isSelected = false; + return item; + }); + this.setState({ + isDirentSelected: false, + isAllDirentSelected: false, + direntList: direntList, + selectedDirentList: [], + }); + } else { + let direntList = this.state.direntList.map(item => { + item.isSelected = true; + return item; + }); + this.setState({ + isDirentSelected: true, + isAllDirentSelected: true, + direntList: direntList, + selectedDirentList: direntList, + }); + } + } + + onFileTagChanged = (dirent, direntPath) => { + let repoID = this.state.repoID; + seafileAPI.listFileTags(repoID, direntPath).then(res => { + let fileTags = res.data.file_tags.map(item => { + return new FileTag(item); + }); + this.updateDirent(dirent, 'file_tags', fileTags); + }); + } + + onMenuClick = () => { + this.props.onMenuClick(); + } + + onPathClick = (path) => { + this.updateDirentList(path); + this.setState({path: path}); + + let fileUrl = siteRoot + 'librarys/' + this.state.repoID + path; + window.history.pushState({url: fileUrl, path: path}, path, fileUrl); + } + + updateDirent = (dirent, paramKey, paramValue) => { + let newDirentList = this.state.direntList.map(item => { + if (item.name === dirent.name) { + item[paramKey] = paramValue; + } + return item; + }); + this.setState({direntList: newDirentList}); + } + + onFileUploadSuccess = () => { + // todo update upload file to direntList + } + + onSearchedClick = () => { + // todo + } + + resetSelected = () => { + this.setState({isDirentSelected: false, isAllDirentSelected: false}); + } + + addItem = (dirent, type) => { + let direntList = this.state.direntList.map(item => {return item}); //clone + if (type === 'dir') { + direntList.unshift(dirent); + return direntList; + } + direntList.push(dirent); + return direntList; + } + + deleteItem = (dirent) => { + return this.state.direntList.filter(item => { + return item.name !== dirent.name; + }); + } + + renameItem = (dirent, newName) => { + return this.state.direntList.map(item => { + if (item.name === dirent.name) { + item.name = newName; + } + return item; + }); + } + + deleteItems = (dirNames) => { + let direntList = this.state.direntList.map(item => {return item}); //clone + while (dirNames.length) { + for (let i = 0; i < direntList.length; i++) { + if (direntList[i].name === dirNames[0]) { + direntList.splice(i, 1); + break; + } + } + dirNames.shift(); + } + return direntList; + } + + createDirent(name, type) { + let data = new Date().getTime()/1000; + let dirent = null; + if (type === 'dir') { + dirent = new Dirent({ + id: '000000000000000000', + name: name, + type: type, + mtime: data, + permission: 'rw', + }); + } else { + dirent = new Dirent({ + id: '000000000000000000', + name: name, + type: type, + mtime: data, + permission: 'rw', + size: 0, + starred: false, + is_locked: false, + lock_time: '', + lock_owner: null, + locked_by_me: false, + modifier_name: '', + modifier_email: '', + modifier_contact_email: '', + file_tags: [] + }); + } + return dirent; + } + + getSelectedDirentNames = () => { + let names = []; + this.state.selectedDirentList.forEach(selectedDirent => { + names.push(selectedDirent.name); + }); + return names; + } + + isMarkdownFile(filePath) { + let index = filePath.lastIndexOf('.'); + if (index === -1) { + return false; + } else { + let type = filePath.substring(index).toLowerCase(); + if (type === '.md' || type === '.markdown') { + return true; + } else { + return false; + } + } + } + + render() { + return ( + + ); + } +} + +DirView.propTypes = propTypes; + +export default DirView; diff --git a/frontend/src/components/dirent-detail/detail-list-view.js b/frontend/src/components/dirent-detail/detail-list-view.js index 8a1d74ccfa..654dea48cf 100644 --- a/frontend/src/components/dirent-detail/detail-list-view.js +++ b/frontend/src/components/dirent-detail/detail-list-view.js @@ -11,7 +11,7 @@ const propTypes = { dirent: PropTypes.object.isRequired, direntType: PropTypes.string.isRequired, direntDetail: PropTypes.object.isRequired, - direntPath: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, fileTagList: PropTypes.array.isRequired, onFileTagChanged: PropTypes.func.isRequired, }; @@ -26,7 +26,8 @@ class DetailListView extends React.Component { } getDirentPostion = () => { - let { repo, direntPath } = this.props; + let { repo } = this.props; + let direntPath = this.getDirentPath(); let position = repo.repo_name; if (direntPath !== '/') { let index = direntPath.lastIndexOf('/'); @@ -43,12 +44,19 @@ class DetailListView extends React.Component { } onFileTagChanged = () => { - this.props.onFileTagChanged(this.props.dirent, this.props.direntPath); + let direntPath = this.getDirentPath(); + this.props.onFileTagChanged(this.props.dirent, direntPath); + } + + getDirentPath = () => { + let { dirent, path } = this.props; + return Utils.joinPath(path, dirent.name); } render() { let { direntType, direntDetail, fileTagList } = this.props; let position = this.getDirentPostion(); + let direntPath = this.getDirentPath(); if (direntType === 'dir') { return ( @@ -93,7 +101,7 @@ class DetailListView extends React.Component { diff --git a/frontend/src/components/dirent-detail/dirent-details.js b/frontend/src/components/dirent-detail/dirent-details.js index 0534006725..ccf6e8097d 100644 --- a/frontend/src/components/dirent-detail/dirent-details.js +++ b/frontend/src/components/dirent-detail/dirent-details.js @@ -1,5 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Utils } from '../../utils/utils'; +import { siteRoot } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import DetailListView from './detail-list-view'; import Repo from '../../models/repo'; @@ -8,9 +10,8 @@ import '../../css/dirent-detail.css'; const propTypes = { repoID: PropTypes.string.isRequired, - serviceUrl: PropTypes.string.isRequired, dirent: PropTypes.object.isRequired, - direntPath: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, onItemDetailsClose: PropTypes.func.isRequired, onFileTagChanged: PropTypes.func.isRequired, }; @@ -28,7 +29,8 @@ class DirentDetail extends React.Component { } componentDidMount() { - let { dirent, direntPath, repoID } = this.props; + let { dirent, path, repoID } = this.props; + let direntPath = Utils.joinPath(path, dirent.name); seafileAPI.getRepoInfo(repoID).then(res => { let repo = new Repo(res.data); this.setState({repo: repo}); @@ -37,7 +39,9 @@ class DirentDetail extends React.Component { } componentWillReceiveProps(nextProps) { - this.updateDetailView(nextProps.dirent, nextProps.direntPath); + let { dirent, path } = nextProps; + let direntPath = Utils.joinPath(path, dirent.name); + this.updateDetailView(dirent, direntPath); } updateDetailView = (dirent, direntPath) => { @@ -68,27 +72,27 @@ class DirentDetail extends React.Component { } render() { - let { dirent, serviceUrl } = this.props; + let { dirent } = this.props; return (
- icon + icon {dirent.name}
- icon + icon
{this.state.direntDetail &&
{ - let direntPath = this.getDirentPath(this.props.dirent); - this.props.onItemClick(direntPath); + this.props.onItemClick(this.props.dirent); } onItemDelete = (e) => { @@ -244,8 +242,7 @@ class DirentListItem extends React.Component { } onDetailsItem = () => { - let direntPath = this.getDirentPath(this.props.dirent); - this.props.onItemDetails(this.props.dirent, direntPath); + this.props.onItemDetails(this.props.dirent); this.onItemMenuHide(); } @@ -368,7 +365,7 @@ class DirentListItem extends React.Component { } render() { - let { dirent, serviceUrl } = this.props; + let { dirent } = this.props; return (
@@ -381,8 +378,8 @@ class DirentListItem extends React.Component { diff --git a/frontend/src/components/tree-view/node.js b/frontend/src/components/tree-view/node.js index 1e89693ba6..42bbb8f9e9 100644 --- a/frontend/src/components/tree-view/node.js +++ b/frontend/src/components/tree-view/node.js @@ -65,6 +65,9 @@ class Node { } isMarkdown() { + if (this.isDir()) { + return false; + } let index = this.name.lastIndexOf('.'); if (index == -1) { return false; diff --git a/frontend/src/css/layout.css b/frontend/src/css/layout.css index 1adfce6cf8..056d7df755 100644 --- a/frontend/src/css/layout.css +++ b/frontend/src/css/layout.css @@ -121,6 +121,7 @@ display: flex; flex: 1; flex-direction: column; + min-height: 0; } .header { diff --git a/frontend/src/pages/dashboard/files-activities.js b/frontend/src/pages/dashboard/files-activities.js index 53862d0b45..9b49f8683c 100644 --- a/frontend/src/pages/dashboard/files-activities.js +++ b/frontend/src/pages/dashboard/files-activities.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { seafileAPI } from '../../utils/seafile-api'; import { gettext, siteRoot } from '../../utils/constants'; @@ -17,7 +17,7 @@ class FileActivitiesContent extends Component { return

{error_msg}

; } else { return ( -
+
- {gettext('file - {dirent.is_locked && {gettext('locked')}} + {gettext('file + {dirent.is_locked && {gettext('locked')}}
diff --git a/frontend/src/components/dirent-list-view/dirent-list-view.js b/frontend/src/components/dirent-list-view/dirent-list-view.js index 8c4337bc3c..999f814bcf 100644 --- a/frontend/src/components/dirent-list-view/dirent-list-view.js +++ b/frontend/src/components/dirent-list-view/dirent-list-view.js @@ -7,7 +7,6 @@ import DirentListItem from './dirent-list-item'; const propTypes = { path: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired, - serviceUrl: PropTypes.string.isRequired, isRepoOwner: PropTypes.bool, currentRepo: PropTypes.object, isAllItemSelected: PropTypes.bool.isRequired, @@ -45,8 +44,8 @@ class DirentListView extends React.Component { this.onFreezedItem(); } - onItemDetails = (dirent, direntPath) => { - this.props.onItemDetails(dirent, direntPath); + onItemDetails = (dirent) => { + this.props.onItemDetails(dirent); } render() { @@ -81,7 +80,6 @@ class DirentListView extends React.Component { dirent={dirent} path={this.props.path} repoID={this.props.repoID} - serviceUrl={this.props.serviceUrl} currentRepo={this.props.currentRepo} isRepoOwner={this.props.isRepoOwner} onItemClick={this.props.onItemClick} diff --git a/frontend/src/components/file-uploader/file-uploader.js b/frontend/src/components/file-uploader/file-uploader.js index 6f3387d3e9..1031d3d396 100644 --- a/frontend/src/components/file-uploader/file-uploader.js +++ b/frontend/src/components/file-uploader/file-uploader.js @@ -26,7 +26,7 @@ const propTypes = { fileTypeErrorCallback: PropTypes.func, dragAndDrop: PropTypes.bool.isRequired, path: PropTypes.string.isRequired, - onFileSuccess: PropTypes.func.isRequired, + onFileUploadSuccess: PropTypes.func.isRequired, }; class FileUploader extends React.Component { @@ -97,7 +97,7 @@ class FileUploader extends React.Component { this.resumable.on('fileAdded', this.onFileAdded); this.resumable.on('filesAddedComplete', this.filesAddedComplete); this.resumable.on('fileProgress', this.onFileProgress); - this.resumable.on('fileSuccess', this.onFileSuccess); + this.resumable.on('fileSuccess', this.onFileUploadSuccess); this.resumable.on('progress', this.onProgress); this.resumable.on('complete', this.onComplete); this.resumable.on('pause', this.onPause); @@ -205,7 +205,7 @@ class FileUploader extends React.Component { this.setState({uploadFileList: uploadFileList}); } - onFileSuccess = (file) => { + onFileUploadSuccess = (file) => { // todos, update uploadList or updateList; } diff --git a/frontend/src/components/search/search.js b/frontend/src/components/search/search.js index b87187f7b0..72034daf28 100644 --- a/frontend/src/components/search/search.js +++ b/frontend/src/components/search/search.js @@ -179,7 +179,7 @@ class Search extends Component { for (let key in queryData) { params += key + '=' + queryData[key] + '&'; } - window.location = siteRoot + '/search/?' + params.slice(0, params.length - 1); + window.location = siteRoot + 'search/?' + params.slice(0, params.length - 1); } renderSearchResult() { diff --git a/frontend/src/components/toolbar/dir-operation-toolbar.js b/frontend/src/components/toolbar/dir-operation-toolbar.js index c4ee406420..483a3a0c6d 100644 --- a/frontend/src/components/toolbar/dir-operation-toolbar.js +++ b/frontend/src/components/toolbar/dir-operation-toolbar.js @@ -1,17 +1,16 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { Utils } from '../../utils/utils'; -import { gettext } from '../../utils/constants'; +import { gettext, siteRoot } from '../../utils/constants'; import ModalPortal from '../modal-portal'; import CreateFolder from '../../components/dialog/create-folder-dialog'; import CreateFile from '../../components/dialog/create-file-dialog'; const propTypes = { - isViewFile: PropTypes.bool, + isViewFile: PropTypes.bool, // just for view file, + permission: PropTypes.string, //just for view file, and premission is file permission path: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired, - serviceUrl: PropTypes.string.isRequired, - permission: PropTypes.string.isRequired, onAddFile: PropTypes.func.isRequired, onAddFolder: PropTypes.func.isRequired, onUploadFile: PropTypes.func.isRequired, @@ -58,8 +57,8 @@ class DirOperationToolbar extends React.Component { onEditClick = (e) => { e.preventDefault(); - let { path, repoID, serviceUrl } = this.props; - window.location.href= serviceUrl + '/lib/' + repoID + '/file' + path + '?mode=edit'; + let { path, repoID } = this.props; + window.location.href= siteRoot + 'lib/' + repoID + '/file' + path + '?mode=edit'; } onUploadClick = (e) => { diff --git a/frontend/src/components/tree-dir-view/tree-dir-list.js b/frontend/src/components/tree-dir-view/tree-dir-list.js index eb986370d6..dea36fe84c 100644 --- a/frontend/src/components/tree-dir-view/tree-dir-list.js +++ b/frontend/src/components/tree-dir-view/tree-dir-list.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { serviceUrl } from '../../utils/constants'; +import { siteRoot } from '../../utils/constants'; const propTypes = { node: PropTypes.object.isRequired, @@ -35,7 +35,7 @@ class TreeDirList extends React.Component { return (
- icon + icon {node.name} {node.size}
@@ -32,7 +32,7 @@ class FileActivitiesContent extends Component {
{has_more ? : ''} {error_msg ?

{error_msg}

: ''} - + ); } } diff --git a/frontend/src/pages/repo-wiki-mode/main-panel.js b/frontend/src/pages/repo-wiki-mode/main-panel.js index 4d1e8ac58a..4a97305953 100644 --- a/frontend/src/pages/repo-wiki-mode/main-panel.js +++ b/frontend/src/pages/repo-wiki-mode/main-panel.js @@ -1,7 +1,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import cookie from 'react-cookies'; -import { gettext, repoID, serviceUrl, slug, permission } from '../../utils/constants'; +import { gettext, repoID, siteRoot, slug, permission } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import Repo from '../../models/repo'; @@ -81,7 +81,7 @@ class MainPanel extends Component { switchViewMode = (mode) => { cookie.save('view_mode', mode, { path: '/' }); let dirPath = this.props.isViewFile ? Utils.getDirName(this.props.path) : this.props.path; - window.location.href = serviceUrl + '/#common/lib/' + repoID + dirPath; + window.location.href = siteRoot + '#common/lib/' + repoID + dirPath; } onSideNavMenuClick = () => { @@ -92,10 +92,9 @@ class MainPanel extends Component { this.props.onMainNavBarClick(path); } - onItemDetails = (dirent, direntPath) => { + onItemDetails = (dirent) => { this.setState({ currentDirent: dirent, - direntPath: direntPath, isDirentDetailShow: true, }); } @@ -118,14 +117,16 @@ class MainPanel extends Component { this.uploader.onFolderUpload(); } - onFileSuccess = (file) => { + onFileUploadSuccess = (file) => { // todo } render() { + const ErrMessage = (

{gettext('Folder does not exist.')}

); + return (
-
+
@@ -141,7 +142,6 @@ class MainPanel extends Component {
- { !this.props.pathExist ? -

{gettext('Folder does not exist.')}

: + {!this.props.pathExist ? + ErrMessage : { this.props.isViewFile ? @@ -212,18 +211,18 @@ class MainPanel extends Component { }
- { this.state.isDirentDetailShow && + {this.state.isDirentDetailShow && (
- } + )}
); diff --git a/frontend/src/pages/wiki/main-panel.js b/frontend/src/pages/wiki/main-panel.js index e7b4b8230a..b2e503f974 100644 --- a/frontend/src/pages/wiki/main-panel.js +++ b/frontend/src/pages/wiki/main-panel.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { gettext, repoID, serviceUrl, slug, siteRoot } from '../../utils/constants'; +import { gettext, repoID, slug, siteRoot } from '../../utils/constants'; import CommonToolbar from '../../components/toolbar/common-toolbar'; import MarkdownViewer from '../../components/markdown-viewer'; import TreeDirView from '../../components/tree-dir-view/tree-dir-view'; @@ -39,7 +39,7 @@ class MainPanel extends Component { onEditClick = (e) => { // const w=window.open('about:blank') e.preventDefault(); - window.location.href= serviceUrl + '/lib/' + repoID + '/file' + this.props.filePath + '?mode=edit'; + window.location.href= siteRoot + 'lib/' + repoID + '/file' + this.props.filePath + '?mode=edit'; } onMainNavBarClick = (e) => { diff --git a/frontend/src/repo-wiki-mode.js b/frontend/src/repo-wiki-mode.js index 1ecfc2217b..f8471bb588 100644 --- a/frontend/src/repo-wiki-mode.js +++ b/frontend/src/repo-wiki-mode.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import moment from 'moment'; -import { gettext, repoID, serviceUrl, initialPath, isDir } from './utils/constants'; +import { gettext, repoID, siteRoot, initialPath, isDir } from './utils/constants'; import { seafileAPI } from './utils/seafile-api'; import { Utils } from './utils/utils'; import SidePanel from './pages/repo-wiki-mode/side-panel'; @@ -133,14 +133,13 @@ class Wiki extends Component { }); } - onMoveItem = (repo, direntPath, moveToDirentPath) => { + onMoveItem = (destRepo, dirent, moveToDirentPath) => { //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(() => { + let dirName = dirent.name + let direntPath = Utils.joinPath(this.state.path, dirName); + seafileAPI.moveDir(repoID, destRepo.repo_id,moveToDirentPath, this.state.path, dirName).then(() => { - this.moveTreeNode(direntPath, moveToDirentPath, repo); + this.moveTreeNode(direntPath, moveToDirentPath, destRepo); this.moveDirent(direntPath); let message = gettext('Successfully moved %(name)s.'); @@ -153,13 +152,12 @@ class Wiki extends Component { }); } - onCopyItem = (repo, direntPath, copyToDirentPath) => { + onCopyItem = (destRepo, dirent, copyToDirentPath) => { //just for view list state - let index = direntPath.lastIndexOf('/'); - let dirPath = direntPath.slice(0, index + 1); - let dirName = direntPath.slice(index + 1); - seafileAPI.copyDir(repoID, repo.repo_id, copyToDirentPath, dirPath, dirName).then(() => { - this.copyTreeNode(direntPath, copyToDirentPath, repo); + let dirName = dirent.name; + let direntPath = Utils.joinPath(this.state.path, dirName); + seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(() => { + this.copyTreeNode(direntPath, copyToDirentPath, destRepo); let message = gettext('Successfully copied %(name)s.'); message = message.replace('%(name)s', dirName); Toast.success(message); @@ -224,7 +222,7 @@ class Wiki extends Component { }); }); - let fileUrl = serviceUrl + '/wiki/lib/' + repoID + filePath; + let fileUrl = siteRoot + 'wiki/lib/' + repoID + filePath; window.history.pushState({url: fileUrl, path: filePath}, filePath, fileUrl); } @@ -236,7 +234,7 @@ class Wiki extends Component { }); // update location url - let url = serviceUrl + '/wiki/lib/' + repoID + path; + let url = siteRoot + 'wiki/lib/' + repoID + path; window.history.pushState({ url: url, path: path}, path, url); } @@ -312,8 +310,9 @@ class Wiki extends Component { this.showDir(node.path); } - onDirentClick = (direntPath) => { + onDirentClick = (dirent) => { this.resetSelected(); + let direntPath = Utils.joinPath(this.state.path, dirent.name); let tree = this.state.treeData.clone(); let node = tree.getNodeByPath(direntPath); let parentNode = tree.findNodeParentFromTree(node); @@ -327,7 +326,7 @@ class Wiki extends Component { this.showDir(node.path); } else { const w=window.open('about:blank'); - const url = serviceUrl + '/lib/' + repoID + '/file' + node.path; + const url = siteRoot + 'lib/' + repoID + '/file' + node.path; w.location.href = url; } } @@ -552,7 +551,7 @@ class Wiki extends Component { } } else { const w = window.open('about:blank'); - const url = serviceUrl + '/lib/' + repoID + '/file' + node.path; + const url = siteRoot + 'lib/' + repoID + '/file' + node.path; w.location.href = url; } } @@ -688,24 +687,24 @@ class Wiki extends Component { } isInternalMarkdownLink(url) { - var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '.*\.md$'); + var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '.*\.md$'); return re.test(url); } isInternalDirLink(url) { - var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '/.*'); + var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*'); return re.test(url); } getPathFromInternalMarkdownLink(url) { - var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '(.*\.md)'); + var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '(.*\.md)'); var array = re.exec(url); var path = decodeURIComponent(array[1]); return path; } getPathFromInternalDirLink(url) { - var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '(/.*)'); + var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)'); var array = re.exec(url); var path = decodeURIComponent(array[1]); diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index be8f8f5c8a..ce2e604aa5 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -23,16 +23,12 @@ export const folderPermEnabled = window.app.pageOptions.folderPermEnabled === 'T export const enableUploadFolder = window.app.pageOptions.enableUploadFolder === 'True'; export const enableResumableFileUpload = window.app.pageOptions.enableResumableFileUpload === 'True'; -//main-page -export const mainServiceUrl = window.main ? window.main.pageOptions.serviceUrl : ''; // wiki export const slug = window.wiki ? window.wiki.config.slug : ''; export const repoID = window.wiki ? window.wiki.config.repoId : ''; export const initialPath = window.wiki ? window.wiki.config.initial_path : ''; export const permission = window.wiki ? window.wiki.config.permission === 'True' : ''; export const isDir = window.wiki ? window.wiki.config.isDir : ''; -export const serviceUrl = window.wiki ? window.wiki.config.serviceUrl : ''; - // file history export const PER_PAGE = 25; diff --git a/frontend/src/wiki.js b/frontend/src/wiki.js index 8577d11d16..66530bd775 100644 --- a/frontend/src/wiki.js +++ b/frontend/src/wiki.js @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom'; import SidePanel from './pages/wiki/side-panel'; import MainPanel from './pages/wiki/main-panel'; import moment from 'moment'; -import { slug, repoID, serviceUrl, initialFilePath } from './utils/constants'; +import { slug, repoID, siteRoot, initialFilePath } from './utils/constants'; import editorUtilities from './utils/editor-utilties'; import Node from './components/tree-view/node'; import Tree from './components/tree-view/tree'; @@ -65,7 +65,7 @@ class Wiki extends Component { }); }); const hash = window.location.hash; - let fileUrl = serviceUrl + '/wikis/' + slug + filePath + hash; + let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash; window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl); } }, () => { @@ -90,7 +90,7 @@ class Wiki extends Component { }); const hash = window.location.hash; - let fileUrl = serviceUrl + '/wikis/' + slug + filePath + hash; + let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash; window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl); } @@ -133,7 +133,7 @@ class Wiki extends Component { this.exitViewFileState(tree, node); // update location url - let fileUrl = serviceUrl + '/wikis/' + slug + node.path; + let fileUrl = siteRoot + 'wikis/' + slug + node.path; window.history.pushState({urlPath: fileUrl, filePath: node.path},node.path, fileUrl); } @@ -147,7 +147,7 @@ class Wiki extends Component { this.exitViewFileState(tree, node); } else { const w=window.open('about:blank'); - const url = serviceUrl + '/lib/' + repoID + '/file' + node.path; + const url = siteRoot + 'lib/' + repoID + '/file' + node.path; w.location.href = url; } } @@ -169,7 +169,7 @@ class Wiki extends Component { this.exitViewFileState(tree, node); } else { const w=window.open('about:blank'); - const url = serviceUrl + '/lib/' + repoID + '/file' + node.path; + const url = siteRoot + 'lib/' + repoID + '/file' + node.path; w.location.href = url; } } @@ -371,7 +371,7 @@ class Wiki extends Component { filePath: newNode.path, isViewFileState: false }); - let fileUrl = serviceUrl + '/wikis/' + slug + newNode.path; + let fileUrl = siteRoot + 'wikis/' + slug + newNode.path; window.history.pushState({urlPath: fileUrl, filePath: newNode.path}, newNode.path, fileUrl); } @@ -431,24 +431,24 @@ class Wiki extends Component { } isInternalMarkdownLink(url) { - var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '.*\.md$'); + var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '.*\.md$'); return re.test(url); } isInternalDirLink(url) { - var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '/.*'); + var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*'); return re.test(url); } getPathFromInternalMarkdownLink(url) { - var re = new RegExp(serviceUrl + '/lib/' + repoID + '/file' + '(.*\.md)'); + var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '(.*\.md)'); var array = re.exec(url); var path = decodeURIComponent(array[1]); return path; } getPathFromInternalDirLink(url) { - var re = new RegExp(serviceUrl + '/#[a-z\-]*?/lib/' + repoID + '(/.*)'); + var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)'); var array = re.exec(url); var path = decodeURIComponent(array[1]); diff --git a/media/css/seahub_react.css b/media/css/seahub_react.css index 94ac68b3c4..a48b894fb1 100644 --- a/media/css/seahub_react.css +++ b/media/css/seahub_react.css @@ -1017,14 +1017,6 @@ table .menu-toggle { /* end file-tag */ -/* begin activity page */ -.activity-table-container { - flex: 1; - padding: 10px 16px 20px; - overflow: auto; -} -/* end activity page */ - .dirent-item.tag-list { display: flex; align-items: center; diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 0618e5f83e..c1dcb61a13 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -1212,6 +1212,4 @@ def choose_register(request): @login_required def react_fake_view(request): - return render(request, "react_app.html", { - "service_url": get_service_url().rstrip('/') - }) + return render(request, "react_app.html") diff --git a/seahub/wiki/views.py b/seahub/wiki/views.py index c24c18529e..30bd347cf7 100644 --- a/seahub/wiki/views.py +++ b/seahub/wiki/views.py @@ -76,7 +76,6 @@ def slug(request, slug, file_path="home.md"): "repo_id": wiki.repo_id, "search_repo_id": wiki.repo_id, "search_wiki": True, - "service_url": get_service_url().rstrip('/') })