mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-16 07:08:55 +00:00
share wiki (#6413)
* share wiki update leave and return update get group info * format optimize * Update wikis.js * Update wiki2.py * fix empty group_wikis * update * remove-useless-code * add-owner-nickname * Update wiki2.py * Update wiki2.py * Update wiki2.py --------- Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com> Co-authored-by: r350178982 <32759763+r350178982@users.noreply.github.com>
This commit is contained in:
@@ -113,6 +113,7 @@ const propTypes = {
|
|||||||
itemPath: PropTypes.string.isRequired,
|
itemPath: PropTypes.string.isRequired,
|
||||||
itemType: PropTypes.string.isRequired,
|
itemType: PropTypes.string.isRequired,
|
||||||
repoID: PropTypes.string.isRequired,
|
repoID: PropTypes.string.isRequired,
|
||||||
|
repoType: PropTypes.string.isRequired,
|
||||||
isRepoOwner: PropTypes.bool.isRequired,
|
isRepoOwner: PropTypes.bool.isRequired,
|
||||||
onAddCustomPermissionToggle: PropTypes.func,
|
onAddCustomPermissionToggle: PropTypes.func,
|
||||||
};
|
};
|
||||||
@@ -126,7 +127,8 @@ class ShareToGroup extends React.Component {
|
|||||||
selectedOption: null,
|
selectedOption: null,
|
||||||
errorMsg: [],
|
errorMsg: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
sharedItems: []
|
sharedItems: [],
|
||||||
|
isWiki: this.props.repoType === 'wiki'
|
||||||
};
|
};
|
||||||
this.permissions = [];
|
this.permissions = [];
|
||||||
let { itemType, isRepoOwner } = props;
|
let { itemType, isRepoOwner } = props;
|
||||||
@@ -141,6 +143,9 @@ class ShareToGroup extends React.Component {
|
|||||||
if (this.props.isGroupOwnedRepo) {
|
if (this.props.isGroupOwnedRepo) {
|
||||||
this.permissions = ['rw', 'r', 'cloud-edit', 'preview'];
|
this.permissions = ['rw', 'r', 'cloud-edit', 'preview'];
|
||||||
}
|
}
|
||||||
|
if (this.state.isWiki) {
|
||||||
|
this.permissions = ['rw', 'r'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
@@ -344,6 +349,7 @@ class ShareToGroup extends React.Component {
|
|||||||
permissions={this.permissions}
|
permissions={this.permissions}
|
||||||
onPermissionChanged={this.setPermission}
|
onPermissionChanged={this.setPermission}
|
||||||
enableAddCustomPermission={isPro}
|
enableAddCustomPermission={isPro}
|
||||||
|
isWiki={this.state.isWiki}
|
||||||
onAddCustomPermissionToggle={this.props.onAddCustomPermissionToggle}
|
onAddCustomPermissionToggle={this.props.onAddCustomPermissionToggle}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
@@ -154,6 +154,7 @@ const propTypes = {
|
|||||||
itemType: PropTypes.string.isRequired,
|
itemType: PropTypes.string.isRequired,
|
||||||
repoID: PropTypes.string.isRequired,
|
repoID: PropTypes.string.isRequired,
|
||||||
isRepoOwner: PropTypes.bool.isRequired,
|
isRepoOwner: PropTypes.bool.isRequired,
|
||||||
|
repoType: PropTypes.string.isRequired,
|
||||||
onAddCustomPermissionToggle: PropTypes.func,
|
onAddCustomPermissionToggle: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -165,7 +166,8 @@ class ShareToUser extends React.Component {
|
|||||||
selectedOption: null,
|
selectedOption: null,
|
||||||
errorMsg: [],
|
errorMsg: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
sharedItems: []
|
sharedItems: [],
|
||||||
|
isWiki: this.props.repoType === 'wiki'
|
||||||
};
|
};
|
||||||
this.options = [];
|
this.options = [];
|
||||||
this.permissions = [];
|
this.permissions = [];
|
||||||
@@ -181,6 +183,9 @@ class ShareToUser extends React.Component {
|
|||||||
if (this.props.isGroupOwnedRepo) {
|
if (this.props.isGroupOwnedRepo) {
|
||||||
this.permissions = ['rw', 'r', 'cloud-edit', 'preview'];
|
this.permissions = ['rw', 'r', 'cloud-edit', 'preview'];
|
||||||
}
|
}
|
||||||
|
if (this.state.isWiki) {
|
||||||
|
this.permissions = ['rw', 'r'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
@@ -370,6 +375,7 @@ class ShareToUser extends React.Component {
|
|||||||
permissions={this.permissions}
|
permissions={this.permissions}
|
||||||
onPermissionChanged={this.setPermission}
|
onPermissionChanged={this.setPermission}
|
||||||
enableAddCustomPermission={isPro}
|
enableAddCustomPermission={isPro}
|
||||||
|
isWiki={this.state.isWiki}
|
||||||
onAddCustomPermissionToggle={this.props.onAddCustomPermissionToggle}
|
onAddCustomPermissionToggle={this.props.onAddCustomPermissionToggle}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
174
frontend/src/components/dialog/share-wiki-dialog.js
Normal file
174
frontend/src/components/dialog/share-wiki-dialog.js
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Modal, ModalHeader, ModalBody, TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap';
|
||||||
|
import { gettext, username, additionalShareDialogNote, canShareRepo } from '../../utils/constants';
|
||||||
|
import ShareToUser from './share-to-user';
|
||||||
|
import ShareToGroup from './share-to-group';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
import Loading from '../loading';
|
||||||
|
import toaster from '../toast';
|
||||||
|
|
||||||
|
import '../../css/share-link-dialog.css';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
isGroupOwnedRepo: PropTypes.bool,
|
||||||
|
itemType: PropTypes.string.isRequired, // there will be three choose: ['library', 'dir', 'file']
|
||||||
|
itemName: PropTypes.string.isRequired,
|
||||||
|
itemPath: PropTypes.string.isRequired,
|
||||||
|
toggleDialog: PropTypes.func.isRequired,
|
||||||
|
repoID: PropTypes.string.isRequired,
|
||||||
|
repoEncrypted: PropTypes.bool,
|
||||||
|
enableDirPrivateShare: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShareWikiDialog extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
activeTab: 'shareToUser',
|
||||||
|
isRepoJudgemented: false,
|
||||||
|
isRepoOwner: false,
|
||||||
|
isGroupOwnedRepo: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let repoID = this.props.repoID;
|
||||||
|
seafileAPI.getRepoInfo(repoID).then(res => {
|
||||||
|
const isGroupOwnedRepo = res.data.owner_email.indexOf('@seafile_group') > -1;
|
||||||
|
let isRepoOwner = res.data.owner_email === username;
|
||||||
|
this.setState({
|
||||||
|
isRepoJudgemented: true,
|
||||||
|
isRepoOwner: isRepoOwner,
|
||||||
|
repoType: res.data.repo_type,
|
||||||
|
isGroupOwnedRepo: isGroupOwnedRepo,
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = (tab) => {
|
||||||
|
if (this.state.activeTab !== tab) {
|
||||||
|
this.setState({ activeTab: tab });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onAddCustomPermissionToggle = () => {
|
||||||
|
this.toggle('customSharePermission');
|
||||||
|
};
|
||||||
|
|
||||||
|
renderDirContent = () => {
|
||||||
|
|
||||||
|
if (!this.state.isRepoJudgemented) {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeTab = this.state.activeTab;
|
||||||
|
let { repoEncrypted, enableDirPrivateShare, itemType } = this.props;
|
||||||
|
if (repoEncrypted) {
|
||||||
|
enableDirPrivateShare = itemType == 'library';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className="share-dialog-side">
|
||||||
|
<Nav pills>
|
||||||
|
{enableDirPrivateShare &&
|
||||||
|
<Fragment>
|
||||||
|
{ canShareRepo && (
|
||||||
|
<NavItem role="tab" aria-selected={activeTab === 'shareToUser'} aria-controls="share-to-user-panel">
|
||||||
|
<NavLink className={activeTab === 'shareToUser' ? 'active' : ''} onClick={this.toggle.bind(this, 'shareToUser')} tabIndex="0" onKeyDown={this.onTabKeyDown}>
|
||||||
|
{gettext('Share to user')}
|
||||||
|
</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
)}
|
||||||
|
{ canShareRepo && (
|
||||||
|
<NavItem role="tab" aria-selected={activeTab === 'shareToGroup'} aria-controls="share-to-group-panel">
|
||||||
|
<NavLink className={activeTab === 'shareToGroup' ? 'active' : ''} onClick={this.toggle.bind(this, 'shareToGroup')} tabIndex="0" onKeyDown={this.onTabKeyDown}>
|
||||||
|
{gettext('Share to group')}
|
||||||
|
</NavLink>
|
||||||
|
</NavItem>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
</Nav>
|
||||||
|
</div>
|
||||||
|
<div className="share-dialog-main">
|
||||||
|
<TabContent activeTab={this.state.activeTab}>
|
||||||
|
{enableDirPrivateShare &&
|
||||||
|
<Fragment>
|
||||||
|
{(activeTab === 'shareToUser' && canShareRepo) &&
|
||||||
|
<TabPane tabId="shareToUser" role="tabpanel" id="share-to-user-panel">
|
||||||
|
<ShareToUser
|
||||||
|
itemType={this.props.itemType}
|
||||||
|
isGroupOwnedRepo={this.state.isGroupOwnedRepo}
|
||||||
|
itemPath={this.props.itemPath}
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
isRepoOwner={this.state.isRepoOwner}
|
||||||
|
repoType={this.state.repoType}
|
||||||
|
onAddCustomPermissionToggle={this.onAddCustomPermissionToggle}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
}
|
||||||
|
{(activeTab === 'shareToGroup' && canShareRepo) &&
|
||||||
|
<TabPane tabId="shareToGroup" role="tabpanel" id="share-to-group-panel">
|
||||||
|
<ShareToGroup
|
||||||
|
itemType={this.props.itemType}
|
||||||
|
isGroupOwnedRepo={this.state.isGroupOwnedRepo}
|
||||||
|
itemPath={this.props.itemPath}
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
isRepoOwner={this.state.isRepoOwner}
|
||||||
|
repoType={this.state.repoType}
|
||||||
|
onAddCustomPermissionToggle={this.onAddCustomPermissionToggle}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
</TabContent>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
onTabKeyDown = (e) => {
|
||||||
|
if (e.key == 'Enter' || e.key == 'Space') {
|
||||||
|
e.target.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderExternalShareMessage = () => {
|
||||||
|
if (additionalShareDialogNote && (typeof additionalShareDialogNote) === 'object') {
|
||||||
|
return (
|
||||||
|
<div className="external-share-message mt-2">
|
||||||
|
<h6>{additionalShareDialogNote.title}</h6>
|
||||||
|
<p style={{ fontSize: '14px', color: '#666' }} className="text-wrap m-0">{additionalShareDialogNote.content}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { itemType, itemName } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Modal isOpen={true} style={{ maxWidth: '760px' }} className="share-dialog" toggle={this.props.toggleDialog}>
|
||||||
|
<ModalHeader toggle={this.props.toggleDialog} tag="div">
|
||||||
|
<h5 className="text-truncate">{gettext('Share')} <span className="op-target" title={itemName}>{itemName}</span></h5>
|
||||||
|
{this.renderExternalShareMessage()}
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalBody className="share-dialog-content" role="tablist">
|
||||||
|
{(itemType === 'library') && this.renderDirContent()}
|
||||||
|
</ModalBody>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShareWikiDialog.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default ShareWikiDialog;
|
@@ -9,6 +9,7 @@ const propTypes = {
|
|||||||
isTextMode: PropTypes.bool.isRequired, // there will be two mode. first: text and select. second: just select
|
isTextMode: PropTypes.bool.isRequired, // there will be two mode. first: text and select. second: just select
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
isEditIconShow: PropTypes.bool.isRequired,
|
isEditIconShow: PropTypes.bool.isRequired,
|
||||||
|
isWiki: PropTypes.bool,
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus: PropTypes.bool,
|
||||||
options: PropTypes.array.isRequired,
|
options: PropTypes.array.isRequired,
|
||||||
currentOption: PropTypes.string.isRequired,
|
currentOption: PropTypes.string.isRequired,
|
||||||
@@ -56,7 +57,7 @@ class SelectEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { enableAddCustomPermission } = this.props;
|
const { enableAddCustomPermission } = this.props;
|
||||||
if (enableAddCustomPermission) {
|
if (enableAddCustomPermission && !this.props.isWiki) {
|
||||||
const option = {
|
const option = {
|
||||||
value: gettext('Add custom permission'),
|
value: gettext('Add custom permission'),
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
|
@@ -11,6 +11,7 @@ const propTypes = {
|
|||||||
repoID: PropTypes.string,
|
repoID: PropTypes.string,
|
||||||
isTextMode: PropTypes.bool.isRequired,
|
isTextMode: PropTypes.bool.isRequired,
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
|
isWiki: PropTypes.bool,
|
||||||
autoFocus: PropTypes.bool,
|
autoFocus: PropTypes.bool,
|
||||||
isEditIconShow: PropTypes.bool.isRequired,
|
isEditIconShow: PropTypes.bool.isRequired,
|
||||||
permissions: PropTypes.array.isRequired,
|
permissions: PropTypes.array.isRequired,
|
||||||
@@ -136,6 +137,7 @@ class SharePermissionEditor extends React.Component {
|
|||||||
translateOption={this.translatePermission}
|
translateOption={this.translatePermission}
|
||||||
translateExplanation={this.translateExplanation}
|
translateExplanation={this.translateExplanation}
|
||||||
enableAddCustomPermission={this.props.enableAddCustomPermission}
|
enableAddCustomPermission={this.props.enableAddCustomPermission}
|
||||||
|
isWiki={this.props.isWiki}
|
||||||
onAddCustomPermissionToggle={this.props.onAddCustomPermissionToggle}
|
onAddCustomPermissionToggle={this.props.onAddCustomPermissionToggle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -7,7 +7,9 @@ import { SIDE_PANEL_FOLDED_WIDTH } from '../../constants';
|
|||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
wikis: PropTypes.array.isRequired,
|
wikis: PropTypes.array.isRequired,
|
||||||
|
group: PropTypes.object,
|
||||||
deleteWiki: PropTypes.func.isRequired,
|
deleteWiki: PropTypes.func.isRequired,
|
||||||
|
unshareGroupWiki: PropTypes.func.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
isDepartment: PropTypes.bool.isRequired,
|
isDepartment: PropTypes.bool.isRequired,
|
||||||
isShowAvatar: PropTypes.bool.isRequired,
|
isShowAvatar: PropTypes.bool.isRequired,
|
||||||
@@ -48,23 +50,39 @@ class WikiCardGroup extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { wikis, title, isDepartment, toggelAddWikiDialog } = this.props;
|
const { wikis, title, isDepartment, toggelAddWikiDialog, group } = this.props;
|
||||||
const containerWidth = this.getContainerWidth();
|
const containerWidth = this.getContainerWidth();
|
||||||
const numberOfWiki = Math.floor(containerWidth / 180);
|
const numberOfWiki = Math.floor(containerWidth / 180);
|
||||||
const grids = (Math.floor((containerWidth - (numberOfWiki + 1) * 16) / numberOfWiki) + 'px ').repeat(numberOfWiki);
|
const grids = (Math.floor((containerWidth - (numberOfWiki + 1) * 16) / numberOfWiki) + 'px ').repeat(numberOfWiki);
|
||||||
|
let isGroup = false;
|
||||||
|
let depIcon = false;
|
||||||
|
if (group) {
|
||||||
|
isGroup = true;
|
||||||
|
depIcon = group.owner === 'system admin';
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className='wiki-card-group mb-4'>
|
<div className='wiki-card-group mb-4'>
|
||||||
<h4 className="sf-heading">
|
<h4 className="sf-heading">
|
||||||
<span className={`sf3-font nav-icon sf3-font-${isDepartment ? 'department' : 'mine'}`} aria-hidden="true"></span>
|
<span className={`sf3-font nav-icon sf3-font-${(isDepartment && depIcon) ? 'department' : isDepartment ? 'group' : 'mine'}`} aria-hidden="true"></span>
|
||||||
{title}
|
{title}
|
||||||
</h4>
|
</h4>
|
||||||
<div className='wiki-card-group-items' style={{ gridTemplateColumns: isMobile ? '48% 48%' : grids }} ref={this.groupItemsRef}>
|
<div className='wiki-card-group-items' style={{ gridTemplateColumns: isMobile ? '48% 48%' : grids }} ref={this.groupItemsRef}>
|
||||||
{wikis.map((wiki, index) => {
|
{wikis.map((wiki, index) => {
|
||||||
return (
|
return (isGroup ?
|
||||||
<WikiCardItem
|
<WikiCardItem
|
||||||
|
key={index + wiki.id + wiki.name}
|
||||||
|
group={group}
|
||||||
|
wiki={wiki}
|
||||||
|
deleteWiki={this.props.deleteWiki}
|
||||||
|
unshareGroupWiki={this.props.unshareGroupWiki}
|
||||||
|
isDepartment={isDepartment}
|
||||||
|
isShowAvatar={this.props.isShowAvatar}
|
||||||
|
renameWiki={this.props.renameWiki}
|
||||||
|
/> : <WikiCardItem
|
||||||
key={index + wiki.id + wiki.name}
|
key={index + wiki.id + wiki.name}
|
||||||
wiki={wiki}
|
wiki={wiki}
|
||||||
deleteWiki={this.props.deleteWiki}
|
deleteWiki={this.props.deleteWiki}
|
||||||
|
unshareGroupWiki={this.props.unshareGroupWiki}
|
||||||
isDepartment={isDepartment}
|
isDepartment={isDepartment}
|
||||||
isShowAvatar={this.props.isShowAvatar}
|
isShowAvatar={this.props.isShowAvatar}
|
||||||
renameWiki={this.props.renameWiki}
|
renameWiki={this.props.renameWiki}
|
||||||
|
@@ -2,14 +2,17 @@ import React, { Component } from 'react';
|
|||||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { siteRoot, gettext } from '../../utils/constants';
|
import { siteRoot, gettext, username } from '../../utils/constants';
|
||||||
import ModalPortal from '../modal-portal';
|
import ModalPortal from '../modal-portal';
|
||||||
import DeleteWikiDialog from '../dialog/delete-wiki-dialog';
|
import DeleteWikiDialog from '../dialog/delete-wiki-dialog';
|
||||||
import RenameWikiDialog from '../dialog/rename-wiki-dialog';
|
import RenameWikiDialog from '../dialog/rename-wiki-dialog';
|
||||||
|
import ShareWikiDialog from '../dialog/share-wiki-dialog';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
wiki: PropTypes.object.isRequired,
|
wiki: PropTypes.object.isRequired,
|
||||||
|
group: PropTypes.object,
|
||||||
deleteWiki: PropTypes.func.isRequired,
|
deleteWiki: PropTypes.func.isRequired,
|
||||||
|
unshareGroupWiki: PropTypes.func.isRequired,
|
||||||
renameWiki: PropTypes.func.isRequired,
|
renameWiki: PropTypes.func.isRequired,
|
||||||
isDepartment: PropTypes.bool.isRequired,
|
isDepartment: PropTypes.bool.isRequired,
|
||||||
isShowAvatar: PropTypes.bool.isRequired,
|
isShowAvatar: PropTypes.bool.isRequired,
|
||||||
@@ -22,6 +25,7 @@ class WikiCardItem extends Component {
|
|||||||
isShowDeleteDialog: false,
|
isShowDeleteDialog: false,
|
||||||
isShowRenameDialog: false,
|
isShowRenameDialog: false,
|
||||||
isItemMenuShow: false,
|
isItemMenuShow: false,
|
||||||
|
isShowShareDialog: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +35,12 @@ class WikiCardItem extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onShareToggle = (e) => {
|
||||||
|
this.setState({
|
||||||
|
isShowShareDialog: !this.state.isShowShareDialog,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onDeleteToggle = (e) => {
|
onDeleteToggle = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -52,6 +62,14 @@ class WikiCardItem extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onItemUnshare = () => {
|
||||||
|
let wiki = this.props.wiki;
|
||||||
|
this.props.unshareGroupWiki(wiki, this.props.group.group_id);
|
||||||
|
this.setState({
|
||||||
|
isShowDeleteDialog: !this.state.isShowDeleteDialog,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
renameWiki = (newName) => {
|
renameWiki = (newName) => {
|
||||||
if (this.props.wiki.name !== newName) {
|
if (this.props.wiki.name !== newName) {
|
||||||
this.props.renameWiki(this.props.wiki, newName);
|
this.props.renameWiki(this.props.wiki, newName);
|
||||||
@@ -94,10 +112,49 @@ class WikiCardItem extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { wiki, isDepartment, isShowAvatar } = this.props;
|
const { wiki, isDepartment, isShowAvatar } = this.props;
|
||||||
|
let isAdmin = false;
|
||||||
|
if (wiki.admins) {
|
||||||
|
isAdmin = wiki.admins.includes(username);
|
||||||
|
}
|
||||||
|
let isGroupOwner = false;
|
||||||
|
if (this.props.group) {
|
||||||
|
isGroupOwner = wiki.owner.split('@')[0] === this.props.group.group_id.toString();
|
||||||
|
}
|
||||||
|
let isWikiOwner = username === wiki.owner;
|
||||||
let isOldVersion = wiki.version !== 'v2';
|
let isOldVersion = wiki.version !== 'v2';
|
||||||
let publishedUrl = `${siteRoot}published/${encodeURIComponent(wiki.slug)}/`;
|
let publishedUrl = `${siteRoot}published/${encodeURIComponent(wiki.slug)}/`;
|
||||||
let editUrl = `${siteRoot}wikis/${wiki.id}/`;
|
let editUrl = `${siteRoot}wikis/${wiki.id}/`;
|
||||||
let wikiName = isOldVersion ? `${wiki.name} (old version)` : wiki.name;
|
let wikiName = isOldVersion ? `${wiki.name} (old version)` : wiki.name;
|
||||||
|
let showRename = false;
|
||||||
|
let showShare = false;
|
||||||
|
let showDelete = false;
|
||||||
|
let showLeaveShare = false;
|
||||||
|
let showDropdownMenu = false;
|
||||||
|
|
||||||
|
if (isDepartment) {
|
||||||
|
if (isAdmin) {
|
||||||
|
if (isGroupOwner) {
|
||||||
|
showDelete = true;
|
||||||
|
showShare = true;
|
||||||
|
showRename = true;
|
||||||
|
} else {
|
||||||
|
showLeaveShare = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isAdmin || isWikiOwner) {
|
||||||
|
showShare = true;
|
||||||
|
showDelete = true;
|
||||||
|
showRename = true;
|
||||||
|
} else {
|
||||||
|
showLeaveShare = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOldVersion || showRename || showShare || showDelete || showLeaveShare) {
|
||||||
|
showDropdownMenu = true;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -106,6 +163,7 @@ class WikiCardItem extends Component {
|
|||||||
>
|
>
|
||||||
<div className="wiki-card-item-top">
|
<div className="wiki-card-item-top">
|
||||||
<span className="sf3-font-wiki sf3-font" aria-hidden="true"></span>
|
<span className="sf3-font-wiki sf3-font" aria-hidden="true"></span>
|
||||||
|
{showDropdownMenu &&
|
||||||
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleDropDownMenu} onClick={this.onClickDropdown}>
|
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleDropDownMenu} onClick={this.onClickDropdown}>
|
||||||
<DropdownToggle
|
<DropdownToggle
|
||||||
tag="i"
|
tag="i"
|
||||||
@@ -120,14 +178,23 @@ class WikiCardItem extends Component {
|
|||||||
style={{ 'minWidth': '0' }}
|
style={{ 'minWidth': '0' }}
|
||||||
/>
|
/>
|
||||||
<DropdownMenu right={true} className="dtable-dropdown-menu">
|
<DropdownMenu right={true} className="dtable-dropdown-menu">
|
||||||
<DropdownItem onClick={this.onRenameToggle}>{gettext('Rename')}</DropdownItem>
|
{showRename &&
|
||||||
{isOldVersion ?
|
<DropdownItem onClick={this.onRenameToggle}>{gettext('Rename')}</DropdownItem>}
|
||||||
|
{showShare &&
|
||||||
|
<DropdownItem onClick={this.onShareToggle}>{gettext('Share')}</DropdownItem>
|
||||||
|
}
|
||||||
|
{isOldVersion &&
|
||||||
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Unpublish')}</DropdownItem>
|
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Unpublish')}</DropdownItem>
|
||||||
:
|
}
|
||||||
|
{showDelete &&
|
||||||
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Delete')}</DropdownItem>
|
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Delete')}</DropdownItem>
|
||||||
}
|
}
|
||||||
|
{showLeaveShare &&
|
||||||
|
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Leave')}</DropdownItem>
|
||||||
|
}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className="wiki-item-name text-truncate" title={wikiName} aria-label={wikiName}>{wikiName}</div>
|
<div className="wiki-item-name text-truncate" title={wikiName} aria-label={wikiName}>{wikiName}</div>
|
||||||
<div className="wiki-item-owner">
|
<div className="wiki-item-owner">
|
||||||
@@ -137,22 +204,41 @@ class WikiCardItem extends Component {
|
|||||||
</div>
|
</div>
|
||||||
{this.state.isShowDeleteDialog &&
|
{this.state.isShowDeleteDialog &&
|
||||||
<ModalPortal>
|
<ModalPortal>
|
||||||
{isOldVersion ?
|
{isOldVersion &&
|
||||||
<DeleteWikiDialog
|
<DeleteWikiDialog
|
||||||
toggleCancel={this.onDeleteCancel}
|
toggleCancel={this.onDeleteCancel}
|
||||||
handleSubmit={this.deleteWiki}
|
handleSubmit={this.deleteWiki}
|
||||||
title={gettext('Unpublish Wiki')}
|
title={gettext('Unpublish Wiki')}
|
||||||
content={<p>{gettext('Are you sure you want to unpublish Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
content={<p>{gettext('Are you sure you want to unpublish Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
||||||
footer={gettext('Unpublish')}
|
footer={gettext('Unpublish')}
|
||||||
/>
|
/>}
|
||||||
:
|
{(isDepartment && isGroupOwner) ?
|
||||||
<DeleteWikiDialog
|
<DeleteWikiDialog
|
||||||
toggleCancel={this.onDeleteCancel}
|
toggleCancel={this.onDeleteCancel}
|
||||||
handleSubmit={this.deleteWiki}
|
handleSubmit={this.deleteWiki}
|
||||||
title={gettext('Delete Wiki')}
|
title={gettext('Delete Wiki')}
|
||||||
content={<p>{gettext('Are you sure you want to delete Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
content={<p>{gettext('Are you sure you want to delete Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
||||||
footer={gettext('Delete')}
|
footer={gettext('Delete')}
|
||||||
|
/> : isDepartment ? <DeleteWikiDialog
|
||||||
|
toggleCancel={this.onDeleteCancel}
|
||||||
|
handleSubmit={this.onItemUnshare}
|
||||||
|
title={gettext('Leave Share Wiki')}
|
||||||
|
content={<p>{gettext('Are you sure you want to leave share Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
||||||
|
footer={gettext('Leave')}
|
||||||
|
/> : (isWikiOwner ? <DeleteWikiDialog
|
||||||
|
toggleCancel={this.onDeleteCancel}
|
||||||
|
handleSubmit={this.deleteWiki}
|
||||||
|
title={gettext('Delete Wiki')}
|
||||||
|
content={<p>{gettext('Are you sure you want to delete Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
||||||
|
footer={gettext('Delete')}
|
||||||
|
/> : <DeleteWikiDialog
|
||||||
|
toggleCancel={this.onDeleteCancel}
|
||||||
|
handleSubmit={this.deleteWiki}
|
||||||
|
title={gettext('Leave Share Wiki')}
|
||||||
|
content={<p>{gettext('Are you sure you want to leave share Wiki')}{' '}<b>{wiki.name}</b> ?</p>}
|
||||||
|
footer={gettext('Leave')}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
}
|
}
|
||||||
@@ -165,6 +251,19 @@ class WikiCardItem extends Component {
|
|||||||
/>
|
/>
|
||||||
</ModalPortal>
|
</ModalPortal>
|
||||||
}
|
}
|
||||||
|
{this.state.isShowShareDialog &&
|
||||||
|
<ModalPortal>
|
||||||
|
<ShareWikiDialog
|
||||||
|
itemType={'library'}
|
||||||
|
itemName={wiki.name}
|
||||||
|
itemPath={'/'}
|
||||||
|
repoID={wiki.repo_id}
|
||||||
|
repoEncrypted={ false }
|
||||||
|
enableDirPrivateShare={true}
|
||||||
|
toggleDialog={this.onShareToggle}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,8 @@ const propTypes = {
|
|||||||
data: PropTypes.object.isRequired,
|
data: PropTypes.object.isRequired,
|
||||||
deleteWiki: PropTypes.func.isRequired,
|
deleteWiki: PropTypes.func.isRequired,
|
||||||
renameWiki: PropTypes.func.isRequired,
|
renameWiki: PropTypes.func.isRequired,
|
||||||
|
leaveSharedWiki: PropTypes.func.isRequired,
|
||||||
|
unshareGroupWiki: PropTypes.func.isRequired,
|
||||||
toggelAddWikiDialog: PropTypes.func,
|
toggelAddWikiDialog: PropTypes.func,
|
||||||
sidePanelRate: PropTypes.number,
|
sidePanelRate: PropTypes.number,
|
||||||
isSidePanelFolded: PropTypes.bool,
|
isSidePanelFolded: PropTypes.bool,
|
||||||
@@ -30,7 +32,7 @@ class WikiCardView extends Component {
|
|||||||
if (!canPublishRepo || !isPro) return;
|
if (!canPublishRepo || !isPro) return;
|
||||||
let departmentMap = {};
|
let departmentMap = {};
|
||||||
wikiAPI.listWikiDepartments().then(res => {
|
wikiAPI.listWikiDepartments().then(res => {
|
||||||
res.data.forEach(item => departmentMap[item.email] = true);
|
res.data.forEach(item => departmentMap[item.id] = true);
|
||||||
this.setState({ departmentMap });
|
this.setState({ departmentMap });
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
@@ -41,12 +43,15 @@ class WikiCardView extends Component {
|
|||||||
classifyWikis = (wikis) => {
|
classifyWikis = (wikis) => {
|
||||||
let v1Wikis = [];
|
let v1Wikis = [];
|
||||||
let myWikis = [];
|
let myWikis = [];
|
||||||
|
let sharedWikis = [];
|
||||||
let department2WikisMap = {};
|
let department2WikisMap = {};
|
||||||
for (let i = 0; i < wikis.length; i++) {
|
for (let i = 0; i < wikis.length; i++) {
|
||||||
if (wikis[i].version === 'v1') {
|
if (wikis[i].version === 'v1') {
|
||||||
v1Wikis.push(wikis[i]);
|
v1Wikis.push(wikis[i]);
|
||||||
} else if (wikis[i].owner === username) {
|
} else if (wikis[i].owner === username) {
|
||||||
myWikis.push(wikis[i]);
|
myWikis.push(wikis[i]);
|
||||||
|
} else if (wikis[i].type === 'shared') {
|
||||||
|
sharedWikis.push(wikis[i]);
|
||||||
} else {
|
} else {
|
||||||
if (!department2WikisMap[wikis[i].owner]) {
|
if (!department2WikisMap[wikis[i].owner]) {
|
||||||
department2WikisMap[wikis[i].owner] = [];
|
department2WikisMap[wikis[i].owner] = [];
|
||||||
@@ -54,11 +59,12 @@ class WikiCardView extends Component {
|
|||||||
department2WikisMap[wikis[i].owner].push(wikis[i]);
|
department2WikisMap[wikis[i].owner].push(wikis[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { department2WikisMap, myWikis, v1Wikis };
|
return { department2WikisMap, myWikis, v1Wikis, sharedWikis };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { loading, errorMsg, wikis } = this.props.data;
|
let { loading, errorMsg, wikis, groupWikis } = this.props.data;
|
||||||
const { toggelAddWikiDialog, sidePanelRate, isSidePanelFolded } = this.props;
|
const { toggelAddWikiDialog, sidePanelRate, isSidePanelFolded } = this.props;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@@ -67,13 +73,14 @@ class WikiCardView extends Component {
|
|||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
return <p className="error text-center">{errorMsg}</p>;
|
return <p className="error text-center">{errorMsg}</p>;
|
||||||
}
|
}
|
||||||
const { v1Wikis, myWikis, department2WikisMap } = this.classifyWikis(wikis);
|
const { v1Wikis, myWikis, sharedWikis } = this.classifyWikis(wikis);
|
||||||
let wikiCardGroups = [];
|
let wikiCardGroups = [];
|
||||||
wikiCardGroups.push(
|
wikiCardGroups.push(
|
||||||
<WikiCardGroup
|
<WikiCardGroup
|
||||||
key='my-Wikis'
|
key='my-Wikis'
|
||||||
deleteWiki={this.props.deleteWiki}
|
deleteWiki={this.props.deleteWiki}
|
||||||
renameWiki={this.props.renameWiki}
|
renameWiki={this.props.renameWiki}
|
||||||
|
unshareGroupWiki={this.props.unshareGroupWiki}
|
||||||
sidePanelRate={sidePanelRate}
|
sidePanelRate={sidePanelRate}
|
||||||
isSidePanelFolded={isSidePanelFolded}
|
isSidePanelFolded={isSidePanelFolded}
|
||||||
wikis={myWikis}
|
wikis={myWikis}
|
||||||
@@ -83,19 +90,37 @@ class WikiCardView extends Component {
|
|||||||
toggelAddWikiDialog={canPublishRepo ? toggelAddWikiDialog.bind(this, null) : null}
|
toggelAddWikiDialog={canPublishRepo ? toggelAddWikiDialog.bind(this, null) : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
for (let deptEmail in department2WikisMap) {
|
|
||||||
wikiCardGroups.push(
|
wikiCardGroups.push(
|
||||||
<WikiCardGroup
|
<WikiCardGroup
|
||||||
key={'department-Wikis-' + deptEmail}
|
key='shared-Wikis'
|
||||||
|
deleteWiki={this.props.leaveSharedWiki}
|
||||||
|
renameWiki={this.props.renameWiki}
|
||||||
|
unshareGroupWiki={this.props.unshareGroupWiki}
|
||||||
|
wikis={sharedWikis}
|
||||||
|
title={gettext('Shared with me')}
|
||||||
|
isDepartment={false}
|
||||||
|
isShowAvatar={false}
|
||||||
|
sidePanelRate={sidePanelRate}
|
||||||
|
isSidePanelFolded={isSidePanelFolded}
|
||||||
|
toggelAddWikiDialog={null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
for (let deptID in groupWikis) {
|
||||||
|
groupWikis[deptID].wiki_info.length !== 0 &&
|
||||||
|
wikiCardGroups.push(
|
||||||
|
<WikiCardGroup
|
||||||
|
key={'group-Wikis-' + deptID}
|
||||||
deleteWiki={this.props.deleteWiki}
|
deleteWiki={this.props.deleteWiki}
|
||||||
|
unshareGroupWiki={this.props.unshareGroupWiki}
|
||||||
renameWiki={this.props.renameWiki}
|
renameWiki={this.props.renameWiki}
|
||||||
sidePanelRate={sidePanelRate}
|
sidePanelRate={sidePanelRate}
|
||||||
isSidePanelFolded={isSidePanelFolded}
|
isSidePanelFolded={isSidePanelFolded}
|
||||||
wikis={department2WikisMap[deptEmail]}
|
group={groupWikis[deptID]}
|
||||||
title={department2WikisMap[deptEmail][0].owner_nickname}
|
wikis={groupWikis[deptID].wiki_info}
|
||||||
|
title={groupWikis[deptID].group_name}
|
||||||
isDepartment={true}
|
isDepartment={true}
|
||||||
isShowAvatar={false}
|
isShowAvatar={false}
|
||||||
toggelAddWikiDialog={(canPublishRepo && this.state.departmentMap[deptEmail]) ? toggelAddWikiDialog.bind(this, deptEmail) : null}
|
toggelAddWikiDialog={(canPublishRepo && this.state.departmentMap[groupWikis[deptID]['group_id']]) ? toggelAddWikiDialog.bind(this, deptID) : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -104,6 +129,7 @@ class WikiCardView extends Component {
|
|||||||
key='old-Wikis'
|
key='old-Wikis'
|
||||||
deleteWiki={this.props.deleteWiki}
|
deleteWiki={this.props.deleteWiki}
|
||||||
renameWiki={this.props.renameWiki}
|
renameWiki={this.props.renameWiki}
|
||||||
|
unshareGroupWiki={this.props.unshareGroupWiki}
|
||||||
isSidePanelFolded={isSidePanelFolded}
|
isSidePanelFolded={isSidePanelFolded}
|
||||||
sidePanelRate={sidePanelRate}
|
sidePanelRate={sidePanelRate}
|
||||||
wikis={v1Wikis}
|
wikis={v1Wikis}
|
||||||
|
@@ -9,6 +9,8 @@ import EmptyTip from '../../components/empty-tip';
|
|||||||
import AddWikiDialog from '../../components/dialog/add-wiki-dialog';
|
import AddWikiDialog from '../../components/dialog/add-wiki-dialog';
|
||||||
import wikiAPI from '../../utils/wiki-api';
|
import wikiAPI from '../../utils/wiki-api';
|
||||||
import WikiCardView from '../../components/wiki-card-view/wiki-card-view';
|
import WikiCardView from '../../components/wiki-card-view/wiki-card-view';
|
||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
sidePanelRate: PropTypes.number,
|
sidePanelRate: PropTypes.number,
|
||||||
@@ -23,6 +25,7 @@ class Wikis extends Component {
|
|||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
currentDeptEmail: '',
|
currentDeptEmail: '',
|
||||||
wikis: [],
|
wikis: [],
|
||||||
|
groupWikis: [],
|
||||||
isShowAddWikiMenu: false,
|
isShowAddWikiMenu: false,
|
||||||
isShowAddDialog: false,
|
isShowAddDialog: false,
|
||||||
isDropdownMenuShown: false,
|
isDropdownMenuShown: false,
|
||||||
@@ -35,6 +38,7 @@ class Wikis extends Component {
|
|||||||
|
|
||||||
getWikis = () => {
|
getWikis = () => {
|
||||||
let wikis = [];
|
let wikis = [];
|
||||||
|
let groupWikis = [];
|
||||||
wikiAPI.listWikis().then(res => {
|
wikiAPI.listWikis().then(res => {
|
||||||
wikis = wikis.concat(res.data.data);
|
wikis = wikis.concat(res.data.data);
|
||||||
wikis.map(wiki => {
|
wikis.map(wiki => {
|
||||||
@@ -42,12 +46,20 @@ class Wikis extends Component {
|
|||||||
});
|
});
|
||||||
wikiAPI.listWikis2().then(res => {
|
wikiAPI.listWikis2().then(res => {
|
||||||
let wikis2 = res.data.wikis;
|
let wikis2 = res.data.wikis;
|
||||||
|
groupWikis = res.data.group_wikis;
|
||||||
|
groupWikis.forEach(group => {
|
||||||
|
group.wiki_info.forEach(wiki => {
|
||||||
|
wiki.version = 'v2';
|
||||||
|
wiki.admins = group.group_admins;
|
||||||
|
});
|
||||||
|
});
|
||||||
wikis2.map(wiki => {
|
wikis2.map(wiki => {
|
||||||
return wiki['version'] = 'v2';
|
return wiki['version'] = 'v2';
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
wikis: wikis.concat(wikis2)
|
wikis: wikis.concat(wikis2),
|
||||||
|
groupWikis: groupWikis
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -89,12 +101,24 @@ class Wikis extends Component {
|
|||||||
addWiki = (wikiName, currentDeptID) => {
|
addWiki = (wikiName, currentDeptID) => {
|
||||||
wikiAPI.addWiki2(wikiName, currentDeptID).then((res) => {
|
wikiAPI.addWiki2(wikiName, currentDeptID).then((res) => {
|
||||||
let wikis = this.state.wikis.slice(0);
|
let wikis = this.state.wikis.slice(0);
|
||||||
|
let groupWikis = this.state.groupWikis;
|
||||||
let new_wiki = res.data;
|
let new_wiki = res.data;
|
||||||
new_wiki['version'] = 'v2';
|
new_wiki['version'] = 'v2';
|
||||||
|
if (currentDeptID) {
|
||||||
|
groupWikis.filter(group => {
|
||||||
|
if (group.group_id === currentDeptID) {
|
||||||
|
group.wiki_info.push(new_wiki);
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
wikis.push(new_wiki);
|
wikis.push(new_wiki);
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
wikis,
|
wikis,
|
||||||
currentDeptEmail: '',
|
currentDeptEmail: '',
|
||||||
|
groupWikis,
|
||||||
|
currentDeptID: '',
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
@@ -110,7 +134,14 @@ class Wikis extends Component {
|
|||||||
let wikis = this.state.wikis.filter(item => {
|
let wikis = this.state.wikis.filter(item => {
|
||||||
return item.id !== wiki.id;
|
return item.id !== wiki.id;
|
||||||
});
|
});
|
||||||
this.setState({ wikis: wikis });
|
let groupWikis = this.state.groupWikis.filter(group => {
|
||||||
|
group.wiki_info = group.wiki_info.filter(item => item.name !== wiki.name);
|
||||||
|
return group;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
wikis: wikis,
|
||||||
|
groupWikis: groupWikis,
|
||||||
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let errorMsg = error.response.data.error_msg;
|
let errorMsg = error.response.data.error_msg;
|
||||||
@@ -122,7 +153,14 @@ class Wikis extends Component {
|
|||||||
let wikis = this.state.wikis.filter(item => {
|
let wikis = this.state.wikis.filter(item => {
|
||||||
return item.id !== wiki.id;
|
return item.id !== wiki.id;
|
||||||
});
|
});
|
||||||
this.setState({ wikis: wikis });
|
let groupWikis = this.state.groupWikis.filter(group => {
|
||||||
|
group.wiki_info = group.wiki_info.filter(item => item.name !== wiki.name);
|
||||||
|
return group;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
wikis: wikis,
|
||||||
|
groupWikis: groupWikis,
|
||||||
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let errorMsg = error.response.data.error_msg;
|
let errorMsg = error.response.data.error_msg;
|
||||||
@@ -132,6 +170,57 @@ class Wikis extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
leaveSharedWiki = (wiki) => {
|
||||||
|
if (!wiki.owner.includes('@seafile_group')) {
|
||||||
|
let options = {
|
||||||
|
'share_type': 'personal',
|
||||||
|
'from': wiki.owner
|
||||||
|
};
|
||||||
|
seafileAPI.leaveShareRepo(wiki.repo_id, options).then(res => {
|
||||||
|
let wikis = this.state.wikis.filter(item => {
|
||||||
|
return item.name !== wiki.name;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
wikis: wikis,
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
let errorMsg = Utils.getErrorMsg(error, true);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
seafileAPI.leaveShareGroupOwnedRepo(wiki.repo_id).then(res => {
|
||||||
|
let wikis = this.state.wikis.filter(item => {
|
||||||
|
return item.name !== wiki.name;
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
wikis: wikis,
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
let errorMsg = Utils.getErrorMsg(error, true);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
unshareGroupWiki = (wiki, groupId) => {
|
||||||
|
seafileAPI.unshareRepoToGroup(wiki.repo_id, groupId).then(() => {
|
||||||
|
let groupWikis = this.state.groupWikis.map(group => {
|
||||||
|
if (group.group_id === groupId) {
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
wiki_info: group.wiki_info.filter(item => item.name !== wiki.name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
});
|
||||||
|
this.setState({ groupWikis: groupWikis });
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
renameWiki = (wiki, newName) => {
|
renameWiki = (wiki, newName) => {
|
||||||
if (wiki.version === 'v1') {
|
if (wiki.version === 'v1') {
|
||||||
wikiAPI.renameWiki(wiki.id, newName).then(() => {
|
wikiAPI.renameWiki(wiki.id, newName).then(() => {
|
||||||
@@ -150,13 +239,7 @@ class Wikis extends Component {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
wikiAPI.renameWiki2(wiki.id, newName).then(() => {
|
wikiAPI.renameWiki2(wiki.id, newName).then(() => {
|
||||||
let wikis = this.state.wikis.map(item => {
|
this.getWikis();
|
||||||
if (item.id === wiki.id && item.version === 'v2') {
|
|
||||||
item.name = newName;
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
this.setState({ wikis: wikis });
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let errorMsg = error.response.data.error_msg;
|
let errorMsg = error.response.data.error_msg;
|
||||||
@@ -208,11 +291,13 @@ class Wikis extends Component {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(this.state.loading || this.state.wikis.length !== 0) &&
|
{(this.state.loading || this.state.wikis.length !== 0 || this.state.groupWikis.length !== 0) &&
|
||||||
<div className="cur-view-content pb-4">
|
<div className="cur-view-content pb-4">
|
||||||
<WikiCardView
|
<WikiCardView
|
||||||
data={this.state}
|
data={this.state}
|
||||||
deleteWiki={this.deleteWiki}
|
deleteWiki={this.deleteWiki}
|
||||||
|
leaveSharedWiki={this.leaveSharedWiki}
|
||||||
|
unshareGroupWiki={this.unshareGroupWiki}
|
||||||
renameWiki={this.renameWiki}
|
renameWiki={this.renameWiki}
|
||||||
toggelAddWikiDialog={this.toggelAddWikiDialog}
|
toggelAddWikiDialog={this.toggelAddWikiDialog}
|
||||||
sidePanelRate={this.props.sidePanelRate}
|
sidePanelRate={this.props.sidePanelRate}
|
||||||
@@ -220,7 +305,7 @@ class Wikis extends Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{(!this.state.loading && this.state.wikis.length === 0) &&
|
{(!this.state.loading && this.state.wikis.length === 0 && this.state.groupWikis.length === 0) &&
|
||||||
<div className="cur-view-content">
|
<div className="cur-view-content">
|
||||||
<EmptyTip>
|
<EmptyTip>
|
||||||
<h2>{gettext('No Wikis')}</h2>
|
<h2>{gettext('No Wikis')}</h2>
|
||||||
|
@@ -373,7 +373,7 @@ class RepoView(APIView):
|
|||||||
result = {
|
result = {
|
||||||
"repo_id": repo.id,
|
"repo_id": repo.id,
|
||||||
"repo_name": repo.name,
|
"repo_name": repo.name,
|
||||||
|
"repo_type": repo.repo_type,
|
||||||
"owner_email": repo_owner,
|
"owner_email": repo_owner,
|
||||||
"owner_name": email2nickname(repo_owner),
|
"owner_name": email2nickname(repo_owner),
|
||||||
"owner_contact_email": email2contact_email(repo_owner),
|
"owner_contact_email": email2contact_email(repo_owner),
|
||||||
|
@@ -42,6 +42,7 @@ from seahub.settings import SEADOC_SERVER_URL, ENABLE_STORAGE_CLASSES, STORAGE_C
|
|||||||
ENCRYPTED_LIBRARY_VERSION
|
ENCRYPTED_LIBRARY_VERSION
|
||||||
from seahub.seadoc.sdoc_server_api import SdocServerAPI
|
from seahub.seadoc.sdoc_server_api import SdocServerAPI
|
||||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr, datetime_to_isoformat_timestr
|
from seahub.utils.timeutils import timestamp_to_isoformat_timestr, datetime_to_isoformat_timestr
|
||||||
|
from seahub.utils.ccnet_db import CcnetDB
|
||||||
from seahub.tags.models import FileUUIDMap
|
from seahub.tags.models import FileUUIDMap
|
||||||
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocCommentReply
|
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocCommentReply
|
||||||
from seahub.base.models import FileComment
|
from seahub.base.models import FileComment
|
||||||
@@ -49,12 +50,38 @@ from seahub.api2.views import HTTP_447_TOO_MANY_FILES_IN_LIBRARY
|
|||||||
from seahub.group.utils import group_id_to_name, is_group_admin
|
from seahub.group.utils import group_id_to_name, is_group_admin
|
||||||
from seahub.utils.rpc import SeafileAPI
|
from seahub.utils.rpc import SeafileAPI
|
||||||
from seahub.constants import PERMISSION_READ_WRITE
|
from seahub.constants import PERMISSION_READ_WRITE
|
||||||
|
from seaserv import ccnet_api
|
||||||
|
|
||||||
HTTP_520_OPERATION_FAILED = 520
|
HTTP_520_OPERATION_FAILED = 520
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def _merge_wiki_in_groups(group_wikis):
|
||||||
|
|
||||||
|
group_ids = [gw.group_id for gw in group_wikis]
|
||||||
|
group_id_wikis_map = {key: [] for key in group_ids}
|
||||||
|
for gw in group_wikis:
|
||||||
|
wiki = Wiki(gw)
|
||||||
|
wiki_info = wiki.to_dict()
|
||||||
|
owner = gw.owner
|
||||||
|
if ('@seafile_group') in owner:
|
||||||
|
group_id = int(owner.split('@')[0])
|
||||||
|
owner_nickname = group_id_to_name(group_id)
|
||||||
|
else:
|
||||||
|
owner_nickname = email2nickname(owner)
|
||||||
|
repo_info = {
|
||||||
|
"type": "group",
|
||||||
|
"permission": gw.permission,
|
||||||
|
"owner_nickname": owner_nickname
|
||||||
|
}
|
||||||
|
wiki_info.update(repo_info)
|
||||||
|
group_id = gw.group_id
|
||||||
|
group_id_wikis_map[group_id].append(wiki_info)
|
||||||
|
return group_id_wikis_map
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Wikis2View(APIView):
|
class Wikis2View(APIView):
|
||||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
@@ -64,63 +91,82 @@ class Wikis2View(APIView):
|
|||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
"""List all wikis.
|
"""List all wikis.
|
||||||
"""
|
"""
|
||||||
# parse request params
|
|
||||||
filter_by = {
|
|
||||||
'mine': False,
|
|
||||||
'shared': False,
|
|
||||||
'group': False,
|
|
||||||
'org': False,
|
|
||||||
}
|
|
||||||
|
|
||||||
rtype = request.GET.get('type', "")
|
|
||||||
if not rtype:
|
|
||||||
# set all to True, no filter applied
|
|
||||||
filter_by = filter_by.fromkeys(iter(filter_by.keys()), True)
|
|
||||||
|
|
||||||
for f in rtype.split(','):
|
|
||||||
f = f.strip()
|
|
||||||
filter_by[f] = True
|
|
||||||
|
|
||||||
username = request.user.username
|
username = request.user.username
|
||||||
org_id = request.user.org.org_id if is_org_context(request) else None
|
org_id = request.user.org.org_id if is_org_context(request) else None
|
||||||
(owned, shared, groups, public) = get_user_repos(username, org_id)
|
(owned, shared, groups, public) = get_user_repos(username, org_id)
|
||||||
|
|
||||||
wikis = []
|
# list user groups
|
||||||
username = request.user.username
|
if is_org_context(request):
|
||||||
if filter_by['mine']:
|
org_id = request.user.org.org_id
|
||||||
|
user_groups = ccnet_api.get_org_groups_by_user(org_id, username, return_ancestors=True)
|
||||||
|
else:
|
||||||
|
user_groups = ccnet_api.get_groups(username, return_ancestors=True)
|
||||||
|
|
||||||
owned_wikis = [r for r in owned if is_wiki_repo(r)]
|
owned_wikis = [r for r in owned if is_wiki_repo(r)]
|
||||||
|
wiki_list = []
|
||||||
for r in owned_wikis:
|
for r in owned_wikis:
|
||||||
r.owner = username
|
r.owner = username
|
||||||
r.permission = 'rw'
|
r.permission = 'rw'
|
||||||
wikis.append(r)
|
wiki = Wiki(r)
|
||||||
|
wiki_info = wiki.to_dict()
|
||||||
|
repo_info = {
|
||||||
|
"type": "mine",
|
||||||
|
"permission": 'rw',
|
||||||
|
"owner_nickname": email2nickname(username)
|
||||||
|
}
|
||||||
|
wiki_info.update(repo_info)
|
||||||
|
wiki_list.append(wiki_info)
|
||||||
|
|
||||||
if filter_by['shared']:
|
|
||||||
shared_wikis = [r for r in shared if is_wiki_repo(r)]
|
shared_wikis = [r for r in shared if is_wiki_repo(r)]
|
||||||
for r in shared_wikis:
|
for r in shared_wikis:
|
||||||
r.owner = r.user
|
owner = r.user
|
||||||
wikis.append(r)
|
r.owner = owner
|
||||||
|
wiki = Wiki(r)
|
||||||
|
if ('@seafile_group') in r.owner:
|
||||||
|
group_id = int(owner.split('@')[0])
|
||||||
|
owner_nickname = group_id_to_name(group_id)
|
||||||
|
else:
|
||||||
|
owner_nickname = email2nickname(owner)
|
||||||
|
wiki_info = wiki.to_dict()
|
||||||
|
repo_info = {
|
||||||
|
"type": "shared",
|
||||||
|
"permission": r.permission,
|
||||||
|
"owner_nickname": owner_nickname
|
||||||
|
}
|
||||||
|
wiki_info.update(repo_info)
|
||||||
|
wiki_list.append(wiki_info)
|
||||||
|
|
||||||
if filter_by['group']:
|
|
||||||
group_wikis = [r for r in groups if is_wiki_repo(r)]
|
group_wikis = [r for r in groups if is_wiki_repo(r)]
|
||||||
|
group_id_in_wikis = list(set([r.group_id for r in group_wikis]))
|
||||||
|
try:
|
||||||
|
group_ids_admins_map = {}
|
||||||
|
if group_id_in_wikis:
|
||||||
|
ccnet_db = CcnetDB()
|
||||||
|
group_ids_admins_map = ccnet_db.get_group_ids_admins_map(group_id_in_wikis)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
|
||||||
|
user_wiki_groups = [ug for ug in user_groups if ug.id in group_id_in_wikis]
|
||||||
for r in group_wikis:
|
for r in group_wikis:
|
||||||
r.owner = r.user
|
r.owner = r.user
|
||||||
wikis.append(r)
|
|
||||||
|
|
||||||
wikis = list(set(wikis))
|
group_wiki_list = []
|
||||||
wiki_list = []
|
group_id_wikis_map = _merge_wiki_in_groups(group_wikis)
|
||||||
for w in wikis:
|
for group_obj in user_wiki_groups:
|
||||||
wiki = Wiki(w)
|
group_wiki = {
|
||||||
wiki_info = wiki.to_dict()
|
'group_name': group_obj.group_name,
|
||||||
if is_group_wiki(wiki):
|
'group_id': group_obj.id,
|
||||||
group_id = int(wiki.owner.split('@')[0])
|
'group_admins': group_ids_admins_map.get(group_obj.id) or [],
|
||||||
wiki_info['owner_nickname'] = group_id_to_name(group_id)
|
"owner": group_obj.creator_name,
|
||||||
else:
|
'wiki_info': group_id_wikis_map[group_obj.id]
|
||||||
wiki_info['owner_nickname'] = email2nickname(wiki.owner)
|
}
|
||||||
wiki_list.append(wiki_info)
|
group_wiki_list.append(group_wiki)
|
||||||
|
|
||||||
wiki_list = sorted(wiki_list, key=lambda x: x.get('updated_at'), reverse=True)
|
wiki_list = sorted(wiki_list, key=lambda x: x.get('updated_at'), reverse=True)
|
||||||
|
|
||||||
return Response({'wikis': wiki_list})
|
return Response({'wikis': wiki_list, 'group_wikis': group_wiki_list})
|
||||||
|
|
||||||
def post(self, request, format=None):
|
def post(self, request, format=None):
|
||||||
"""Add a new wiki.
|
"""Add a new wiki.
|
||||||
|
@@ -162,3 +162,24 @@ class CcnetDB:
|
|||||||
users.append(users_obj)
|
users.append(users_obj)
|
||||||
|
|
||||||
return users, total_count
|
return users, total_count
|
||||||
|
|
||||||
|
|
||||||
|
def get_group_ids_admins_map(self, group_ids):
|
||||||
|
group_admins = {}
|
||||||
|
group_ids_str = ','.join(str(id) for id in group_ids)
|
||||||
|
sql = f"""
|
||||||
|
SELECT user_name, group_id
|
||||||
|
FROM
|
||||||
|
`{self.db_name}`.`GroupUser`
|
||||||
|
WHERE
|
||||||
|
group_id IN ({group_ids_str}) AND is_staff = 1
|
||||||
|
"""
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute(sql)
|
||||||
|
result = cursor.fetchall()
|
||||||
|
for user, group_id in result:
|
||||||
|
if group_id in group_admins:
|
||||||
|
group_admins[group_id].append(user)
|
||||||
|
else:
|
||||||
|
group_admins[group_id] = [user]
|
||||||
|
return group_admins
|
||||||
|
@@ -81,13 +81,8 @@ def check_wiki_admin_permission(wiki, username):
|
|||||||
|
|
||||||
|
|
||||||
def check_wiki_permission(wiki, username):
|
def check_wiki_permission(wiki, username):
|
||||||
if is_group_wiki(wiki):
|
permission = seafile_api.check_permission_by_path(wiki.repo_id, '/', username)
|
||||||
group_id = int(wiki.owner.split('@')[0])
|
return permission
|
||||||
return is_group_member(group_id, username)
|
|
||||||
else:
|
|
||||||
if username == wiki.owner:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_page_ids_in_folder(navigation, folder_id):
|
def get_page_ids_in_folder(navigation, folder_id):
|
||||||
|
@@ -2,26 +2,20 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import posixpath
|
import posixpath
|
||||||
import time
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from seaserv import seafile_api
|
from seaserv import seafile_api
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.http import Http404
|
||||||
from django.http import HttpResponseRedirect, Http404
|
from django.shortcuts import render
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
|
||||||
|
|
||||||
from seahub.share.models import FileShare
|
|
||||||
from seahub.wiki2.models import Wiki2 as Wiki
|
from seahub.wiki2.models import Wiki2 as Wiki
|
||||||
from seahub.views import check_folder_permission
|
from seahub.utils import get_file_type_and_ext, render_permission_error
|
||||||
from seahub.utils import get_file_type_and_ext, render_permission_error, is_pro_version
|
from seahub.utils.file_types import SEADOC
|
||||||
from seahub.utils.file_types import IMAGE, SEADOC
|
|
||||||
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token
|
|
||||||
from seahub.auth.decorators import login_required
|
from seahub.auth.decorators import login_required
|
||||||
from seahub.wiki2.utils import can_edit_wiki, check_wiki_permission, get_wiki_config
|
from seahub.wiki2.utils import check_wiki_permission, get_wiki_config
|
||||||
|
|
||||||
from seahub.utils.file_op import check_file_lock, ONLINE_OFFICE_LOCK_OWNER, if_locked_by_online_office
|
from seahub.utils.repo import get_repo_owner
|
||||||
from seahub.utils.repo import parse_repo_perm, get_repo_owner
|
|
||||||
from seahub.settings import SEADOC_SERVER_URL
|
from seahub.settings import SEADOC_SERVER_URL
|
||||||
|
|
||||||
# Get an instance of a logger
|
# Get an instance of a logger
|
||||||
@@ -56,6 +50,7 @@ def wiki_view(request, wiki_id):
|
|||||||
|
|
||||||
# perm check
|
# perm check
|
||||||
req_user = request.user.username
|
req_user = request.user.username
|
||||||
|
permission = check_wiki_permission(wiki, req_user)
|
||||||
if not check_wiki_permission(wiki, req_user):
|
if not check_wiki_permission(wiki, req_user):
|
||||||
return render_permission_error(request, 'Permission denied.')
|
return render_permission_error(request, 'Permission denied.')
|
||||||
|
|
||||||
@@ -79,5 +74,6 @@ def wiki_view(request, wiki_id):
|
|||||||
"repo_name": repo.name if repo else '',
|
"repo_name": repo.name if repo else '',
|
||||||
"modifier": latest_contributor,
|
"modifier": latest_contributor,
|
||||||
"modify_time": last_modified,
|
"modify_time": last_modified,
|
||||||
"seadoc_server_url": SEADOC_SERVER_URL
|
"seadoc_server_url": SEADOC_SERVER_URL,
|
||||||
|
"permission": permission
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user