1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-03 16:10:26 +00:00

add-share-link-authentication (#6201)

* add-share-link-authentication

* [share link - link creation] 'set scope': redesigned it

* update

* Update share_link_auth.py

* Update share_link_auth.py

* Update share_link_auth.py

* Update share_link_auth.py

* Update share_link_auth.py

* Update share_link_auth.py

* [share link - link details] redesigned the panel

* [share dialog - share link] 'authenticated users/emails' panels: redesigned them

* [share dialog - share link'] UI details, UX, and code improvements for 'link detais, authenticated users/emails' panels

* [share dialog - share link] updated the 'submit' handler for the 'authenticated emails/users' panels; fixup for 'set scope' in the 'link creation' panel'

* [share dialog - share link] deleted 'share-link-api.js', moved api modification to seafile-js; fixed 'change scope'  & etc. in 'link details'

* [share dialog - share link] link authenticated users: update 'submit' handler according to the python API update

* [share dialog - share link] added 'share-link-api.js' back & used it

* [share dialog - share link] handled eslint warnings and etc.

---------

Co-authored-by: llj <lingjun.li1@gmail.com>
This commit is contained in:
Ranjiwei
2024-07-19 18:12:35 +08:00
committed by GitHub
parent 69eb6263cf
commit add4689b02
25 changed files with 1621 additions and 291 deletions

View File

@@ -2,12 +2,14 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Button, Form, FormGroup, Label, Input, InputGroup, InputGroupAddon, Alert } from 'reactstrap';
import { gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel } from '../../utils/constants';
import { gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, shareLinkForceUsePassword, shareLinkPasswordMinLength, shareLinkPasswordStrengthLevel, isEmailConfigured } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { shareLinkAPI } from '../../utils/share-link-api';
import { Utils } from '../../utils/utils';
import ShareLink from '../../models/share-link';
import toaster from '../toast';
import SetLinkExpiration from '../set-link-expiration';
import UserSelect from '../user-select';
const propTypes = {
itemPath: PropTypes.string.isRequired,
@@ -42,7 +44,11 @@ class LinkCreation extends React.Component {
password: '',
passwdnew: '',
errorInfo: '',
currentPermission: props.currentPermission
currentPermission: props.currentPermission,
currentScope: 'all_users',
selectedOption: null,
inputEmails: ''
};
}
@@ -108,7 +114,7 @@ class LinkCreation extends React.Component {
let expirationTime = '';
if (isExpireChecked) {
if (expType == 'by-days') {
if (expType === 'by-days') {
expirationTime = moment().add(parseInt(expireDays), 'days').format();
} else {
expirationTime = expDate.format();
@@ -116,15 +122,23 @@ class LinkCreation extends React.Component {
}
let request;
if (type == 'batch') {
let users;
if (type === 'batch') {
const autoGeneratePassword = shareLinkForceUsePassword || isShowPasswordInput;
request = seafileAPI.batchCreateMultiShareLink(repoID, itemPath, linkAmount, autoGeneratePassword, expirationTime, permissions);
} else {
request = seafileAPI.createMultiShareLink(repoID, itemPath, password, expirationTime, permissions);
const { currentScope, selectedOption, inputEmails } = this.state;
if ( currentScope === 'specific_users' && selectedOption ) {
users = selectedOption.map((item, index) => item.email);
}
if (currentScope === 'specific_emails' && inputEmails) {
users = inputEmails;
}
request = shareLinkAPI.createMultiShareLink(repoID, itemPath, password, expirationTime, permissions, currentScope, users);
}
request.then((res) => {
if (type == 'batch') {
if (type === 'batch') {
const newLinks = res.data.map(item => new ShareLink(item));
this.props.updateAfterCreation(newLinks);
} else {
@@ -157,7 +171,7 @@ class LinkCreation extends React.Component {
const { type } = this.props;
let { linkAmount, isShowPasswordInput, password, passwdnew, isExpireChecked, expType, expireDays, expDate } = this.state;
if (type == 'batch') {
if (type === 'batch') {
if (!Number.isInteger(parseInt(linkAmount)) || parseInt(linkAmount) <= 1) {
this.setState({ errorInfo: gettext('Please enter an integer bigger than 1 as number of links.') });
return false;
@@ -168,7 +182,7 @@ class LinkCreation extends React.Component {
}
}
if (type == 'single' && isShowPasswordInput) {
if (type === 'single' && isShowPasswordInput) {
if (password.length === 0) {
this.setState({ errorInfo: gettext('Please enter a password.') });
return false;
@@ -188,7 +202,7 @@ class LinkCreation extends React.Component {
}
if (isExpireChecked) {
if (expType == 'by-date') {
if (expType === 'by-date') {
if (!expDate) {
this.setState({ errorInfo: gettext('Please select an expiration time') });
return false;
@@ -211,7 +225,7 @@ class LinkCreation extends React.Component {
let minDays = shareLinkExpireDaysMin;
let maxDays = shareLinkExpireDaysMax;
if (minDays !== 0 && maxDays == 0) {
if (minDays !== 0 && maxDays === 0) {
if (expireDays < minDays) {
this.setState({ errorInfo: 'Please enter valid days' });
return false;
@@ -248,6 +262,20 @@ class LinkCreation extends React.Component {
this.props.setMode('');
};
setScope = (e) => {
this.setState({ currentScope: e.target.value, selectedOption: null, inputEmails: '' });
};
handleSelectChange = (option) => {
this.setState({ selectedOption: option });
};
handleInputChange = (e) => {
this.setState({
inputEmails: e.target.value
});
};
render() {
const { userPerm, type, permissionOptions } = this.props;
const { isCustomPermission } = Utils.getUserPermission(userPerm);
@@ -257,11 +285,11 @@ class LinkCreation extends React.Component {
<div className="d-flex align-items-center pb-2 border-bottom">
<h6 className="font-weight-normal m-0">
<button className="sf3-font sf3-font-arrow rotate-180 d-inline-block back-icon border-0 bg-transparent text-secondary p-0 mr-2" onClick={this.goBack} title={gettext('Back')} aria-label={gettext('Back')}></button>
{type == 'batch' ? gettext('Generate links in batch') : gettext('Generate Link')}
{type === 'batch' ? gettext('Generate links in batch') : gettext('Generate Link')}
</h6>
</div>
<Form className="pt-4">
{type == 'batch' && (
{type === 'batch' && (
<FormGroup>
<Label for="link-number" className="p-0">{gettext('Number of links')}</Label>
<Input type="number" id="link-number" value={this.state.linkAmount} onChange={this.onLinkAmountChange} style={{ width: inputWidth }} />
@@ -279,7 +307,7 @@ class LinkCreation extends React.Component {
<span>{gettext('Add password protection')}</span>
</Label>
)}
{type != 'batch' && this.state.isShowPasswordInput &&
{type !== 'batch' && this.state.isShowPasswordInput &&
<div className="ml-4">
<FormGroup>
<Label for="passwd">{gettext('Password')}</Label>
@@ -345,6 +373,44 @@ class LinkCreation extends React.Component {
})}
</FormGroup>
)}
{type !== 'batch' && (
<FormGroup check>
<Label check>
<span>{gettext('Set scope')}</span>
</Label>
<FormGroup check className="ml-4">
<Label check>
<Input type="radio" name='scope' value={'all_users'} checked={this.state.currentScope === 'all_users'} onChange={this.setScope} className="mr-1" />
{gettext('Anyone with the link')}
</Label>
</FormGroup>
<FormGroup check className="ml-4">
<Label check>
<Input type="radio" name='scope' value={'specific_users'} checked={this.state.currentScope === 'specific_users'} onChange={this.setScope} className="mr-1" />
{gettext('Specific users in the team')}
</Label>
{this.state.currentScope === 'specific_users' &&
<UserSelect
ref="userSelect"
isMulti={true}
placeholder={gettext('Search users')}
onSelectChange={this.handleSelectChange}
/>
}
</FormGroup>
{isEmailConfigured && (
<FormGroup check className="ml-4">
<Label check>
<Input type="radio" name='scope' value={'specific_emails'} checked={this.state.currentScope === 'specific_emails'} onChange={this.setScope} className="mr-1" />
{gettext('Specific people with email address')}
</Label>
{this.state.currentScope === 'specific_emails' &&
<input type="text" className="form-control" value={this.state.inputEmails} onChange={this.handleInputChange} placeholder={gettext('Emails, separated by \',\'')}/>
}
</FormGroup>
)}
</FormGroup>
)}
{this.state.errorInfo && <Alert color="danger" className="mt-2">{gettext(this.state.errorInfo)}</Alert>}
<Button color="primary" onClick={this.generateShareLink} className="mt-2 ml-1 mb-1">{gettext('Generate')}</Button>
</Form>