mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-06 17:33:18 +00:00
[dtable] cleanup: removed related files and code (#4314)
This commit is contained in:
@@ -1,161 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Select from 'react-select';
|
|
||||||
import makeAnimated from 'react-select/lib/animated';
|
|
||||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
|
||||||
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Form, FormGroup, Label, Alert } from 'reactstrap';
|
|
||||||
import { gettext } from '../../utils/constants';
|
|
||||||
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
createDTable: PropTypes.func.isRequired,
|
|
||||||
onAddDTable: PropTypes.func.isRequired,
|
|
||||||
currentWorkspace: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
class CreateTableDialog extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
tableName: '',
|
|
||||||
errMessage: '',
|
|
||||||
isSubmitBtnActive: false,
|
|
||||||
selectedOption: null,
|
|
||||||
options:[],
|
|
||||||
};
|
|
||||||
this.newInput = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.newInput.focus();
|
|
||||||
this.newInput.setSelectionRange(0,0);
|
|
||||||
|
|
||||||
let options = [];
|
|
||||||
seafileAPI.getAccountInfo().then((res) => {
|
|
||||||
let obj = {};
|
|
||||||
obj.value = 'Personal';
|
|
||||||
obj.email = res.data.email;
|
|
||||||
obj.label = 'Personal';
|
|
||||||
options.push(obj);
|
|
||||||
seafileAPI.listGroups().then((res) => {
|
|
||||||
for (let i = 0 ; i < res.data.length; i++) {
|
|
||||||
let obj = {};
|
|
||||||
obj.value = res.data[i].name;
|
|
||||||
obj.email = res.data[i].id + '@seafile_group';
|
|
||||||
obj.label = res.data[i].name;
|
|
||||||
options.push(obj);
|
|
||||||
}
|
|
||||||
this.setState({options: options});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleChange = (e) => {
|
|
||||||
if (!e.target.value.trim()) {
|
|
||||||
this.setState({isSubmitBtnActive: false});
|
|
||||||
} else {
|
|
||||||
this.setState({isSubmitBtnActive: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
tableName: e.target.value,
|
|
||||||
}) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
|
||||||
this.setState({selectedOption: option});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSubmit = () => {
|
|
||||||
if (!this.state.isSubmitBtnActive) return;
|
|
||||||
if (!this.validateInputParams()) return;
|
|
||||||
const space = this.props.currentWorkspace;
|
|
||||||
const options = this.state.options;
|
|
||||||
let email;
|
|
||||||
if (space) {
|
|
||||||
for (let i = 0; i < options.length; i++) {
|
|
||||||
if ((space.owner_type === 'Personal' && options[i].value === 'Personal') || (space.owner_type === 'Group' && options[i].value === space.owner_name)) {
|
|
||||||
email = options[i].email;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
email = this.state.selectedOption.email;
|
|
||||||
}
|
|
||||||
this.props.createDTable(this.state.tableName.trim(), email);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.handleSubmit();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
this.props.onAddDTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
validateInputParams = () => {
|
|
||||||
let errMessage = '';
|
|
||||||
let tableName = this.state.tableName.trim();
|
|
||||||
if (!tableName.length) {
|
|
||||||
errMessage = gettext('Name is required');
|
|
||||||
this.setState({errMessage: errMessage});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tableName.indexOf('/') > -1) {
|
|
||||||
errMessage = gettext('Name should not include \'/\'.');
|
|
||||||
this.setState({errMessage: errMessage});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { currentWorkspace } = this.props;
|
|
||||||
return (
|
|
||||||
<Modal isOpen={true} toggle={this.toggle}>
|
|
||||||
<ModalHeader toggle={this.toggle}>{gettext('New Table')}</ModalHeader>
|
|
||||||
<ModalBody>
|
|
||||||
<Form>
|
|
||||||
<FormGroup>
|
|
||||||
<Label for="tableName">{gettext('Name')}</Label>
|
|
||||||
<Input
|
|
||||||
id="tableName"
|
|
||||||
onKeyPress={this.handleKeyPress}
|
|
||||||
innerRef={input => {this.newInput = input;}}
|
|
||||||
value={this.state.tableName}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Form>
|
|
||||||
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
|
||||||
{!currentWorkspace &&
|
|
||||||
<Fragment>
|
|
||||||
<Label>{gettext('Belong to')}</Label>
|
|
||||||
<Select
|
|
||||||
isClearable
|
|
||||||
isMulti={false}
|
|
||||||
maxMenuHeight={200}
|
|
||||||
hideSelectedOptions={true}
|
|
||||||
components={makeAnimated()}
|
|
||||||
placeholder=''
|
|
||||||
options={this.state.options}
|
|
||||||
onChange={this.handleSelectChange}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
|
||||||
<Button color="primary" onClick={this.handleSubmit} disabled={!this.state.isSubmitBtnActive}>{gettext('Submit')}</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateTableDialog.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default CreateTableDialog;
|
|
@@ -1,39 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { gettext } from '../../utils/constants';
|
|
||||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
currentTable: PropTypes.object.isRequired,
|
|
||||||
deleteCancel: PropTypes.func.isRequired,
|
|
||||||
handleSubmit: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class DeleteTableDialog extends React.Component {
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
this.props.deleteCancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let currentTable = this.props.currentTable;
|
|
||||||
let name = currentTable.name;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen={true} toggle={this.toggle}>
|
|
||||||
<ModalHeader toggle={this.toggle}>{gettext('Delete Table')}</ModalHeader>
|
|
||||||
<ModalBody>
|
|
||||||
<p>{gettext('Are you sure to delete')}{' '}<b>{name}</b> ?</p>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
|
|
||||||
<Button color="primary" onClick={this.props.handleSubmit}>{gettext('Delete')}</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteTableDialog.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default DeleteTableDialog;
|
|
@@ -1,378 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import moment from 'moment';
|
|
||||||
import copy from 'copy-to-clipboard';
|
|
||||||
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
|
|
||||||
import { gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants';
|
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
|
||||||
import { Utils } from '../../utils/utils';
|
|
||||||
import ShareLink from '../../models/share-link';
|
|
||||||
import toaster from '../toast';
|
|
||||||
import Loading from '../loading';
|
|
||||||
import DTableShareLink from '../../models/dtable-share-link';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
workspaceID: PropTypes.number.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
closeShareDialog: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class GenerateDTableShareLink extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.isExpireDaysNoLimit = (parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) === 0 && shareLinkExpireDaysDefault == 0);
|
|
||||||
this.defaultExpireDays = this.isExpireDaysNoLimit ? '' : shareLinkExpireDaysDefault;
|
|
||||||
|
|
||||||
this.isExpireDaysNoLimit = true;
|
|
||||||
this.defaultExpireDays = '';
|
|
||||||
|
|
||||||
this.permissionOptions = ['read-only', 'read-write'];
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isValidate: false,
|
|
||||||
isShowPasswordInput: false,
|
|
||||||
isPasswordVisible: false,
|
|
||||||
isExpireChecked: !this.isExpireDaysNoLimit,
|
|
||||||
password: '',
|
|
||||||
passwdnew: '',
|
|
||||||
expireDays: this.defaultExpireDays,
|
|
||||||
errorInfo: '',
|
|
||||||
sharedLinkInfo: null,
|
|
||||||
isNoticeMessageShow: false,
|
|
||||||
isLoading: true,
|
|
||||||
currentPermission: this.permissionOptions[0],
|
|
||||||
isSendLinkShown: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
let workspaceID = this.props.workspaceID;
|
|
||||||
let name = this.props.name;
|
|
||||||
seafileAPI.getDTableShareLink(workspaceID, name).then((res) => {
|
|
||||||
window.res = res;
|
|
||||||
if (res.data.dtable_share_links.length !== 0) {
|
|
||||||
let sharedLinkInfo = new ShareLink(res.data.dtable_share_links[0]);
|
|
||||||
this.setState({
|
|
||||||
isLoading: false,
|
|
||||||
sharedLinkInfo: sharedLinkInfo
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({isLoading: false});
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onPasswordInputChecked = () => {
|
|
||||||
this.setState({
|
|
||||||
isShowPasswordInput: !this.state.isShowPasswordInput,
|
|
||||||
password: '',
|
|
||||||
passwdnew: '',
|
|
||||||
errorInfo: ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
togglePasswordVisible = () => {
|
|
||||||
this.setState({
|
|
||||||
isPasswordVisible: !this.state.isPasswordVisible
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
generatePassword = () => {
|
|
||||||
let val = Utils.generatePassword(shareLinkPasswordMinLength);
|
|
||||||
this.setState({
|
|
||||||
password: val,
|
|
||||||
passwdnew: val
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
inputPassword = (e) => {
|
|
||||||
let passwd = e.target.value.trim();
|
|
||||||
this.setState({password: passwd});
|
|
||||||
}
|
|
||||||
|
|
||||||
inputPasswordNew = (e) => {
|
|
||||||
let passwd = e.target.value.trim();
|
|
||||||
this.setState({passwdnew: passwd});
|
|
||||||
}
|
|
||||||
|
|
||||||
generateDTableShareLink = () => {
|
|
||||||
let isValid = this.validateParamsInput();
|
|
||||||
if (isValid) {
|
|
||||||
this.setState({errorInfo: ''});
|
|
||||||
let { workspaceID, name } = this.props;
|
|
||||||
let { password, isExpireChecked, expireDays } = this.state;
|
|
||||||
let permission = Utils.getDTableShareLinkPermissionObject(this.state.currentPermission).permission;
|
|
||||||
const expireDaysSent = isExpireChecked ? expireDays : '';
|
|
||||||
seafileAPI.createDTableShareLink(workspaceID, name, password, expireDaysSent, permission).then((res) => {
|
|
||||||
let sharedLinkInfo = new DTableShareLink(res.data);
|
|
||||||
this.setState({sharedLinkInfo: sharedLinkInfo});
|
|
||||||
}).catch((error) => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCopySharedLink = () => {
|
|
||||||
let sharedLink = this.state.sharedLinkInfo.link;
|
|
||||||
copy(sharedLink);
|
|
||||||
toaster.success(gettext('Share link is copied to the clipboard.'));
|
|
||||||
this.props.closeShareDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteShareLink = () => {
|
|
||||||
let sharedLinkInfo = this.state.sharedLinkInfo;
|
|
||||||
seafileAPI.deleteDTableShareLink(sharedLinkInfo.token).then(() => {
|
|
||||||
this.setState({
|
|
||||||
password: '',
|
|
||||||
passwordnew: '',
|
|
||||||
isShowPasswordInput: false,
|
|
||||||
expireDays: this.defaultExpireDays,
|
|
||||||
isExpireChecked: !this.isExpireDaysNoLimit,
|
|
||||||
errorInfo: '',
|
|
||||||
sharedLinkInfo: null,
|
|
||||||
isNoticeMessageShow: false,
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onExpireChecked = (e) => {
|
|
||||||
this.setState({isExpireChecked: e.target.checked});
|
|
||||||
}
|
|
||||||
|
|
||||||
onExpireDaysChanged = (e) => {
|
|
||||||
let day = e.target.value.trim();
|
|
||||||
this.setState({expireDays: day});
|
|
||||||
}
|
|
||||||
|
|
||||||
setPermission = (e) => {
|
|
||||||
this.setState({currentPermission: e.target.value});
|
|
||||||
}
|
|
||||||
|
|
||||||
validateParamsInput = () => {
|
|
||||||
let { isShowPasswordInput , password, passwdnew, isExpireChecked, expireDays } = this.state;
|
|
||||||
// validate password
|
|
||||||
if (isShowPasswordInput) {
|
|
||||||
if (password.length === 0) {
|
|
||||||
this.setState({errorInfo: 'Please enter password'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (password.length < shareLinkPasswordMinLength) {
|
|
||||||
this.setState({errorInfo: 'Password is too short'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (password !== passwdnew) {
|
|
||||||
this.setState({errorInfo: 'Passwords don\'t match'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate days
|
|
||||||
// no limit
|
|
||||||
let reg = /^\d+$/;
|
|
||||||
if (this.isExpireDaysNoLimit) {
|
|
||||||
if (isExpireChecked) {
|
|
||||||
if (!expireDays) {
|
|
||||||
this.setState({errorInfo: 'Please enter days'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!reg.test(expireDays)) {
|
|
||||||
this.setState({errorInfo: 'Please enter a non-negative integer'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.setState({expireDays: parseInt(expireDays)});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!expireDays) {
|
|
||||||
this.setState({errorInfo: 'Please enter days'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!reg.test(expireDays)) {
|
|
||||||
this.setState({errorInfo: 'Please enter a non-negative integer'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
expireDays = parseInt(expireDays);
|
|
||||||
let minDays = parseInt(shareLinkExpireDaysMin);
|
|
||||||
let maxDays = parseInt(shareLinkExpireDaysMax);
|
|
||||||
|
|
||||||
if (minDays !== 0 && maxDays !== maxDays) {
|
|
||||||
if (expireDays < minDays) {
|
|
||||||
this.setState({errorInfo: 'Please enter valid days'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minDays === 0 && maxDays !== 0 ) {
|
|
||||||
if (expireDays > maxDays) {
|
|
||||||
this.setState({errorInfo: 'Please enter valid days'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minDays !== 0 && maxDays !== 0) {
|
|
||||||
if (expireDays < minDays || expireDays > maxDays) {
|
|
||||||
this.setState({errorInfo: 'Please enter valid days'});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({expireDays: expireDays});
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onNoticeMessageToggle = () => {
|
|
||||||
this.setState({isNoticeMessageShow: !this.state.isNoticeMessageShow});
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSendLink = () => {
|
|
||||||
this.setState({ isSendLinkShown: !this.state.isSendLinkShown });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
if (this.state.isLoading) {
|
|
||||||
return <Loading />;
|
|
||||||
}
|
|
||||||
|
|
||||||
let passwordLengthTip = gettext('(at least {passwordLength} characters)');
|
|
||||||
passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength);
|
|
||||||
|
|
||||||
if (this.state.sharedLinkInfo) {
|
|
||||||
let sharedLinkInfo = this.state.sharedLinkInfo;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Form className="mb-4">
|
|
||||||
<FormGroup className="mb-0">
|
|
||||||
<dt className="text-secondary font-weight-normal">{gettext('Link:')}</dt>
|
|
||||||
<dd className="d-flex">
|
|
||||||
<span>{sharedLinkInfo.link}</span>{' '}
|
|
||||||
{sharedLinkInfo.is_expired ?
|
|
||||||
<span className="err-message">({gettext('Expired')})</span> :
|
|
||||||
<span className="far fa-copy action-icon" onClick={this.onCopySharedLink}/>
|
|
||||||
}
|
|
||||||
</dd>
|
|
||||||
</FormGroup>
|
|
||||||
{sharedLinkInfo.expire_date && (
|
|
||||||
<FormGroup className="mb-0">
|
|
||||||
<dt className="text-secondary font-weight-normal">{gettext('Expiration Date:')}</dt>
|
|
||||||
<dd>{moment(sharedLinkInfo.expire_date).format('YYYY-MM-DD hh:mm:ss')}</dd>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
{(canSendShareLinkEmail && !this.state.isSendLinkShown && !this.state.isNoticeMessageShow) &&
|
|
||||||
<Button onClick={this.toggleSendLink} className='mr-2'>{gettext('Send')}</Button>
|
|
||||||
}
|
|
||||||
{(!this.state.isSendLinkShown && !this.state.isNoticeMessageShow) &&
|
|
||||||
<Button onClick={this.onNoticeMessageToggle}>{gettext('Delete')}</Button>
|
|
||||||
}
|
|
||||||
{this.state.isNoticeMessageShow &&
|
|
||||||
<div className="alert alert-warning">
|
|
||||||
<h4 className="alert-heading">{gettext('Are you sure you want to delete the share link?')}</h4>
|
|
||||||
<p className="mb-4">{gettext('If the share link is deleted, no one will be able to access it any more.')}</p>
|
|
||||||
<button className="btn btn-primary" onClick={this.deleteShareLink}>{gettext('Delete')}</button>{' '}
|
|
||||||
<button className="btn btn-secondary" onClick={this.onNoticeMessageToggle}>{gettext('Cancel')}</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Form className="generate-share-link">
|
|
||||||
<FormGroup check>
|
|
||||||
<Label check>
|
|
||||||
<Input type="checkbox" onChange={this.onPasswordInputChecked}/>{' '}{gettext('Add password protection')}
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
{this.state.isShowPasswordInput &&
|
|
||||||
<FormGroup className="link-operation-content" check>
|
|
||||||
<Label className="font-weight-bold">{gettext('Password')}</Label>{' '}<span className="tip">{passwordLengthTip}</span>
|
|
||||||
<InputGroup className="passwd">
|
|
||||||
<Input type={this.state.isPasswordVisible ? 'text' : 'password'} value={this.state.password || ''} onChange={this.inputPassword}/>
|
|
||||||
<InputGroupAddon addonType="append">
|
|
||||||
<Button onClick={this.togglePasswordVisible}><i className={`link-operation-icon fas ${this.state.isPasswordVisible ? 'fa-eye': 'fa-eye-slash'}`}></i></Button>
|
|
||||||
<Button onClick={this.generatePassword}><i className="link-operation-icon fas fa-magic"></i></Button>
|
|
||||||
</InputGroupAddon>
|
|
||||||
</InputGroup>
|
|
||||||
<Label className="font-weight-bold">{gettext('Password again')}</Label>
|
|
||||||
<Input className="passwd" type={this.state.isPasswordVisible ? 'text' : 'password'} value={this.state.passwdnew || ''} onChange={this.inputPasswordNew} />
|
|
||||||
</FormGroup>
|
|
||||||
}
|
|
||||||
{this.isExpireDaysNoLimit && (
|
|
||||||
<Fragment>
|
|
||||||
<FormGroup check>
|
|
||||||
<Label check>
|
|
||||||
<Input className="expire-checkbox" type="checkbox" onChange={this.onExpireChecked} />{' '}{gettext('Add auto expiration')}
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
{this.state.isExpireChecked &&
|
|
||||||
<FormGroup check>
|
|
||||||
<Label check>
|
|
||||||
<Input className="expire-input expire-input-border" type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} readOnly={!this.state.isExpireChecked} /><span className="expir-span">{gettext('days')}</span>
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
}
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
{!this.isExpireDaysNoLimit && (
|
|
||||||
<Fragment>
|
|
||||||
<FormGroup check>
|
|
||||||
<Label check>
|
|
||||||
<Input className="expire-checkbox" type="checkbox" onChange={this.onExpireChecked} checked readOnly disabled/>{' '}{gettext('Add auto expiration')}
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup check>
|
|
||||||
<Label check>
|
|
||||||
<Input className="expire-input expire-input-border" type="text" value={this.state.expireDays} onChange={this.onExpireDaysChanged} /><span className="expir-span">{gettext('days')}</span>
|
|
||||||
{(parseInt(shareLinkExpireDaysMin) !== 0 && parseInt(shareLinkExpireDaysMax) !== 0) && (
|
|
||||||
<span className="d-inline-block ml-7">({shareLinkExpireDaysMin} - {shareLinkExpireDaysMax}{' '}{gettext('days')})</span>
|
|
||||||
)}
|
|
||||||
{(parseInt(shareLinkExpireDaysMin) !== 0 && parseInt(shareLinkExpireDaysMax) === 0) && (
|
|
||||||
<span className="d-inline-block ml-7">({gettext('Greater than or equal to')} {shareLinkExpireDaysMin}{' '}{gettext('days')})</span>
|
|
||||||
)}
|
|
||||||
{(parseInt(shareLinkExpireDaysMin) === 0 && parseInt(shareLinkExpireDaysMax) !== 0) && (
|
|
||||||
<span className="d-inline-block ml-7">({gettext('Less than or equal to')} {shareLinkExpireDaysMax}{' '}{gettext('days')})</span>
|
|
||||||
)}
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Fragment>
|
|
||||||
<FormGroup check>
|
|
||||||
<Label check>
|
|
||||||
<span>{gettext('Set permission')}</span>
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
{this.permissionOptions.map((item, index) => {
|
|
||||||
return (
|
|
||||||
<FormGroup check className="permission" key={index}>
|
|
||||||
<Label className="form-check-label">
|
|
||||||
<Input type="radio" name="permission" value={item} checked={this.state.currentPermission == item} onChange={this.setPermission} className="mr-1" />
|
|
||||||
{Utils.getDTableShareLinkPermissionObject(item).text}
|
|
||||||
</Label>
|
|
||||||
</FormGroup>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Fragment>
|
|
||||||
|
|
||||||
{this.state.errorInfo && <Alert color="danger" className="mt-2">{gettext(this.state.errorInfo)}</Alert>}
|
|
||||||
<Button onClick={this.generateDTableShareLink} className="mt-2">{gettext('Generate')}</Button>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GenerateDTableShareLink.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default GenerateDTableShareLink;
|
|
@@ -1,93 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {gettext} from '../../utils/constants';
|
|
||||||
import {Modal, ModalHeader, ModalBody, Nav, NavItem, NavLink, TabContent, TabPane} from 'reactstrap';
|
|
||||||
import ShareTableToUser from './share-table-to-user';
|
|
||||||
|
|
||||||
import '../../css/share-link-dialog.css';
|
|
||||||
import GenerateDTableShareLink from './generate-dtable-share-link';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
currentTable: PropTypes.object.isRequired,
|
|
||||||
ShareCancel: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShareTableDialog extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
activeTab: 'shareToUser',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle = (tab) => {
|
|
||||||
if (this.state.activeTab !== tab) {
|
|
||||||
this.setState({activeTab: tab});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderContent = () => {
|
|
||||||
let activeTab = this.state.activeTab;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<div className="share-dialog-side">
|
|
||||||
<Nav pills vertical>
|
|
||||||
<Fragment>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink
|
|
||||||
className={activeTab === 'shareToUser' ? 'active' : ''}
|
|
||||||
onClick={this.toggle.bind(this, 'shareToUser')}
|
|
||||||
>{gettext('Share to user')}
|
|
||||||
</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
<NavItem>
|
|
||||||
<NavLink
|
|
||||||
className={activeTab === 'shareLink' ? 'active' : ''}
|
|
||||||
onClick={this.toggle.bind(this, 'shareLink')}
|
|
||||||
>{gettext('Share link')}
|
|
||||||
</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
</Fragment>
|
|
||||||
</Nav>
|
|
||||||
</div>
|
|
||||||
<div className="share-dialog-main">
|
|
||||||
<TabContent activeTab={this.state.activeTab}>
|
|
||||||
<Fragment>
|
|
||||||
<TabPane tabId="shareToUser">
|
|
||||||
<ShareTableToUser
|
|
||||||
currentTable={this.props.currentTable}
|
|
||||||
/>
|
|
||||||
</TabPane>
|
|
||||||
{activeTab === 'shareLink' &&
|
|
||||||
<GenerateDTableShareLink
|
|
||||||
workspaceID={this.props.currentTable.workspace_id}
|
|
||||||
name={this.props.currentTable.name}
|
|
||||||
closeShareDialog={this.props.ShareCancel}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</Fragment>
|
|
||||||
</TabContent>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let currentTable = this.props.currentTable;
|
|
||||||
let name = currentTable.name;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen={true} toggle={this.props.ShareCancel} style={{maxWidth: '720px'}} className="share-dialog" >
|
|
||||||
<ModalHeader toggle={this.props.ShareCancel}>{gettext('Share')} <span className="op-target" title={name}>{name}</span></ModalHeader>
|
|
||||||
<ModalBody className="share-dialog-content">
|
|
||||||
{this.renderContent()}
|
|
||||||
</ModalBody>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShareTableDialog.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default ShareTableDialog;
|
|
@@ -1,237 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { gettext, canInvitePeople, siteRoot } from '../../utils/constants';
|
|
||||||
import { Button } from 'reactstrap';
|
|
||||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
|
||||||
import UserSelect from '../user-select';
|
|
||||||
import DtableSharePermissionEditor from '../select-editor/dtable-share-permission-editor';
|
|
||||||
import toaster from '../toast';
|
|
||||||
|
|
||||||
import '../../css/invitations.css';
|
|
||||||
|
|
||||||
const userItemPropTypes = {
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
deleteTableShare: PropTypes.func.isRequired,
|
|
||||||
updateTableShare: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserItem extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isOperationShow: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseEnter = () => {
|
|
||||||
this.setState({isOperationShow: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
onMouseLeave = () => {
|
|
||||||
this.setState({isOperationShow: false});
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteTableShare = () => {
|
|
||||||
this.props.deleteTableShare(this.props.item.email);
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTableShare = (permission) => {
|
|
||||||
this.props.updateTableShare(this.props.item.email, permission);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let item = this.props.item;
|
|
||||||
let currentPermission = item.permission;
|
|
||||||
return (
|
|
||||||
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
|
||||||
<td className="name">{item.name}</td>
|
|
||||||
<td>
|
|
||||||
<DtableSharePermissionEditor
|
|
||||||
isTextMode={true}
|
|
||||||
isEditIconShow={this.state.isOperationShow}
|
|
||||||
currentPermission={currentPermission}
|
|
||||||
onPermissionChanged={this.updateTableShare}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
className={`sf2-icon-x3 action-icon ${this.state.isOperationShow ? '' : 'hide'}`}
|
|
||||||
onClick={this.deleteTableShare}
|
|
||||||
title={gettext('Delete')}
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UserItem.propTypes = userItemPropTypes;
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
currentTable: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShareTableToUser extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
selectedOptions: null,
|
|
||||||
permission: 'rw',
|
|
||||||
userList: []
|
|
||||||
};
|
|
||||||
this.workspaceID = this.props.currentTable.workspace_id;
|
|
||||||
this.tableName = this.props.currentTable.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
seafileAPI.listTableShares(this.workspaceID, this.tableName).then((res) => {
|
|
||||||
this.setState({userList: res.data.user_list});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSelectChange = (options) => {
|
|
||||||
this.setState({ selectedOptions: options });
|
|
||||||
};
|
|
||||||
|
|
||||||
setPermission = (permission) => {
|
|
||||||
this.setState({permission: permission});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleError = (e) => {
|
|
||||||
if (e.response) {
|
|
||||||
toaster.danger(e.response.data.error_msg || e.response.data.detail || gettext('Error'), {duration: 3});
|
|
||||||
} else {
|
|
||||||
toaster.danger(gettext('Please check the network.'), {duration: 3});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addTableShare = () => {
|
|
||||||
const { selectedOptions, permission, userList } = this.state;
|
|
||||||
if (!selectedOptions || selectedOptions.length === 0) return;
|
|
||||||
for (let i = 0; i < selectedOptions.length; i++) {
|
|
||||||
let name = selectedOptions[i].value;
|
|
||||||
let email = selectedOptions[i].email;
|
|
||||||
seafileAPI.addTableShare(this.workspaceID, this.tableName, email, permission).then((res) => {
|
|
||||||
let userInfo = {
|
|
||||||
name: name,
|
|
||||||
email: email,
|
|
||||||
permission: permission,
|
|
||||||
};
|
|
||||||
userList.push(userInfo);
|
|
||||||
this.setState({ userList: userList });
|
|
||||||
}).catch(error => {
|
|
||||||
this.handleError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.setState({ selectedOption: null });
|
|
||||||
this.refs.userSelect.clearSelect();
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteTableShare = (email) => {
|
|
||||||
seafileAPI.deleteTableShare(this.workspaceID, this.tableName, email).then((res) => {
|
|
||||||
let userList = this.state.userList.filter(userInfo => {
|
|
||||||
return userInfo.email !== email;
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
userList: userList,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
this.handleError(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTableShare = (email, permission) => {
|
|
||||||
seafileAPI.updateTableShare(this.workspaceID, this.tableName, email, permission).then((res) => {
|
|
||||||
let userList = this.state.userList.filter(userInfo => {
|
|
||||||
if (userInfo.email === email) {
|
|
||||||
userInfo.permission = permission;
|
|
||||||
}
|
|
||||||
return userInfo;
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
userList: userList,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
this.handleError(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const renderUserList = this.state.userList.map((item, index) => {
|
|
||||||
return (
|
|
||||||
<UserItem
|
|
||||||
key={index}
|
|
||||||
item={item}
|
|
||||||
permissions={['rw', 'r']}
|
|
||||||
deleteTableShare={this.deleteTableShare}
|
|
||||||
updateTableShare={this.updateTableShare}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="50%">{gettext('User')}</th>
|
|
||||||
<th width="35%">{gettext('Permission')}</th>
|
|
||||||
<th width="15%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<UserSelect
|
|
||||||
ref="userSelect"
|
|
||||||
isMulti={true}
|
|
||||||
className="reviewer-select"
|
|
||||||
placeholder={gettext('Select users...')}
|
|
||||||
onSelectChange={this.handleSelectChange}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<DtableSharePermissionEditor
|
|
||||||
isTextMode={false}
|
|
||||||
isEditIconShow={false}
|
|
||||||
currentPermission={this.state.permission}
|
|
||||||
onPermissionChanged={this.setPermission}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<Button onClick={this.addTableShare}>{gettext('Submit')}</Button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div className="share-list-container">
|
|
||||||
<table className="table-thead-hidden">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="50%">{gettext('User')}</th>
|
|
||||||
<th width="35%">{gettext('Permission')}</th>
|
|
||||||
<th width="15%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{renderUserList}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{ canInvitePeople &&
|
|
||||||
<a href={siteRoot + 'invitations/'} className="invite-link-in-popup">
|
|
||||||
<i className="sf2-icon-invite invite-link-icon-in-popup"></i>
|
|
||||||
<span className="invite-link-icon-in-popup">{gettext('Invite People')}</span>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShareTableToUser.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default ShareTableToUser;
|
|
@@ -1,310 +0,0 @@
|
|||||||
import React, {Fragment} from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {gettext} from '../../utils/constants';
|
|
||||||
import {Modal, ModalHeader, ModalBody, Button, Input} from 'reactstrap';
|
|
||||||
import DtableSharePermissionEditor from '../select-editor/dtable-share-permission-editor';
|
|
||||||
import {seafileAPI} from '../../utils/seafile-api';
|
|
||||||
import toaster from '../toast';
|
|
||||||
import copy from 'copy-to-clipboard';
|
|
||||||
import Loading from '../loading';
|
|
||||||
|
|
||||||
import '../../css/share-link-dialog.css';
|
|
||||||
|
|
||||||
|
|
||||||
const apiTokenItemPropTypes = {
|
|
||||||
item: PropTypes.object.isRequired,
|
|
||||||
deleteAPIToken: PropTypes.func.isRequired,
|
|
||||||
updateAPIToken: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class APITokenItem extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isOperationShow: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onMouseEnter = () => {
|
|
||||||
this.setState({isOperationShow: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
onMouseLeave = () => {
|
|
||||||
this.setState({isOperationShow: false});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDeleteAPIToken = () => {
|
|
||||||
this.props.deleteAPIToken(this.props.item.app_name);
|
|
||||||
};
|
|
||||||
|
|
||||||
onUpdateAPIToken = (permission) => {
|
|
||||||
this.props.updateAPIToken(this.props.item.app_name, permission);
|
|
||||||
};
|
|
||||||
|
|
||||||
onCopyAPIToken = () => {
|
|
||||||
let api_token = this.props.item.api_token;
|
|
||||||
copy(api_token);
|
|
||||||
toaster.success(gettext('API Token is copied to the clipboard.'));
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let item = this.props.item;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
|
||||||
<td className="name">{item.app_name}</td>
|
|
||||||
<td>
|
|
||||||
<DtableSharePermissionEditor
|
|
||||||
isTextMode={true}
|
|
||||||
isEditIconShow={this.state.isOperationShow}
|
|
||||||
currentPermission={item.permission}
|
|
||||||
onPermissionChanged={this.onUpdateAPIToken}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>{item.api_token}</td>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
className="far fa-copy action-icon"
|
|
||||||
onClick={this.onCopyAPIToken}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
className={`sf2-icon-x3 action-icon ${this.state.isOperationShow ? '' : 'hide'}`}
|
|
||||||
onClick={this.onDeleteAPIToken}
|
|
||||||
title={gettext('Delete')}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
APITokenItem.propTypes = apiTokenItemPropTypes;
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
currentTable: PropTypes.object.isRequired,
|
|
||||||
onTableAPITokenToggle: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class TableAPITokenDialog extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
apiTokenList: [],
|
|
||||||
permission: 'rw',
|
|
||||||
appName: '',
|
|
||||||
errorMsg: '',
|
|
||||||
loading: true,
|
|
||||||
isSubmitBtnActive: true,
|
|
||||||
};
|
|
||||||
this.workspaceID = this.props.currentTable.workspace_id;
|
|
||||||
this.tableName = this.props.currentTable.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
listAPITokens = () => {
|
|
||||||
seafileAPI.listTableAPITokens(this.workspaceID, this.tableName).then((res) => {
|
|
||||||
this.setState({
|
|
||||||
apiTokenList: res.data.api_tokens,
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
if (error.response.status === 403) {
|
|
||||||
this.setState({
|
|
||||||
errorMsg: gettext('Permission denied'),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.handleError(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onInputChange = (e) => {
|
|
||||||
let appName = e.target.value;
|
|
||||||
this.setState({
|
|
||||||
appName: appName,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onKeyDown = (e) => {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.addAPIToken();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setPermission = (permission) => {
|
|
||||||
this.setState({permission: permission});
|
|
||||||
};
|
|
||||||
|
|
||||||
addAPIToken = () => {
|
|
||||||
if (!this.state.appName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
isSubmitBtnActive: false,
|
|
||||||
});
|
|
||||||
const {appName, permission, apiTokenList} = this.state;
|
|
||||||
|
|
||||||
seafileAPI.addTableAPIToken(this.workspaceID, this.tableName, appName, permission).then((res) => {
|
|
||||||
apiTokenList.push(res.data);
|
|
||||||
this.setState({
|
|
||||||
apiTokenList: apiTokenList,
|
|
||||||
isSubmitBtnActive: true,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
this.handleError(error);
|
|
||||||
this.setState({
|
|
||||||
isSubmitBtnActive: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteAPIToken = (appName) => {
|
|
||||||
seafileAPI.deleteTableAPIToken(this.workspaceID, this.tableName, appName).then((res) => {
|
|
||||||
const apiTokenList = this.state.apiTokenList.filter(item => {
|
|
||||||
return item.app_name !== appName;
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
apiTokenList: apiTokenList,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
this.handleError(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateAPIToken = (appName, permission) => {
|
|
||||||
seafileAPI.updateTableAPIToken(this.workspaceID, this.tableName, appName, permission).then((res) => {
|
|
||||||
let userList = this.state.apiTokenList.filter(item => {
|
|
||||||
if (item.app_name === appName) {
|
|
||||||
item.permission = permission;
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
userList: userList,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
this.handleError(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleError = (e) => {
|
|
||||||
if (e.response) {
|
|
||||||
toaster.danger(e.response.data.error_msg || e.response.data.detail || gettext('Error'), {duration: 3});
|
|
||||||
} else {
|
|
||||||
toaster.danger(gettext('Please check the network.'), {duration: 3});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.listAPITokens();
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContent = () => {
|
|
||||||
const renderAPITokenList = this.state.apiTokenList.map((item, index) => {
|
|
||||||
return (
|
|
||||||
<APITokenItem
|
|
||||||
key={index}
|
|
||||||
item={item}
|
|
||||||
deleteAPIToken={this.deleteAPIToken}
|
|
||||||
updateAPIToken={this.updateAPIToken}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
{this.state.errorMsg &&
|
|
||||||
<div className='w-100'>
|
|
||||||
<p className="error text-center">{this.state.errorMsg}</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{!this.state.errorMsg &&
|
|
||||||
<div className='mx-5 mb-5' style={{height: 'auto'}}>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="45%">{gettext('App Name')}</th>
|
|
||||||
<th width="40%">{gettext('Permission')}</th>
|
|
||||||
<th width="15%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
id="appName"
|
|
||||||
value={this.state.appName}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
onKeyDown={this.onKeyDown}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<DtableSharePermissionEditor
|
|
||||||
isTextMode={false}
|
|
||||||
isEditIconShow={false}
|
|
||||||
currentPermission={this.state.permission}
|
|
||||||
onPermissionChanged={this.setPermission}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<Button onClick={this.addAPIToken} disabled={!this.state.isSubmitBtnActive}>{gettext('Submit')}</Button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{this.state.apiTokenList.length !== 0 &&
|
|
||||||
<div className='o-auto' style={{height: 'calc(100% - 91px)'}}>
|
|
||||||
<div className="h-100" style={{maxHeight: '18rem'}}>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th width="22%">{gettext('App Name')}</th>
|
|
||||||
<th width="15%">{gettext('Permission')}</th>
|
|
||||||
<th width="53%">{gettext('Access Token')}</th>
|
|
||||||
<th width="5%"></th>
|
|
||||||
<th width="5%"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{renderAPITokenList}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{this.state.loading &&
|
|
||||||
<Loading/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let currentTable = this.props.currentTable;
|
|
||||||
let name = currentTable.name;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={true} className="share-dialog" style={{maxWidth: '720px'}}
|
|
||||||
toggle={this.props.onTableAPITokenToggle}
|
|
||||||
>
|
|
||||||
<ModalHeader toggle={this.props.onTableAPITokenToggle}>
|
|
||||||
{gettext('API Token')} <span className="op-target" title={name}>{name}</span></ModalHeader>
|
|
||||||
<ModalBody className="share-dialog-content">
|
|
||||||
{this.renderContent()}
|
|
||||||
</ModalBody>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TableAPITokenDialog.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default TableAPITokenDialog;
|
|
@@ -1,81 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Select from 'react-select';
|
|
||||||
import { gettext } from '../../utils/constants';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
isTextMode: PropTypes.bool.isRequired,
|
|
||||||
isEditIconShow: PropTypes.bool.isRequired,
|
|
||||||
currentPermission: PropTypes.string.isRequired,
|
|
||||||
onPermissionChanged: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class DtableSharePermissionEditor extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isEditing: false,
|
|
||||||
};
|
|
||||||
this.options = [
|
|
||||||
{ value: 'rw', label: <div>{gettext('Read-Write')}</div> },
|
|
||||||
{ value: 'r', label: <div>{gettext('Read-Only')}</div> }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.addEventListener('click', this.onHideSelect);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('click', this.onHideSelect);
|
|
||||||
}
|
|
||||||
|
|
||||||
onHideSelect = () => {
|
|
||||||
this.setState({ isEditing: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditPermission = (e) => {
|
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
|
||||||
this.setState({ isEditing: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
onPermissionChanged = (e) => {
|
|
||||||
if (e.value !== this.props.currentPermission) {
|
|
||||||
this.props.onPermissionChanged(e.value);
|
|
||||||
}
|
|
||||||
this.setState({ isEditing: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelectHandler = (e) => {
|
|
||||||
e.nativeEvent.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { currentPermission, isTextMode } = this.props;
|
|
||||||
let optionTranslation = currentPermission === 'r' ? gettext('Read-Only') : gettext('Read-Write');
|
|
||||||
return (
|
|
||||||
<div onClick={this.onSelectHandler}>
|
|
||||||
{(isTextMode && !this.state.isEditing) ?
|
|
||||||
<Fragment>
|
|
||||||
<span>{optionTranslation}</span>
|
|
||||||
{this.props.isEditIconShow &&
|
|
||||||
<span title={gettext('Edit')} className="fa fa-pencil-alt attr-action-icon" onClick={this.onEditPermission}></span>
|
|
||||||
}
|
|
||||||
</Fragment>
|
|
||||||
:
|
|
||||||
<Select
|
|
||||||
options={this.options}
|
|
||||||
placeholder={optionTranslation}
|
|
||||||
onChange={this.onPermissionChanged}
|
|
||||||
captureMenuScroll={false}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DtableSharePermissionEditor.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default DtableSharePermissionEditor;
|
|
@@ -1,42 +0,0 @@
|
|||||||
import i18n from 'i18next';
|
|
||||||
import Backend from 'i18next-xhr-backend';
|
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
||||||
import { initReactI18next } from 'react-i18next';
|
|
||||||
|
|
||||||
let mediaUrl = window.app.pageOptions.mediaUrl;
|
|
||||||
const lang = window.app.pageOptions.lang;
|
|
||||||
|
|
||||||
i18n
|
|
||||||
.use(Backend)
|
|
||||||
.use(LanguageDetector)
|
|
||||||
.use(initReactI18next)
|
|
||||||
.init({
|
|
||||||
lng: lang,
|
|
||||||
fallbackLng: 'en',
|
|
||||||
ns: ['dtable', 'translations'],
|
|
||||||
defaultNS: 'dtable',
|
|
||||||
|
|
||||||
whitelist: ['en', 'zh-CN', 'fr', 'de', 'cs', 'es', 'es-AR', 'es-MX', 'ru'],
|
|
||||||
|
|
||||||
backend: {
|
|
||||||
loadPath: mediaUrl + 'assets/frontend/locales/{{ lng }}/{{ ns }}.json',
|
|
||||||
// loadPath: '/media/locales/{{lng}}/{{ns}}.json',
|
|
||||||
// loadPath: function(lng, ns) {
|
|
||||||
},
|
|
||||||
|
|
||||||
debug: true, // console log if debug: true
|
|
||||||
|
|
||||||
interpolation: {
|
|
||||||
escapeValue: false, // not needed for react!!
|
|
||||||
},
|
|
||||||
contextSeparator: " ",
|
|
||||||
|
|
||||||
|
|
||||||
load: 'currentOnly',
|
|
||||||
|
|
||||||
react: {
|
|
||||||
wait: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default i18n;
|
|
@@ -1,16 +0,0 @@
|
|||||||
class DTableShareLink {
|
|
||||||
|
|
||||||
constructor(object) {
|
|
||||||
this.workspaceID = object.workspace_id;
|
|
||||||
this.permissions = object.permission;
|
|
||||||
this.username = object.username;
|
|
||||||
this.is_expired = object.is_expired;
|
|
||||||
this.expire_date = object.expire_date;
|
|
||||||
this.token = object.token;
|
|
||||||
this.link = object.link;
|
|
||||||
this.ctime = object.ctime;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DTableShareLink;
|
|
@@ -627,23 +627,6 @@ export const Utils = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
getDTableShareLinkPermissionObject: function(permission) {
|
|
||||||
switch (permission) {
|
|
||||||
case 'read-only':
|
|
||||||
return {
|
|
||||||
value: permission,
|
|
||||||
text: 'read-only',
|
|
||||||
permission: 'r'
|
|
||||||
};
|
|
||||||
case 'read-write':
|
|
||||||
return {
|
|
||||||
value: permission,
|
|
||||||
text: 'read-write',
|
|
||||||
permission: 'rw'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
formatSize: function(options) {
|
formatSize: function(options) {
|
||||||
/*
|
/*
|
||||||
* param: {bytes, precision}
|
* param: {bytes, precision}
|
||||||
|
Reference in New Issue
Block a user