1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-01 23:38:37 +00:00

Wiki add new page below or above (#7118)

* Wiki add new page below or above

* change var name

* remove sibling page

* update create wiki page logic

* fix page does not exist

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
This commit is contained in:
Michael An 2024-11-29 22:03:59 +08:00 committed by GitHub
parent 51d5706aa6
commit d63b68e8ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 185 additions and 48 deletions

View File

@ -132,7 +132,7 @@
background-color: #DFDFDD;
}
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
.wiki-nav .wiki-page-item .wiki-add-page-btn .sf3-font.sf3-font-enlarge,
.wiki-nav .wiki-page-item .more-wiki-page-operation .seafile-multicolor-icon-more-level {
font-size: 14px;
cursor: pointer;
@ -270,13 +270,13 @@
}
.wiki-nav,
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge:hover,
.wiki-nav .wiki-page-item .wiki-add-page-btn .sf3-font.sf3-font-enlarge:hover,
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level:hover {
color: #212529;
}
.wiki-nav .wiki2-trash .sf3-font,
.wiki-nav .wiki-page-item .sf3-font.sf3-font-enlarge,
.wiki-nav .wiki-page-item .wiki-add-page-btn .sf3-font.sf3-font-enlarge,
.wiki-nav .wiki-page-item .seafile-multicolor-icon-more-level {
color: #666;
}

View File

