1
0
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:
llj
2019-11-28 11:34:41 +08:00
committed by Daniel Pan
parent 10999de67a
commit 4ce33e4949
10 changed files with 0 additions and 1374 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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}