diff --git a/frontend/src/pages/wiki2/css/wiki-nav.css b/frontend/src/pages/wiki2/css/wiki-nav.css
index 35e7025a0e..7c7e2dedb4 100644
--- a/frontend/src/pages/wiki2/css/wiki-nav.css
+++ b/frontend/src/pages/wiki2/css/wiki-nav.css
@@ -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;
}
diff --git a/frontend/src/pages/wiki2/side-panel.js b/frontend/src/pages/wiki2/side-panel.js
index bbb0c46286..0932ceb63a 100644
--- a/frontend/src/pages/wiki2/side-panel.js
+++ b/frontend/src/pages/wiki2/side-panel.js
@@ -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}
/>
}
diff --git a/frontend/src/pages/wiki2/wiki-nav/add-new-page-dialog.js b/frontend/src/pages/wiki2/wiki-nav/add-new-page-dialog.js
index c22d62a7e2..fd2d09d9d1 100644
--- a/frontend/src/pages/wiki2/wiki-nav/add-new-page-dialog.js
+++ b/frontend/src/pages/wiki2/wiki-nav/add-new-page-dialog.js
@@ -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;
diff --git a/frontend/src/pages/wiki2/wiki-nav/constants.js b/frontend/src/pages/wiki2/wiki-nav/constants.js
new file mode 100644
index 0000000000..7ad3caedb4
--- /dev/null
+++ b/frontend/src/pages/wiki2/wiki-nav/constants.js
@@ -0,0 +1,5 @@
+export const INSERT_POSITION = {
+ ABOVE: 'above',
+ BELOW: 'below',
+ INNER: 'inner',
+};
diff --git a/frontend/src/pages/wiki2/wiki-nav/page-utils.js b/frontend/src/pages/wiki2/wiki-nav/page-utils.js
index 4244f20e62..829b613620 100644
--- a/frontend/src/pages/wiki2/wiki-nav/page-utils.js
+++ b/frontend/src/pages/wiki2/wiki-nav/page-utils.js
@@ -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 });
});
}
diff --git a/frontend/src/pages/wiki2/wiki-nav/pages/page-dropdownmenu.js b/frontend/src/pages/wiki2/wiki-nav/pages/page-dropdownmenu.js
index 21c6e07372..da9d82d35f 100644
--- a/frontend/src/pages/wiki2/wiki-nav/pages/page-dropdownmenu.js
+++ b/frontend/src/pages/wiki2/wiki-nav/pages/page-dropdownmenu.js
@@ -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 {
{gettext('Modify name')}
+
+
+ {gettext('Add page above')}
+
+
+
+ {gettext('Add page below')}
+
{gettext('Duplicate page')}
diff --git a/frontend/src/pages/wiki2/wiki-nav/pages/page-item.js b/frontend/src/pages/wiki2/wiki-nav/pages/page-item.js
index d255135faf..6f3d9067a3 100644
--- a/frontend/src/pages/wiki2/wiki-nav/pages/page-item.js
+++ b/frontend/src/pages/wiki2/wiki-nav/pages/page-item.js
@@ -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}
/>
}
@@ -275,9 +297,18 @@ class PageItem extends Component {
{this.state.isShowInsertPage &&
+ }
+ {this.state.isShowAddSiblingPage &&
+
}
diff --git a/frontend/src/pages/wiki2/wiki-nav/wiki-nav.js b/frontend/src/pages/wiki2/wiki-nav/wiki-nav.js
index 18d8894a2c..b4ba9b7645 100644
--- a/frontend/src/pages/wiki2/wiki-nav/wiki-nav.js
+++ b/frontend/src/pages/wiki2/wiki-nav/wiki-nav.js
@@ -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}
diff --git a/frontend/src/utils/wiki-api.js b/frontend/src/utils/wiki-api.js
index 34b6eefbfb..7a635e1fa6 100644
--- a/frontend/src/utils/wiki-api.js
+++ b/frontend/src/utils/wiki-api.js
@@ -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);
}
diff --git a/seahub/api2/endpoints/wiki2.py b/seahub/api2/endpoints/wiki2.py
index cbee2dac25..56ecd9d73e 100644
--- a/seahub/api2/endpoints/wiki2.py
+++ b/seahub/api2/endpoints/wiki2.py
@@ -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,
diff --git a/seahub/wiki2/utils.py b/seahub/wiki2/utils.py
index c39314567b..8e50befb88 100644
--- a/seahub/wiki2/utils.py
+++ b/seahub/wiki2/utils.py
@@ -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=[]):