mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-01 15:09:14 +00:00
Merge branch '7.0'
This commit is contained in:
@@ -64,11 +64,6 @@ module.exports = {
|
|||||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||||
paths.appSrc + "/wiki.js",
|
paths.appSrc + "/wiki.js",
|
||||||
],
|
],
|
||||||
repoview: [
|
|
||||||
require.resolve('./polyfills'),
|
|
||||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
|
||||||
paths.appSrc + "/repo-wiki-mode.js",
|
|
||||||
],
|
|
||||||
fileHistory: [
|
fileHistory: [
|
||||||
require.resolve('./polyfills'),
|
require.resolve('./polyfills'),
|
||||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||||
|
@@ -61,7 +61,6 @@ module.exports = {
|
|||||||
markdownEditor: [require.resolve('./polyfills'), paths.appIndexJs],
|
markdownEditor: [require.resolve('./polyfills'), paths.appIndexJs],
|
||||||
TCAccept: [require.resolve('./polyfills'), paths.appSrc + "/tc-accept.js"],
|
TCAccept: [require.resolve('./polyfills'), paths.appSrc + "/tc-accept.js"],
|
||||||
wiki: [require.resolve('./polyfills'), paths.appSrc + "/wiki.js"],
|
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"],
|
fileHistory: [require.resolve('./polyfills'), paths.appSrc + "/file-history.js"],
|
||||||
fileHistoryOld: [require.resolve('./polyfills'), paths.appSrc + "/file-history-old.js"],
|
fileHistoryOld: [require.resolve('./polyfills'), paths.appSrc + "/file-history-old.js"],
|
||||||
app: [require.resolve('./polyfills'), paths.appSrc + "/app.js"],
|
app: [require.resolve('./polyfills'), paths.appSrc + "/app.js"],
|
||||||
|
@@ -494,13 +494,10 @@ class DirentListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderItemOperation = () => {
|
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
|
// 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 (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@@ -514,9 +511,11 @@ class DirentListItem extends React.Component {
|
|||||||
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i>
|
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
<li className="operation-group-item">
|
{showShareBtn && (
|
||||||
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
|
<li className="operation-group-item">
|
||||||
</li>
|
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
{dirent.permission === 'rw' && (
|
{dirent.permission === 'rw' && (
|
||||||
<li className="operation-group-item">
|
<li className="operation-group-item">
|
||||||
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
|
<i className="op-icon sf2-icon-delete" title={gettext('Delete')} onClick={this.onItemDelete}></i>
|
||||||
@@ -546,7 +545,7 @@ class DirentListItem extends React.Component {
|
|||||||
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i>
|
<i className="op-icon sf2-icon-download" title={gettext('Download')} onClick={this.onItemDownload}></i>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{(
|
{showShareBtn && (
|
||||||
<li className="operation-group-item">
|
<li className="operation-group-item">
|
||||||
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
|
<i className="op-icon sf2-icon-share" title={gettext('Share')} onClick={this.onItemShare}></i>
|
||||||
</li>
|
</li>
|
||||||
|
@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Resumablejs from '@seafile/resumablejs';
|
import Resumablejs from '@seafile/resumablejs';
|
||||||
import MD5 from 'MD5';
|
import MD5 from 'MD5';
|
||||||
import { enableResumableFileUpload, resumableUploadFileBlockSize } from '../../utils/constants';
|
import { enableResumableFileUpload, resumableUploadFileBlockSize, maxUploadFileSize } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
@@ -18,13 +18,11 @@ const propTypes = {
|
|||||||
chunkSize: PropTypes.number,
|
chunkSize: PropTypes.number,
|
||||||
withCredentials: PropTypes.bool,
|
withCredentials: PropTypes.bool,
|
||||||
maxFiles: PropTypes.number,
|
maxFiles: PropTypes.number,
|
||||||
maxFileSize: PropTypes.number,
|
|
||||||
testMethod: PropTypes.string,
|
testMethod: PropTypes.string,
|
||||||
testChunks: PropTypes.number,
|
testChunks: PropTypes.number,
|
||||||
simultaneousUploads: PropTypes.number,
|
simultaneousUploads: PropTypes.number,
|
||||||
fileParameterName: PropTypes.string,
|
fileParameterName: PropTypes.string,
|
||||||
maxFilesErrorCallback: PropTypes.func,
|
maxFilesErrorCallback: PropTypes.func,
|
||||||
maxFileSizeErrorCallback: PropTypes.func,
|
|
||||||
minFileSizeErrorCallback: PropTypes.func,
|
minFileSizeErrorCallback: PropTypes.func,
|
||||||
fileTypeErrorCallback: PropTypes.func,
|
fileTypeErrorCallback: PropTypes.func,
|
||||||
dragAndDrop: PropTypes.bool.isRequired,
|
dragAndDrop: PropTypes.bool.isRequired,
|
||||||
@@ -39,6 +37,7 @@ class FileUploader extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
retryFileList: [],
|
retryFileList: [],
|
||||||
uploadFileList: [],
|
uploadFileList: [],
|
||||||
|
forbidUploadFileList: [],
|
||||||
totalProgress: 0,
|
totalProgress: 0,
|
||||||
isUploadProgressDialogShow: false,
|
isUploadProgressDialogShow: false,
|
||||||
isUploadRemindDialogShow: false,
|
isUploadRemindDialogShow: false,
|
||||||
@@ -64,7 +63,7 @@ class FileUploader extends React.Component {
|
|||||||
query: this.setQuery || {},
|
query: this.setQuery || {},
|
||||||
fileType: this.props.filetypes,
|
fileType: this.props.filetypes,
|
||||||
maxFiles: this.props.maxFiles,
|
maxFiles: this.props.maxFiles,
|
||||||
maxFileSize: this.props.maxFileSize,
|
maxFileSize: maxUploadFileSize * 1000 * 1000 || undefined,
|
||||||
testMethod: this.props.testMethod || 'post',
|
testMethod: this.props.testMethod || 'post',
|
||||||
testChunks: this.props.testChunks || false,
|
testChunks: this.props.testChunks || false,
|
||||||
headers: this.setHeaders || {},
|
headers: this.setHeaders || {},
|
||||||
@@ -105,7 +104,7 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindCallbackHandler = () => {
|
bindCallbackHandler = () => {
|
||||||
let {maxFilesErrorCallback, minFileSizeErrorCallback, maxFileSizeErrorCallback, fileTypeErrorCallback } = this.props;
|
let {maxFilesErrorCallback, minFileSizeErrorCallback, fileTypeErrorCallback } = this.props;
|
||||||
|
|
||||||
if (maxFilesErrorCallback) {
|
if (maxFilesErrorCallback) {
|
||||||
this.resumable.opts.maxFilesErrorCallback = this.props.maxFilesErrorCallback;
|
this.resumable.opts.maxFilesErrorCallback = this.props.maxFilesErrorCallback;
|
||||||
@@ -115,8 +114,8 @@ class FileUploader extends React.Component {
|
|||||||
this.resumable.opts.minFileSizeErrorCallback = this.props.minFileSizeErrorCallback;
|
this.resumable.opts.minFileSizeErrorCallback = this.props.minFileSizeErrorCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxFileSizeErrorCallback) {
|
if (this.maxFileSizeErrorCallback) {
|
||||||
this.resumable.opts.maxFileSizeErrorCallback = this.props.maxFileSizeErrorCallback;
|
this.resumable.opts.maxFileSizeErrorCallback = this.maxFileSizeErrorCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileTypeErrorCallback) {
|
if (fileTypeErrorCallback) {
|
||||||
@@ -142,6 +141,12 @@ class FileUploader extends React.Component {
|
|||||||
this.resumable.on('dragstart', this.onDragStart.bind(this));
|
this.resumable.on('dragstart', this.onDragStart.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxFileSizeErrorCallback = (file) => {
|
||||||
|
let { forbidUploadFileList } = this.state;
|
||||||
|
forbidUploadFileList.push(file);
|
||||||
|
this.setState({forbidUploadFileList: forbidUploadFileList});
|
||||||
|
}
|
||||||
|
|
||||||
onChunkingComplete = (resumableFile) => {
|
onChunkingComplete = (resumableFile) => {
|
||||||
|
|
||||||
let allFilesUploaded = this.state.allFilesUploaded;
|
let allFilesUploaded = this.state.allFilesUploaded;
|
||||||
@@ -229,7 +234,13 @@ class FileUploader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filesAddedComplete = (resumable, files) => {
|
filesAddedComplete = (resumable, files) => {
|
||||||
// single file uploading can check repetition, because custom dialog conn't prevent program execution;
|
let { forbidUploadFileList } = this.state;
|
||||||
|
if (forbidUploadFileList.length > 0 && files.length === 0) {
|
||||||
|
this.setState({
|
||||||
|
isUploadProgressDialogShow: true,
|
||||||
|
totalProgress: 100
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setUploadFileList = () => {
|
setUploadFileList = () => {
|
||||||
@@ -377,7 +388,16 @@ class FileUploader extends React.Component {
|
|||||||
if (!message) {
|
if (!message) {
|
||||||
error = gettext('Network error');
|
error = gettext('Network error');
|
||||||
} else {
|
} 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 => {
|
let uploadFileList = this.state.uploadFileList.map(item => {
|
||||||
@@ -481,7 +501,7 @@ class FileUploader extends React.Component {
|
|||||||
this.resumable.files = [];
|
this.resumable.files = [];
|
||||||
// reset upload link loaded
|
// reset upload link loaded
|
||||||
this.isUploadLinkLoaded = false;
|
this.isUploadLinkLoaded = false;
|
||||||
this.setState({isUploadProgressDialogShow: false, uploadFileList: []});
|
this.setState({isUploadProgressDialogShow: false, uploadFileList: [], forbidUploadFileList: []});
|
||||||
Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', false);
|
Utils.registerGlobalVariable('uploader', 'isUploadProgressDialogShow', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,6 +687,7 @@ class FileUploader extends React.Component {
|
|||||||
<UploadProgressDialog
|
<UploadProgressDialog
|
||||||
retryFileList={this.state.retryFileList}
|
retryFileList={this.state.retryFileList}
|
||||||
uploadFileList={this.state.uploadFileList}
|
uploadFileList={this.state.uploadFileList}
|
||||||
|
forbidUploadFileList={this.state.forbidUploadFileList}
|
||||||
totalProgress={this.state.totalProgress}
|
totalProgress={this.state.totalProgress}
|
||||||
uploadBitrate={this.state.uploadBitrate}
|
uploadBitrate={this.state.uploadBitrate}
|
||||||
allFilesUploaded={this.state.allFilesUploaded}
|
allFilesUploaded={this.state.allFilesUploaded}
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext, maxUploadFileSize } from '../../utils/constants';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
file: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ForbidUploadListItem extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { file } = this.props;
|
||||||
|
let msg = gettext('Please upload files less than {placeholder}M').replace('{placeholder}', maxUploadFileSize);
|
||||||
|
return (
|
||||||
|
<tr className="file-upload-item">
|
||||||
|
<td className="upload-name">
|
||||||
|
<div className="ellipsis">{file.name}</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td colSpan={3} className="error">{msg}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForbidUploadListItem.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default ForbidUploadListItem;
|
@@ -1,14 +1,16 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import UploadListItem from './upload-list-item';
|
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
|
import UploadListItem from './upload-list-item';
|
||||||
|
import ForbidUploadListItem from './forbid-upload-list-item';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
uploadBitrate: PropTypes.number.isRequired,
|
uploadBitrate: PropTypes.number.isRequired,
|
||||||
totalProgress: PropTypes.number.isRequired,
|
totalProgress: PropTypes.number.isRequired,
|
||||||
retryFileList: PropTypes.array.isRequired,
|
retryFileList: PropTypes.array.isRequired,
|
||||||
uploadFileList: PropTypes.array.isRequired,
|
uploadFileList: PropTypes.array.isRequired,
|
||||||
|
forbidUploadFileList: PropTypes.array.isRequired,
|
||||||
onCloseUploadDialog: PropTypes.func.isRequired,
|
onCloseUploadDialog: PropTypes.func.isRequired,
|
||||||
onCancelAllUploading: PropTypes.func.isRequired,
|
onCancelAllUploading: PropTypes.func.isRequired,
|
||||||
onUploadCancel: PropTypes.func.isRequired,
|
onUploadCancel: PropTypes.func.isRequired,
|
||||||
@@ -94,6 +96,11 @@ class UploadProgressDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{
|
||||||
|
this.props.forbidUploadFileList.map((file, index) => {
|
||||||
|
return (<ForbidUploadListItem key={index} file={file} />);
|
||||||
|
})
|
||||||
|
}
|
||||||
{
|
{
|
||||||
this.props.uploadFileList.map((resumableFile, index) => {
|
this.props.uploadFileList.map((resumableFile, index) => {
|
||||||
return (
|
return (
|
||||||
|
@@ -1264,7 +1264,11 @@ class LibContentView extends React.Component {
|
|||||||
let direntList = this.state.direntList.filter(item => {
|
let direntList = this.state.direntList.filter(item => {
|
||||||
return item.name !== name;
|
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);
|
this.updateReadmeMarkdown(direntList);
|
||||||
} else if (Utils.isAncestorPath(direntPath, this.state.path)) {
|
} else if (Utils.isAncestorPath(direntPath, this.state.path)) {
|
||||||
// the deleted item is ancester of the current item
|
// the deleted item is ancester of the current item
|
||||||
@@ -1283,6 +1287,10 @@ class LibContentView extends React.Component {
|
|||||||
let direntList = this.state.direntList.filter(item => {
|
let direntList = this.state.direntList.filter(item => {
|
||||||
return item.name !== name;
|
return item.name !== name;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Recalculate the state of the selection
|
||||||
|
this.recaculateSelectedStateAfterDirentDeleted(name, direntList);
|
||||||
|
|
||||||
this.setState({direntList: direntList});
|
this.setState({direntList: direntList});
|
||||||
this.updateReadmeMarkdown(direntList);
|
this.updateReadmeMarkdown(direntList);
|
||||||
}
|
}
|
||||||
@@ -1531,6 +1539,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 = () => {
|
onLibDecryptDialog = () => {
|
||||||
this.setState({libNeedDecrypt: false});
|
this.setState({libNeedDecrypt: false});
|
||||||
this.loadDirData(this.state.path);
|
this.loadDirData(this.state.path);
|
||||||
|
@@ -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 '<tr>'
|
|
||||||
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 = (<div className="message err-tip">{gettext('Folder does not exist.')}</div>);
|
|
||||||
const showRepoInfoBar = this.props.path === '/' && (
|
|
||||||
this.props.usedRepoTags.length != 0 || this.props.readmeMarkdown != null ||
|
|
||||||
this.props.draftCounts != 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="main-panel wiki-main-panel o-hidden">
|
|
||||||
<div className="main-panel-north border-left-show">
|
|
||||||
<div className="cur-view-toolbar">
|
|
||||||
<span className="sf2-icon-menu hidden-md-up d-md-none side-nav-toggle" title={gettext('Side Nav Menu')} onClick={this.onSideNavMenuClick}></span>
|
|
||||||
<div className="dir-operation">
|
|
||||||
{this.props.isDirentSelected ?
|
|
||||||
<MultipleDirOperationToolbar
|
|
||||||
repoID={repoID}
|
|
||||||
path={this.props.path}
|
|
||||||
selectedDirentList={this.props.selectedDirentList}
|
|
||||||
onItemsMove={this.props.onItemsMove}
|
|
||||||
onItemsCopy={this.props.onItemsCopy}
|
|
||||||
onItemsDelete={this.props.onItemsDelete}
|
|
||||||
/> :
|
|
||||||
<DirOperationToolBar
|
|
||||||
path={this.props.path}
|
|
||||||
repoID={repoID}
|
|
||||||
repoName={this.props.repoName}
|
|
||||||
repoEncrypted={this.props.repoEncrypted}
|
|
||||||
isDraft={this.props.isDraft}
|
|
||||||
hasDraft={this.props.hasDraft}
|
|
||||||
direntList={this.props.direntList}
|
|
||||||
permission={this.props.permission}
|
|
||||||
isViewFile={this.props.isViewFile}
|
|
||||||
showShareBtn={this.props.showShareBtn}
|
|
||||||
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
|
||||||
userPerm={this.props.userPerm}
|
|
||||||
isAdmin={this.props.isAdmin}
|
|
||||||
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
|
||||||
onAddFile={this.props.onAddFile}
|
|
||||||
onAddFolder={this.props.onAddFolder}
|
|
||||||
onUploadFile={this.onUploadFile}
|
|
||||||
onUploadFolder={this.onUploadFolder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<ViewModeToolbar currentMode={this.state.currentMode} switchViewMode={this.switchViewMode}/>
|
|
||||||
</div>
|
|
||||||
<CommonToolbar repoID={repoID} onSearchedClick={this.props.onSearchedClick} searchPlaceholder={gettext('Search files in this library')}/>
|
|
||||||
</div>
|
|
||||||
<div className="main-panel-center flex-row">
|
|
||||||
<div className="cur-view-container">
|
|
||||||
<div className="cur-view-path">
|
|
||||||
{this.state.currentRepoInfo && (
|
|
||||||
<CurDirPath
|
|
||||||
repoID={repoID}
|
|
||||||
repoName={this.state.currentRepoInfo.repo_name}
|
|
||||||
currentPath={this.props.path}
|
|
||||||
permission={permission}
|
|
||||||
onPathClick={this.onMainNavBarClick}
|
|
||||||
isViewFile={this.props.isViewFile}
|
|
||||||
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="cur-view-content">
|
|
||||||
{!this.props.pathExist && ErrMessage }
|
|
||||||
{(this.props.pathExist && this.props.isViewFile) && (
|
|
||||||
<WikiMarkdownViewer
|
|
||||||
isFileLoading={this.props.isFileLoading}
|
|
||||||
markdownContent={this.props.content}
|
|
||||||
latestContributor={this.props.latestContributor}
|
|
||||||
lastModified = {this.props.lastModified}
|
|
||||||
onLinkClick={this.props.onLinkClick}
|
|
||||||
>
|
|
||||||
<Fragment>
|
|
||||||
{(!this.props.isDraft && this.props.hasDraft) &&
|
|
||||||
<div className='seafile-btn-view-review text-center'>
|
|
||||||
<div className='tag tag-green'>
|
|
||||||
{gettext('This file is in draft stage.')}
|
|
||||||
<a className="ml-2" onMouseDown={this.props.goDraftPage}>{gettext('View Draft')}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Fragment>
|
|
||||||
</WikiMarkdownViewer>
|
|
||||||
)}
|
|
||||||
{(this.props.pathExist && !this.props.isViewFile) && (
|
|
||||||
<Fragment>
|
|
||||||
{showRepoInfoBar && (
|
|
||||||
<RepoInfoBar
|
|
||||||
repoID={repoID}
|
|
||||||
currentPath={this.props.path}
|
|
||||||
usedRepoTags={this.props.usedRepoTags}
|
|
||||||
readmeMarkdown={this.props.readmeMarkdown}
|
|
||||||
draftCounts={this.props.draftCounts}
|
|
||||||
updateUsedRepoTags={this.props.updateUsedRepoTags}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<DirentListView
|
|
||||||
path={this.props.path}
|
|
||||||
repoID={repoID}
|
|
||||||
repoEncrypted={this.props.repoEncrypted}
|
|
||||||
isRepoOwner={this.props.isRepoOwner}
|
|
||||||
isAdmin={this.props.isAdmin}
|
|
||||||
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
|
|
||||||
enableDirPrivateShare={this.props.enableDirPrivateShare}
|
|
||||||
direntList={this.props.direntList}
|
|
||||||
sortBy={this.props.sortBy}
|
|
||||||
sortOrder={this.props.sortOrder}
|
|
||||||
sortItems={this.props.sortItems}
|
|
||||||
onAddFile={this.props.onAddFile}
|
|
||||||
onItemClick={this.onItemClick}
|
|
||||||
onItemDelete={this.props.onItemDelete}
|
|
||||||
onItemRename={this.props.onItemRename}
|
|
||||||
onItemMove={this.props.onItemMove}
|
|
||||||
onItemCopy={this.props.onItemCopy}
|
|
||||||
onDirentClick={this.onDirentClick}
|
|
||||||
onItemDetails={this.onItemDetails}
|
|
||||||
isDirentListLoading={this.props.isDirentListLoading}
|
|
||||||
updateDirent={this.props.updateDirent}
|
|
||||||
currentRepoInfo={this.state.currentRepoInfo}
|
|
||||||
isAllItemSelected={this.props.isAllDirentSelected}
|
|
||||||
onAllItemSelected={this.props.onAllDirentSelected}
|
|
||||||
onItemSelected={this.props.onItemSelected}
|
|
||||||
/>
|
|
||||||
<FileUploader
|
|
||||||
ref={uploader => this.uploader = uploader}
|
|
||||||
dragAndDrop={true}
|
|
||||||
path={this.props.path}
|
|
||||||
repoID={repoID}
|
|
||||||
direntList={this.props.direntList}
|
|
||||||
onFileUploadSuccess={this.onFileUploadSuccess}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{this.state.isDirentDetailShow && (
|
|
||||||
<div className="cur-view-detail">
|
|
||||||
<DirentDetail
|
|
||||||
repoID={repoID}
|
|
||||||
path={this.props.path}
|
|
||||||
dirent={this.state.currentDirent}
|
|
||||||
direntPath={this.state.direntPath}
|
|
||||||
onItemDetailsClose={this.onItemDetailsClose}
|
|
||||||
onFileTagChanged={this.onFileTagChanged}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MainPanel.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default MainPanel;
|
|
File diff suppressed because it is too large
Load Diff
@@ -58,6 +58,7 @@ export const canAddPublicRepo = window.app.pageOptions.canAddPublicRepo;
|
|||||||
export const canInvitePeople = window.app.pageOptions.canInvitePeople;
|
export const canInvitePeople = window.app.pageOptions.canInvitePeople;
|
||||||
export const canLockUnlockFile = window.app.pageOptions.canLockUnlockFile;
|
export const canLockUnlockFile = window.app.pageOptions.canLockUnlockFile;
|
||||||
export const customNavItems = window.app.pageOptions.customNavItems;
|
export const customNavItems = window.app.pageOptions.customNavItems;
|
||||||
|
export const maxUploadFileSize = window.app.pageOptions.maxUploadFileSize;
|
||||||
|
|
||||||
export const curNoteMsg = window.app.pageOptions.curNoteMsg;
|
export const curNoteMsg = window.app.pageOptions.curNoteMsg;
|
||||||
export const curNoteID = window.app.pageOptions.curNoteID;
|
export const curNoteID = window.app.pageOptions.curNoteID;
|
||||||
|
@@ -92,7 +92,7 @@ class Groups(APIView):
|
|||||||
username = request.user.username
|
username = request.user.username
|
||||||
if is_org_context(request):
|
if is_org_context(request):
|
||||||
org_id = request.user.org.org_id
|
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:
|
else:
|
||||||
user_groups = ccnet_api.get_groups(username, return_ancestors=True)
|
user_groups = ccnet_api.get_groups(username, return_ancestors=True)
|
||||||
|
|
||||||
|
@@ -96,6 +96,9 @@
|
|||||||
canAddPublicRepo: {% if can_add_public_repo %} true {% else %} false {% endif %},
|
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 %},
|
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 %},
|
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 %}
|
{% if request.user.is_authenticated and request.cur_note %}
|
||||||
curNoteMsg: '{{ request.cur_note.message|urlize|escapejs }}',
|
curNoteMsg: '{{ request.cur_note.message|urlize|escapejs }}',
|
||||||
|
@@ -81,7 +81,6 @@ $('#signup-form').on('submit', function(){
|
|||||||
var email = $.trim($('input[name="email"]').val()),
|
var email = $.trim($('input[name="email"]').val()),
|
||||||
pwd1 = $.trim($('input[name="password1"]').val()),
|
pwd1 = $.trim($('input[name="password1"]').val()),
|
||||||
pwd2 = $.trim($('input[name="password2"]').val());
|
pwd2 = $.trim($('input[name="password2"]').val());
|
||||||
level = getStrengthLevel(pwd1);
|
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
$('.error').html("{% trans "Email cannot be blank" %}").removeClass('hide');
|
$('.error').html("{% trans "Email cannot be blank" %}").removeClass('hide');
|
||||||
@@ -100,6 +99,7 @@ $('#signup-form').on('submit', function(){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
{% if strong_pwd_required %}
|
{% if strong_pwd_required %}
|
||||||
|
var level = getStrengthLevel(pwd1);
|
||||||
if (level < {{level}}) {
|
if (level < {{level}}) {
|
||||||
$('.error').html(passwd_tip).removeClass('hide');
|
$('.error').html(passwd_tip).removeClass('hide');
|
||||||
return false;
|
return false;
|
||||||
|
@@ -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 %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.wiki = {
|
|
||||||
config: {
|
|
||||||
repoId: "{{ repo_id }}",
|
|
||||||
serviceUrl: "{{ service_url}}",
|
|
||||||
initial_path: "{{ initial_path|escapejs }}",
|
|
||||||
slug: "{{ repo_name }}",
|
|
||||||
isDir: "{{ is_dir }}",
|
|
||||||
permission: "{{ permission }}"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% render_bundle 'repoview' 'js' %}
|
|
||||||
{% endblock %}
|
|
@@ -1131,10 +1131,17 @@ def react_fake_view(request, **kwargs):
|
|||||||
|
|
||||||
folder_perm_enabled = True if is_pro_version() and ENABLE_FOLDER_PERM else False
|
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", {
|
return render(request, "react_app.html", {
|
||||||
"guide_enabled": guide_enabled,
|
"guide_enabled": guide_enabled,
|
||||||
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
|
'trash_repos_expire_days': expire_days if expire_days > 0 else 30,
|
||||||
'dtable_web_server': DTABLE_WEB_SERVER,
|
'dtable_web_server': DTABLE_WEB_SERVER,
|
||||||
|
'max_upload_file_size': max_upload_file_size,
|
||||||
'seafile_collab_server': SEAFILE_COLLAB_SERVER,
|
'seafile_collab_server': SEAFILE_COLLAB_SERVER,
|
||||||
'storages': get_library_storages(request),
|
'storages': get_library_storages(request),
|
||||||
'enable_repo_snapshot_label': settings.ENABLE_REPO_SNAPSHOT_LABEL,
|
'enable_repo_snapshot_label': settings.ENABLE_REPO_SNAPSHOT_LABEL,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
|
from seaserv import ccnet_api
|
||||||
from seahub.auth.backends import RemoteUserBackend
|
from seahub.auth.backends import RemoteUserBackend
|
||||||
from seahub.base.accounts import User
|
from seahub.base.accounts import User
|
||||||
from registration.models import (
|
from registration.models import (
|
||||||
@@ -43,9 +44,12 @@ class ShibbolethRemoteUserBackend(RemoteUserBackend):
|
|||||||
return
|
return
|
||||||
|
|
||||||
username = self.clean_username(remote_user)
|
username = self.clean_username(remote_user)
|
||||||
try:
|
|
||||||
user = User.objects.get(email=username)
|
local_ccnet_users = ccnet_api.search_emailusers('DB', username, -1, -1)
|
||||||
except User.DoesNotExist:
|
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:
|
if self.create_unknown_user:
|
||||||
user = User.objects.create_user(
|
user = User.objects.create_user(
|
||||||
email=username, is_active=self.activate_after_creation)
|
email=username, is_active=self.activate_after_creation)
|
||||||
|
Reference in New Issue
Block a user