mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-24 04:48:03 +00:00
Add wiki freeze (#7673)
* freeze wiki page * optimize * optimize * update * update * update * update * update * Update wiki2.py * update ui --------- 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:
@@ -40,6 +40,7 @@ class Wiki extends Component {
|
||||
permission: '',
|
||||
isConfigLoading: true,
|
||||
currentPageId: '',
|
||||
currentPageLocked: false,
|
||||
config: new WikiConfig({}),
|
||||
repoId: '',
|
||||
seadoc_access_token: '',
|
||||
@@ -154,6 +155,35 @@ class Wiki extends Component {
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
updatePageLock = (pageId, locked) => {
|
||||
wikiAPI.updateWiki2PageLock(wikiId, pageId, locked).then(res => {
|
||||
this.setState(prevState => {
|
||||
const updatedPages = prevState.config.pages.map(page => {
|
||||
if (page.id === pageId) {
|
||||
return {
|
||||
...page,
|
||||
locked: res.data.is_locked
|
||||
};
|
||||
}
|
||||
return page;
|
||||
});
|
||||
|
||||
return {
|
||||
currentPageLocked: res.data.is_locked,
|
||||
config: new WikiConfig({
|
||||
...prevState.config,
|
||||
pages: updatedPages
|
||||
})
|
||||
};
|
||||
});
|
||||
const currentPage = PageUtils.getPageById(this.state.config.pages, pageId);
|
||||
this.updateSdocPage(currentPage.id, currentPage.path);
|
||||
}).catch((error) => {
|
||||
let errorMsg = Utils.getErrorMsg(error);
|
||||
toaster.danger(errorMsg);
|
||||
});
|
||||
};
|
||||
|
||||
saveWikiConfig = (wikiConfig, isUpdateBySide = false) => {
|
||||
this.setState({
|
||||
config: new WikiConfig(wikiConfig),
|
||||
@@ -215,6 +245,7 @@ class Wiki extends Component {
|
||||
} else {
|
||||
getWikiPage = wikiAPI.getWiki2Page(wikiId, pageId);
|
||||
}
|
||||
|
||||
getWikiPage.then(res => {
|
||||
const { permission, seadoc_access_token, assets_url } = res.data;
|
||||
this.setState({
|
||||
@@ -281,6 +312,7 @@ class Wiki extends Component {
|
||||
this.setState({
|
||||
currentPageId: pageId,
|
||||
path: path,
|
||||
currentPageLocked: currentPage.locked,
|
||||
}, () => {
|
||||
callback && callback();
|
||||
eventBus.dispatch('update-wiki-current-page');
|
||||
@@ -408,6 +440,8 @@ class Wiki extends Component {
|
||||
seadoc_access_token={this.state.seadoc_access_token}
|
||||
assets_url={this.state.assets_url}
|
||||
onUpdatePage={this.onUpdatePage}
|
||||
currentPageLocked={this.state.currentPageLocked}
|
||||
updatePageLock={this.updatePageLock}
|
||||
setCurrentPage={this.setCurrentPage}
|
||||
isUpdateBySide={this.state.isUpdateBySide}
|
||||
style={mainPanelStyle}
|
||||
|
@@ -1,7 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||
import { SdocWikiEditor, DocInfo } from '@seafile/sdoc-editor';
|
||||
import { gettext, username, wikiPermission, wikiId, siteRoot } from '../../utils/constants';
|
||||
import { gettext, username, wikiPermission, wikiId, siteRoot, isPro } from '../../utils/constants';
|
||||
import TextTranslation from '../../utils/text-translation';
|
||||
import Switch from '../../components/switch';
|
||||
import Loading from '../../components/loading';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import Account from '../../components/common/account';
|
||||
@@ -9,6 +12,7 @@ import WikiTopNav from './top-nav';
|
||||
import { getCurrentPageConfig } from './utils';
|
||||
import RightHeader from './wiki-right-header';
|
||||
|
||||
|
||||
const propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
pathExist: PropTypes.bool.isRequired,
|
||||
@@ -22,6 +26,8 @@ const propTypes = {
|
||||
currentPageId: PropTypes.string,
|
||||
isUpdateBySide: PropTypes.bool,
|
||||
onUpdatePage: PropTypes.func,
|
||||
updatePageLock: PropTypes.func,
|
||||
currentPageLocked: PropTypes.bool,
|
||||
onAddWikiPage: PropTypes.func,
|
||||
style: PropTypes.object.isRequired,
|
||||
mobileOpenSidePanel: PropTypes.func.isRequired
|
||||
@@ -34,6 +40,7 @@ class MainPanel extends Component {
|
||||
this.state = {
|
||||
docUuid: '',
|
||||
currentPageConfig: {},
|
||||
isDropdownMenuOpen: false,
|
||||
};
|
||||
this.scrollRef = React.createRef();
|
||||
}
|
||||
@@ -60,15 +67,57 @@ class MainPanel extends Component {
|
||||
return { ...props, docUuid: window.seafile.docUuid, currentPageConfig };
|
||||
}
|
||||
|
||||
handleEditorStateChange = ({ pageId, locked }) => {
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
openHistory = () => {
|
||||
window.location.href = `${siteRoot}wiki/file_revisions/${wikiId}/?page_id=${this.state.currentPageConfig.id}`;
|
||||
};
|
||||
|
||||
toggleDropdownMenu = () => {
|
||||
this.setState({
|
||||
isDropdownMenuOpen: !this.state.isDropdownMenuOpen
|
||||
});
|
||||
};
|
||||
|
||||
getMenu = () => {
|
||||
const list = [];
|
||||
if (wikiPermission === 'rw' && this.state.currentPageConfig) {
|
||||
const { HISTORY, FREEZE_PAGE } = TextTranslation;
|
||||
if (isPro) {
|
||||
list.push(FREEZE_PAGE);
|
||||
}
|
||||
list.push(HISTORY);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
onMenuItemClick = (item) => {
|
||||
const { key } = item;
|
||||
switch (key) {
|
||||
case 'History':
|
||||
this.openHistory();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
onMenuItemKeyDown = (e, item) => {
|
||||
if (e.key == 'Enter' || e.key == 'Space') {
|
||||
this.onMenuItemClick(item);
|
||||
}
|
||||
};
|
||||
|
||||
toggleFreezeStatus = () => {
|
||||
this.props.updatePageLock(this.state.currentPageConfig.id, !this.props.currentPageLocked);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { permission, pathExist, isDataLoading, config, onUpdatePage, isUpdateBySide, style } = this.props;
|
||||
const { currentPageConfig = {} } = this.state;
|
||||
const menuItems = this.getMenu();
|
||||
const { permission, pathExist, isDataLoading, config, onUpdatePage, isUpdateBySide, style, currentPageLocked } = this.props;
|
||||
const { currentPageConfig = {}, isDropdownMenuOpen } = this.state;
|
||||
const isViewingFile = pathExist && !isDataLoading;
|
||||
const isReadOnly = !(permission === 'rw');
|
||||
const isReadOnly = currentPageLocked || !(permission === 'rw');
|
||||
return (
|
||||
<div className="wiki2-main-panel" style={style}>
|
||||
<div className='wiki2-main-panel-north'>
|
||||
@@ -84,8 +133,9 @@ class MainPanel extends Component {
|
||||
<WikiTopNav
|
||||
config={config}
|
||||
currentPageId={this.props.currentPageId}
|
||||
currentPageConfig={currentPageConfig}
|
||||
currentPageLocked={currentPageLocked}
|
||||
setCurrentPage={this.props.setCurrentPage}
|
||||
toggleFreezeStatus={this.toggleFreezeStatus}
|
||||
/>
|
||||
{isViewingFile &&
|
||||
<DocInfo key={this.props.currentPageId} initContext={true} />
|
||||
@@ -93,10 +143,43 @@ class MainPanel extends Component {
|
||||
</div>
|
||||
</div>
|
||||
<div className='d-flex align-items-center'>
|
||||
{(wikiPermission === 'rw' && this.state.currentPageConfig) &&
|
||||
<div className='wiki2-file-history-button' onClick={this.openHistory} role="button">
|
||||
<i className='sf3-font sf3-font-history' aria-hidden="true" />
|
||||
</div>
|
||||
{menuItems.length > 0 &&
|
||||
<Dropdown isOpen={isDropdownMenuOpen} toggle={this.toggleDropdownMenu} className='wiki2-file-history-button'>
|
||||
<DropdownToggle
|
||||
tag="i"
|
||||
id="cur-folder-more-op-toggle"
|
||||
className='wiki2-file-history-button sf3-font-more sf3-font'
|
||||
data-toggle="dropdown"
|
||||
title={gettext('More operations')}
|
||||
aria-label={gettext('More operations')}
|
||||
aria-expanded={isDropdownMenuOpen}
|
||||
>
|
||||
</DropdownToggle>
|
||||
<DropdownMenu>
|
||||
{menuItems.map((menuItem, index) => {
|
||||
if (menuItem.key === 'Freeze page') {
|
||||
return <Switch
|
||||
checked={currentPageLocked}
|
||||
disabled={false}
|
||||
size="small"
|
||||
textPosition="left"
|
||||
className='freeze-document-switch w-100 dropdown-item'
|
||||
onChange={this.toggleFreezeStatus}
|
||||
placeholder={gettext('Freeze page')}
|
||||
/>;
|
||||
} else {
|
||||
return (
|
||||
<DropdownItem
|
||||
key={index}
|
||||
onClick={this.onMenuItemClick.bind(this, menuItem)}
|
||||
onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)}
|
||||
>{menuItem.value}
|
||||
</DropdownItem>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
}
|
||||
{username && <Account />}
|
||||
</div>
|
||||
|
@@ -7,6 +7,7 @@ class Page {
|
||||
this.docUuid = object.docUuid;
|
||||
this.cover_img_url = object.cover_img_url;
|
||||
this.children = Array.isArray(object.children) ? object.children.map(item => new Page(item)) : [];
|
||||
this.locked = object.locked;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,14 +2,16 @@ import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import NavItemIcon from '../common/nav-item-icon';
|
||||
import CustomIcon from '../custom-icon';
|
||||
import { gettext, mediaUrl } from '../../../utils/constants';
|
||||
import { getPaths } from '../utils/index';
|
||||
|
||||
import './index.css';
|
||||
|
||||
function WikiTopNav({ config, currentPageId, setCurrentPage }) {
|
||||
function WikiTopNav({ config, currentPageId, setCurrentPage, currentPageLocked }) {
|
||||
const { navigation, pages } = config;
|
||||
const paths = getPaths(navigation, currentPageId, pages);
|
||||
|
||||
const lockedImageUrl = `${mediaUrl}img/file-freezed-32.svg`;
|
||||
return (
|
||||
<div className="wiki2-top-nav d-flex align-items-center">
|
||||
{paths.map((item, index) => {
|
||||
@@ -17,12 +19,17 @@ function WikiTopNav({ config, currentPageId, setCurrentPage }) {
|
||||
<Fragment key={item.id}>
|
||||
<div className='wiki2-top-nav-item d-flex align-items-center' onClick={() => {setCurrentPage(item.id);}}>
|
||||
{item.icon ? <CustomIcon icon={item.icon} /> : <NavItemIcon symbol={'file'} disable={true} />}
|
||||
<span className='text-truncate' title={item.name} aria-label={item.name}>{item.name}</span>
|
||||
<div className="d-flex align-items-center">
|
||||
<span className='text-truncate' title={item.name} aria-label={item.name}>{item.name}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{index !== paths.length - 1 && <span className="item-split">/</span>}
|
||||
</Fragment>
|
||||
|
||||
);
|
||||
})}
|
||||
{paths.length > 0 && currentPageLocked && <img className="locked" src={lockedImageUrl} alt={gettext('freezed')} title={gettext('Page is frozen')}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -30,7 +37,8 @@ function WikiTopNav({ config, currentPageId, setCurrentPage }) {
|
||||
WikiTopNav.propTypes = {
|
||||
config: PropTypes.object,
|
||||
currentPageId: PropTypes.string,
|
||||
setCurrentPage: PropTypes.func.isRequired
|
||||
setCurrentPage: PropTypes.func.isRequired,
|
||||
currentPageLocked: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default WikiTopNav;
|
||||
|
@@ -211,12 +211,10 @@ class PageItem extends Component {
|
||||
const { connectDragSource, connectDragPreview, connectDropTarget, page, pagesLength, isOnlyOnePage, pathStr } = this.props;
|
||||
const { isShowNameEditor, pageName, isSelected, isMouseEnter } = this.state;
|
||||
if (isSelected) this.setDocUuid(page.docUuid);
|
||||
|
||||
let navItemId = `page-editor-${page.id}`;
|
||||
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
|
||||
const customIcon = page.icon;
|
||||
const folded = this.props.getFoldState(page.id);
|
||||
|
||||
if (wikiPermission === 'rw') {
|
||||
return (
|
||||
<div>
|
||||
|
@@ -72,7 +72,10 @@ const PageTitle = ({ isUpdateBySide, currentPageConfig, onUpdatePage }) => {
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<PageTitleEditor isUpdateBySide={isUpdateBySide} currentPageConfig={currentPageConfig} onUpdatePage={onUpdatePage} />
|
||||
{!currentPageConfig.locked ?
|
||||
<PageTitleEditor isUpdateBySide={isUpdateBySide} currentPageConfig={currentPageConfig} onUpdatePage={onUpdatePage} /> :
|
||||
<div className='wiki-sdoc-title'>{currentPageConfig.name}</div>
|
||||
}
|
||||
</>
|
||||
:
|
||||
<div className='wiki-sdoc-title'>{currentPageConfig.name}</div>}
|
||||
|
@@ -135,3 +135,31 @@ body {
|
||||
.sdoc-editor-container .sdoc-article-container .article #sdoc-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wiki2-file-history-button .freeze-document-switch {
|
||||
padding: 0.25rem 1rem;
|
||||
/* display: flex; */
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wiki2-file-history-button .freeze-document-switch .custom-switch-description {
|
||||
margin-left: 0;
|
||||
color: #212529;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.wiki2-file-history-button .freeze-document-switch:hover .custom-switch-description {
|
||||
color: #fff;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.wiki2-file-history-button .freeze-document-switch .custom-switch-indicator {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.wiki2-top-nav .locked {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
top: 60%;
|
||||
left: 50%;
|
||||
}
|
||||
|
@@ -91,6 +91,10 @@ const TextTranslation = {
|
||||
key: 'Unfreeze Document',
|
||||
value: gettext('Unfreeze Document')
|
||||
},
|
||||
FREEZE_PAGE: {
|
||||
key: 'Freeze page',
|
||||
value: gettext('Freeze page')
|
||||
},
|
||||
CONVERT_AND_EXPORT: {
|
||||
key: 'Convert & Export',
|
||||
value: gettext('Convert & Export')
|
||||
|
@@ -224,6 +224,14 @@ class WikiAPI {
|
||||
return this.req.get(url);
|
||||
}
|
||||
|
||||
updateWiki2PageLock(wikiId, pageId, locked) {
|
||||
const url = this.server + '/api/v2.1/wiki2/' + wikiId + '/page/' + pageId + '/';
|
||||
let params = {
|
||||
is_lock_page: locked
|
||||
};
|
||||
return this.req.put(url, params);
|
||||
}
|
||||
|
||||
renameWiki2(wikiId, wikiName) {
|
||||
const url = this.server + '/api/v2.1/wiki2/' + wikiId + '/';
|
||||
let params = {
|
||||
|
Reference in New Issue
Block a user