1
0
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:
awu0403
2025-04-02 13:05:17 +08:00
committed by GitHub
parent cc99ce2e90
commit 073af84027
10 changed files with 256 additions and 16 deletions

View File

@@ -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}

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>}

View File

@@ -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%;
}

View File

@@ -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')

View File

@@ -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 = {