mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-01 23:20:51 +00:00
Merge branch '7.0'
This commit is contained in:
@@ -24,6 +24,12 @@ class GenerateShareLink extends React.Component {
|
|||||||
|
|
||||||
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && shareLinkExpireDaysDefault == 0);
|
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && shareLinkExpireDaysDefault == 0);
|
||||||
this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault;
|
this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault;
|
||||||
|
|
||||||
|
if (isPro) {
|
||||||
|
this.editOption = 'edit_download';
|
||||||
|
this.permissionOptions = ['preview_download', 'preview_only'];
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isValidate: false,
|
isValidate: false,
|
||||||
isShowPasswordInput: false,
|
isShowPasswordInput: false,
|
||||||
@@ -36,14 +42,9 @@ class GenerateShareLink extends React.Component {
|
|||||||
sharedLinkInfo: null,
|
sharedLinkInfo: null,
|
||||||
isNoticeMessageShow: false,
|
isNoticeMessageShow: false,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
fileInfo: null,
|
currentPermission: isPro ? this.permissionOptions[0] : '',
|
||||||
isSendLinkShown: false
|
isSendLinkShown: false
|
||||||
};
|
};
|
||||||
this.permissions = {
|
|
||||||
'can_edit': false,
|
|
||||||
'can_download': true
|
|
||||||
};
|
|
||||||
this.isOfficeFile = Utils.isEditableOfficeFile(this.props.itemPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -63,10 +64,11 @@ class GenerateShareLink extends React.Component {
|
|||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
});
|
});
|
||||||
if (this.isOfficeFile) {
|
|
||||||
|
if (isPro && Utils.isEditableOfficeFile(path)) {
|
||||||
seafileAPI.getFileInfo(repoID, path).then((res) => {
|
seafileAPI.getFileInfo(repoID, path).then((res) => {
|
||||||
if (res.data) {
|
if (res.data.can_edit) {
|
||||||
this.setState({fileInfo: res.data});
|
this.permissionOptions.push(this.editOption);
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
@@ -108,23 +110,8 @@ class GenerateShareLink extends React.Component {
|
|||||||
this.setState({passwdnew: passwd});
|
this.setState({passwdnew: passwd});
|
||||||
}
|
}
|
||||||
|
|
||||||
setPermission = (permission) => {
|
setPermission = (e) => {
|
||||||
if (permission == 'previewAndDownload') {
|
this.setState({currentPermission: e.target.value});
|
||||||
this.permissions = {
|
|
||||||
'can_edit': false,
|
|
||||||
'can_download': true
|
|
||||||
};
|
|
||||||
} else if (permission == 'preview') {
|
|
||||||
this.permissions = {
|
|
||||||
'can_edit': false,
|
|
||||||
'can_download': false
|
|
||||||
};
|
|
||||||
} else if (permission == 'editOnCloudAndDownload'){
|
|
||||||
this.permissions = {
|
|
||||||
'can_edit': true,
|
|
||||||
'can_download': true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generateShareLink = () => {
|
generateShareLink = () => {
|
||||||
@@ -133,7 +120,11 @@ class GenerateShareLink extends React.Component {
|
|||||||
this.setState({errorInfo: ''});
|
this.setState({errorInfo: ''});
|
||||||
let { itemPath, repoID } = this.props;
|
let { itemPath, repoID } = this.props;
|
||||||
let { password, isExpireChecked, expireDays } = this.state;
|
let { password, isExpireChecked, expireDays } = this.state;
|
||||||
let permissions = isPro ? JSON.stringify(this.permissions) : '';
|
let permissions;
|
||||||
|
if (isPro) {
|
||||||
|
const permissionDetails = Utils.getShareLinkPermissionObject(this.state.currentPermission).permissionDetails;
|
||||||
|
permissions = JSON.stringify(permissionDetails);
|
||||||
|
}
|
||||||
const expireDaysSent = isExpireChecked ? expireDays : '';
|
const expireDaysSent = isExpireChecked ? expireDays : '';
|
||||||
seafileAPI.createShareLink(repoID, itemPath, password, expireDaysSent, permissions).then((res) => {
|
seafileAPI.createShareLink(repoID, itemPath, password, expireDaysSent, permissions).then((res) => {
|
||||||
let sharedLinkInfo = new ShareLink(res.data);
|
let sharedLinkInfo = new ShareLink(res.data);
|
||||||
@@ -172,10 +163,6 @@ class GenerateShareLink extends React.Component {
|
|||||||
sharedLinkInfo: null,
|
sharedLinkInfo: null,
|
||||||
isNoticeMessageShow: false,
|
isNoticeMessageShow: false,
|
||||||
});
|
});
|
||||||
this.permissions = {
|
|
||||||
'can_edit': false,
|
|
||||||
'can_download': true
|
|
||||||
};
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
@@ -340,7 +327,6 @@ class GenerateShareLink extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let fileInfo = this.state.fileInfo;
|
|
||||||
return (
|
return (
|
||||||
<Form className="generate-share-link">
|
<Form className="generate-share-link">
|
||||||
<FormGroup check>
|
<FormGroup check>
|
||||||
@@ -403,28 +389,21 @@ class GenerateShareLink extends React.Component {
|
|||||||
)}
|
)}
|
||||||
{isPro && (
|
{isPro && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<FormGroup check>
|
<FormGroup check>
|
||||||
<Label check>
|
<Label check>
|
||||||
<span>{' '}{gettext('Set permission')}</span>
|
<span>{gettext('Set permission')}</span>
|
||||||
</Label>
|
</Label>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup check className="permission">
|
{this.permissionOptions.map((item, index) => {
|
||||||
<Label className="form-check-label">
|
return (
|
||||||
<Input type="radio" name="radio1" defaultChecked={true} onChange={() => this.setPermission('previewAndDownload')}/>{' '}{gettext('Preview and download')}
|
<FormGroup check className="permission" key={index}>
|
||||||
</Label>
|
<Label className="form-check-label">
|
||||||
</FormGroup>
|
<Input type="radio" name="permission" value={item} checked={this.state.currentPermission == item} onChange={this.setPermission} className="mr-1" />
|
||||||
<FormGroup check className="permission">
|
{Utils.getShareLinkPermissionObject(item).text}
|
||||||
<Label className="form-check-label">
|
</Label>
|
||||||
<Input type="radio" name="radio1" onChange={() => this.setPermission('preview')} />{' '}{gettext('Preview only')}
|
</FormGroup>
|
||||||
</Label>
|
);
|
||||||
</FormGroup>
|
})}
|
||||||
{(this.isOfficeFile && fileInfo && fileInfo.can_edit) &&
|
|
||||||
<FormGroup check className="permission">
|
|
||||||
<Label className="form-check-label">
|
|
||||||
<Input type="radio" name="radio1" onChange={() => this.setPermission('editOnCloudAndDownload')} />{' '}{gettext('Edit on cloud and download')}
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
}
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
{this.state.errorInfo && <Alert color="danger" className="mt-2">{gettext(this.state.errorInfo)}</Alert>}
|
{this.state.errorInfo && <Alert color="danger" className="mt-2">{gettext(this.state.errorInfo)}</Alert>}
|
||||||
|
@@ -249,12 +249,12 @@ class FolderItem extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLink = (node) => {
|
renderLink = ({ href, name, path }) => {
|
||||||
const className = node.path === this.props.currentPath ? 'wiki-nav-content wiki-nav-content-highlight' : 'wiki-nav-content';
|
const className = `wiki-nav-content ${path === this.props.currentPath ? 'wiki-nav-content-highlight' : ''}`;
|
||||||
if (node.href && node.name) {
|
if (href && name) {
|
||||||
return <div className={className}><a href={node.href} data-path={node.path} onClick={this.toggleExpanded}>{node.name}</a></div>;
|
return <div className={className}><a href={href} data-path={path} onClick={this.toggleExpanded} title={name}>{name}</a></div>;
|
||||||
} else if (node.name) {
|
} else if (name) {
|
||||||
return <div className="wiki-nav-content"><span onClick={this.toggleExpanded}>{node.name}</span></div>;
|
return <div className="wiki-nav-content"><span onClick={this.toggleExpanded} title={name}>{name}</span></div>;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -54,7 +54,9 @@ class MainSideNav extends React.Component {
|
|||||||
|
|
||||||
this.groupsHeight = (groupList.length + 1) * _this.listHeight;
|
this.groupsHeight = (groupList.length + 1) * _this.listHeight;
|
||||||
_this.setState({
|
_this.setState({
|
||||||
groupItems: groupList
|
groupItems: groupList.sort((a, b) => {
|
||||||
|
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
|
|
||||||
const { err, trafficOverLimit } = window.shared.pageOptions;
|
const { err, trafficOverLimit, zipped, filePath } = window.shared.pageOptions;
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
errorMsg: PropTypes.string
|
errorMsg: PropTypes.string
|
||||||
@@ -21,7 +21,7 @@ class SharedFileViewTip extends React.Component {
|
|||||||
<div className="file-view-tip">
|
<div className="file-view-tip">
|
||||||
{errorMsg}
|
{errorMsg}
|
||||||
{!trafficOverLimit &&
|
{!trafficOverLimit &&
|
||||||
<a href="?dl=1" className="btn btn-secondary">{gettext('Download')}</a>
|
<a href={`?${zipped ? 'p=' + encodeURIComponent(filePath) + '&' : ''}dl=1`} className="btn btn-secondary">{gettext('Download')}</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import ModalPortal from '../modal-portal';
|
import ModalPortal from '../modal-portal';
|
||||||
@@ -35,6 +36,7 @@ class DirOperationToolbar extends React.Component {
|
|||||||
isCreateMenuShow: false,
|
isCreateMenuShow: false,
|
||||||
isShareDialogShow: false,
|
isShareDialogShow: false,
|
||||||
operationMenuStyle: '',
|
operationMenuStyle: '',
|
||||||
|
isMobileOpMenuOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +48,10 @@ class DirOperationToolbar extends React.Component {
|
|||||||
document.removeEventListener('click', this.hideOperationMenu);
|
document.removeEventListener('click', this.hideOperationMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleMobileOpMenu = () => {
|
||||||
|
this.setState({isMobileOpMenuOpen: !this.state.isMobileOpMenuOpen});
|
||||||
|
}
|
||||||
|
|
||||||
hideOperationMenu = () => {
|
hideOperationMenu = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isUploadMenuShow: false,
|
isUploadMenuShow: false,
|
||||||
@@ -161,17 +167,34 @@ class DirOperationToolbar extends React.Component {
|
|||||||
|
|
||||||
let itemType = path === '/' ? 'library' : 'dir';
|
let itemType = path === '/' ? 'library' : 'dir';
|
||||||
let itemName = path == '/' ? repoName : Utils.getFolderName(path);
|
let itemName = path == '/' ? repoName : Utils.getFolderName(path);
|
||||||
|
|
||||||
|
const content = Utils.isDesktop() ? (
|
||||||
|
<Fragment>
|
||||||
|
{Utils.isSupportUploadFolder() ?
|
||||||
|
<button className="btn btn-secondary operation-item" title={gettext('Upload')} onClick={this.onUploadClick}>{gettext('Upload')}</button> :
|
||||||
|
<button className="btn btn-secondary operation-item" title={gettext('Upload')} onClick={this.onUploadFile}>{gettext('Upload')}</button>}
|
||||||
|
<button className="btn btn-secondary operation-item" title={gettext('New')} onClick={this.onCreateClick}>{gettext('New')}</button>
|
||||||
|
{this.props.showShareBtn &&
|
||||||
|
<button className="btn btn-secondary operation-item" title={gettext('Share')} onClick={this.onShareClick}>{gettext('Share')}</button>}
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<Dropdown isOpen={this.state.isMobileOpMenuOpen} toggle={this.toggleMobileOpMenu}>
|
||||||
|
<DropdownToggle
|
||||||
|
tag="span"
|
||||||
|
className="sf2-icon-plus mobile-toolbar-icon"
|
||||||
|
/>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownItem onClick={this.onUploadFile}>{gettext('Upload')}</DropdownItem>
|
||||||
|
<DropdownItem onClick={this.onCreateFolderToggle}>{gettext('New Folder')}</DropdownItem>
|
||||||
|
<DropdownItem onClick={this.onCreateFileToggle}>{gettext('New File')}</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div className="operation">
|
<div className="operation">
|
||||||
{Utils.isSupportUploadFolder() ?
|
{content}
|
||||||
<button className="btn btn-secondary operation-item" title={gettext('Upload')} onClick={this.onUploadClick}>{gettext('Upload')}</button> :
|
|
||||||
<button className="btn btn-secondary operation-item" title={gettext('Upload')} onClick={this.onUploadFile}>{gettext('Upload')}</button>
|
|
||||||
}
|
|
||||||
<button className="btn btn-secondary operation-item" title={gettext('New')} onClick={this.onCreateClick}>{gettext('New')}</button>
|
|
||||||
{this.props.showShareBtn &&
|
|
||||||
<button className="btn btn-secondary operation-item" title={gettext('Share')} onClick={this.onShareClick}>{gettext('Share')}</button>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
{this.state.isUploadMenuShow && (
|
{this.state.isUploadMenuShow && (
|
||||||
<ul className="menu dropdown-menu" style={this.state.operationMenuStyle}>
|
<ul className="menu dropdown-menu" style={this.state.operationMenuStyle}>
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import CommonToolbar from '../../components/toolbar/common-toolbar';
|
import CommonToolbar from '../../components/toolbar/common-toolbar';
|
||||||
import ViewModeToolbar from '../../components/toolbar/view-mode-toolbar';
|
import ViewModeToolbar from '../../components/toolbar/view-mode-toolbar';
|
||||||
@@ -123,7 +124,7 @@ class LibContentToolbar extends React.Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<ViewModeToolbar currentMode={this.props.currentMode} switchViewMode={this.props.switchViewMode}/>
|
{Utils.isDesktop() && <ViewModeToolbar currentMode={this.props.currentMode} switchViewMode={this.props.switchViewMode} />}
|
||||||
</div>
|
</div>
|
||||||
<CommonToolbar repoID={this.props.repoID} onSearchedClick={this.props.onSearchedClick} searchPlaceholder={gettext('Search files in this library')}/>
|
<CommonToolbar repoID={this.props.repoID} onSearchedClick={this.props.onSearchedClick} searchPlaceholder={gettext('Search files in this library')}/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cookie from 'react-cookies';
|
import cookie from 'react-cookies';
|
||||||
import MediaQuery from 'react-responsive';
|
import MediaQuery from 'react-responsive';
|
||||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap';
|
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { gettext, loginUrl, canAddPublicRepo } from '../../utils/constants';
|
import { gettext, loginUrl, canAddPublicRepo } from '../../utils/constants';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
|
@@ -86,15 +86,18 @@ def _handle_login_form_valid(request, user, redirect_to, remember_me):
|
|||||||
@csrf_protect
|
@csrf_protect
|
||||||
@never_cache
|
@never_cache
|
||||||
def login(request, template_name='registration/login.html',
|
def login(request, template_name='registration/login.html',
|
||||||
redirect_if_logged_in=None,
|
redirect_if_logged_in='libraries',
|
||||||
redirect_field_name=REDIRECT_FIELD_NAME,
|
redirect_field_name=REDIRECT_FIELD_NAME,
|
||||||
authentication_form=AuthenticationForm):
|
authentication_form=AuthenticationForm):
|
||||||
"""Displays the login form and handles the login action."""
|
"""Displays the login form and handles the login action."""
|
||||||
|
|
||||||
if request.user.is_authenticated() and redirect_if_logged_in:
|
|
||||||
return HttpResponseRedirect(reverse(redirect_if_logged_in))
|
|
||||||
|
|
||||||
redirect_to = request.GET.get(redirect_field_name, '')
|
redirect_to = request.GET.get(redirect_field_name, '')
|
||||||
|
if request.user.is_authenticated():
|
||||||
|
if redirect_to:
|
||||||
|
return HttpResponseRedirect(redirect_to)
|
||||||
|
else:
|
||||||
|
return HttpResponseRedirect(reverse(redirect_if_logged_in))
|
||||||
|
|
||||||
ip = get_remote_ip(request)
|
ip = get_remote_ip(request)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
Reference in New Issue
Block a user