mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-18 15:08:22 +00:00
freeze wiki page
This commit is contained in:
parent
6614fd20a6
commit
304efde162
@ -40,6 +40,7 @@ class Wiki extends Component {
|
|||||||
permission: '',
|
permission: '',
|
||||||
isConfigLoading: true,
|
isConfigLoading: true,
|
||||||
currentPageId: '',
|
currentPageId: '',
|
||||||
|
currentPageLocked: false,
|
||||||
config: new WikiConfig({}),
|
config: new WikiConfig({}),
|
||||||
repoId: '',
|
repoId: '',
|
||||||
seadoc_access_token: '',
|
seadoc_access_token: '',
|
||||||
@ -154,6 +155,34 @@ class Wiki extends Component {
|
|||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
updatePageLockedToServer = (pageId, locked) => {
|
||||||
|
wikiAPI.updateWiki2PageLocked(wikiId, pageId, locked).then(res => {
|
||||||
|
this.setState(prevState => {
|
||||||
|
// 更新 wikiConfig 中的 pages
|
||||||
|
const updatedPages = prevState.config.pages.map(page => {
|
||||||
|
if (page.id === pageId) {
|
||||||
|
return {
|
||||||
|
...page,
|
||||||
|
locked: !prevState.currentPageLocked
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return page;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentPageLocked: !prevState.currentPageLocked,
|
||||||
|
config: new WikiConfig({
|
||||||
|
...prevState.config,
|
||||||
|
pages: updatedPages
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
let errorMsg = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errorMsg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
saveWikiConfig = (wikiConfig, isUpdateBySide = false) => {
|
saveWikiConfig = (wikiConfig, isUpdateBySide = false) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
config: new WikiConfig(wikiConfig),
|
config: new WikiConfig(wikiConfig),
|
||||||
@ -215,6 +244,7 @@ class Wiki extends Component {
|
|||||||
} else {
|
} else {
|
||||||
getWikiPage = wikiAPI.getWiki2Page(wikiId, pageId);
|
getWikiPage = wikiAPI.getWiki2Page(wikiId, pageId);
|
||||||
}
|
}
|
||||||
|
// TODO: fix wiki2 page locked
|
||||||
getWikiPage.then(res => {
|
getWikiPage.then(res => {
|
||||||
const { permission, seadoc_access_token, assets_url } = res.data;
|
const { permission, seadoc_access_token, assets_url } = res.data;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -222,6 +252,7 @@ class Wiki extends Component {
|
|||||||
seadoc_access_token,
|
seadoc_access_token,
|
||||||
assets_url,
|
assets_url,
|
||||||
path: filePath,
|
path: filePath,
|
||||||
|
// currentPageLocked: res.data.is_freezed,
|
||||||
});
|
});
|
||||||
const docUuid = assets_url.slice(assets_url.lastIndexOf('/') + 1);
|
const docUuid = assets_url.slice(assets_url.lastIndexOf('/') + 1);
|
||||||
this.getSdocFileContent(docUuid, seadoc_access_token);
|
this.getSdocFileContent(docUuid, seadoc_access_token);
|
||||||
@ -281,6 +312,7 @@ class Wiki extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
currentPageId: pageId,
|
currentPageId: pageId,
|
||||||
path: path,
|
path: path,
|
||||||
|
currentPageLocked: currentPage.locked,
|
||||||
}, () => {
|
}, () => {
|
||||||
callback && callback();
|
callback && callback();
|
||||||
eventBus.dispatch('update-wiki-current-page');
|
eventBus.dispatch('update-wiki-current-page');
|
||||||
@ -408,6 +440,8 @@ class Wiki extends Component {
|
|||||||
seadoc_access_token={this.state.seadoc_access_token}
|
seadoc_access_token={this.state.seadoc_access_token}
|
||||||
assets_url={this.state.assets_url}
|
assets_url={this.state.assets_url}
|
||||||
onUpdatePage={this.onUpdatePage}
|
onUpdatePage={this.onUpdatePage}
|
||||||
|
currentPageLocked={this.state.currentPageLocked}
|
||||||
|
updatePageLockedToServer={this.updatePageLockedToServer}
|
||||||
setCurrentPage={this.setCurrentPage}
|
setCurrentPage={this.setCurrentPage}
|
||||||
isUpdateBySide={this.state.isUpdateBySide}
|
isUpdateBySide={this.state.isUpdateBySide}
|
||||||
style={mainPanelStyle}
|
style={mainPanelStyle}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Dropdown, DropdownMenu, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||||
import { SdocWikiEditor, DocInfo } from '@seafile/sdoc-editor';
|
import { SdocWikiEditor, DocInfo } from '@seafile/sdoc-editor';
|
||||||
import { gettext, username, wikiPermission, wikiId, siteRoot } from '../../utils/constants';
|
import { gettext, username, wikiPermission, wikiId, siteRoot } from '../../utils/constants';
|
||||||
|
import TextTranslation from '../../utils/text-translation';
|
||||||
|
import Switch from '../../components/switch';
|
||||||
|
import toaster from '../../components/toast';
|
||||||
import Loading from '../../components/loading';
|
import Loading from '../../components/loading';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import Account from '../../components/common/account';
|
import Account from '../../components/common/account';
|
||||||
import WikiTopNav from './top-nav';
|
import WikiTopNav from './top-nav';
|
||||||
import { getCurrentPageConfig } from './utils';
|
import { getCurrentPageConfig, getCurrentPageLocked } from './utils';
|
||||||
import RightHeader from './wiki-right-header';
|
import RightHeader from './wiki-right-header';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
@ -22,6 +26,8 @@ const propTypes = {
|
|||||||
currentPageId: PropTypes.string,
|
currentPageId: PropTypes.string,
|
||||||
isUpdateBySide: PropTypes.bool,
|
isUpdateBySide: PropTypes.bool,
|
||||||
onUpdatePage: PropTypes.func,
|
onUpdatePage: PropTypes.func,
|
||||||
|
updatePageLockedToServer: PropTypes.func,
|
||||||
|
currentPageLocked: PropTypes.bool,
|
||||||
onAddWikiPage: PropTypes.func,
|
onAddWikiPage: PropTypes.func,
|
||||||
style: PropTypes.object.isRequired,
|
style: PropTypes.object.isRequired,
|
||||||
mobileOpenSidePanel: PropTypes.func.isRequired
|
mobileOpenSidePanel: PropTypes.func.isRequired
|
||||||
@ -34,6 +40,7 @@ class MainPanel extends Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
docUuid: '',
|
docUuid: '',
|
||||||
currentPageConfig: {},
|
currentPageConfig: {},
|
||||||
|
isDropdownMenuOpen: false,
|
||||||
};
|
};
|
||||||
this.scrollRef = React.createRef();
|
this.scrollRef = React.createRef();
|
||||||
}
|
}
|
||||||
@ -64,11 +71,81 @@ class MainPanel extends Component {
|
|||||||
window.location.href = `${siteRoot}wiki/file_revisions/${wikiId}/?page_id=${this.state.currentPageConfig.id}`;
|
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_DOCUMENT } = TextTranslation;
|
||||||
|
list.push(FREEZE_DOCUMENT);
|
||||||
|
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 = () => {
|
||||||
|
// console.log(this.state.currentPageConfig.locked, '----currentPageConfig.locked')
|
||||||
|
console.log(!this.props.currentPageLocked, '----currentPageLocked');
|
||||||
|
this.props.updatePageLockedToServer(this.state.currentPageConfig.id, !this.props.currentPageLocked);
|
||||||
|
// this.setState({
|
||||||
|
// locked: !this.state.locked
|
||||||
|
// }, () => {
|
||||||
|
// console.log(this.state.locked, '----locked')
|
||||||
|
// });
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleLockFile = () => {
|
||||||
|
// if (this.state.isLocked) {
|
||||||
|
// seafileAPI.unlockfile(repoID, filePath).then((res) => {
|
||||||
|
// this.setState({
|
||||||
|
// isLocked: false,
|
||||||
|
// lockedByMe: false
|
||||||
|
// });
|
||||||
|
// }).catch((error) => {
|
||||||
|
// const errorMsg = Utils.getErrorMsg(error);
|
||||||
|
// toaster.danger(errorMsg);
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// seafileAPI.lockfile(repoID, filePath).then((res) => {
|
||||||
|
// this.setState({
|
||||||
|
// isLocked: true,
|
||||||
|
// lockedByMe: true
|
||||||
|
// });
|
||||||
|
// }).catch((error) => {
|
||||||
|
// const errorMsg = Utils.getErrorMsg(error);
|
||||||
|
// toaster.danger(errorMsg);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { permission, pathExist, isDataLoading, config, onUpdatePage, isUpdateBySide, style } = this.props;
|
// console.log(this.state.currentPageConfig)
|
||||||
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 isViewingFile = pathExist && !isDataLoading;
|
||||||
const isReadOnly = !(permission === 'rw');
|
const isReadOnly = !(permission === 'rw');
|
||||||
|
console.log(currentPageLocked, '----currentPageLocked');
|
||||||
return (
|
return (
|
||||||
<div className="wiki2-main-panel" style={style}>
|
<div className="wiki2-main-panel" style={style}>
|
||||||
<div className='wiki2-main-panel-north'>
|
<div className='wiki2-main-panel-north'>
|
||||||
@ -86,6 +163,7 @@ class MainPanel extends Component {
|
|||||||
currentPageId={this.props.currentPageId}
|
currentPageId={this.props.currentPageId}
|
||||||
currentPageConfig={currentPageConfig}
|
currentPageConfig={currentPageConfig}
|
||||||
setCurrentPage={this.props.setCurrentPage}
|
setCurrentPage={this.props.setCurrentPage}
|
||||||
|
toggleLockFile={this.toggleLockFile}
|
||||||
/>
|
/>
|
||||||
{isViewingFile &&
|
{isViewingFile &&
|
||||||
<DocInfo key={this.props.currentPageId} initContext={true} />
|
<DocInfo key={this.props.currentPageId} initContext={true} />
|
||||||
@ -93,11 +171,50 @@ class MainPanel extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='d-flex align-items-center'>
|
<div className='d-flex align-items-center'>
|
||||||
{(wikiPermission === 'rw' && this.state.currentPageConfig) &&
|
{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 Document') {
|
||||||
|
return <Switch
|
||||||
|
checked={currentPageLocked}
|
||||||
|
disabled={false}
|
||||||
|
size="small"
|
||||||
|
textPosition="left"
|
||||||
|
className='freeze-document-switch w-100 dropdown-item'
|
||||||
|
onChange={this.toggleFreezeStatus}
|
||||||
|
placeholder={gettext('Freeze Document')}
|
||||||
|
/>;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<DropdownItem
|
||||||
|
key={index}
|
||||||
|
onClick={this.onMenuItemClick.bind(this, menuItem)}
|
||||||
|
onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)}
|
||||||
|
>{menuItem.value}
|
||||||
|
</DropdownItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
}
|
||||||
|
{/* {(wikiPermission === 'rw' && this.state.currentPageConfig) &&
|
||||||
<div className='wiki2-file-history-button' onClick={this.openHistory} role="button">
|
<div className='wiki2-file-history-button' onClick={this.openHistory} role="button">
|
||||||
<i className='sf3-font sf3-font-history' aria-hidden="true" />
|
<i className='sf3-font sf3-font-history' aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
}
|
} */}
|
||||||
|
|
||||||
{username && <Account />}
|
{username && <Account />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,6 +7,7 @@ class Page {
|
|||||||
this.docUuid = object.docUuid;
|
this.docUuid = object.docUuid;
|
||||||
this.cover_img_url = object.cover_img_url;
|
this.cover_img_url = object.cover_img_url;
|
||||||
this.children = Array.isArray(object.children) ? object.children.map(item => new Page(item)) : [];
|
this.children = Array.isArray(object.children) ? object.children.map(item => new Page(item)) : [];
|
||||||
|
this.locked = object.locked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,14 +2,52 @@ import React, { Fragment } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import NavItemIcon from '../common/nav-item-icon';
|
import NavItemIcon from '../common/nav-item-icon';
|
||||||
import CustomIcon from '../custom-icon';
|
import CustomIcon from '../custom-icon';
|
||||||
|
import IconButton from '../../../components/icon-button';
|
||||||
|
import { gettext } from '../../../utils/constants';
|
||||||
import { getPaths } from '../utils/index';
|
import { getPaths } from '../utils/index';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
function WikiTopNav({ config, currentPageId, setCurrentPage }) {
|
function WikiTopNav({ config, currentPageId, setCurrentPage, toggleLockFile }) {
|
||||||
|
// handleLockClick
|
||||||
const { navigation, pages } = config;
|
const { navigation, pages } = config;
|
||||||
const paths = getPaths(navigation, currentPageId, pages);
|
const paths = getPaths(navigation, currentPageId, pages);
|
||||||
|
const { canLockUnlockFile, isLocked, lockedByMe } = window.app.pageOptions;
|
||||||
|
const handleLockClick = async (pageId) => {
|
||||||
|
try {
|
||||||
|
// 发送锁定/解锁请求
|
||||||
|
const response = await fetch('/api/pages/lock', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
pageId: pageId
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
// 处理成功响应
|
||||||
|
// 可以更新页面状态或显示提示信息
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 处理错误
|
||||||
|
console.error('Failed to lock/unlock page:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let showLockUnlockBtn = false;
|
||||||
|
let lockUnlockText; let lockUnlockIcon;
|
||||||
|
if (canLockUnlockFile) {
|
||||||
|
if (!isLocked) {
|
||||||
|
showLockUnlockBtn = true;
|
||||||
|
lockUnlockText = gettext('Lock');
|
||||||
|
lockUnlockIcon = 'lock';
|
||||||
|
} else if (lockedByMe) {
|
||||||
|
showLockUnlockBtn = true;
|
||||||
|
lockUnlockText = gettext('Unlock');
|
||||||
|
lockUnlockIcon = 'unlock';
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="wiki2-top-nav d-flex align-items-center">
|
<div className="wiki2-top-nav d-flex align-items-center">
|
||||||
{paths.map((item, index) => {
|
{paths.map((item, index) => {
|
||||||
@ -17,12 +55,22 @@ function WikiTopNav({ config, currentPageId, setCurrentPage }) {
|
|||||||
<Fragment key={item.id}>
|
<Fragment key={item.id}>
|
||||||
<div className='wiki2-top-nav-item d-flex align-items-center' onClick={() => {setCurrentPage(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} />}
|
{item.icon ? <CustomIcon icon={item.icon} /> : <NavItemIcon symbol={'file'} disable={true} />}
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
<span className='text-truncate' title={item.name} aria-label={item.name}>{item.name}</span>
|
<span className='text-truncate' title={item.name} aria-label={item.name}>{item.name}</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{index !== paths.length - 1 && <span className="item-split">/</span>}
|
{index !== paths.length - 1 && <span className="item-split">/</span>}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{/* <IconButton
|
||||||
|
id="lock-unlock-file"
|
||||||
|
icon={lockUnlockIcon}
|
||||||
|
text={lockUnlockText}
|
||||||
|
onClick={toggleLockFile}
|
||||||
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -30,7 +78,10 @@ function WikiTopNav({ config, currentPageId, setCurrentPage }) {
|
|||||||
WikiTopNav.propTypes = {
|
WikiTopNav.propTypes = {
|
||||||
config: PropTypes.object,
|
config: PropTypes.object,
|
||||||
currentPageId: PropTypes.string,
|
currentPageId: PropTypes.string,
|
||||||
setCurrentPage: PropTypes.func.isRequired
|
setCurrentPage: PropTypes.func.isRequired,
|
||||||
|
toggleLockFile: PropTypes.func,
|
||||||
|
// handleLockClick: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default WikiTopNav;
|
export default WikiTopNav;
|
||||||
|
@ -47,6 +47,11 @@ const getCurrentPageConfig = (pages, pageId) => {
|
|||||||
return pages.filter(page => page.id === pageId)[0];
|
return pages.filter(page => page.id === pageId)[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCurrentPageLocked = (pages, pageId) => {
|
||||||
|
const page = pages.filter(page => page.id === pageId)[0];
|
||||||
|
return page ? page.locked : false;
|
||||||
|
};
|
||||||
|
|
||||||
const getWikPageLink = (pageId) => {
|
const getWikPageLink = (pageId) => {
|
||||||
let { origin, pathname } = window.location;
|
let { origin, pathname } = window.location;
|
||||||
let pathArr = pathname.split('/');
|
let pathArr = pathname.split('/');
|
||||||
@ -141,6 +146,7 @@ export {
|
|||||||
isObjectNotEmpty,
|
isObjectNotEmpty,
|
||||||
getIconURL,
|
getIconURL,
|
||||||
getCurrentPageConfig,
|
getCurrentPageConfig,
|
||||||
|
getCurrentPageLocked,
|
||||||
getWikPageLink,
|
getWikPageLink,
|
||||||
throttle,
|
throttle,
|
||||||
getPaths,
|
getPaths,
|
||||||
|
@ -216,7 +216,7 @@ class PageItem extends Component {
|
|||||||
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
|
let childNumber = Array.isArray(page.children) ? page.children.length : 0;
|
||||||
const customIcon = page.icon;
|
const customIcon = page.icon;
|
||||||
const folded = this.props.getFoldState(page.id);
|
const folded = this.props.getFoldState(page.id);
|
||||||
|
// console.log('page', page.locked);
|
||||||
if (wikiPermission === 'rw') {
|
if (wikiPermission === 'rw') {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -240,6 +240,7 @@ class PageItem extends Component {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<span className="wiki-page-title text-truncate" title={page.name}>{page.name}</span>
|
<span className="wiki-page-title text-truncate" title={page.name}>{page.name}</span>
|
||||||
|
<span>{page.locked ? '🔒' : '🔓'}</span>
|
||||||
{isShowNameEditor && (
|
{isShowNameEditor && (
|
||||||
<NameEditPopover
|
<NameEditPopover
|
||||||
oldName={pageName}
|
oldName={pageName}
|
||||||
|
@ -135,3 +135,24 @@ body {
|
|||||||
.sdoc-editor-container .sdoc-article-container .article #sdoc-editor {
|
.sdoc-editor-container .sdoc-article-container .article #sdoc-editor {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wiki2-file-history-button .freeze-document-switch {
|
||||||
|
padding: 4px 16px;
|
||||||
|
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: 30px;
|
||||||
|
}
|
||||||
|
@ -224,6 +224,14 @@ class WikiAPI {
|
|||||||
return this.req.get(url);
|
return this.req.get(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateWiki2PageLocked(wikiId, pageId, locked) {
|
||||||
|
const url = this.server + '/api/v2.1/wiki2/' + wikiId + '/page/' + pageId + '/';
|
||||||
|
let params = {
|
||||||
|
locked: locked
|
||||||
|
};
|
||||||
|
return this.req.put(url, params);
|
||||||
|
}
|
||||||
|
|
||||||
renameWiki2(wikiId, wikiName) {
|
renameWiki2(wikiId, wikiName) {
|
||||||
const url = this.server + '/api/v2.1/wiki2/' + wikiId + '/';
|
const url = this.server + '/api/v2.1/wiki2/' + wikiId + '/';
|
||||||
let params = {
|
let params = {
|
||||||
|
@ -40,10 +40,10 @@ from seahub.utils import is_org_context, get_user_repos, is_pro_version, is_vali
|
|||||||
from seahub.views import check_folder_permission
|
from seahub.views import check_folder_permission
|
||||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||||
from seahub.utils.file_op import check_file_lock
|
from seahub.utils.file_op import check_file_lock
|
||||||
from seahub.utils.repo import get_repo_owner, is_valid_repo_id_format, is_group_repo_staff, is_repo_owner
|
from seahub.utils.repo import get_repo_owner, is_valid_repo_id_format, is_group_repo_staff, is_repo_owner, get_locked_files_by_dir
|
||||||
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token, copy_sdoc_images_with_sdoc_uuid
|
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token, copy_sdoc_images_with_sdoc_uuid
|
||||||
from seahub.settings import ENABLE_STORAGE_CLASSES, STORAGE_CLASS_MAPPING_POLICY, \
|
from seahub.settings import ENABLE_STORAGE_CLASSES, STORAGE_CLASS_MAPPING_POLICY, \
|
||||||
ENCRYPTED_LIBRARY_VERSION
|
ENCRYPTED_LIBRARY_VERSION, FILE_LOCK_EXPIRATION_DAYS
|
||||||
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
|
||||||
from seahub.utils.ccnet_db import CcnetDB
|
from seahub.utils.ccnet_db import CcnetDB
|
||||||
from seahub.tags.models import FileUUIDMap
|
from seahub.tags.models import FileUUIDMap
|
||||||
@ -459,7 +459,17 @@ class Wiki2ConfigView(APIView):
|
|||||||
|
|
||||||
wiki = wiki.to_dict()
|
wiki = wiki.to_dict()
|
||||||
wiki_config = get_wiki_config(repo.repo_id, request.user.username)
|
wiki_config = get_wiki_config(repo.repo_id, request.user.username)
|
||||||
|
pages = wiki_config.get('pages', [])
|
||||||
|
locked_files = seafile_api.get_locked_files(wiki_id)
|
||||||
|
locked_file_paths = []
|
||||||
|
for locked_file in locked_files:
|
||||||
|
locked_file_paths.append(locked_file.path)
|
||||||
|
for page in pages:
|
||||||
|
if page['path'][1:] in locked_file_paths:
|
||||||
|
page['locked'] = True
|
||||||
|
else:
|
||||||
|
page['locked'] = False
|
||||||
|
wiki_config['pages'] = pages
|
||||||
wiki['wiki_config'] = wiki_config
|
wiki['wiki_config'] = wiki_config
|
||||||
|
|
||||||
return Response({'wiki': wiki})
|
return Response({'wiki': wiki})
|
||||||
@ -746,13 +756,14 @@ class Wiki2PageView(APIView):
|
|||||||
|
|
||||||
assets_url = '/api/v2.1/seadoc/download-image/' + doc_uuid
|
assets_url = '/api/v2.1/seadoc/download-image/' + doc_uuid
|
||||||
seadoc_access_token = gen_seadoc_access_token(doc_uuid, filename, request.user.username, permission=permission, default_title='')
|
seadoc_access_token = gen_seadoc_access_token(doc_uuid, filename, request.user.username, permission=permission, default_title='')
|
||||||
|
is_freezed = dirent.expire is not None and dirent.expire < 0
|
||||||
return Response({
|
return Response({
|
||||||
"latest_contributor": email2nickname(latest_contributor),
|
"latest_contributor": email2nickname(latest_contributor),
|
||||||
"last_modified": last_modified,
|
"last_modified": last_modified,
|
||||||
"permission": permission,
|
"permission": permission,
|
||||||
"seadoc_access_token": seadoc_access_token,
|
"seadoc_access_token": seadoc_access_token,
|
||||||
"assets_url": assets_url,
|
"assets_url": assets_url,
|
||||||
|
"is_freezed": is_freezed,
|
||||||
})
|
})
|
||||||
|
|
||||||
def delete(self, request, wiki_id, page_id):
|
def delete(self, request, wiki_id, page_id):
|
||||||
@ -838,6 +849,65 @@ class Wiki2PageView(APIView):
|
|||||||
|
|
||||||
return Response({'success': True})
|
return Response({'success': True})
|
||||||
|
|
||||||
|
def put(self, request, wiki_id, page_id):
|
||||||
|
wiki = Wiki.objects.get(wiki_id=wiki_id)
|
||||||
|
if not wiki:
|
||||||
|
error_msg = "Wiki not found."
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
repo_owner = get_repo_owner(request, wiki_id)
|
||||||
|
wiki.owner = repo_owner
|
||||||
|
|
||||||
|
username = request.user.username
|
||||||
|
if not check_wiki_permission(wiki, username):
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
repo_id = wiki.repo_id
|
||||||
|
repo = seafile_api.get_repo(repo_id)
|
||||||
|
if not repo:
|
||||||
|
error_msg = 'Library %s not found.' % repo_id
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
locked = request.data.get('locked', None)
|
||||||
|
if locked is None:
|
||||||
|
error_msg = 'locked is required.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
wiki_config = get_wiki_config(repo_id, username)
|
||||||
|
pages = wiki_config.get('pages', [])
|
||||||
|
for page in pages:
|
||||||
|
if page['id'] == page_id:
|
||||||
|
page['locked'] = locked
|
||||||
|
path = page['path']
|
||||||
|
break
|
||||||
|
wiki_config['pages'] = pages
|
||||||
|
wiki_config = json.dumps(wiki_config)
|
||||||
|
save_wiki_config(wiki, username, wiki_config)
|
||||||
|
|
||||||
|
expire = request.data.get('expire', -1)
|
||||||
|
try:
|
||||||
|
expire = int(expire)
|
||||||
|
except ValueError:
|
||||||
|
error_msg = 'expire invalid.'
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||||
|
|
||||||
|
if locked:
|
||||||
|
try:
|
||||||
|
seafile_api.lock_file(repo_id, path.lstrip('/'), username, expire)
|
||||||
|
except SearpcError as e:
|
||||||
|
logger.error(e)
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
|
||||||
|
else:
|
||||||
|
# unlock file
|
||||||
|
try:
|
||||||
|
seafile_api.unlock_file(repo_id, path.lstrip('/'))
|
||||||
|
except SearpcError as e:
|
||||||
|
logger.error(e)
|
||||||
|
error_msg = 'Internal Server Error'
|
||||||
|
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
|
||||||
|
return Response('success', status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class Wiki2PublishPageView(APIView):
|
class Wiki2PublishPageView(APIView):
|
||||||
throttle_classes = (UserRateThrottle,)
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
Loading…
Reference in New Issue
Block a user