@ -83,16 +83,16 @@ class SidePanel extends PureComponent {
});
};
addPage = (page, parentId, successCallback, errorCallback, jumpToNewPage = true) => {
addPage = (page, parent_id, successCallback, errorCallback, jumpToNewPage = true) => {
const { config } = this.props;
const navigation = config.navigation;
const pageId = page.id;
const page_id = page.id;
config.pages.push(page);
PageUtils.addPage(navigation, pageId, parentId);
PageUtils.addPage({ navigation, page_id, parent_id });
config.navigation = navigation;
JSON.stringify(config);
this.props.updateWikiConfig(config);
jumpToNewPage && this.props.setCurrentPage(pageId, successCallback);
jumpToNewPage && this.props.setCurrentPage(page_id, successCallback);
successCallback && successCallback();
};
@ -104,6 +104,19 @@ class SidePanel extends PureComponent {
this.props.updateWikiConfig(config);
};
addSiblingPage = (page, parent_id, insert_position, sibling_page_id, successCallback) => {
const { config } = this.props;
const navigation = config.navigation;
const page_id = page.page_id;
config.pages.push(page);
PageUtils.addPage({ navigation, page_id, parent_id, insert_position, sibling_page_id });
config.navigation = navigation;
JSON.stringify(config);
this.props.updateWikiConfig(config);
this.props.setCurrentPage(page_id, successCallback);
successCallback && successCallback();
};
toggleTrashDialog = () => {
this.setState({ isShowTrashDialog: !this.state.isShowTrashDialog });
};
@ -128,6 +141,7 @@ class SidePanel extends PureComponent {
getCurrentPageId={this.props.getCurrentPageId}
addPageInside={this.addPageInside}
toggleTrashDialog={this.toggleTrashDialog}
addSiblingPage={this.addSiblingPage}
/>
}
</div>

View File

@ -6,14 +6,16 @@ import { Utils } from '../../../utils/utils';
import toaster from '../../../components/toast';
import Loading from '../../../components/loading';
import wikiAPI from '../../../utils/wiki-api';
import { INSERT_POSITION } from './constants';
import '../css/add-new-page-dialog.css';
const propTypes = {
page: PropTypes.object,
title: PropTypes.node,
toggle: PropTypes.func.isRequired,
onAddNewPage: PropTypes.func,
getCurrentPageId: PropTypes.func.isRequired,
insertPosition: PropTypes.string,
};
@ -71,9 +73,11 @@ class AddNewPageDialog extends React.Component {
};
createPage = (pageName) => {
wikiAPI.createWiki2Page(wikiId, pageName, this.props.getCurrentPageId()).then(res => {
const { insertPosition, page } = this.props;
wikiAPI.createWiki2Page(wikiId, pageName, page.id, insertPosition).then(res => {
const { page_id, obj_name, doc_uuid, parent_dir } = res.data.file_info;
this.props.onAddNewPage({
id: page_id,
page_id: page_id,
name: pageName,
icon: '',
@ -126,4 +130,8 @@ class AddNewPageDialog extends React.Component {
AddNewPageDialog.propTypes = propTypes;
AddNewPageDialog.defaultProps = {
insertPosition: INSERT_POSITION.INNER,
};
export default AddNewPageDialog;

View File

@ -0,0 +1,5 @@
export const INSERT_POSITION = {
ABOVE: 'above',
BELOW: 'below',
INNER: 'inner',
};

View File

@ -1,3 +1,5 @@
import { INSERT_POSITION } from './constants';
class NewPage {
constructor(id) {
this.id = id;
@ -8,26 +10,52 @@ class NewPage {
export default class PageUtils {
static addPage(navigation, page_id, parentId) {
if (!parentId) {
navigation.push(new NewPage(page_id));
static addPage({ navigation, page_id, parent_id, insert_position, sibling_page_id }) {
if (!parent_id) {
const newPage = new NewPage(page_id);
if (sibling_page_id) {
let insertIndex = navigation.findIndex(item => item.id === sibling_page_id);
if (insertIndex > -1) {
if (insert_position === INSERT_POSITION.ABOVE) {
insertIndex -= 1;
}
navigation.splice(insertIndex + 1, 0, newPage);
} else {
navigation.push(newPage);
}
} else {
navigation.push(newPage);
}
} else {
navigation.forEach(item => {
this._addPageRecursion(page_id, item, parentId);
this._addPageRecursion({ page_id, item, parent_id, insert_position, sibling_page_id });
});
}
}
static _addPageRecursion(page_id, item, parentId) {
static _addPageRecursion({ page_id, item, parent_id, insert_position, sibling_page_id }) {
if (!Array.isArray(item.children)) {
item.children = [];
}
if (item.id === parentId) {
item.children.push(new NewPage(page_id));
if (item.id === parent_id) {
const newPage = new NewPage(page_id);
if (sibling_page_id) {
let insertIndex = item.children.findIndex(item => item.id === sibling_page_id);
if (insertIndex > -1) {
if (insert_position === INSERT_POSITION.ABOVE) {
insertIndex -= 1;
}
item.children.splice(insertIndex + 1, 0, newPage);
} else {
item.children.push(newPage);
}
} else {
item.children.push(newPage);
}
return true;
}
item.children && item.children.forEach(item => {
this._addPageRecursion(page_id, item, parentId);
this._addPageRecursion({ page_id, item, parent_id, insert_position, sibling_page_id });
});
}

View File

@ -4,6 +4,7 @@ import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap
import toaster from '../../../../components/toast';
import { gettext } from '../../../../utils/constants';
import { getWikPageLink } from '../../utils';
import { INSERT_POSITION } from '../constants';
export default class PageDropdownMenu extends Component {
@ -13,6 +14,7 @@ export default class PageDropdownMenu extends Component {
pagesLength: PropTypes.number,
toggle: PropTypes.func,
toggleNameEditor: PropTypes.func,
toggleInsertSiblingPage: PropTypes.func,
duplicatePage: PropTypes.func,
onDeletePage: PropTypes.func,
isOnlyOnePage: PropTypes.bool,
@ -46,6 +48,14 @@ export default class PageDropdownMenu extends Component {
this.props.onDeletePage();
};
addPageAbove = () => {
this.props.toggleInsertSiblingPage(INSERT_POSITION.ABOVE);
};
addPageBelow = () => {
this.props.toggleInsertSiblingPage(INSERT_POSITION.BELOW);
};
duplicatePage = () => {
const { page } = this.props;
this.props.duplicatePage({ from_page_id: page.id }, () => {}, this.duplicatePageFailure);
@ -98,6 +108,14 @@ export default class PageDropdownMenu extends Component {
<i className="sf3-font sf3-font-rename" />
<span className="item-text">{gettext('Modify name')}</span>
</DropdownItem>
<DropdownItem onClick={this.addPageAbove}>
<i className="sf3-font sf3-font-enlarge" />
<span className="item-text">{gettext('Add page above')}</span>
</DropdownItem>
<DropdownItem onClick={this.addPageBelow}>
<i className="sf3-font sf3-font-enlarge" />
<span className="item-text">{gettext('Add page below')}</span>
</DropdownItem>
<DropdownItem onClick={this.duplicatePage}>
<i className="sf3-font sf3-font-copy1" />
<span className="item-text">{gettext('Duplicate page')}</span>

View File

@ -11,6 +11,7 @@ import Icon from '../../../../components/icon';
import DraggedPageItem from './dragged-page-item';
import CustomIcon from '../../custom-icon';
import { eventBus } from '../../../../components/common/event-bus';
import { INSERT_POSITION } from '../constants';
class PageItem extends Component {
@ -21,6 +22,8 @@ class PageItem extends Component {
isShowOperationDropdown: false,
isShowDeleteDialog: false,
isShowInsertPage: false,
isShowAddSiblingPage: false,
insertPosition: '',
pageName: props.page.name || '',
isSelected: props.getCurrentPageId() === props.page.id,
isMouseEnter: false,
@ -73,6 +76,17 @@ class PageItem extends Component {
this.setState({ isShowInsertPage: !this.state.isShowInsertPage });
};
toggleInsertSiblingPage = (position) => {
let insertPosition = null;
if (position === INSERT_POSITION.BELOW || position === INSERT_POSITION.ABOVE) {
insertPosition = position;
}
this.setState({
insertPosition,
isShowAddSiblingPage: !this.state.isShowAddSiblingPage,
});
};
savePageProperties = () => {
const { name, id } = this.props.page;
const pageName = this.state.pageName.trim();
@ -134,6 +148,7 @@ class PageItem extends Component {
isOnlyOnePage={isOnlyOnePage}
page={Object.assign({}, pages.find(item => item.id === id), page)}
pageIndex={index}
parentPageId={this.props.page.id}
isEditMode={isEditMode}
duplicatePage={this.props.duplicatePage}
setCurrentPage={this.props.setCurrentPage}
@ -150,6 +165,7 @@ class PageItem extends Component {
setClassName={this.props.setClassName}
getClassName={this.props.getClassName}
layerDragProps={this.props.layerDragProps}
addSiblingPage={this.props.addSiblingPage}
/>
);
};
@ -176,6 +192,11 @@ class PageItem extends Component {
this.props.addPageInside(Object.assign({ parentPageId: this.props.page.id }, newPage));
};
onAddSiblingPage = (newPage) => {
const { page } = this.props;
this.props.addSiblingPage(newPage, this.props.parentPageId, this.state.insertPosition, page.id, this.toggleInsertSiblingPage);
};
getPageClassName = () => {
const { isOver, canDrop, isEditMode, layerDragProps } = this.props;
const isOverPage = isOver && canDrop;
@ -257,6 +278,7 @@ class PageItem extends Component {
toggleNameEditor={this.toggleNameEditor}
duplicatePage={this.props.duplicatePage}
onDeletePage={this.openDeleteDialog}
toggleInsertSiblingPage={this.toggleInsertSiblingPage}
/>
}
</div>
@ -275,9 +297,18 @@ class PageItem extends Component {
{this.state.isShowInsertPage &&
<AddNewPageDialog
toggle={this.toggleInsertPage}
getCurrentPageId={this.props.getCurrentPageId}
onAddNewPage={this.onAddNewPage}
title={gettext('Add page inside')}
page={this.props.page}
/>
}
{this.state.isShowAddSiblingPage &&
<AddNewPageDialog
toggle={this.toggleInsertSiblingPage}
onAddNewPage={this.onAddSiblingPage}
title={gettext('Add page')}
insertPosition={this.state.insertPosition}
page={this.props.page}
/>
}
</div>

View File

@ -19,6 +19,7 @@ class WikiNav extends Component {
onDeletePage: PropTypes.func,
onMovePage: PropTypes.func,
duplicatePage: PropTypes.func,
addSiblingPage: PropTypes.func,
getCurrentPageId: PropTypes.func,
addPageInside: PropTypes.func,
updateWikiConfig: PropTypes.func.isRequired,
@ -91,6 +92,7 @@ class WikiNav extends Component {
pathStr={page.id}
getCurrentPageId={this.props.getCurrentPageId}
addPageInside={this.props.addPageInside}
addSiblingPage={this.props.addSiblingPage}
getFoldState={this.getFoldState}
toggleExpand={this.toggleExpand}
id_page_map={id_page_map}

View File

@ -181,13 +181,16 @@ class WikiAPI {
return this.req.get(url);
}
createWiki2Page(wikiId, pageName, currentId) {
createWiki2Page(wikiId, pageName, currentId, insertPosition) {
const url = this.server + '/api/v2.1/wiki2/' + wikiId + '/pages/';
let form = new FormData();
form.append('page_name', pageName);
if (currentId) {
form.append('current_id', currentId);
}
if (insertPosition) {
form.append('insert_position', insertPosition);
}
return this._sendPostRequest(url, form);
}

View File

@ -500,6 +500,12 @@ class Wiki2PagesView(APIView):
error_msg = 'page_name invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
current_id = request.data.get('current_id', None)
insert_position = request.data.get('insert_position', None)
positions = ['above', 'below', 'inner']
if insert_position and insert_position not in positions:
error_msg = 'insert_position invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
wiki = Wiki.objects.get(wiki_id=wiki_id)
if not wiki:
@ -514,16 +520,15 @@ class Wiki2PagesView(APIView):
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
repo_id = wiki.repo_id
# resource check
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)
current_id = request.data.get('current_id', None)
wiki_config = get_wiki_config(repo_id, request.user.username)
navigation = wiki_config.get('navigation', [])
# side panel create Untitled page
if not current_id:
page_ids = {element.get('id') for element in navigation if element.get('type') != 'folder'}
else:
@ -537,22 +542,6 @@ class Wiki2PagesView(APIView):
new_file_name = page_name + '.sdoc'
parent_dir = os.path.join(WIKI_PAGES_DIR, str(sdoc_uuid))
path = os.path.join(parent_dir, new_file_name)
seafile_api.mkdir_with_parents(repo_id, '/', parent_dir.strip('/'), request.user.username)
# create new empty file
if not is_valid_dirent_name(new_file_name):
return api_error(status.HTTP_400_BAD_REQUEST, 'name invalid.')
try:
seafile_api.post_empty_file(repo_id, parent_dir, new_file_name, request.user.username)
except Exception as e:
if str(e) == 'Too many files in library.':
error_msg = _("The number of files in library exceeds the limit")
return api_error(HTTP_447_TOO_MANY_FILES_IN_LIBRARY, error_msg)
else:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
new_file_path = posixpath.join(parent_dir, new_file_name)
file_info = self.get_file_info(repo_id, new_file_path)
file_info['doc_uuid'] = sdoc_uuid
@ -565,7 +554,27 @@ class Wiki2PagesView(APIView):
id_set = get_all_wiki_ids(navigation)
new_page_id = gen_unique_id(id_set)
file_info['page_id'] = new_page_id
gen_new_page_nav_by_id(navigation, new_page_id, current_id)
is_find = [False]
gen_new_page_nav_by_id(navigation, new_page_id, current_id, insert_position, is_find)
if not is_find[0]:
error_msg = 'Current page does not exist'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# create new empty file
seafile_api.mkdir_with_parents(repo_id, '/', parent_dir.strip('/'), request.user.username)
if not is_valid_dirent_name(new_file_name):
return api_error(status.HTTP_400_BAD_REQUEST, 'name invalid.')
try:
seafile_api.post_empty_file(repo_id, parent_dir, new_file_name, request.user.username)
except Exception as e:
if str(e) == 'Too many files in library.':
error_msg = _("The number of files in library exceeds the limit")
return api_error(HTTP_447_TOO_MANY_FILES_IN_LIBRARY, error_msg)
else:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
new_page = {
'id': new_page_id,
'name': page_name,

View File

@ -173,22 +173,41 @@ def get_and_gen_page_nav_by_id(id_set, navigation, page_id, old_to_new):
get_and_gen_page_nav_by_id(id_set, new_navigation, page_id, old_to_new)
def gen_new_page_nav_by_id(navigation, page_id, current_id):
def gen_new_page_nav_by_id(navigation, page_id, current_id, insert_position, is_find):
new_nav = {
'id': page_id,
'type': 'page',
}
if current_id:
for nav in navigation:
if nav.get('type') == 'page' and nav.get('id') == current_id:
sub_nav = nav.get('children', [])
sub_nav.append(new_nav)
nav['children'] = sub_nav
return
else:
gen_new_page_nav_by_id(nav.get('children', []), page_id, current_id)
if insert_position == 'inner':
for nav in navigation:
if nav.get('type') == 'page' and nav.get('id') == current_id:
sub_nav = nav.get('children', [])
sub_nav.append(new_nav)
nav['children'] = sub_nav
is_find[0] = True
return True
else:
gen_new_page_nav_by_id(nav.get('children', []), page_id, current_id, insert_position, is_find)
elif insert_position == 'above':
for index, nav in enumerate(navigation):
if nav.get('type') == 'page' and nav.get('id') == current_id:
navigation.insert(index, new_nav)
is_find[0] = True
return True
else:
gen_new_page_nav_by_id(nav.get('children', []), page_id, current_id, insert_position, is_find)
elif insert_position == 'below':
for index, nav in enumerate(navigation):
if nav.get('type') == 'page' and nav.get('id') == current_id:
navigation.insert(index+1, new_nav)
is_find[0] = True
return True
else:
gen_new_page_nav_by_id(nav.get('children', []), page_id, current_id, insert_position, is_find)
else:
navigation.append(new_nav)
return True
def get_current_level_page_ids(navigation, page_id, ids=[]):