From 33973ab2546f53f1e4af7609c91850fa4053b5d9 Mon Sep 17 00:00:00 2001 From: lian Date: Wed, 4 Dec 2019 11:09:31 +0800 Subject: [PATCH 1/9] update share links api --- seahub/api2/endpoints/share_links.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seahub/api2/endpoints/share_links.py b/seahub/api2/endpoints/share_links.py index 95533e0567..67f631cf7f 100644 --- a/seahub/api2/endpoints/share_links.py +++ b/seahub/api2/endpoints/share_links.py @@ -93,7 +93,7 @@ def get_share_link_info(fileshare): if repo and path != '/' and not data['is_dir']: dirent = seafile_api.get_dirent_by_path(repo_id, path) try: - can_edit, _ = can_edit_file(obj_name, dirent.size, repo) + can_edit, error_msg = can_edit_file(obj_name, dirent.size, repo) data['can_edit'] = can_edit except Exception as e: logger.error(e) @@ -311,7 +311,7 @@ class ShareLinks(APIView): s_type = 'd' if stat.S_ISDIR(dirent.mode) else 'f' if s_type == 'f': file_name = os.path.basename(path.rstrip('/')) - can_edit, _ = can_edit_file(file_name, dirent.size, repo) + can_edit, error_msg = can_edit_file(file_name, dirent.size, repo) if not can_edit and perm in (FileShare.PERM_EDIT_DL, FileShare.PERM_EDIT_ONLY): error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) @@ -428,7 +428,7 @@ class ShareLink(APIView): if fs.s_type == 'f': file_name = os.path.basename(fs.path.rstrip('/')) - can_edit, _ = can_edit_file(file_name, dirent.size, repo) + can_edit, error_msg = can_edit_file(file_name, dirent.size, repo) if not can_edit and perm in (FileShare.PERM_EDIT_DL, FileShare.PERM_EDIT_ONLY): error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) From d5f4f036b3d15b226d639f4e001ef06e1c9a3d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=A1=BA=E5=BC=BA?= Date: Thu, 5 Dec 2019 17:50:42 +0800 Subject: [PATCH 2/9] delete unnecessary code (#4337) --- frontend/config/webpack.config.dev.js | 5 - frontend/config/webpack.config.prod.js | 1 - .../src/pages/repo-wiki-mode/main-panel.js | 315 ----- .../src/pages/repo-wiki-mode/side-panel.js | 250 ---- frontend/src/repo-wiki-mode.js | 1113 ----------------- seahub/templates/view_lib_as_wiki.html | 22 - 6 files changed, 1706 deletions(-) delete mode 100644 frontend/src/pages/repo-wiki-mode/main-panel.js delete mode 100644 frontend/src/pages/repo-wiki-mode/side-panel.js delete mode 100644 frontend/src/repo-wiki-mode.js delete mode 100644 seahub/templates/view_lib_as_wiki.html diff --git a/frontend/config/webpack.config.dev.js b/frontend/config/webpack.config.dev.js index 3a2792d7de..4b5aa698ec 100644 --- a/frontend/config/webpack.config.dev.js +++ b/frontend/config/webpack.config.dev.js @@ -59,11 +59,6 @@ module.exports = { require.resolve('react-dev-utils/webpackHotDevClient'), paths.appSrc + "/wiki.js", ], - repoview: [ - require.resolve('./polyfills'), - require.resolve('react-dev-utils/webpackHotDevClient'), - paths.appSrc + "/repo-wiki-mode.js", - ], fileHistory: [ require.resolve('./polyfills'), require.resolve('react-dev-utils/webpackHotDevClient'), diff --git a/frontend/config/webpack.config.prod.js b/frontend/config/webpack.config.prod.js index 30b7976956..8d79dcf066 100644 --- a/frontend/config/webpack.config.prod.js +++ b/frontend/config/webpack.config.prod.js @@ -60,7 +60,6 @@ module.exports = { entry: { markdownEditor: [require.resolve('./polyfills'), paths.appIndexJs], wiki: [require.resolve('./polyfills'), paths.appSrc + "/wiki.js"], - repoview: [require.resolve('./polyfills'), paths.appSrc + "/repo-wiki-mode.js"], fileHistory: [require.resolve('./polyfills'), paths.appSrc + "/file-history.js"], fileHistoryOld: [require.resolve('./polyfills'), paths.appSrc + "/file-history-old.js"], app: [require.resolve('./polyfills'), paths.appSrc + "/app.js"], diff --git a/frontend/src/pages/repo-wiki-mode/main-panel.js b/frontend/src/pages/repo-wiki-mode/main-panel.js deleted file mode 100644 index 3929b27005..0000000000 --- a/frontend/src/pages/repo-wiki-mode/main-panel.js +++ /dev/null @@ -1,315 +0,0 @@ -import React, { Component, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import cookie from 'react-cookies'; -import { gettext, repoID, siteRoot, permission } from '../../utils/constants'; -import { seafileAPI } from '../../utils/seafile-api'; -import { Utils } from '../../utils/utils'; -import RepoInfo from '../../models/repo-info'; -import CommonToolbar from '../../components/toolbar/common-toolbar'; -import ViewModeToolbar from '../../components/toolbar/view-mode-toolbar'; -import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar'; -import MultipleDirOperationToolbar from '../../components/toolbar/multiple-dir-operation-toolbar'; -import CurDirPath from '../../components/cur-dir-path'; -import WikiMarkdownViewer from '../../components/wiki-markdown-viewer'; -import DirentListView from '../../components/dirent-list-view/dirent-list-view'; -import DirentDetail from '../../components/dirent-detail/dirent-details'; -import FileUploader from '../../components/file-uploader/file-uploader'; -import RepoInfoBar from '../../components/repo-info-bar'; - -const propTypes = { - content: PropTypes.string, - lastModified: PropTypes.string, - latestContributor: PropTypes.string, - permission: PropTypes.string, - hash: PropTypes.string, - path: PropTypes.string.isRequired, - repoName: PropTypes.string.isRequired, - repoEncrypted: PropTypes.bool.isRequired, - showShareBtn: PropTypes.bool.isRequired, - enableDirPrivateShare: PropTypes.bool.isRequired, - userPerm: PropTypes.string.isRequired, - isAdmin: PropTypes.bool.isRequired, - isGroupOwnedRepo: PropTypes.bool.isRequired, - // whether the file or dir corresponding to the path exist - pathExist: PropTypes.bool.isRequired, - isFileLoading: PropTypes.bool.isRequired, - isViewFile: PropTypes.bool.isRequired, - isDirentListLoading: PropTypes.bool.isRequired, - isDirentSelected: PropTypes.bool.isRequired, - isAllDirentSelected: PropTypes.bool.isRequired, - direntList: PropTypes.array.isRequired, - sortBy: PropTypes.string.isRequired, - sortOrder: PropTypes.string.isRequired, - sortItems: PropTypes.func.isRequired, - selectedDirentList: PropTypes.array.isRequired, - updateDirent: PropTypes.func.isRequired, - onSideNavMenuClick: PropTypes.func.isRequired, - onSearchedClick: PropTypes.func.isRequired, - onMainNavBarClick: PropTypes.func.isRequired, - onItemClick: PropTypes.func.isRequired, - onAllDirentSelected: PropTypes.func.isRequired, - onItemSelected: PropTypes.func.isRequired, - onItemDelete: PropTypes.func.isRequired, - onItemRename: PropTypes.func.isRequired, - onItemMove: PropTypes.func.isRequired, - onItemCopy: PropTypes.func.isRequired, - onAddFile: PropTypes.func.isRequired, - onAddFolder: PropTypes.func.isRequired, - onFileTagChanged: PropTypes.func.isRequired, - onItemsMove: PropTypes.func.isRequired, - onItemsCopy: PropTypes.func.isRequired, - onItemsDelete: PropTypes.func.isRequired, - onLinkClick: PropTypes.func.isRequired, - onFileUploadSuccess: PropTypes.func.isRequired, - isDraft: PropTypes.bool, - hasDraft: PropTypes.bool, - goDraftPage: PropTypes.func, - usedRepoTags: PropTypes.array.isRequired, - readmeMarkdown: PropTypes.object, - draftCounts: PropTypes.number, - updateUsedRepoTags: PropTypes.func.isRequired, -}; - -class MainPanel extends Component { - - constructor(props) { - super(props); - this.state = { - currentMode: 'wiki', - isDirentDetailShow: false, - currentDirent: null, - direntPath: '', - currentRepoInfo: null, - }; - } - - componentDidMount() { - seafileAPI.getRepoInfo(repoID).then(res => { - let repoInfo = new RepoInfo(res.data); - this.setState({ - currentRepoInfo: repoInfo, - }); - }); - if (this.props.hash) { - let hash = this.props.hash; - setTimeout(function() { - window.location.hash = hash; - }, 500); - } - } - - switchViewMode = (mode) => { - cookie.save('view_mode', mode, { path: '/' }); - let repoName = this.state.currentRepoInfo.repo_name; - let dirPath = this.props.isViewFile ? Utils.getDirName(this.props.path) : this.props.path; - window.location.href = siteRoot + 'library/' + repoID + '/' + repoName + dirPath; - } - - onSideNavMenuClick = () => { - this.props.onSideNavMenuClick(); - } - - onItemClick = (dirent) => { - this.setState({isDirentDetailShow: false}); - this.props.onItemClick(dirent); - } - - - onMainNavBarClick = (path) => { - this.setState({isDirentDetailShow: false}); - this.props.onMainNavBarClick(path); - } - - // on '' - onDirentClick = (dirent) => { - if (this.state.isDirentDetailShow) { - this.onItemDetails(dirent); - } - } - - onItemDetails = (dirent) => { - this.setState({ - currentDirent: dirent, - isDirentDetailShow: true, - }); - } - - onItemDetailsClose = () => { - this.setState({isDirentDetailShow: false}); - } - - onFileTagChanged = (dirent, direntPath) => { - this.props.onFileTagChanged(dirent, direntPath); - } - - onUploadFile = (e) => { - e.nativeEvent.stopImmediatePropagation(); - this.uploader.onFileUpload(); - } - - onUploadFolder = (e) => { - e.nativeEvent.stopImmediatePropagation(); - this.uploader.onFolderUpload(); - } - - onFileUploadSuccess = (direntObject) => { - this.props.onFileUploadSuccess(direntObject); - } - - render() { - - const ErrMessage = (
{gettext('Folder does not exist.')}
); - const showRepoInfoBar = this.props.path === '/' && ( - this.props.usedRepoTags.length != 0 || this.props.readmeMarkdown != null || - this.props.draftCounts != 0); - - return ( -
-
-
- -
- {this.props.isDirentSelected ? - : - - } -
- -
- -
-
-
-
- {this.state.currentRepoInfo && ( - - )} -
-
- {!this.props.pathExist && ErrMessage } - {(this.props.pathExist && this.props.isViewFile) && ( - - - {(!this.props.isDraft && this.props.hasDraft) && -
-
- {gettext('This file is in draft stage.')} - {gettext('View Draft')} -
-
- } -
-
- )} - {(this.props.pathExist && !this.props.isViewFile) && ( - - {showRepoInfoBar && ( - - )} - - this.uploader = uploader} - dragAndDrop={true} - path={this.props.path} - repoID={repoID} - direntList={this.props.direntList} - onFileUploadSuccess={this.onFileUploadSuccess} - /> - - )} -
-
- {this.state.isDirentDetailShow && ( -
- -
- )} -
-
- ); - } -} - -MainPanel.propTypes = propTypes; - -export default MainPanel; diff --git a/frontend/src/pages/repo-wiki-mode/side-panel.js b/frontend/src/pages/repo-wiki-mode/side-panel.js deleted file mode 100644 index 0680c5366e..0000000000 --- a/frontend/src/pages/repo-wiki-mode/side-panel.js +++ /dev/null @@ -1,250 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { gettext, permission } from '../../utils/constants'; -import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap'; -import TreeView from '../../components/tree-view/tree-view'; -import Logo from '../../components/logo'; -import Loading from '../../components/loading'; -import ModalPortal from '../../components/modal-portal'; -import Delete from '../../components/dialog/delete-dialog'; -import Rename from '../../components/dialog/rename-dialog'; -import CreateFolder from '../../components/dialog/create-folder-dialog'; -import CreateFile from '../../components/dialog/create-file-dialog'; - -const propTypes = { - currentNode: PropTypes.object, - isTreeDataLoading: PropTypes.bool.isRequired, - treeData: PropTypes.object.isRequired, - currentPath: PropTypes.string.isRequired, - closeSideBar: PropTypes.bool.isRequired, - onCloseSide: PropTypes.func.isRequired, - onNodeClick: PropTypes.func.isRequired, - onNodeCollapse: PropTypes.func.isRequired, - onNodeExpanded: PropTypes.func.isRequired, - onRenameNode: PropTypes.func.isRequired, - onDeleteNode: PropTypes.func.isRequired, - onAddFileNode: PropTypes.func.isRequired, - onAddFolderNode: PropTypes.func.isRequired, -}; - -class SidePanel extends Component { - - constructor(props) { - super(props); - this.state = { - opNode: null, - isLoadFailed: false, - isMenuIconShow: false, - isHeaderMenuShow: false, - isDeleteDialogShow: false, - isAddFileDialogShow: false, - isAddFolderDialogShow: false, - isRenameDialogShow: false, - }; - this.isNodeMenuShow = true; - } - - componentWillReceiveProps(nextProps) { - this.setState({opNode: nextProps.currentNode}); - } - - onMouseEnter = () => { - this.setState({isMenuIconShow: true}); - } - - onMouseLeave = () => { - this.setState({isMenuIconShow: false}); - } - - onDropdownToggleClick = (e) => { - e.preventDefault(); - this.toggleOperationMenu(); - } - - toggleOperationMenu = () => { - this.setState({isHeaderMenuShow: !this.state.isHeaderMenuShow}); - } - - onNodeClick = (node) => { - this.setState({opNode: node}); - this.props.onNodeClick(node); - } - - onMenuItemClick = (operation, node) => { - this.setState({opNode: node}); - switch (operation) { - case 'New Folder': - this.onAddFolderToggle(); - break; - case 'New File': - this.onAddFileToggle(); - break; - case 'Rename': - this.onRenameToggle(); - break; - case 'Delete': - this.onDeleteToggle(); - break; - } - } - - onAddFileToggle = (type) => { - if (type === 'root') { - let root = this.props.treeData.root; - this.setState({ - isAddFileDialogShow: !this.state.isAddFileDialogShow, - opNode: root, - }); - } else { - this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow}); - } - } - - onAddFolderToggle = (type) => { - if (type === 'root') { - let root = this.props.treeData.root; - this.setState({ - isAddFolderDialogShow: !this.state.isAddFolderDialogShow, - opNode: root, - }); - } else { - this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow}); - } - } - - onRenameToggle = () => { - this.setState({isRenameDialogShow: !this.state.isRenameDialogShow}); - } - - onDeleteToggle = () => { - this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow}); - } - - onAddFolderNode = (dirPath) => { - this.setState({isAddFolderDialogShow: !this.state.isAddFolderDialogShow}); - this.props.onAddFolderNode(dirPath); - } - - onAddFileNode = (filePath, isDraft) => { - this.setState({isAddFileDialogShow: !this.state.isAddFileDialogShow}); - this.props.onAddFileNode(filePath, isDraft); - } - - onRenameNode = (newName) => { - this.setState({isRenameDialogShow: !this.state.isRenameDialogShow}); - let node = this.state.opNode; - this.props.onRenameNode(node, newName); - } - - onDeleteNode = () => { - this.setState({isDeleteDialogShow: !this.state.isDeleteDialogShow}); - let node = this.state.opNode; - this.props.onDeleteNode(node); - } - - checkDuplicatedName = (newName) => { - let node = this.state.opNode; - // root node to new node conditions: parentNode is null, - let parentNode = node.parentNode ? node.parentNode : node; - let childrenObject = parentNode.children.map(item => { - return item.object; - }); - let isDuplicated = childrenObject.some(object => { - return object.name === newName; - }); - return isDuplicated; - } - - render() { - return ( -
-
- -
- - {this.state.isAddFolderDialogShow && ( - - - - )} - {this.state.isAddFileDialogShow && ( - - - - )} - {this.state.isRenameDialogShow && ( - - - - )} - {this.state.isDeleteDialogShow && ( - - - - )} -
- ); - } -} - -SidePanel.propTypes = propTypes; - -export default SidePanel; \ No newline at end of file diff --git a/frontend/src/repo-wiki-mode.js b/frontend/src/repo-wiki-mode.js deleted file mode 100644 index 169e0c284d..0000000000 --- a/frontend/src/repo-wiki-mode.js +++ /dev/null @@ -1,1113 +0,0 @@ -import React, { Component } from 'react'; -import ReactDOM from 'react-dom'; -import moment from 'moment'; -import { gettext, repoID, siteRoot, initialPath, isDir, canGenerateShareLink, canGenerateUploadLink, username, isDocs } from './utils/constants'; -import { seafileAPI } from './utils/seafile-api'; -import { Utils } from './utils/utils'; -import collabServer from './utils/collab-server'; -import SidePanel from './pages/repo-wiki-mode/side-panel'; -import MainPanel from './pages/repo-wiki-mode/main-panel'; -import TreeNode from './components/tree-view/tree-node'; -import treeHelper from './components/tree-view/tree-helper'; -import toaster from './components/toast'; -import LibDecryptDialog from './components/dialog/lib-decrypt-dialog'; -import ModalPortal from './components/modal-portal'; -import Dirent from './models/dirent'; -import FileTag from './models/file-tag'; -import RepoTag from './models/repo-tag'; -import RepoInfo from './models/repo-info'; - -import './assets/css/fa-solid.css'; -import './assets/css/fa-regular.css'; -import './assets/css/fontawesome.css'; -import './css/layout.css'; -import './css/side-panel.css'; -import './css/wiki.css'; -import './css/toolbar.css'; -import './css/search.css'; - -class Wiki extends Component { - constructor(props) { - super(props); - this.state = { - repoName: '', - repoEncrypted: false, - isGroupOwnedRepo: false, - isAdmin: false, - ownerEmail: '', - userPerm: '', - - path: '', - pathExist: true, - treeData: treeHelper.buildTree(), - closeSideBar: false, - currentNode: null, - isTreeDataLoading: true, - isDirentListLoading: true, - isViewFile: false, - sortBy: 'name', // 'name' or 'time' - sortOrder: 'asc', // 'asc' or 'desc' - direntList: [], - isFileLoading: true, - content: '', - lastModified: '', - latestContributor: '', - permission: '', - isDirentSelected: false, - isAllDirentSelected: false, - selectedDirentList: [], - libNeedDecrypt: false, - isDraft: false, - hasDraft: false, - draftID: '', - dirID: '', - usedRepoTags: [], - readmeMarkdown: null, - draftCounts: 0, - }; - window.onpopstate = this.onpopstate; - this.hash = ''; - this.lastModifyTime = new Date(); - } - - componentWillMount() { - const hash = window.location.hash; - if (hash.slice(0, 1) === '#') { - this.hash = hash; - } - } - - componentDidMount() { - seafileAPI.getRepoInfo(repoID).then(res => { - let repoInfo = new RepoInfo(res.data); - this.setState({ - libNeedDecrypt: res.data.lib_need_decrypt, - repoName: repoInfo.repo_name, - repoEncrypted: repoInfo.encrypted, - isVirtual: repoInfo.is_virtual, - isAdmin: repoInfo.is_admin, - ownerEmail: repoInfo.owner_email - }); - - const ownerEmail = repoInfo.owner_email; - if (repoInfo.owner_email.indexOf('@seafile_group') != -1) { - const groupID = ownerEmail.substring(0, ownerEmail.indexOf('@')); - this.getGroupInfo(groupID); - this.setState({ - isGroupOwnedRepo: true - }); - } - - if (!res.data.lib_need_decrypt) { - this.loadWikiData(); - } - }); - } - - componentWillUnmount() { - collabServer.unwatchRepo(repoID, this.onRepoUpdateEvent); - } - - componentDidUpdate() { - this.lastModifyTime = new Date(); - } - - getGroupInfo = (groupID) => { - seafileAPI.getGroup(groupID).then(res => { - if (res.data.admins.indexOf(username) != -1) { - this.setState({isDepartmentAdmin: true}); - } - }); - } - - onRepoUpdateEvent = () => { - let currentTime = new Date(); - if ((parseFloat(currentTime - this.lastModifyTime)/1000) <= 5) { - return; - } - let { path, dirID } = this.state; - seafileAPI.dirMetaData(repoID, path).then((res) => { - if (res.data.id !== dirID) { - toaster.notify( - - {gettext('This folder has been updated. ')} - {gettext('Refresh')} - , - {id: 'repo_updated', duration: 3600} - ); - } - }); - } - - loadWikiData = () => { - // listen current repo - collabServer.watchRepo(repoID, this.onRepoUpdateEvent); - - // list used FileTags - this.updateUsedRepoTags(); - - // list draft counts and revierw counts - if (isDocs) { - seafileAPI.getRepoDraftCounts(repoID).then(res => { - this.setState({ - draftCounts: res.data.draft_counts, - }); - }); - } - - // load side-panel data - this.loadSidePanel(initialPath); - - // load main-panel data - if (isDir === 'None') { - this.setState({pathExist: false}); - } else if (isDir === 'True') { - this.showDir(initialPath); - } else if (isDir === 'False') { - this.showFile(initialPath); - } - - } - - loadSidePanel = (initialPath) => { - - if (initialPath === '/' || isDir === 'None') { - seafileAPI.listDir(repoID, '/').then(res => { - let tree = this.state.treeData; - this.addResponseListToNode(res.data.dirent_list, tree.root); - this.setState({ - isTreeDataLoading: false, - treeData: tree - }); - }).catch(() => { - this.setState({isLoadFailed: true}); - }); - } else { - this.loadNodeAndParentsByPath(initialPath); - } - } - - showDir = (path) => { - this.loadDirentList(path); - this.setState({ - path: path, - isViewFile: false - }); - - // update location url - let url = siteRoot + 'wiki/lib/' + repoID + Utils.encodePath(path); - window.history.pushState({ url: url, path: path}, path, url); - } - - showFile = (filePath) => { - this.setState({ - path: filePath, - isViewFile: true - }); - - this.setState({isFileLoading: true}); - seafileAPI.getFileInfo(repoID, filePath).then((res) => { - let { mtime, permission, last_modifier_name, is_draft, has_draft, draft_id } = 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, - isDraft: is_draft, - hasDraft: has_draft, - draftID: draft_id - }); - }); - }); - }); - - let fileUrl = siteRoot + 'wiki/lib/' + repoID + Utils.encodePath(filePath); - window.history.pushState({url: fileUrl, path: filePath}, filePath, fileUrl); - } - - updateReadmeMarkdown = (direntList) => { - this.setState({readmeMarkdown: null}); - direntList.some(item => { - let fileName = item.name.toLowerCase(); - if (fileName === 'readme.md' || fileName === 'readme.markdown') { - this.setState({readmeMarkdown: item}); - return true; - } - }); - } - - loadDirentList = (path) => { - this.setState({isDirentListLoading: true}); - seafileAPI.listDir(repoID, path, {'with_thumbnail': true}).then(res => { - let direntList = []; - res.data.dirent_list.forEach(item => { - let fileName = item.name.toLowerCase(); - if (fileName === 'readme.md' || fileName === 'readme.markdown') { - this.setState({readmeMarkdown: item}); - } - let dirent = new Dirent(item); - direntList.push(dirent); - }); - - this.setState({ - userPerm: res.data.user_perm, - direntList: Utils.sortDirents(direntList, this.state.sortBy, this.state.sortOrder), - isDirentListLoading: false, - dirID: res.data.dir_id, - }); - - if (!this.state.repoEncrypted && direntList.length) { - this.getThumbnails(repoID, path, this.state.direntList); - } - }); - } - - getThumbnails = (repoID, path, direntList) => { - let items = direntList.filter((item) => { - return Utils.imageCheck(item.name) && !item.encoded_thumbnail_src; - }); - if (items.length == 0) { - return ; - } - - const _this = this; - const len = items.length; - const thumbnailSize = 48; - let getThumbnail = (i) => { - const curItem = items[i]; - const curItemPath = [path, curItem.name].join('/'); - seafileAPI.createThumbnail(repoID, curItemPath, thumbnailSize).then((res) => { - curItem.encoded_thumbnail_src = res.data.encoded_thumbnail_src; - }).catch((error) => { - // do nothing - }).then(() => { - if (i < len - 1) { - getThumbnail(++i); - } else { - _this.setState({ - direntList: direntList - }); - } - }); - }; - getThumbnail(0); - } - - onLinkClick = (link) => { - const url = link; - if (Utils.isInternalMarkdownLink(url, repoID)) { - let path = Utils.getPathFromInternalMarkdownLink(url, repoID); - this.showFile(path); - } else if (Utils.isInternalDirLink(url, repoID)) { - let path = Utils.getPathFromInternalDirLink(url, repoID); - this.showDir(path); - } else { - window.open(url); - } - } - - updateUsedRepoTags = () => { - seafileAPI.listRepoTags(repoID).then(res => { - let usedRepoTags = []; - res.data.repo_tags.forEach(item => { - let usedRepoTag = new RepoTag(item); - if (usedRepoTag.fileCount > 0) { - usedRepoTags.push(usedRepoTag); - } - }); - this.setState({usedRepoTags: usedRepoTags}); - }); - } - - 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}); - } - - onpopstate = (event) => { - if (event.state && event.state.path) { - let path = event.state.path; - if (Utils.isMarkdownFile(path)) { - this.showFile(path); - } else { - this.loadDirentList(path); - this.setState({ - path: path, - isViewFile: false - }); - } - } - } - - onSearchedClick = (item) => { - let path = item.is_dir ? item.path.slice(0, item.path.length - 1) : item.path; - if (this.state.currentPath === path) { - return; - } - - // load sidePanel - let index = -1; - let paths = Utils.getPaths(path); - for (let i = 0; i < paths.length; i++) { - let node = this.state.treeData.getNodeByPath(node); - if (!node) { - index = i; - break; - } - } - if (index === -1) { // all the data has been loaded already. - let node = this.state.treeData.getNodeByPath(path); - this.setState({currentNode: node}); - } else { - this.loadNodeAndParentsByPath(path); - } - - // load mainPanel - if (item.is_dir) { - this.showDir(path); - } else { - if (Utils.isMarkdownFile(path)) { - this.showFile(path); - } else { - let url = siteRoot + 'lib/' + item.repo_id + '/file' + Utils.encodePath(path); - let newWindow = window.open('about:blank'); - newWindow.location.href = url; - } - } - } - - onMainNavBarClick = (nodePath) => { - //just for dir - this.resetSelected(); - let tree = this.state.treeData.clone(); - let node = tree.getNodeByPath(nodePath); - tree.expandNode(node); - - this.setState({treeData: tree, currentNode: node}); - this.showDir(node.path); - } - - onAddFolder = (dirPath) => { - // todo Double name check - seafileAPI.createDir(repoID, dirPath).then(() => { - let name = Utils.getFileName(dirPath); - let parentPath = Utils.getDirName(dirPath); - - this.addNodeToTree(name, parentPath, 'dir'); - if (parentPath === this.state.path && !this.state.isViewFile) { - this.addDirent(name, 'dir'); - } - }).catch(() => { - // return error message - }); - } - - onAddFile = (filePath, isDraft) => { - // Double name check - 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', res.data.size); - } - }).catch(() => { - // todo - }); - } - - onRenameTreeNode = (node, newName) => { - this.renameItem(node.path, node.object.isDir(), newName); - } - - onDeleteTreeNode = (node) => { - this.deleteItem(node.path, node.object.isDir()); - } - - onMainPanelItemRename = (dirent, newName) => { - let path = Utils.joinPath(this.state.path, dirent.name); - this.renameItem(path, dirent.isDir(), newName); - } - - onMainPanelItemDelete = (dirent) => { - let path = Utils.joinPath(this.state.path, dirent.name); - this.deleteItem(path, dirent.isDir()); - } - - renameItem = (path, isDir, newName) => { - //validate task - if (isDir) { - seafileAPI.renameDir(repoID, path, newName).then(() => { - this.renameItemAjaxCallback(path, newName); - }).catch(() => { - //todos; - }); - } else { - seafileAPI.renameFile(repoID, path, newName).then(() => { - this.renameItemAjaxCallback(path, newName); - }).catch(() => { - //todos; - }); - } - } - - renameItemAjaxCallback(path, newName) { - this.renameTreeNode(path, newName); - this.renameDirent(path, newName); - } - - deleteItem(path, isDir) { - 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; - }); - } - } - - deleteItemAjaxCallback(path) { - this.deleteTreeNode(path); - this.deleteDirent(path); - } - - onMoveItem = (destRepo, dirent, moveToDirentPath) => { - //just for view list state - let dirName = dirent.name; - let direntPath = Utils.joinPath(this.state.path, dirName); - seafileAPI.moveDir(repoID, destRepo.repo_id,moveToDirentPath, this.state.path, dirName).then(res => { - let nodeName = res.data[0].obj_name; - this.moveTreeNode(direntPath, moveToDirentPath, destRepo, nodeName); - this.moveDirent(direntPath); - - let message = gettext('Successfully moved %(name)s.'); - message = message.replace('%(name)s', dirName); - toaster.success(message); - }).catch(() => { - let message = gettext('Failed to move %(name)s'); - message = message.replace('%(name)s', dirName); - toaster.danger(message); - }); - } - - onCopyItem = (destRepo, dirent, copyToDirentPath) => { - //just for view list state - let dirName = dirent.name; - let direntPath = Utils.joinPath(this.state.path, dirName); - seafileAPI.copyDir(repoID, destRepo.repo_id, copyToDirentPath, this.state.path, dirName).then(res => { - let nodeName = res.data[0].obj_name; - this.copyTreeNode(direntPath, copyToDirentPath, destRepo, nodeName); - let message = gettext('Successfully copied %(name)s.'); - message = message.replace('%(name)s', dirName); - toaster.success(message); - }).catch(() => { - let message = gettext('Failed to copy %(name)s'); - message = message.replace('%(name)s', dirName); - toaster.danger(message); - }); - } - - onMoveItems = (destRepo, destDirentPath) => { - let direntPaths = this.getSelectedDirentPaths(); - let dirNames = this.getSelectedDirentNames(); - - seafileAPI.moveDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(res => { - let names = res.data.map(item => { - return item.obj_name; - }); - direntPaths.forEach((direntPath, index) => { - this.moveTreeNode(direntPath, destDirentPath, destRepo, names[index]); - this.moveDirent(direntPath); - }); - let message = Utils.getMoveSuccessMessage(dirNames); - toaster.success(message); - }).catch(() => { - let message = Utils.getMoveFailedMessage(dirNames); - toaster.danger(message); - }); - } - - onCopyItems = (destRepo, destDirentPath) => { - let direntPaths = this.getSelectedDirentPaths(); - let dirNames = this.getSelectedDirentNames(); - - seafileAPI.copyDir(repoID, destRepo.repo_id, destDirentPath, this.state.path, dirNames).then(res => { - let names = res.data.map(item => { - return item.obj_name; - }); - direntPaths.forEach((direntPath, index) => { - this.copyTreeNode(direntPath, destDirentPath, destRepo, names[index]); - }); - let message = Utils.getCopySuccessfulMessage(dirNames); - toaster.success(message); - }).catch(() => { - let message = Utils.getCopyFailedMessage(dirNames); - toaster.danger(message); - }); - } - - onDeleteItems = () => { - let direntPaths = this.getSelectedDirentPaths(); - let dirNames = this.getSelectedDirentNames(); - - seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => { - direntPaths.forEach(direntPath => { - this.deleteTreeNode(direntPath); - this.deleteDirent(direntPath); - }); - }); - } - - onDirentClick = (dirent) => { - this.resetSelected(); - let direntPath = Utils.joinPath(this.state.path, dirent.name); - if (dirent.isDir()) { // is dir - this.loadTreeNodeByPath(direntPath); - this.showDir(direntPath); - } else { // is file - if (Utils.isMarkdownFile(direntPath)) { - this.showFile(direntPath); - } else { - const w=window.open('about:blank'); - const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(direntPath); - w.location.href = url; - } - } - } - - onDirentSelected = (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: [] - }); - } - } - - onAllDirentSelected = () => { - 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) => { - seafileAPI.listFileTags(repoID, direntPath).then(res => { - let fileTags = res.data.file_tags.map(item => { - return new FileTag(item); - }); - this.updateDirent(dirent, 'file_tags', fileTags); - }); - - this.updateUsedRepoTags(); - } - - onFileUploadSuccess = (direntObject) => { - let isExist = this.state.direntList.some(item => { - return item.name === direntObject.name && item.type === direntObject.type; - }); - if (isExist) { - let direntList = this.state.direntList; - for (let i = 0; i < direntList.length; i++) { - let dirent = direntList[i]; - if (dirent.name === direntObject.name && dirent.type === direntObject.type) { - let mtime = moment.unix(direntObject.mtime).fromNow(); - this.updateDirent(dirent, 'mtime', mtime); // todo file size is need update too, api is not return; - break; - } - } - } else { - direntObject.permission = 'rw'; - let dirent = new Dirent(direntObject); - this.addNodeToTree(dirent.name, this.state.path, dirent.type); - if (direntObject.type === 'dir') { - this.setState({direntList: [dirent, ...this.state.direntList]}); - } else { - this.setState({direntList: [...this.state.direntList, dirent]}); - this.updateReadmeMarkdown(this.state.direntList); - } - } - } - - addDirent = (name, type, size) => { - let item = this.createDirent(name, type, size); - let direntList = this.state.direntList; - if (type === 'dir') { - direntList.unshift(item); - } else { - // there will be there conditions; - // first: direntList.length === 0; - // second: all the direntList's items are dir; - // third: direntList has dir and file; - let length = direntList.length; - if (length === 0 || direntList[length - 1].type === 'dir') { - direntList.push(item); - } else { - let index = 0; - for (let i = 0; i <= length; i++) { - if (direntList[i].type === 'file') { - index = i; - break; - } - } - direntList.splice(index, 0, item); - } - } - this.setState({direntList: direntList}); - this.updateReadmeMarkdown(direntList); - } - - renameDirent = (direntPath, newName) => { - let parentPath = Utils.getDirName(direntPath); - let newDirentPath = Utils.joinPath(parentPath, newName); - if (direntPath === this.state.path) { - // the renamed item is current viewed item - // example: direntPath = /A/B/C, state.path = /A/B/C - - this.setState({ path: newDirentPath }); - let url = siteRoot + 'wiki/lib/' + repoID + newDirentPath; - window.history.replaceState({ url: url, path: newDirentPath}, newDirentPath, url); - } 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 }); - this.updateReadmeMarkdown(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) { - 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 }); - this.updateReadmeMarkdown(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 - } - - moveDirent = (direntPath) => { - let name = direntPath.slice(direntPath.lastIndexOf('/') + 1); - let direntList = this.state.direntList.filter(item => { - return item.name !== name; - }); - this.setState({direntList: direntList}); - this.updateReadmeMarkdown(direntList); - } - - onSideNavMenuClick = () => { - this.setState({ - closeSideBar: !this.state.closeSideBar, - }); - } - - onCloseSide = () => { - this.setState({ - closeSideBar: !this.state.closeSideBar, - }); - } - - loadTreeNodeByPath = (path) => { - let tree = this.state.treeData.clone(); - let node = tree.getNodeByPath(path); - if (!node.isLoaded) { - seafileAPI.listDir(repoID, node.path).then(res => { - this.addResponseListToNode(res.data.dirent_list, node); - let parentNode = tree.getNodeByPath(node.parentNode.path); - parentNode.isExpanded = true; - this.setState({ - treeData: tree, - currentNode: node - }); - }); - } else { - let parentNode = tree.getNodeByPath(node.parentNode.path); - parentNode.isExpanded = true; - this.setState({treeData: tree, currentNode: node}); //tree - } - } - - loadNodeAndParentsByPath = (path) => { - let tree = this.state.treeData.clone(); - if (Utils.isMarkdownFile(path)) { - path = Utils.getDirName(path); - } - seafileAPI.listDir(repoID, path, {with_parents: true}).then(res => { - let direntList = res.data.dirent_list; - let results = {}; - for (let i = 0; i < direntList.length; i++) { - let object = direntList[i]; - let key = object.parent_dir; - if (!results[key]) { - results[key] = []; - } - results[key].push(object); - } - for (let key in results) { - let node = tree.getNodeByPath(key); - if (!node.isLoaded) { - this.addResponseListToNode(results[key], node); - } - } - this.setState({ - isTreeDataLoading: false, - treeData: tree - }); - }).catch(() => { - this.setState({isLoadFailed: true}); - }); - } - - onTreeNodeClick = (node) => { - this.resetSelected(); - if (!this.state.pathExist) { - this.setState({pathExist: true}); - } - - if (node.object.isDir()) { - if (!node.isLoaded) { - let tree = this.state.treeData.clone(); - node = tree.getNodeByPath(node.path); - seafileAPI.listDir(repoID, node.path).then(res => { - this.addResponseListToNode(res.data.dirent_list, node); - tree.collapseNode(node); - this.setState({treeData: tree}); - }); - } - if (node.path === this.state.path) { - if (node.isExpanded) { - let tree = treeHelper.collapseNode(this.state.treeData, node); - this.setState({treeData: tree}); - } else { - let tree = this.state.treeData.clone(); - node = tree.getNodeByPath(node.path); - tree.expandNode(node); - this.setState({treeData: tree}); - } - } - } - - if (node.path === this.state.path ) { - return; - } - - if (node.object.isDir()) { // isDir - this.showDir(node.path); - } else { - if (Utils.isMarkdownFile(node.path)) { - if (node.path !== this.state.path) { - this.showFile(node.path); - } - } else { - const w = window.open('about:blank'); - const url = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(node.path); - w.location.href = url; - } - } - } - - onTreeNodeCollapse = (node) => { - let tree = treeHelper.collapseNode(this.state.treeData, node); - this.setState({treeData: tree}); - } - - onTreeNodeExpanded = (node) => { - let tree = this.state.treeData.clone(); - node = tree.getNodeByPath(node.path); - if (!node.isLoaded) { - seafileAPI.listDir(repoID, node.path).then(res => { - this.addResponseListToNode(res.data.dirent_list, node); - this.setState({treeData: tree}); - }); - } else { - tree.expandNode(node); - this.setState({treeData: tree}); - } - } - - addNodeToTree = (name, parentPath, type) => { - let node = this.createTreeNode(name, type); - let tree = treeHelper.addNodeToParentByPath(this.state.treeData, node, parentPath); - this.setState({treeData: tree}); - } - - renameTreeNode = (path, newName) => { - let tree = treeHelper.renameNodeByPath(this.state.treeData, path, newName); - this.setState({treeData: tree}); - } - - deleteTreeNode = (path) => { - let tree = treeHelper.deleteNodeByPath(this.state.treeData, path); - this.setState({treeData: tree}); - } - - moveTreeNode = (nodePath, moveToPath, moveToRepo, nodeName) => { - if (repoID !== moveToRepo.repo_id) { - let tree = treeHelper.deleteNodeByPath(this.state.treeData, nodePath); - this.setState({treeData: tree}); - return; - } - let tree = treeHelper.moveNodeByPath(this.state.treeData, nodePath, moveToPath, nodeName); - this.setState({treeData: tree}); - } - - copyTreeNode = (nodePath, copyToPath, destRepo, nodeName) => { - if (repoID !== destRepo.repo_id) { - return; - } - let tree = treeHelper.copyNodeByPath(this.state.treeData, nodePath, copyToPath, nodeName); - this.setState({treeData: tree}); - } - - createTreeNode(name, type) { - let object = this.createDirent(name, type); - return new TreeNode({object}); - } - - createDirent(name, type, size) { - let mtime = new Date().getTime()/1000; - let dirent = new Dirent({name, type, mtime, size}); - return dirent; - } - - addResponseListToNode = (list, node) => { - node.isLoaded = true; - node.isExpanded = true; - let direntList = list.map(item => { - return new Dirent(item); - }); - direntList = Utils.sortDirents(direntList, 'name', 'asc'); - - let nodeList = direntList.map(object => { - return new TreeNode({object}); - }); - node.addChildren(nodeList); - } - - getSelectedDirentPaths = () => { - let paths = []; - this.state.selectedDirentList.forEach(selectedDirent => { - paths.push(Utils.joinPath(this.state.path, selectedDirent.name)); - }); - return paths; - } - - getSelectedDirentNames = () => { - let names = []; - this.state.selectedDirentList.forEach(selectedDirent => { - names.push(selectedDirent.name); - }); - return names; - } - - resetSelected = () => { - this.setState({ - isDirentSelected: false, - isAllDirentSelected: false, - }); - } - - onLibDecryptDialog = () => { - this.setState({libNeedDecrypt: false}); - this.loadWikiData(); - } - - goDraftPage = () => { - window.location.href = siteRoot + 'drafts/' + this.state.draftID + '/'; - } - - sortItems = (sortBy, sortOrder) => { - this.setState({ - sortBy: sortBy, - sortOrder: sortOrder, - items: Utils.sortDirents(this.state.direntList, sortBy, sortOrder) - }); - } - - render() { - let { libNeedDecrypt } = this.state; - if (libNeedDecrypt) { - return ( - - - - ); - } - - let showShareBtn = false, - enableDirPrivateShare = false; - const { repoEncrypted, isAdmin, ownerEmail, userPerm, isVirtual, isDepartmentAdmin } = this.state; - const isRepoOwner = ownerEmail == username; - if (!repoEncrypted && ( - canGenerateShareLink || canGenerateUploadLink || - isRepoOwner || isAdmin) && ( - userPerm == 'rw' || userPerm == 'r')) { - showShareBtn = true; - if (!isVirtual && (isRepoOwner || isAdmin || isDepartmentAdmin)) { - enableDirPrivateShare = true; - } - } - - return ( -
- - -
- ); - } -} - -ReactDOM.render ( - , - document.getElementById('wrapper') -); diff --git a/seahub/templates/view_lib_as_wiki.html b/seahub/templates/view_lib_as_wiki.html deleted file mode 100644 index d9935d3c8a..0000000000 --- a/seahub/templates/view_lib_as_wiki.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "base_for_react.html" %} -{% load render_bundle from webpack_loader %} -{% block extra_style %} -{% render_bundle 'repoview' 'css' %} -{% endblock %} - -{% block extra_script %} - - -{% render_bundle 'repoview' 'js' %} -{% endblock %} From 3970035c207323c76a25191bf4e43241baa93c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=A1=BA=E5=BC=BA?= Date: Fri, 13 Dec 2019 16:16:02 +0800 Subject: [PATCH 3/9] add size control (#4339) * check file size before upload file * update file naming --- .../components/file-uploader/file-uploader.js | 30 +++++++++++++------ .../file-uploader/forbid-upload-list-item.js | 28 +++++++++++++++++ .../file-uploader/upload-progress-dialog.js | 9 +++++- frontend/src/utils/constants.js | 1 + seahub/templates/base_for_react.html | 3 ++ seahub/views/__init__.py | 7 +++++ 6 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/file-uploader/forbid-upload-list-item.js diff --git a/frontend/src/components/file-uploader/file-uploader.js b/frontend/src/components/file-uploader/file-uploader.js index 247ae84626..357d09cc9b 100644 --- a/frontend/src/components/file-uploader/file-uploader.js +++ b/frontend/src/components/file-uploader/file-uploader.js @@ -2,7 +2,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import Resumablejs from '@seafile/resumablejs'; import MD5 from 'MD5'; -import { enableResumableFileUpload, resumableUploadFileBlockSize } from '../../utils/constants'; +import { enableResumableFileUpload, resumableUploadFileBlockSize, maxUploadFileSize } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import { gettext } from '../../utils/constants'; @@ -18,13 +18,11 @@ const propTypes = { chunkSize: PropTypes.number, withCredentials: PropTypes.bool, maxFiles: PropTypes.number, - maxFileSize: PropTypes.number, testMethod: PropTypes.string, testChunks: PropTypes.number, simultaneousUploads: PropTypes.number, fileParameterName: PropTypes.string, maxFilesErrorCallback: PropTypes.func, - maxFileSizeErrorCallback: PropTypes.func, minFileSizeErrorCallback: PropTypes.func, fileTypeErrorCallback: PropTypes.func, dragAndDrop: PropTypes.bool.isRequired, @@ -39,6 +37,7 @@ class FileUploader extends React.Component { this.state = { retryFileList: [], uploadFileList: [], + forbidUploadFileList: [], totalProgress: 0, isUploadProgressDialogShow: false, isUploadRemindDialogShow: false, @@ -64,7 +63,7 @@ class FileUploader extends React.Component { query: this.setQuery || {}, fileType: this.props.filetypes, maxFiles: this.props.maxFiles, - maxFileSize: this.props.maxFileSize, + maxFileSize: maxUploadFileSize * 1000 * 1000 || undefined, testMethod: this.props.testMethod || 'post', testChunks: this.props.testChunks || false, headers: this.setHeaders || {}, @@ -105,7 +104,7 @@ class FileUploader extends React.Component { } bindCallbackHandler = () => { - let {maxFilesErrorCallback, minFileSizeErrorCallback, maxFileSizeErrorCallback, fileTypeErrorCallback } = this.props; + let {maxFilesErrorCallback, minFileSizeErrorCallback, fileTypeErrorCallback } = this.props; if (maxFilesErrorCallback) { this.resumable.opts.maxFilesErrorCallback = this.props.maxFilesErrorCallback; @@ -115,8 +114,8 @@ class FileUploader extends React.Component { this.resumable.opts.minFileSizeErrorCallback = this.props.minFileSizeErrorCallback; } - if (maxFileSizeErrorCallback) { - this.resumable.opts.maxFileSizeErrorCallback = this.props.maxFileSizeErrorCallback; + if (this.maxFileSizeErrorCallback) { + this.resumable.opts.maxFileSizeErrorCallback = this.maxFileSizeErrorCallback; } if (fileTypeErrorCallback) { @@ -142,6 +141,12 @@ class FileUploader extends React.Component { this.resumable.on('dragstart', this.onDragStart.bind(this)); } + maxFileSizeErrorCallback = (file) => { + let { forbidUploadFileList } = this.state; + forbidUploadFileList.push(file); + this.setState({forbidUploadFileList: forbidUploadFileList}); + } + onChunkingComplete = (resumableFile) => { let allFilesUploaded = this.state.allFilesUploaded; @@ -229,7 +234,13 @@ class FileUploader extends React.Component { } filesAddedComplete = (resumable, files) => { - // single file uploading can check repetition, because custom dialog conn't prevent program execution; + let { forbidUploadFileList } = this.state; + if (forbidUploadFileList.length > 0 && files.length === 0) { + this.setState({ + isUploadProgressDialogShow: true, + totalProgress: 100 + }); + } } setUploadFileList = () => { @@ -481,7 +492,7 @@ class FileUploader extends React.Component { this.resumable.files = []; // reset upload link loaded this.isUploadLinkLoaded = false; - this.setState({isUploadProgressDialogShow: false, uploadFileList: []}); + this.setState({isUploadProgressDialogShow: false, uploadFileList: [], forbidUploadFileList: []}); Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', false); } @@ -667,6 +678,7 @@ class FileUploader extends React.Component { + +
{file.name}
+ + + {msg} + + ); + } +} + +ForbidUploadListItem.propTypes = propTypes; + +export default ForbidUploadListItem; diff --git a/frontend/src/components/file-uploader/upload-progress-dialog.js b/frontend/src/components/file-uploader/upload-progress-dialog.js index 2cbfecbf68..7a94726e62 100644 --- a/frontend/src/components/file-uploader/upload-progress-dialog.js +++ b/frontend/src/components/file-uploader/upload-progress-dialog.js @@ -1,14 +1,16 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { gettext } from '../../utils/constants'; -import UploadListItem from './upload-list-item'; import { Utils } from '../../utils/utils'; +import UploadListItem from './upload-list-item'; +import ForbidUploadListItem from './forbid-upload-list-item'; const propTypes = { uploadBitrate: PropTypes.number.isRequired, totalProgress: PropTypes.number.isRequired, retryFileList: PropTypes.array.isRequired, uploadFileList: PropTypes.array.isRequired, + forbidUploadFileList: PropTypes.array.isRequired, onCloseUploadDialog: PropTypes.func.isRequired, onCancelAllUploading: PropTypes.func.isRequired, onUploadCancel: PropTypes.func.isRequired, @@ -94,6 +96,11 @@ class UploadProgressDialog extends React.Component { } + { + this.props.forbidUploadFileList.map((file, index) => { + return (); + }) + } { this.props.uploadFileList.map((resumableFile, index) => { return ( diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index 254d8f48d9..3db994dfc2 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -54,6 +54,7 @@ export const canAddPublicRepo = window.app.pageOptions.canAddPublicRepo; export const canInvitePeople = window.app.pageOptions.canInvitePeople; export const canLockUnlockFile = window.app.pageOptions.canLockUnlockFile; export const customNavItems = window.app.pageOptions.customNavItems; +export const maxUploadFileSize = window.app.pageOptions.maxUploadFileSize; export const curNoteMsg = window.app.pageOptions.curNoteMsg; export const curNoteID = window.app.pageOptions.curNoteID; diff --git a/seahub/templates/base_for_react.html b/seahub/templates/base_for_react.html index 189810a25e..d3dc4496e7 100644 --- a/seahub/templates/base_for_react.html +++ b/seahub/templates/base_for_react.html @@ -91,6 +91,9 @@ canAddPublicRepo: {% if can_add_public_repo %} true {% else %} false {% endif %}, canInvitePeople: {% if enable_guest_invitation and user.permissions.can_invite_guest %} true {% else %} false {% endif %}, customNavItems: {% if custom_nav_items %} JSON.parse('{{ custom_nav_items | escapejs }}') {% else %} {{'[]'}} {% endif %}, + {% if max_upload_file_size > 0 %} + maxUploadFileSize: {{ max_upload_file_size }}, + {% endif %} {% if request.user.is_authenticated and request.cur_note %} curNoteMsg: '{{ request.cur_note.message|urlize|escapejs }}', diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 3a836023d1..69e9821575 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -1252,7 +1252,14 @@ def choose_register(request): def react_fake_view(request, **kwargs): folder_perm_enabled = True if is_pro_version() and ENABLE_FOLDER_PERM else False + try: + max_upload_file_size = seafile_api.get_server_config_int('fileserver', 'max_upload_size') + except Exception as e: + logger.error(e) + max_upload_file_size = -1 + return render(request, "react_app.html", { + 'max_upload_file_size': max_upload_file_size, 'seafile_collab_server': SEAFILE_COLLAB_SERVER, 'storages': get_library_storages(request), 'enable_repo_snapshot_label': settings.ENABLE_REPO_SNAPSHOT_LABEL, From 69a482df9f70a3218679c3165e48eec2c1870c18 Mon Sep 17 00:00:00 2001 From: lian Date: Fri, 13 Dec 2019 21:44:54 +0800 Subject: [PATCH 4/9] only check local user when login via shib (#4357) --- thirdpart/shibboleth/backends.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/thirdpart/shibboleth/backends.py b/thirdpart/shibboleth/backends.py index 1f16fdf1b2..67cec514e9 100644 --- a/thirdpart/shibboleth/backends.py +++ b/thirdpart/shibboleth/backends.py @@ -1,6 +1,7 @@ from django.conf import settings from django.db import connection +from seaserv import ccnet_api from seahub.auth.backends import RemoteUserBackend from seahub.base.accounts import User from registration.models import ( @@ -43,9 +44,12 @@ class ShibbolethRemoteUserBackend(RemoteUserBackend): return username = self.clean_username(remote_user) - try: - user = User.objects.get(email=username) - except User.DoesNotExist: + + local_ccnet_users = ccnet_api.search_emailusers('DB', username, -1, -1) + if not local_ccnet_users: + local_ccnet_users = ccnet_api.search_emailusers('LDAP', username, -1, -1) + + if not local_ccnet_users: if self.create_unknown_user: user = User.objects.create_user( email=username, is_active=self.activate_after_creation) From c5950058bbe498e7b67a144885247a96f4a91644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=A1=BA=E5=BC=BA?= Date: Mon, 23 Dec 2019 12:03:06 +0800 Subject: [PATCH 5/9] repair share bug (#4373) --- .../dirent-list-view/dirent-list-item.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/dirent-list-view/dirent-list-item.js b/frontend/src/components/dirent-list-view/dirent-list-item.js index 6720ba3d14..0c70345407 100644 --- a/frontend/src/components/dirent-list-view/dirent-list-item.js +++ b/frontend/src/components/dirent-list-view/dirent-list-item.js @@ -494,13 +494,10 @@ class DirentListItem extends React.Component { } renderItemOperation = () => { - let { dirent, selectedDirentList } = this.props; + let { dirent, currentRepoInfo, selectedDirentList } = this.props; - // no need to check whether show shareBtn or not. - // according to specification below, shareBtn aways show. - // check for "generate uploadlink" or other tabs should put inside the shareDialog. // https://dev.seafile.com/seahub/lib/d6f300e7-bb2b-4722-b83e-cf45e370bfbc/file/seaf-server%20%E5%8A%9F%E8%83%BD%E8%AE%BE%E8%AE%A1/%E6%9D%83%E9%99%90%E7%9B%B8%E5%85%B3/%E8%B5%84%E6%96%99%E5%BA%93%E6%9D%83%E9%99%90%E8%A7%84%E8%8C%83.md - // let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, dirent.permission, dirent); + let showShareBtn = Utils.isHasPermissionToShare(currentRepoInfo, dirent.permission, dirent); return ( @@ -514,9 +511,11 @@ class DirentListItem extends React.Component { )} -
  • - -
  • + {showShareBtn && ( +
  • + +
  • + )} {dirent.permission === 'rw' && (
  • @@ -546,7 +545,7 @@ class DirentListItem extends React.Component {
  • )} - {( + {showShareBtn && (
  • From 0cd689b97125983af5d4d627ca228aabffbab351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=A1=BA=E5=BC=BA?= Date: Mon, 23 Dec 2019 15:47:11 +0800 Subject: [PATCH 6/9] optimized error copyright (#4374) --- .../src/components/file-uploader/file-uploader.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/file-uploader/file-uploader.js b/frontend/src/components/file-uploader/file-uploader.js index 357d09cc9b..7aebac3d70 100644 --- a/frontend/src/components/file-uploader/file-uploader.js +++ b/frontend/src/components/file-uploader/file-uploader.js @@ -388,7 +388,16 @@ class FileUploader extends React.Component { if (!message) { error = gettext('Network error'); } else { - error = message; + // eg: '{"error": "Internal error" \n }' + let errorMessage = message.replace(/\n/g, ''); + errorMessage = JSON.parse(errorMessage); + error = errorMessage.error; + if (error === 'File locked by others.') { + error = gettext('File Locked by others.'); + } + if (error === 'Internal error.') { + error = gettext('Internal error.'); + } } let uploadFileList = this.state.uploadFileList.map(item => { From 520f6d4639c41c0192d918a03cd144de12fe0812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=A1=BA=E5=BC=BA?= Date: Wed, 25 Dec 2019 10:16:09 +0800 Subject: [PATCH 7/9] Repair toolbar operation state (#4375) * repair toolbar operation state error bug * add comment * optimized code --- .../lib-content-view/lib-content-view.js | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/lib-content-view/lib-content-view.js b/frontend/src/pages/lib-content-view/lib-content-view.js index c37648d143..161287c234 100644 --- a/frontend/src/pages/lib-content-view/lib-content-view.js +++ b/frontend/src/pages/lib-content-view/lib-content-view.js @@ -1270,7 +1270,11 @@ class LibContentView extends React.Component { let direntList = this.state.direntList.filter(item => { return item.name !== name; }); - this.setState({ direntList: direntList }); + + // Recalculate the state of the selection + this.recaculateSelectedStateAfterDirentDeleted(name, direntList); + + this.setState({direntList: direntList}); this.updateReadmeMarkdown(direntList); } else if (Utils.isAncestorPath(direntPath, this.state.path)) { // the deleted item is ancester of the current item @@ -1289,6 +1293,10 @@ class LibContentView extends React.Component { let direntList = this.state.direntList.filter(item => { return item.name !== name; }); + + // Recalculate the state of the selection + this.recaculateSelectedStateAfterDirentDeleted(name, direntList); + this.setState({direntList: direntList}); this.updateReadmeMarkdown(direntList); } @@ -1537,6 +1545,20 @@ class LibContentView extends React.Component { }); } + recaculateSelectedStateAfterDirentDeleted = (name, newDirentList) => { + let selectedDirentList = this.state.selectedDirentList.slice(0); + if (selectedDirentList.length > 0) { + selectedDirentList = selectedDirentList.filter(item => { + return item.name !== name; + }); + } + this.setState({ + selectedDirentList: selectedDirentList, + isDirentSelected: selectedDirentList.length > 0, + isAllDirentSelected: selectedDirentList.length === newDirentList.length, + }); + } + onLibDecryptDialog = () => { this.setState({libNeedDecrypt: false}); this.loadDirData(this.state.path); From f285aec5ab77a79676049fcd929e7f1dd61fd6d6 Mon Sep 17 00:00:00 2001 From: lian Date: Fri, 27 Dec 2019 17:32:21 +0800 Subject: [PATCH 8/9] return ancestors when get org groups by users (#4376) --- seahub/api2/endpoints/groups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seahub/api2/endpoints/groups.py b/seahub/api2/endpoints/groups.py index 4251bddc56..295a6a40af 100644 --- a/seahub/api2/endpoints/groups.py +++ b/seahub/api2/endpoints/groups.py @@ -95,7 +95,7 @@ class Groups(APIView): username = request.user.username if is_org_context(request): org_id = request.user.org.org_id - user_groups = seaserv.get_org_groups_by_user(org_id, username) + user_groups = seaserv.get_org_groups_by_user(org_id, username, return_ancestors=True) else: user_groups = ccnet_api.get_groups(username, return_ancestors=True) From 3343d0533c8af7092af4bfb6a236055e55bc246d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E9=A1=BA=E5=BC=BA?= Date: Sat, 4 Jan 2020 10:19:49 +0800 Subject: [PATCH 9/9] repair register user bug (#4392) --- seahub/templates/registration/registration_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seahub/templates/registration/registration_form.html b/seahub/templates/registration/registration_form.html index bf33621b6f..73f4bf147b 100644 --- a/seahub/templates/registration/registration_form.html +++ b/seahub/templates/registration/registration_form.html @@ -81,7 +81,6 @@ $('#signup-form').on('submit', function(){ var email = $.trim($('input[name="email"]').val()), pwd1 = $.trim($('input[name="password1"]').val()), pwd2 = $.trim($('input[name="password2"]').val()); - level = getStrengthLevel(pwd1); if (!email) { $('.error').html("{% trans "Email cannot be blank" %}").removeClass('hide'); @@ -100,6 +99,7 @@ $('#signup-form').on('submit', function(){ return false; } {% if strong_pwd_required %} + var level = getStrengthLevel(pwd1); if (level < {{level}}) { $('.error').html(passwd_tip).removeClass('hide'); return false;