From e58e021e88fbd0775e46e1275129808f0aef9bd3 Mon Sep 17 00:00:00 2001 From: llj Date: Thu, 27 Jun 2019 14:08:10 +0800 Subject: [PATCH] [share dialog] added 'send link' for 'upload link' * added a component send-link.js, and used it in both 'share link' & 'upload link' panel * added 'send link' for 'upload link', fixed UI, interaction, translation, and other problems for 'send link' in 'share link' * added permission check for 'send' --- .../components/dialog/generate-share-link.js | 85 ++---------- .../components/dialog/generate-upload-link.js | 23 +++- frontend/src/components/send-link.js | 128 ++++++++++++++++++ frontend/src/utils/constants.js | 3 +- .../api2/endpoints/send_upload_link_email.py | 4 +- seahub/templates/base_for_react.html | 1 + seahub/templates/file_view_react.html | 1 + seahub/templates/view_file_markdown.html | 1 + 8 files changed, 169 insertions(+), 77 deletions(-) create mode 100644 frontend/src/components/send-link.js diff --git a/frontend/src/components/dialog/generate-share-link.js b/frontend/src/components/dialog/generate-share-link.js index 1541eeef19..1a52428dec 100644 --- a/frontend/src/components/dialog/generate-share-link.js +++ b/frontend/src/components/dialog/generate-share-link.js @@ -3,12 +3,13 @@ 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 } from '../../utils/constants'; +import { gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import SharedLinkInfo from '../../models/shared-link-info'; import toaster from '../toast'; import Loading from '../loading'; +import SendLink from '../send-link'; const propTypes = { itemPath: PropTypes.string.isRequired, @@ -32,11 +33,8 @@ class GenerateShareLink extends React.Component { sharedLinkInfo: null, isNoticeMessageShow: false, isLoading: true, - isShowSendLink: false, - sendLinkEmails: '', - sendLinkMessage: '', - sendLinkErrorMessage: '', fileInfo: null, + isSendLinkShown: false }; this.permissions = { 'can_edit': false, @@ -254,45 +252,8 @@ class GenerateShareLink extends React.Component { this.setState({isNoticeMessageShow: !this.state.isNoticeMessageShow}); } - onSendLinkEmailsChange = (event) => { - if (this.state.sendLinkErrorMessage) this.setState({ sendLinkErrorMessage: '' }); - this.setState({sendLinkEmails: event.target.value}); - } - - onSendLinkMessageChange = (event) => { - if (this.state.sendLinkErrorMessage) this.setState({ sendLinkErrorMessage: '' }); - this.setState({sendLinkMessage: event.target.value}); - } - toggleSendLink = () => { - this.setState({ isShowSendLink: !this.state.isShowSendLink }); - } - - sendShareLink = () => { - if (!this.state.sendLinkEmails) return; - const token = this.state.sharedLinkInfo.token; - const emails = this.state.sendLinkEmails.replace(/\s*/g,''); - const message = this.state.sendLinkMessage.trim(); - this.setState({ isLoading: true }); - seafileAPI.sendShareLink(token, emails, message).then((res) => { - this.props.closeShareDialog(); - if (res.data.failed.length > 0) { - res.data.failed.map(failed => { - toaster.warning(gettext('Failed sent link to') + ' ' + failed.email + ', ' + failed.error_msg); - }); - } - if (res.data.success.length > 0) { - let users = res.data.success.join(','); - toaster.success(gettext('Successfully sent link to') + ' ' + users); - } - }).catch((error) => { - if (error.response) { - this.setState({ - sendLinkErrorMessage: error.response.data.error_msg, - isLoading: false, - }); - } - }); + this.setState({ isSendLinkShown: !this.state.isSendLinkShown }); } render() { @@ -338,38 +299,18 @@ class GenerateShareLink extends React.Component { )} - {(!this.state.isShowSendLink && !this.state.isNoticeMessageShow) && + {(canSendShareLinkEmail && !this.state.isSendLinkShown && !this.state.isNoticeMessageShow) && } - {this.state.isShowSendLink && - -
- - - - - -
- -
-
- {this.state.sendLinkErrorMessage &&

{this.state.sendLinkErrorMessage}

} - {' '} - {' '} -
+ {this.state.isSendLinkShown && + } - {(!this.state.isShowSendLink && !this.state.isNoticeMessageShow) && + {(!this.state.isSendLinkShown && !this.state.isNoticeMessageShow) && } {this.state.isNoticeMessageShow && diff --git a/frontend/src/components/dialog/generate-upload-link.js b/frontend/src/components/dialog/generate-upload-link.js index 56e3256e86..05a24e9294 100644 --- a/frontend/src/components/dialog/generate-upload-link.js +++ b/frontend/src/components/dialog/generate-upload-link.js @@ -2,11 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import copy from 'copy-to-clipboard'; import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap'; -import { gettext, shareLinkPasswordMinLength } from '../../utils/constants'; +import { gettext, shareLinkPasswordMinLength, canSendShareLinkEmail } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import SharedUploadInfo from '../../models/shared-upload-info'; import toaster from '../toast'; +import SendLink from '../send-link'; import SessionExpiredTip from '../session-expired-tip'; const propTypes = { @@ -24,6 +25,7 @@ class GenerateUploadLink extends React.Component { password: '', passwdnew: '', sharedUploadInfo: null, + isSendLinkShown: false }; } @@ -130,8 +132,16 @@ class GenerateUploadLink extends React.Component { }); } + toggleSendLink = () => { + this.setState({ + isSendLinkShown: !this.state.isSendLinkShown + }); + } + render() { + const { isSendLinkShown } = this.state; + let passwordLengthTip = gettext('(at least {passwordLength} characters)'); passwordLengthTip = passwordLengthTip.replace('{passwordLength}', shareLinkPasswordMinLength); if (this.state.sharedUploadInfo) { @@ -147,7 +157,16 @@ class GenerateUploadLink extends React.Component { - + {canSendShareLinkEmail && !isSendLinkShown && } + {!isSendLinkShown && } + {isSendLinkShown && + + } ); } diff --git a/frontend/src/components/send-link.js b/frontend/src/components/send-link.js new file mode 100644 index 0000000000..679928222d --- /dev/null +++ b/frontend/src/components/send-link.js @@ -0,0 +1,128 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button, Form, FormGroup, Label } from 'reactstrap'; +import { seafileAPI } from '../utils/seafile-api'; +import { gettext } from '../utils/constants'; +import toaster from './toast'; + +const propTypes = { + token: PropTypes.string.isRequired, + linkType: PropTypes.string.isRequired, + toggleSendLink: PropTypes.func.isRequired, + closeShareDialog: PropTypes.func.isRequired +}; + +class SendLink extends React.Component { + + constructor(props) { + super(props); + this.state = { + emails: '', + msg: '', + errorMsg: '', + btnDisabled: false, + sending: false + }; + } + + handleEmailsInputChange = (e) => { + this.setState({ + emails: e.target.value + }); + } + + handleMsgInputChange = (e) => { + this.setState({ + msg: e.target.value + }); + } + + sendLink = () => { + const { emails, msg } = this.state; + if (!emails.trim()) { + this.setState({ + errorMsg: gettext('Please input at least an email.') + }); + return; + } + + this.setState({ + btnDisabled: true, + sending: true + }); + + const { token, linkType } = this.props; + const request = linkType == 'uploadLink' ? + seafileAPI.sendUploadLink(token, emails.trim(), msg.trim()) : + seafileAPI.sendShareLink(token, emails.trim(), msg.trim()); + request.then((res) => { + this.props.closeShareDialog(); + const { success, failed } = res.data; + if (success.length) { + const feedbackMsg = gettext('Successfully sent to {placeholder}') + .replace('{placeholder}', success.join(', ')); + toaster.success(feedbackMsg); + } + if (failed.length) { + failed.map((item) => { + const feedbackMsg = gettext('Failed to send to {email_placeholder}: {errorMsg_placeholder}') + .replace('{email_placeholder}', item.email) + .replace('{errorMsg_placeholder}', item.error_msg); + toaster.warning(feedbackMsg); + }); + } + }).catch((error) => { + let errorMsg = ''; + if (error.response) { + if (error.response.data && error.response.data['error_msg']) { + errorMsg = error.response.data['error_msg']; + } else { + errorMsg = gettext('Error'); + } + } else { + errorMsg = gettext('Please check the network.'); + } + this.setState({ + btnDisabled: false, + sending: false, + errorMsg: errorMsg + }); + }); + } + + render() { + const { emails, msg, errorMsg, btnDisabled, sending } = this.state; + return ( +
+ + + + + + + + + {errorMsg &&

{errorMsg}

} + + + {sending &&

{gettext('Sending...')}

} +
+ ); + } +} + +SendLink.propTypes = propTypes; + +export default SendLink; diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index 0fde633b44..e3d4d6a6cc 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -25,7 +25,8 @@ export const username = window.app.pageOptions.username; export const canAddRepo = window.app.pageOptions.canAddRepo; export const canAddGroup = window.app.pageOptions.canAddGroup; export const canGenerateShareLink = window.app.pageOptions.canGenerateShareLink; -export const canGenerateUploadLink = window.app.pageOptions.canGenerateUploadLink ? true : false; +export const canGenerateUploadLink = window.app.pageOptions.canGenerateUploadLink; +export const canSendShareLinkEmail = window.app.pageOptions.canSendShareLinkEmail; export const canViewOrg = window.app.pageOptions.canViewOrg === 'True'; export const fileAuditEnabled = window.app.pageOptions.fileAuditEnabled; export const enableFileComment = window.app.pageOptions.enableFileComment ? true : false; diff --git a/seahub/api2/endpoints/send_upload_link_email.py b/seahub/api2/endpoints/send_upload_link_email.py index c6920511a4..3f49d5bc18 100644 --- a/seahub/api2/endpoints/send_upload_link_email.py +++ b/seahub/api2/endpoints/send_upload_link_email.py @@ -70,7 +70,7 @@ class SendUploadLinkView(APIView): if not is_valid_email(to_email): failed_info['email'] = to_email - failed_info['error_msg'] = 'email invalid.' + failed_info['error_msg'] = _(u'email invalid.') result['failed'].append(failed_info) continue @@ -102,7 +102,7 @@ class SendUploadLinkView(APIView): except Exception as e: logger.error(e) failed_info['email'] = to_email - failed_info['error_msg'] = 'Internal Server Error' + failed_info['error_msg'] = _(u'Internal Server Error') result['failed'].append(failed_info) return Response(result) diff --git a/seahub/templates/base_for_react.html b/seahub/templates/base_for_react.html index 9ed48b5742..866ce103e8 100644 --- a/seahub/templates/base_for_react.html +++ b/seahub/templates/base_for_react.html @@ -54,6 +54,7 @@ canAddGroup: {% if user.permissions.can_add_group %} true {% else %} false {% endif %}, canGenerateShareLink: {% if user.permissions.can_generate_share_link %} true {% else %} false {% endif %}, canGenerateUploadLink: {% if user.permissions.can_generate_upload_link %} true {% else %} false {% endif %}, + canSendShareLinkEmail: {% if user.permissions.can_send_share_link_mail %} true {% else %} false {% endif %}, canViewOrg:'{{ user.permissions.can_view_org }}', fileAuditEnabled: {% if file_audit_enabled %} true {% else %} false {% endif %}, enableFileComment: {% if enableFileComment %} true {% else %} false {% endif %}, diff --git a/seahub/templates/file_view_react.html b/seahub/templates/file_view_react.html index 2a44ec8281..bb15f0cb75 100644 --- a/seahub/templates/file_view_react.html +++ b/seahub/templates/file_view_react.html @@ -11,6 +11,7 @@ window.app.pageOptions = { userNickName: '{{request.user.username|email2nickname|escapejs}}', canGenerateShareLink: {% if user.permissions.can_generate_share_link %} true {% else %} false {% endif %}, + canSendShareLinkEmail: {% if user.permissions.can_send_share_link_mail %} true {% else %} false {% endif %}, shareLinkPasswordMinLength: {{ share_link_password_min_length }}, shareLinkExpireDaysDefault: {{ share_link_expire_days_default }}, shareLinkExpireDaysMin: {{ share_link_expire_days_min }}, diff --git a/seahub/templates/view_file_markdown.html b/seahub/templates/view_file_markdown.html index 088ae37c81..87801d9f17 100644 --- a/seahub/templates/view_file_markdown.html +++ b/seahub/templates/view_file_markdown.html @@ -46,6 +46,7 @@ shareLinkExpireDaysMin: '{{ share_link_expire_days_min }}', shareLinkExpireDaysMax: '{{ share_link_expire_days_max }}', canGenerateShareLink: {% if user.permissions.can_generate_share_link %} true {% else %} false {% endif %}, + canSendShareLinkEmail: {% if user.permissions.can_send_share_link_mail %} true {% else %} false {% endif %}, isLocked: {% if file_locked %}true{% else %}false{% endif %}, lockedByMe: {% if locked_by_me %}true{% else %}false{% endif %}, canLockUnlockFile: {% if can_lock_unlock_file %}true{% else %}false{% endif %},