mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-12 13:24:52 +00:00
Single selector (#5791)
* [single selector] added a new 'single selector' to replace the 'role editor' * [sys admin - group - member] replaced the old 'role selector' with the new 'single selector' * [sys admin - orgs] replaced the old 'role selector' with the new 'single selector' * [sys admin, org admin] replaced different selectors with the new 'single selector' for users & orgs * [share link perm selector] replaced it with the new 'single selector'
This commit is contained in:
@@ -4,7 +4,7 @@ import { Table } from 'reactstrap';
|
||||
import { Utils } from '../utils/utils';
|
||||
import { gettext } from '../utils/constants';
|
||||
import { seafileAPI } from '../utils/seafile-api';
|
||||
import RoleEditor from './select-editor/role-editor';
|
||||
import RoleSelector from './single-selector';
|
||||
import toaster from './toast';
|
||||
import OpIcon from './op-icon';
|
||||
|
||||
@@ -70,14 +70,17 @@ class Member extends React.PureComponent {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.roles = ['Admin', 'Member'];
|
||||
this.roleOptions = [
|
||||
{ value: 'Admin', text: gettext('Admin'), isSelected: false },
|
||||
{ value: 'Member', text: gettext('Member'), isSelected: false }
|
||||
];
|
||||
this.state = ({
|
||||
highlight: false,
|
||||
});
|
||||
}
|
||||
|
||||
onChangeUserRole = (role) => {
|
||||
let isAdmin = role === 'Admin' ? 'True' : 'False';
|
||||
onChangeUserRole = (roleOption) => {
|
||||
let isAdmin = roleOption.value === 'Admin' ? 'True' : 'False';
|
||||
seafileAPI.setGroupAdmin(this.props.groupID, this.props.item.email, isAdmin).then((res) => {
|
||||
this.props.changeMember(res.data);
|
||||
}).catch(error => {
|
||||
@@ -124,8 +127,17 @@ class Member extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { highlight } = this.state;
|
||||
const { item, isOwner } = this.props;
|
||||
const deleteAuthority = (item.role !== 'Owner' && isOwner === true) || (item.role === 'Member' && isOwner === false);
|
||||
|
||||
const { role: curRole } = item;
|
||||
this.roleOptions = this.roleOptions.map(item => {
|
||||
item.isSelected = item.value == curRole;
|
||||
return item;
|
||||
});
|
||||
const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return(
|
||||
<tr onMouseOver={this.handleMouseOver} onMouseLeave={this.handleMouseLeave} className={this.state.highlight ? 'tr-highlight' : ''} tabIndex="0" onFocus={this.handleMouseOver}>
|
||||
<th scope="row"><img className="avatar" src={item.avatar_url} alt=""/></th>
|
||||
@@ -135,12 +147,11 @@ class Member extends React.PureComponent {
|
||||
<span className="group-admin">{this.translateRole(item.role)}</span>
|
||||
}
|
||||
{(isOwner === true && item.role !== 'Owner') &&
|
||||
<RoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={this.state.highlight}
|
||||
currentRole={item.role}
|
||||
roles={this.roles}
|
||||
onRoleChanged={this.onChangeUserRole}
|
||||
<RoleSelector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedOption}
|
||||
options={this.roleOptions}
|
||||
selectOption={this.onChangeUserRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
}
|
||||
|
@@ -1,44 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import SelectEditor from './select-editor';
|
||||
|
||||
const propTypes = {
|
||||
isTextMode: PropTypes.bool.isRequired,
|
||||
isEditIconShow: PropTypes.bool.isRequired,
|
||||
roles: PropTypes.array.isRequired,
|
||||
currentRole: PropTypes.string.isRequired,
|
||||
onRoleChanged: PropTypes.func.isRequired,
|
||||
toggleItemFreezed: PropTypes.func,
|
||||
};
|
||||
|
||||
class RoleEditor extends React.Component {
|
||||
|
||||
translateRole = (role) => {
|
||||
if (role === 'Admin') {
|
||||
return gettext('Admin');
|
||||
}
|
||||
|
||||
if (role === 'Member') {
|
||||
return gettext('Member');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SelectEditor
|
||||
isTextMode={this.props.isTextMode}
|
||||
isEditIconShow={this.props.isEditIconShow}
|
||||
options={this.props.roles}
|
||||
currentOption={this.props.currentRole}
|
||||
onOptionChanged={this.props.onRoleChanged}
|
||||
translateOption={this.translateRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RoleEditor.propTypes = propTypes;
|
||||
|
||||
export default RoleEditor;
|
@@ -1,36 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import SelectEditor from './select-editor';
|
||||
|
||||
const propTypes = {
|
||||
isTextMode: PropTypes.bool.isRequired,
|
||||
isEditIconShow: PropTypes.bool.isRequired,
|
||||
permissionOptions: PropTypes.array.isRequired,
|
||||
currentPermission: PropTypes.string.isRequired,
|
||||
onPermissionChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class ShareLinkPermissionEditor extends React.Component {
|
||||
|
||||
translatePermission = (permission) => {
|
||||
return Utils.getShareLinkPermissionObject(permission).text;
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SelectEditor
|
||||
isTextMode={this.props.isTextMode}
|
||||
isEditIconShow={this.props.isEditIconShow}
|
||||
options={this.props.permissionOptions}
|
||||
currentOption={this.props.currentPermission}
|
||||
onOptionChanged={this.props.onPermissionChanged}
|
||||
translateOption={this.translatePermission}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ShareLinkPermissionEditor.propTypes = propTypes;
|
||||
|
||||
export default ShareLinkPermissionEditor;
|
@@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import SelectEditor from './select-editor';
|
||||
|
||||
const propTypes = {
|
||||
isTextMode: PropTypes.bool.isRequired,
|
||||
isEditIconShow: PropTypes.bool.isRequired,
|
||||
roleOptions: PropTypes.array.isRequired,
|
||||
currentRole: PropTypes.string.isRequired,
|
||||
onRoleChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class SysAdminGroupRoleEditor extends React.Component {
|
||||
|
||||
translateRoles = (role) => {
|
||||
switch (role) {
|
||||
case 'Member':
|
||||
return gettext('Member');
|
||||
case 'Admin':
|
||||
return gettext('Admin');
|
||||
default:
|
||||
return role;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SelectEditor
|
||||
isTextMode={this.props.isTextMode}
|
||||
isEditIconShow={this.props.isEditIconShow}
|
||||
options={this.props.roleOptions}
|
||||
currentOption={this.props.currentRole}
|
||||
onOptionChanged={this.props.onRoleChanged}
|
||||
translateOption={this.translateRoles}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SysAdminGroupRoleEditor.propTypes = propTypes;
|
||||
|
||||
export default SysAdminGroupRoleEditor;
|
@@ -1,41 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import SelectEditor from './select-editor';
|
||||
|
||||
const propTypes = {
|
||||
isTextMode: PropTypes.bool.isRequired,
|
||||
isEditIconShow: PropTypes.bool.isRequired,
|
||||
statusOptions: PropTypes.array.isRequired,
|
||||
currentStatus: PropTypes.string.isRequired,
|
||||
onStatusChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class SysAdminUserStatusEditor extends React.Component {
|
||||
|
||||
translateStatus = (status) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return gettext('Active');
|
||||
case 'inactive':
|
||||
return gettext('Inactive');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SelectEditor
|
||||
isTextMode={this.props.isTextMode}
|
||||
isEditIconShow={this.props.isEditIconShow}
|
||||
options={this.props.statusOptions}
|
||||
currentOption={this.props.currentStatus}
|
||||
onOptionChanged={this.props.onStatusChanged}
|
||||
translateOption={this.translateStatus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SysAdminUserStatusEditor.propTypes = propTypes;
|
||||
|
||||
export default SysAdminUserStatusEditor;
|
@@ -1,43 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import SelectEditor from './select-editor';
|
||||
|
||||
const propTypes = {
|
||||
isTextMode: PropTypes.bool.isRequired,
|
||||
isEditIconShow: PropTypes.bool.isRequired,
|
||||
statusArray: PropTypes.array.isRequired,
|
||||
currentStatus: PropTypes.string.isRequired,
|
||||
onStatusChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class UserStatusEditor extends React.Component {
|
||||
|
||||
translateStatus = (userStatus) => {
|
||||
if (userStatus === 'active') {
|
||||
return gettext('Active');
|
||||
}
|
||||
|
||||
if (userStatus === 'inactive') {
|
||||
return gettext('Inactive');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SelectEditor
|
||||
isTextMode={this.props.isTextMode}
|
||||
isEditIconShow={this.props.isEditIconShow}
|
||||
options={this.props.statusArray}
|
||||
currentOption={this.props.currentStatus}
|
||||
onOptionChanged={this.props.onStatusChanged}
|
||||
translateOption={this.translateStatus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UserStatusEditor.propTypes = propTypes;
|
||||
|
||||
export default UserStatusEditor;
|
@@ -4,7 +4,7 @@ import moment from 'moment';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { Button } from 'reactstrap';
|
||||
import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, canSendShareLinkEmail } from '../../utils/constants';
|
||||
import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor';
|
||||
import Selector from '../../components/single-selector';
|
||||
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
@@ -119,9 +119,9 @@ class LinkDetails extends React.Component {
|
||||
this.setState({isOpIconShown: false});
|
||||
};
|
||||
|
||||
changePerm = (permission) => {
|
||||
changePerm = (permOption) => {
|
||||
const { sharedLinkInfo } = this.props;
|
||||
const { permissionDetails } = Utils.getShareLinkPermissionObject(permission);
|
||||
const { permissionDetails } = Utils.getShareLinkPermissionObject(permOption.value);
|
||||
seafileAPI.updateShareLink(sharedLinkInfo.token, JSON.stringify(permissionDetails)).then((res) => {
|
||||
this.props.updateLink(new ShareLink(res.data));
|
||||
}).catch((error) => {
|
||||
@@ -152,6 +152,15 @@ class LinkDetails extends React.Component {
|
||||
const { sharedLinkInfo, permissionOptions } = this.props;
|
||||
const { isOpIconShown } = this.state;
|
||||
const currentPermission = Utils.getShareLinkPermissionStr(sharedLinkInfo.permissions);
|
||||
this.permOptions = permissionOptions.map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: Utils.getShareLinkPermissionObject(item).text,
|
||||
isSelected: item == currentPermission
|
||||
};
|
||||
});
|
||||
const currentSelectedPermOption = this.permOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button className="fa fa-arrow-left back-icon border-0 bg-transparent text-secondary p-0" onClick={this.goBack} title={gettext('Back')} aria-label={gettext('Back')}></button>
|
||||
@@ -231,12 +240,11 @@ class LinkDetails extends React.Component {
|
||||
<>
|
||||
<dt className="text-secondary font-weight-normal">{gettext('Permission:')}</dt>
|
||||
<dd style={{width:'250px'}} onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
|
||||
<ShareLinkPermissionEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown && !sharedLinkInfo.is_expired}
|
||||
currentPermission={currentPermission}
|
||||
permissionOptions={permissionOptions}
|
||||
onPermissionChanged={this.changePerm}
|
||||
<Selector
|
||||
isDropdownToggleShown={isOpIconShown && !sharedLinkInfo.is_expired}
|
||||
currentSelectedOption={currentSelectedPermOption}
|
||||
options={this.permOptions}
|
||||
selectOption={this.changePerm}
|
||||
/>
|
||||
</dd>
|
||||
</>
|
||||
|
@@ -4,7 +4,6 @@ import moment from 'moment';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import toaster from '../toast';
|
||||
import { isPro, gettext } from '../../utils/constants';
|
||||
import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
|
||||
|
||||
@@ -105,15 +104,7 @@ class LinkItem extends React.Component {
|
||||
{this.cutLink(link)}
|
||||
</td>
|
||||
<td>
|
||||
{(isPro && permissions) && (
|
||||
<ShareLinkPermissionEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={false}
|
||||
currentPermission={currentPermission}
|
||||
permissionOptions={permissionOptions}
|
||||
onPermissionChanged={() => {}}
|
||||
/>
|
||||
)}
|
||||
{(isPro && permissions) && Utils.getShareLinkPermissionObject(currentPermission).text}
|
||||
</td>
|
||||
<td>
|
||||
{expire_date ? moment(expire_date).format('YYYY-MM-DD HH:mm') : '--'}
|
||||
|
89
frontend/src/components/single-selector.js
Normal file
89
frontend/src/components/single-selector.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import '../css/single-selector.css';
|
||||
|
||||
const propTypes = {
|
||||
isDropdownToggleShown: PropTypes.bool.isRequired,
|
||||
currentSelectedOption: PropTypes.object.isRequired,
|
||||
options: PropTypes.array.isRequired,
|
||||
selectOption: PropTypes.func.isRequired,
|
||||
toggleItemFreezed: PropTypes.func
|
||||
};
|
||||
|
||||
class Selector extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isPopoverOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.handleOutsideClick);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.handleOutsideClick);
|
||||
}
|
||||
|
||||
handleOutsideClick = (e) => {
|
||||
const { isPopoverOpen } = this.state;
|
||||
if (isPopoverOpen && !this.selector.contains(e.target)) {
|
||||
this.togglePopover();
|
||||
}
|
||||
};
|
||||
|
||||
togglePopover = () => {
|
||||
this.setState({
|
||||
isPopoverOpen: !this.state.isPopoverOpen
|
||||
}, () => {
|
||||
if (this.props.toggleItemFreezed) {
|
||||
this.props.toggleItemFreezed(this.state.isPopoverOpen);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.togglePopover();
|
||||
};
|
||||
|
||||
selectItem = (e, targetItem) => {
|
||||
e.stopPropagation();
|
||||
this.props.selectOption(targetItem);
|
||||
this.togglePopover();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isPopoverOpen } = this.state;
|
||||
const { currentSelectedOption, options, isDropdownToggleShown } = this.props;
|
||||
return (
|
||||
<div className="sf-single-selector position-relative">
|
||||
<span className="cur-option" onClick={this.onToggleClick}>
|
||||
{currentSelectedOption.text}
|
||||
{isDropdownToggleShown && <i className="fas fa-caret-down ml-2 toggle-icon"></i>}
|
||||
</span>
|
||||
{isPopoverOpen && (
|
||||
<div className="options-container position-absolute rounded shadow mt-1" ref={ref => this.selector = ref}>
|
||||
<ul className="option-list list-unstyled p-3 o-auto">
|
||||
{options.map((item, index) => {
|
||||
return (
|
||||
<li key={index} className="option-item h-6 p-1 rounded d-flex justify-content-between align-items-center" onClick={(e) => {this.selectItem(e, item);}}>
|
||||
<span className="option-item-text flex-shrink-0 mr-3">{item.text}</span>
|
||||
<i className={`sf2-icon-tick text-gray font-weight-bold ${item.isSelected ? '' : 'invisible'}`}></i>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Selector.propTypes = propTypes;
|
||||
|
||||
export default Selector;
|
31
frontend/src/css/single-selector.css
Normal file
31
frontend/src/css/single-selector.css
Normal file
@@ -0,0 +1,31 @@
|
||||
.sf-single-selector .cur-option {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-single-selector .cur-option .toggle-icon {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sf-single-selector .options-container {
|
||||
min-width: 165px;
|
||||
background: #fff;
|
||||
border: 1px solid #e8e8e8;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.sf-single-selector .option-list {
|
||||
min-height: 4rem;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.sf-single-selector .option-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-single-selector .option-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.sf-single-selector .option-item-text {
|
||||
font-size: 14px;
|
||||
}
|
@@ -10,6 +10,7 @@ const propTypes = {
|
||||
toggleDelete: PropTypes.func.isRequired,
|
||||
toggleRevokeAdmin: PropTypes.func.isRequired,
|
||||
orgAdminUsers: PropTypes.array.isRequired,
|
||||
changeStatus: PropTypes.func.isRequired,
|
||||
initOrgAdmin: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -34,6 +35,10 @@ class OrgAdminList extends React.Component {
|
||||
this.setState({isItemFreezed: false});
|
||||
};
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
render() {
|
||||
let orgAdminUsers = this.props.orgAdminUsers;
|
||||
|
||||
@@ -50,17 +55,19 @@ class OrgAdminList extends React.Component {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{orgAdminUsers.map(item => {
|
||||
{orgAdminUsers.map((item, index) => {
|
||||
return (
|
||||
<UserItem
|
||||
key={item.index}
|
||||
key={index}
|
||||
user={item}
|
||||
currentTab="admins"
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
toggleDelete={this.props.toggleDelete}
|
||||
toggleRevokeAdmin={this.props.toggleRevokeAdmin}
|
||||
changeStatus={this.props.changeStatus}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
onUnfreezedItem={this.onUnfreezedItem}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
@@ -7,7 +7,7 @@ import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import MainPanelTopbar from './main-panel-topbar';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import RoleEditor from '../../components/select-editor/role-editor';
|
||||
import RoleSelector from '../../components/single-selector';
|
||||
import AddDepartDialog from '../../components/dialog/org-add-department-dialog';
|
||||
import AddMemberDialog from '../../components/dialog/org-add-member-dialog';
|
||||
import DeleteMemberDialog from '../../components/dialog/org-delete-member-dialog';
|
||||
@@ -396,10 +396,12 @@ class MemberItem extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
showRoleMenu: false,
|
||||
highlight: false
|
||||
};
|
||||
this.roles = ['Admin', 'Member'];
|
||||
this.roleOptions = [
|
||||
{ value: 'Admin', text: gettext('Admin'), isSelected: false },
|
||||
{ value: 'Member', text: gettext('Member'), isSelected: false }
|
||||
];
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
@@ -412,12 +414,8 @@ class MemberItem extends React.Component {
|
||||
this.setState({ highlight: false });
|
||||
};
|
||||
|
||||
toggleMemberRoleMenu = () => {
|
||||
this.setState({ showRoleMenu: !this.state.showRoleMenu });
|
||||
};
|
||||
|
||||
onChangeUserRole = (role) => {
|
||||
let isAdmin = role === 'Admin' ? true : false;
|
||||
onChangeUserRole = (roleOption) => {
|
||||
let isAdmin = roleOption.value === 'Admin' ? true : false;
|
||||
seafileAPI.orgAdminSetGroupMemberRole(orgID, this.props.groupID, this.props.member.email, isAdmin).then((res) => {
|
||||
this.props.onMemberChanged();
|
||||
}).catch(error => {
|
||||
@@ -434,25 +432,29 @@ class MemberItem extends React.Component {
|
||||
const highlight = this.state.highlight;
|
||||
let memberLink = serviceURL + '/org/useradmin/info/' + member.email + '/';
|
||||
if (member.role === 'Owner') return null;
|
||||
|
||||
this.roleOptions = this.roleOptions.map(item => {
|
||||
item.isSelected = item.value == member.role;
|
||||
return item;
|
||||
});
|
||||
const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return (
|
||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td><img src={member.avatar_url} alt="member-header" width="24" className="avatar"/></td>
|
||||
<td><a href={memberLink}>{member.name}</a></td>
|
||||
<td>
|
||||
<RoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={highlight}
|
||||
currentRole={member.role}
|
||||
roles={this.roles}
|
||||
onRoleChanged={this.onChangeUserRole}
|
||||
<RoleSelector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedOption}
|
||||
options={this.roleOptions}
|
||||
selectOption={this.onChangeUserRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
{!this.props.isItemFreezed ?
|
||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteMemberDialog.bind(this, member)}>
|
||||
<span className={`sf2-icon-x3 action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||
</td> : <td></td>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ import { gettext, siteRoot, orgID, username } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import UserStatusEditor from '../../components/select-editor/user-status-editor';
|
||||
import Selector from '../../components/single-selector';
|
||||
|
||||
const propTypes = {
|
||||
user: PropTypes.object,
|
||||
@@ -15,6 +15,7 @@ const propTypes = {
|
||||
toggleDelete: PropTypes.func.isRequired,
|
||||
onFreezedItem: PropTypes.func.isRequired,
|
||||
onUnfreezedItem: PropTypes.func.isRequired,
|
||||
toggleItemFreezed: PropTypes.func.isRequired,
|
||||
changeStatus: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -25,11 +26,8 @@ class UserItem extends React.Component {
|
||||
this.state = {
|
||||
highlight: false,
|
||||
showMenu: false,
|
||||
currentStatus: this.props.user.is_active ? 'active' : 'inactive',
|
||||
isItemMenuShow: false
|
||||
};
|
||||
|
||||
this.statusArray = ['active', 'inactive'];
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
@@ -78,8 +76,8 @@ class UserItem extends React.Component {
|
||||
this.props.toggleRevokeAdmin(email);
|
||||
};
|
||||
|
||||
changeStatus = (value) => {
|
||||
const isActive = value == 'active';
|
||||
changeStatus = (statusOption) => {
|
||||
const isActive = statusOption.value == 'active';
|
||||
if (isActive) {
|
||||
toaster.notify(gettext('It may take some time, please wait.'));
|
||||
}
|
||||
@@ -119,23 +117,44 @@ class UserItem extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
translateStatus = (status) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return gettext('Active');
|
||||
case 'inactive':
|
||||
return gettext('Inactive');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { highlight } = this.state;
|
||||
let { user, currentTab } = this.props;
|
||||
let href = siteRoot + 'org/useradmin/info/' + encodeURIComponent(user.email) + '/';
|
||||
let isOperationMenuShow = (user.email !== username) && this.state.showMenu;
|
||||
let isEditIconShow = isOperationMenuShow;
|
||||
|
||||
// for 'user status'
|
||||
const curStatus = user.is_active ? 'active' : 'inactive';
|
||||
this.statusOptions = ['active', 'inactive'].map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: this.translateStatus(item),
|
||||
isSelected: item == curStatus
|
||||
};
|
||||
});
|
||||
const currentSelectedStatusOption = this.statusOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return (
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td>
|
||||
<a href={href}>{user.name}</a>
|
||||
</td>
|
||||
<td>
|
||||
<UserStatusEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isEditIconShow}
|
||||
currentStatus={user.is_active ? 'active' : 'inactive'}
|
||||
statusArray={this.statusArray}
|
||||
onStatusChanged={this.changeStatus}
|
||||
<Selector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedStatusOption}
|
||||
options={this.statusOptions}
|
||||
selectOption={this.changeStatus}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
<td>{`${Utils.formatSize({bytes: user.quota_usage})} / ${this.getQuotaTotal(user.quota_total)}`}</td>
|
||||
|
@@ -75,6 +75,22 @@ class OrgUsers extends Component {
|
||||
this.toggleAddOrgAdmin();
|
||||
};
|
||||
|
||||
changeStatus = (email, isActive) => {
|
||||
seafileAPI.orgAdminChangeOrgUserStatus(orgID, email, isActive).then(res => {
|
||||
let users = this.state.orgAdminUsers.map(item => {
|
||||
if (item.email == email) {
|
||||
item['is_active']= res.data['is_active'];
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.setState({orgAdminUsers: users});
|
||||
toaster.success(gettext('Edit succeeded.'));
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const topBtn = 'btn btn-secondary operation-item';
|
||||
let topbarChildren;
|
||||
@@ -103,6 +119,7 @@ class OrgUsers extends Component {
|
||||
toggleRevokeAdmin={this.toggleRevokeAdmin}
|
||||
orgAdminUsers={this.state.orgAdminUsers}
|
||||
initOrgAdmin={this.initOrgAdmin}
|
||||
changeStatus={this.changeStatus}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -10,7 +10,7 @@ const propTypes = {
|
||||
orgUsers: PropTypes.array.isRequired,
|
||||
page: PropTypes.number.isRequired,
|
||||
pageNext: PropTypes.bool.isRequired,
|
||||
sortByQuotaUsage: PropTypes.string.isRequired,
|
||||
sortByQuotaUsage: PropTypes.func.isRequired,
|
||||
sortOrder: PropTypes.string.isRequired,
|
||||
sortBy: PropTypes.string.isRequired,
|
||||
};
|
||||
@@ -32,6 +32,10 @@ class OrgUsersList extends React.Component {
|
||||
this.setState({isItemFreezed: false});
|
||||
};
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
onChangePageNum = (e, num) => {
|
||||
e.preventDefault();
|
||||
let page = this.props.page;
|
||||
@@ -86,6 +90,7 @@ class OrgUsersList extends React.Component {
|
||||
changeStatus={this.props.changeStatus}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
onUnfreezedItem={this.onUnfreezedItem}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
/>
|
||||
);})}
|
||||
</tbody>
|
||||
|
@@ -7,7 +7,6 @@ import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { isPro, gettext, siteRoot, canGenerateUploadLink } from '../../utils/constants';
|
||||
import ShareLink from '../../models/share-link';
|
||||
import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor';
|
||||
import Loading from '../../components/loading';
|
||||
import toaster from '../../components/toast';
|
||||
import EmptyTip from '../../components/empty-tip';
|
||||
@@ -16,6 +15,7 @@ import ShareAdminLink from '../../components/dialog/share-admin-link';
|
||||
import SortOptionsDialog from '../../components/dialog/sort-options';
|
||||
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
|
||||
import TopToolbar from '../../components/toolbar/top-toolbar';
|
||||
import Selector from '../../components/single-selector';
|
||||
|
||||
const contentPropTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
@@ -29,6 +29,17 @@ const contentPropTypes = {
|
||||
|
||||
class Content extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
sortByName = (e) => {
|
||||
e.preventDefault();
|
||||
const sortBy = 'name';
|
||||
@@ -67,7 +78,7 @@ class Content extends Component {
|
||||
// only for some columns
|
||||
const columnWidths = isPro ? ['14%', '7%', '14%'] : ['21%', '14%', '20%'];
|
||||
const table = (
|
||||
<table className={`table-hover ${isDesktop ? '': 'table-thead-hidden'}`}>
|
||||
<table className={`${isDesktop ? '': 'table-thead-hidden'}`}>
|
||||
<thead>
|
||||
{isDesktop ? (
|
||||
<tr>
|
||||
@@ -89,7 +100,15 @@ class Content extends Component {
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item, index) => {
|
||||
return (<Item key={index} isDesktop={isDesktop} item={item} onRemoveLink={this.props.onRemoveLink} />);
|
||||
return (<Item
|
||||
key={index}
|
||||
isDesktop={isDesktop}
|
||||
item={item}
|
||||
onRemoveLink={this.props.onRemoveLink}
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -105,7 +124,9 @@ Content.propTypes = contentPropTypes;
|
||||
const itemPropTypes = {
|
||||
item: PropTypes.object.isRequired,
|
||||
isDesktop: PropTypes.bool.isRequired,
|
||||
onRemoveLink: PropTypes.func.isRequired
|
||||
onRemoveLink: PropTypes.func.isRequired,
|
||||
isItemFreezed: PropTypes.bool.isRequired,
|
||||
toggleItemFreezed: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class Item extends Component {
|
||||
@@ -114,6 +135,7 @@ class Item extends Component {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
highlight: false,
|
||||
isOpIconShown: false,
|
||||
isOpMenuOpen: false, // for mobile
|
||||
isPermSelectDialogOpen: false, // for mobile
|
||||
@@ -159,12 +181,22 @@ class Item extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
handleMouseOver = () => {
|
||||
this.setState({isOpIconShown: true});
|
||||
handleMouseEnter = () => {
|
||||
if (!this.props.isItemFreezed) {
|
||||
this.setState({
|
||||
isOpIconShown: true,
|
||||
highlight: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseOut = () => {
|
||||
this.setState({isOpIconShown: false});
|
||||
handleMouseLeave = () => {
|
||||
if (!this.props.isItemFreezed) {
|
||||
this.setState({
|
||||
isOpIconShown: false,
|
||||
highlight: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
viewLink = (e) => {
|
||||
@@ -187,6 +219,11 @@ class Item extends Component {
|
||||
return (<span className={item.is_expired ? 'error' : ''} title={expire_time}>{expire_date}</span>);
|
||||
};
|
||||
|
||||
// for 'selector' in desktop
|
||||
changePermission = (permOption) => {
|
||||
this.changePerm(permOption.value);
|
||||
};
|
||||
|
||||
changePerm = (permission) => {
|
||||
const item = this.props.item;
|
||||
const permissionDetails = Utils.getShareLinkPermissionObject(permission).permissionDetails;
|
||||
@@ -205,6 +242,14 @@ class Item extends Component {
|
||||
render() {
|
||||
const item = this.props.item;
|
||||
const { currentPermission, permissionOptions , isOpIconShown, isPermSelectDialogOpen, isLinkDialogOpen } = this.state;
|
||||
this.permOptions = permissionOptions.map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: Utils.getShareLinkPermissionObject(item).text,
|
||||
isSelected: item == currentPermission
|
||||
};
|
||||
});
|
||||
const currentSelectedPermOption = this.permOptions.filter(item => item.isSelected)[0] || {};
|
||||
|
||||
let iconUrl, objUrl;
|
||||
if (item.is_dir) {
|
||||
@@ -218,7 +263,7 @@ class Item extends Component {
|
||||
|
||||
const deletedTip = item.obj_id === '' ? <span style={{color:'red'}}>{gettext('(deleted)')}</span> : null;
|
||||
const desktopItem = (
|
||||
<tr onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onFocus={this.handleMouseEnter}>
|
||||
<td><img src={iconUrl} width="24" alt="" /></td>
|
||||
<td>
|
||||
{item.is_dir ?
|
||||
@@ -230,12 +275,12 @@ class Item extends Component {
|
||||
<td><Link to={`${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}/`}>{item.repo_name}</Link></td>
|
||||
{isPro &&
|
||||
<td>
|
||||
<ShareLinkPermissionEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown && !item.is_expired}
|
||||
currentPermission={currentPermission}
|
||||
permissionOptions={permissionOptions}
|
||||
onPermissionChanged={this.changePerm}
|
||||
<Selector
|
||||
isDropdownToggleShown={isOpIconShown && !item.is_expired}
|
||||
currentSelectedOption={currentSelectedPermOption}
|
||||
options={this.permOptions}
|
||||
selectOption={this.changePermission}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
}
|
||||
|
@@ -2,8 +2,9 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { seafileAPI } from '../../../utils/seafile-api';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import toaster from '../../../components/toast';
|
||||
import RoleEditor from '../../../components/select-editor/role-editor';
|
||||
import RoleSelector from '../../../components/single-selector';
|
||||
import UserLink from '../user-link';
|
||||
|
||||
const MemberItemPropTypes = {
|
||||
@@ -20,10 +21,12 @@ class MemberItem extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
showRoleMenu: false,
|
||||
highlight: false
|
||||
};
|
||||
this.roles = ['Admin', 'Member'];
|
||||
this.roleOptions = [
|
||||
{ value: 'Admin', text: gettext('Admin'), isSelected: false },
|
||||
{ value: 'Member', text: gettext('Member'), isSelected: false }
|
||||
];
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
@@ -36,12 +39,8 @@ class MemberItem extends React.Component {
|
||||
this.setState({ highlight: false });
|
||||
};
|
||||
|
||||
toggleMemberRoleMenu = () => {
|
||||
this.setState({ showRoleMenu: !this.state.showRoleMenu });
|
||||
};
|
||||
|
||||
onChangeUserRole = (role) => {
|
||||
let isAdmin = role === 'Admin' ? true : false;
|
||||
onChangeUserRole = (roleOption) => {
|
||||
let isAdmin = roleOption.value === 'Admin' ? true : false;
|
||||
seafileAPI.sysAdminUpdateGroupMemberRole(this.props.groupID, this.props.member.email, isAdmin).then((res) => {
|
||||
this.props.onMemberChanged();
|
||||
}).catch(error => {
|
||||
@@ -57,25 +56,27 @@ class MemberItem extends React.Component {
|
||||
const member = this.props.member;
|
||||
const highlight = this.state.highlight;
|
||||
if (member.role === 'Owner') return null;
|
||||
this.roleOptions = this.roleOptions.map(item => {
|
||||
item.isSelected = item.value == member.role;
|
||||
return item;
|
||||
});
|
||||
const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0];
|
||||
return (
|
||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td><img src={member.avatar_url} alt="member-header" width="24" className="avatar"/></td>
|
||||
<td><UserLink email={member.email} name={member.name} /></td>
|
||||
<td>
|
||||
<RoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={highlight}
|
||||
currentRole={member.role}
|
||||
roles={this.roles}
|
||||
onRoleChanged={this.onChangeUserRole}
|
||||
<RoleSelector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedOption}
|
||||
options={this.roleOptions}
|
||||
selectOption={this.onChangeUserRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
{!this.props.isItemFreezed ?
|
||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteMemberDialog.bind(this, member)}>
|
||||
<span className={`sf2-icon-x3 action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||
</td> : <td></td>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ import Loading from '../../../components/loading';
|
||||
import Paginator from '../../../components/paginator';
|
||||
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
|
||||
import SysAdminGroupAddMemberDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-group-add-member-dialog';
|
||||
import SysAdminGroupRoleEditor from '../../../components/select-editor/sysadmin-group-role-editor';
|
||||
import RoleSelector from '../../../components/single-selector';
|
||||
import MainPanelTopbar from '../main-panel-topbar';
|
||||
import UserLink from '../user-link';
|
||||
import GroupNav from './group-nav';
|
||||
@@ -19,8 +19,15 @@ class Content extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
getPreviousPageList = () => {
|
||||
this.props.getListByPage(this.props.pageInfo.current_page - 1);
|
||||
};
|
||||
@@ -43,7 +50,7 @@ class Content extends Component {
|
||||
);
|
||||
const table = (
|
||||
<Fragment>
|
||||
<table className="table-hover">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%">{/* icon */}</th>
|
||||
@@ -57,6 +64,8 @@ class Content extends Component {
|
||||
return (<Item
|
||||
key={index}
|
||||
item={item}
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
removeMember={this.props.removeMember}
|
||||
updateMemberRole={this.props.updateMemberRole}
|
||||
/>);
|
||||
@@ -96,18 +105,24 @@ class Item extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.roleOptions = [
|
||||
{ value: 'Admin', text: gettext('Admin'), isSelected: false },
|
||||
{ value: 'Member', text: gettext('Member'), isSelected: false }
|
||||
];
|
||||
this.state = {
|
||||
isOpIconShown: false,
|
||||
highlighted: false,
|
||||
isDeleteDialogOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({isOpIconShown: true});
|
||||
if (this.props.isItemFreezed) return;
|
||||
this.setState({highlighted: true});
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({isOpIconShown: false});
|
||||
if (this.props.isItemFreezed) return;
|
||||
this.setState({highlighted: false});
|
||||
};
|
||||
|
||||
toggleDeleteDialog = (e) => {
|
||||
@@ -123,37 +138,44 @@ class Item extends Component {
|
||||
this.toggleDeleteDialog();
|
||||
};
|
||||
|
||||
updateMemberRole = (role) => {
|
||||
this.props.updateMemberRole(this.props.item.email, role);
|
||||
updateMemberRole = (roleOption) => {
|
||||
this.props.updateMemberRole(this.props.item.email, roleOption.value);
|
||||
};
|
||||
|
||||
render() {
|
||||
let { isOpIconShown, isDeleteDialogOpen } = this.state;
|
||||
let { highlighted, isDeleteDialogOpen } = this.state;
|
||||
let { item } = this.props;
|
||||
|
||||
let itemName = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
|
||||
let dialogMsg = gettext('Are you sure you want to remove {placeholder} ?').replace('{placeholder}', itemName);
|
||||
|
||||
const { role: curRole } = item;
|
||||
this.roleOptions = this.roleOptions.map(item => {
|
||||
item.isSelected = item.value == curRole;
|
||||
return item;
|
||||
});
|
||||
const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<tr onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<tr className={highlighted ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<td><img src={item.avatar_url} alt="" className="rounded-circle" width="24" /></td>
|
||||
<td><UserLink email={item.email} name={item.name} /></td>
|
||||
<td>
|
||||
{item.role == 'Owner' ?
|
||||
gettext('Owner') :
|
||||
<SysAdminGroupRoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown}
|
||||
roleOptions={['Member', 'Admin']}
|
||||
currentRole={item.role}
|
||||
onRoleChanged={this.updateMemberRole}
|
||||
<RoleSelector
|
||||
isDropdownToggleShown={highlighted}
|
||||
currentSelectedOption={currentSelectedOption}
|
||||
options={this.roleOptions}
|
||||
selectOption={this.updateMemberRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
{item.role != 'Owner' &&
|
||||
<a href="#" className={`action-icon sf2-icon-x3 ${isOpIconShown ? '' : 'invisible'}`} title={gettext('Remove')} onClick={this.toggleDeleteDialog}></a>
|
||||
<a href="#" className={`action-icon sf2-icon-x3 ${highlighted ? '' : 'invisible'}`} title={gettext('Remove')} onClick={this.toggleDeleteDialog}></a>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -175,6 +197,8 @@ Item.propTypes = {
|
||||
item: PropTypes.object.isRequired,
|
||||
removeMember: PropTypes.func.isRequired,
|
||||
updateMemberRole: PropTypes.func.isRequired,
|
||||
isItemFreezed: PropTypes.bool.isRequired,
|
||||
toggleItemFreezed: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class GroupMembers extends Component {
|
||||
|
@@ -8,7 +8,7 @@ import { gettext, username } from '../../../utils/constants';
|
||||
import toaster from '../../../components/toast';
|
||||
import EmptyTip from '../../../components/empty-tip';
|
||||
import Loading from '../../../components/loading';
|
||||
import SysAdminUserStatusEditor from '../../../components/select-editor/sysadmin-user-status-editor';
|
||||
import Selector from '../../../components/single-selector';
|
||||
import SysAdminUserMembershipEditor from '../../../components/select-editor/sysadmin-user-membership-editor';
|
||||
import SysAdminAddUserDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-user-dialog';
|
||||
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
|
||||
@@ -26,6 +26,10 @@ class Content extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
onFreezedItem = () => {
|
||||
this.setState({isItemFreezed: true});
|
||||
};
|
||||
@@ -67,6 +71,7 @@ class Content extends Component {
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
onUnfreezedItem={this.onUnfreezedItem}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
updateStatus={this.props.updateStatus}
|
||||
updateMembership={this.props.updateMembership}
|
||||
deleteUser={this.props.deleteUser}
|
||||
@@ -154,8 +159,8 @@ class Item extends Component {
|
||||
this.setState({isResetPasswordDialogOpen: !this.state.isResetPasswordDialogOpen});
|
||||
};
|
||||
|
||||
updateStatus= (statusValue) => {
|
||||
this.props.updateStatus(this.props.item.email, statusValue);
|
||||
updateStatus= (statusOption) => {
|
||||
this.props.updateStatus(this.props.item.email, statusOption.value);
|
||||
};
|
||||
|
||||
updateMembership= (membershipValue) => {
|
||||
@@ -190,25 +195,45 @@ class Item extends Component {
|
||||
return translateResult;
|
||||
};
|
||||
|
||||
translateStatus = (status) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return gettext('Active');
|
||||
case 'inactive':
|
||||
return gettext('Inactive');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { item } = this.props;
|
||||
const { isOpIconShown, isDeleteDialogOpen, isResetPasswordDialogOpen } = this.state;
|
||||
const { highlight, isOpIconShown, isDeleteDialogOpen, isResetPasswordDialogOpen } = this.state;
|
||||
|
||||
const itemName = '<span class="op-target">' + Utils.HTMLescape(item.name) + '</span>';
|
||||
let deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName);
|
||||
let resetPasswordDialogMsg = gettext('Are you sure you want to reset the password of {placeholder} ?').replace('{placeholder}', itemName);
|
||||
|
||||
// for 'user status'
|
||||
const curStatus = item.active ? 'active' : 'inactive';
|
||||
this.statusOptions = ['active', 'inactive'].map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: this.translateStatus(item),
|
||||
isSelected: item == curStatus
|
||||
};
|
||||
});
|
||||
const currentSelectedStatusOption = this.statusOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<td><UserLink email={item.email} name={item.name} /></td>
|
||||
<td>
|
||||
<SysAdminUserStatusEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown}
|
||||
currentStatus={item.active ? 'active' : 'inactive'}
|
||||
statusOptions={['active', 'inactive']}
|
||||
onStatusChanged={this.updateStatus}
|
||||
<Selector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedStatusOption}
|
||||
options={this.statusOptions}
|
||||
selectOption={this.updateStatus}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -264,6 +289,7 @@ Item.propTypes = {
|
||||
isItemFreezed: PropTypes.bool.isRequired,
|
||||
onFreezedItem: PropTypes.func.isRequired,
|
||||
onUnfreezedItem: PropTypes.func.isRequired,
|
||||
toggleItemFreezed: PropTypes.func.isRequired,
|
||||
updateStatus: PropTypes.func.isRequired,
|
||||
updateMembership: PropTypes.func.isRequired,
|
||||
deleteUser: PropTypes.func.isRequired,
|
||||
|
@@ -8,7 +8,7 @@ import EmptyTip from '../../../components/empty-tip';
|
||||
import Loading from '../../../components/loading';
|
||||
import Paginator from '../../../components/paginator';
|
||||
import { seafileAPI } from '../../../utils/seafile-api';
|
||||
import SysAdminUserRoleEditor from '../../../components/select-editor/sysadmin-user-role-editor';
|
||||
import RoleSelector from '../../../components/single-selector';
|
||||
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
|
||||
import UserLink from '../user-link';
|
||||
import toaster from '../../../components/toast';
|
||||
@@ -17,6 +17,17 @@ const { availableRoles } = window.sysadmin.pageOptions;
|
||||
|
||||
class Content extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
getPreviousPage = () => {
|
||||
this.props.getListByPage(this.props.currentPage - 1);
|
||||
};
|
||||
@@ -39,7 +50,7 @@ class Content extends Component {
|
||||
);
|
||||
const table = (
|
||||
<Fragment>
|
||||
<table className="table-hover">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="20%">{gettext('Name')}</th>
|
||||
@@ -57,6 +68,8 @@ class Content extends Component {
|
||||
item={item}
|
||||
updateRole={this.props.updateRole}
|
||||
deleteOrg={this.props.deleteOrg}
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
/>);
|
||||
})}
|
||||
</tbody>
|
||||
@@ -81,7 +94,6 @@ class Content extends Component {
|
||||
Content.propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
errorMsg: PropTypes.string.isRequired,
|
||||
item: PropTypes.object.isRequired,
|
||||
getListByPage: PropTypes.func.isRequired,
|
||||
currentPage: PropTypes.number,
|
||||
items: PropTypes.array.isRequired,
|
||||
@@ -97,18 +109,20 @@ class Item extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOpIconShown: false,
|
||||
highlighted: false,
|
||||
isDeleteDialogOpen: false,
|
||||
deleteDialogMsg: '',
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({isOpIconShown: true});
|
||||
if (this.props.isItemFreezed) return;
|
||||
this.setState({highlighted: true});
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({isOpIconShown: false});
|
||||
if (this.props.isItemFreezed) return;
|
||||
this.setState({highlighted: false});
|
||||
};
|
||||
|
||||
toggleDeleteDialog = (e) => {
|
||||
@@ -135,8 +149,19 @@ class Item extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
updateRole = (role) => {
|
||||
this.props.updateRole(this.props.item.org_id, role);
|
||||
translateRole = (role) => {
|
||||
switch (role) {
|
||||
case 'default':
|
||||
return gettext('Default');
|
||||
case 'guest':
|
||||
return gettext('Guest');
|
||||
default:
|
||||
return role;
|
||||
}
|
||||
};
|
||||
|
||||
updateRole = (roleOption) => {
|
||||
this.props.updateRole(this.props.item.org_id, roleOption.value);
|
||||
};
|
||||
|
||||
deleteOrg = () => {
|
||||
@@ -146,28 +171,38 @@ class Item extends Component {
|
||||
|
||||
render() {
|
||||
const { item } = this.props;
|
||||
const { isOpIconShown, isDeleteDialogOpen, deleteDialogMsg } = this.state;
|
||||
const { highlighted, isDeleteDialogOpen, deleteDialogMsg } = this.state;
|
||||
|
||||
const { role: curRole } = item;
|
||||
this.roleOptions = availableRoles.map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: this.translateRole(item),
|
||||
isSelected: item == curRole
|
||||
};
|
||||
});
|
||||
const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<tr onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<tr className={highlighted ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
<td><Link to={`${siteRoot}sys/organizations/${item.org_id}/info/`}>{item.org_name}</Link></td>
|
||||
<td>
|
||||
<UserLink email={item.creator_email} name={item.creator_name} />
|
||||
</td>
|
||||
<td>
|
||||
<SysAdminUserRoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown}
|
||||
currentRole={item.role}
|
||||
roleOptions={availableRoles}
|
||||
onRoleChanged={this.updateRole}
|
||||
<RoleSelector
|
||||
isDropdownToggleShown={highlighted}
|
||||
currentSelectedOption={currentSelectedOption}
|
||||
options={this.roleOptions}
|
||||
selectOption={this.updateRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
<td>{`${Utils.bytesToSize(item.quota_usage)} / ${item.quota > 0 ? Utils.bytesToSize(item.quota) : '--'}`}</td>
|
||||
<td>{moment(item.ctime).format('YYYY-MM-DD HH:mm:ss')}</td>
|
||||
<td>
|
||||
<a href="#" className={`action-icon sf2-icon-delete ${isOpIconShown ? '' : 'invisible'}`} title={gettext('Delete')} onClick={this.toggleDeleteDialog}></a>
|
||||
<a href="#" className={`action-icon sf2-icon-delete ${highlighted ? '' : 'invisible'}`} title={gettext('Delete')} onClick={this.toggleDeleteDialog}></a>
|
||||
</td>
|
||||
</tr>
|
||||
{isDeleteDialogOpen &&
|
||||
@@ -188,6 +223,8 @@ Item.propTypes = {
|
||||
item: PropTypes.object.isRequired,
|
||||
updateRole: PropTypes.func.isRequired,
|
||||
deleteOrg: PropTypes.func.isRequired,
|
||||
isItemFreezed: PropTypes.bool.isRequired,
|
||||
toggleItemFreezed: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Content;
|
||||
|
@@ -9,9 +9,7 @@ import toaster from '../../../components/toast';
|
||||
import EmptyTip from '../../../components/empty-tip';
|
||||
import Loading from '../../../components/loading';
|
||||
import Paginator from '../../../components/paginator';
|
||||
import SysAdminUserStatusEditor from '../../../components/select-editor/sysadmin-user-status-editor';
|
||||
import SysAdminUserRoleEditor from '../../../components/select-editor/sysadmin-user-role-editor';
|
||||
import SelectEditor from '../../../components/select-editor/select-editor';
|
||||
import Selector from '../../../components/single-selector';
|
||||
import OpMenu from '../../../components/dialog/op-menu';
|
||||
import SysAdminUserSetQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota';
|
||||
import CommonOperationConfirmationDialog from '../../../components/dialog/common-operation-confirmation-dialog';
|
||||
@@ -28,6 +26,10 @@ class Content extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({ isItemFreezed: isFreezed });
|
||||
};
|
||||
|
||||
onFreezedItem = () => {
|
||||
this.setState({isItemFreezed: true});
|
||||
};
|
||||
@@ -133,6 +135,7 @@ class Content extends Component {
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
onUnfreezedItem={this.onUnfreezedItem}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
updateUser={this.props.updateUser}
|
||||
deleteUser={this.props.deleteUser}
|
||||
updateAdminRole={this.props.updateAdminRole}
|
||||
@@ -247,20 +250,31 @@ class Item extends Component {
|
||||
this.props.onUserSelected(this.props.item);
|
||||
};
|
||||
|
||||
updateStatus= (value) => {
|
||||
const isActive = value == 'active';
|
||||
updateStatus= (roleOption) => {
|
||||
const isActive = roleOption.value == 'active';
|
||||
if (isActive) {
|
||||
toaster.notify(gettext('It may take some time, please wait.'));
|
||||
}
|
||||
this.props.updateUser(this.props.item.email, 'is_active', isActive);
|
||||
};
|
||||
|
||||
updateRole = (value) => {
|
||||
this.props.updateUser(this.props.item.email, 'role', value);
|
||||
updateRole = (roleOption) => {
|
||||
this.props.updateUser(this.props.item.email, 'role', roleOption.value);
|
||||
};
|
||||
|
||||
updateAdminRole = (value) => {
|
||||
this.props.updateAdminRole(this.props.item.email, value);
|
||||
updateAdminRole = (roleOption) => {
|
||||
this.props.updateAdminRole(this.props.item.email, roleOption.value);
|
||||
};
|
||||
|
||||
translateRole = (role) => {
|
||||
switch (role) {
|
||||
case 'default':
|
||||
return gettext('Default');
|
||||
case 'guest':
|
||||
return gettext('Guest');
|
||||
default:
|
||||
return role;
|
||||
}
|
||||
};
|
||||
|
||||
translateAdminRole = (role) => {
|
||||
@@ -278,12 +292,17 @@ class Item extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
updateInstitution = (value) => {
|
||||
this.props.updateUser(this.props.item.email, 'institution', value);
|
||||
translateStatus = (status) => {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return gettext('Active');
|
||||
case 'inactive':
|
||||
return gettext('Inactive');
|
||||
}
|
||||
};
|
||||
|
||||
translateInstitution = (inst) => {
|
||||
return inst;
|
||||
updateInstitution = (instOption) => {
|
||||
this.props.updateUser(this.props.item.email, 'institution', instOption.value);
|
||||
};
|
||||
|
||||
updateQuota = (value) => {
|
||||
@@ -362,6 +381,7 @@ class Item extends Component {
|
||||
render() {
|
||||
const { item, isAdmin } = this.props;
|
||||
const {
|
||||
highlight,
|
||||
isOpIconShown,
|
||||
isSetQuotaDialogOpen,
|
||||
isDeleteUserDialogOpen,
|
||||
@@ -374,6 +394,54 @@ class Item extends Component {
|
||||
const resetPasswordDialogMsg = gettext('Are you sure you want to reset the password of {placeholder} ?').replace('{placeholder}', itemName);
|
||||
const revokeAdminDialogMsg = gettext('Are you sure you want to revoke the admin permission of {placeholder} ?').replace('{placeholder}', itemName);
|
||||
|
||||
// for 'user status'
|
||||
const curStatus = item.is_active ? 'active' : 'inactive';
|
||||
this.statusOptions = ['active', 'inactive'].map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: this.translateStatus(item),
|
||||
isSelected: item == curStatus
|
||||
};
|
||||
});
|
||||
const currentSelectedStatusOption = this.statusOptions.filter(item => item.isSelected)[0];
|
||||
|
||||
let currentSelectedAdminRoleOption;
|
||||
let currentSelectedRoleOption;
|
||||
if (isAdmin) {
|
||||
const { admin_role: curAdminRole } = item;
|
||||
this.adminRoleOptions = availableAdminRoles.map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: this.translateAdminRole(item),
|
||||
isSelected: item == curAdminRole
|
||||
};
|
||||
});
|
||||
currentSelectedAdminRoleOption = this.adminRoleOptions.filter(item => item.isSelected)[0];
|
||||
} else {
|
||||
const { role: curRole } = item;
|
||||
this.roleOptions = availableRoles.map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: this.translateRole(item),
|
||||
isSelected: item == curRole
|
||||
};
|
||||
});
|
||||
currentSelectedRoleOption = this.roleOptions.filter(item => item.isSelected)[0];
|
||||
}
|
||||
|
||||
let currentSelectedInstOption;
|
||||
if (multiInstitution && !isAdmin) {
|
||||
const { institution: curInstitution } = item;
|
||||
this.instOptions = institutions.map(item => {
|
||||
return {
|
||||
value: item,
|
||||
text: item,
|
||||
isSelected: item == curInstitution
|
||||
};
|
||||
});
|
||||
currentSelectedInstOption = this.instOptions.filter(item => item.isSelected)[0];
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
@@ -395,31 +463,31 @@ class Item extends Component {
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<SysAdminUserStatusEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown}
|
||||
currentStatus={item.is_active ? 'active' : 'inactive'}
|
||||
statusOptions={['active', 'inactive']}
|
||||
onStatusChanged={this.updateStatus}
|
||||
<Selector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedStatusOption}
|
||||
options={this.statusOptions}
|
||||
selectOption={this.updateStatus}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
{isPro &&
|
||||
<td>
|
||||
{isAdmin ?
|
||||
<SelectEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown}
|
||||
options={availableAdminRoles}
|
||||
currentOption={item.admin_role}
|
||||
onOptionChanged={this.updateAdminRole}
|
||||
translateOption={this.translateAdminRole}
|
||||
/> :
|
||||
<SysAdminUserRoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown}
|
||||
currentRole={item.role}
|
||||
roleOptions={availableRoles}
|
||||
onRoleChanged={this.updateRole}
|
||||
<Selector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedAdminRoleOption}
|
||||
options={this.adminRoleOptions}
|
||||
selectOption={this.updateAdminRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
:
|
||||
<Selector
|
||||
isDropdownToggleShown={highlight}
|
||||
currentSelectedOption={currentSelectedRoleOption}
|
||||
options={this.roleOptions}
|
||||
selectOption={this.updateRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
}
|
||||
</td>
|
||||
@@ -434,13 +502,12 @@ class Item extends Component {
|
||||
</td>
|
||||
{(multiInstitution && !isAdmin) &&
|
||||
<td>
|
||||
<SelectEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={isOpIconShown && institutions.length > 0}
|
||||
options={institutions}
|
||||
currentOption={item.institution}
|
||||
onOptionChanged={this.updateInstitution}
|
||||
translateOption={this.translateInstitution}
|
||||
<Selector
|
||||
isDropdownToggleShown={highlight && institutions.length > 0}
|
||||
currentSelectedOption={currentSelectedInstOption}
|
||||
options={this.instOptions}
|
||||
selectOption={this.updateInstitution}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
}
|
||||
@@ -508,6 +575,7 @@ Item.propTypes = {
|
||||
isLDAPImported: PropTypes.bool,
|
||||
onFreezedItem: PropTypes.func,
|
||||
onUnfreezedItem: PropTypes.func,
|
||||
toggleItemFreezed: PropTypes.func.isRequired,
|
||||
updateUser: PropTypes.func,
|
||||
deleteUser: PropTypes.func,
|
||||
updateAdminRole: PropTypes.func,
|
||||
|
Reference in New Issue
Block a user