1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-15 13:43:36 +00:00

migrate old wiki to new wiki (#6987)

* migrate old wiki to new wiki

* change style

---------

Co-authored-by: Michael An <2331806369@qq.com>
This commit is contained in:
JoinTyang 2024-11-05 18:03:53 +08:00 committed by GitHub
parent 2cb758e302
commit cc625f7815
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 351 additions and 23 deletions

View File

@ -1,5 +1,5 @@
import SeahubSelect from './seahub-select'; import SeahubSelect from './seahub-select';
import { NoGroupMessage } from './no-group-message'; import { NoGroupMessage } from './no-group-message';
import { MenuSelectStyle, UserSelectStyle } from './seahub-select-style'; import { MenuSelectStyle, UserSelectStyle, NoOptionsStyle } from './seahub-select-style';
export { SeahubSelect, NoGroupMessage, MenuSelectStyle, UserSelectStyle }; export { SeahubSelect, NoGroupMessage, MenuSelectStyle, UserSelectStyle, NoOptionsStyle };

View File

@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { gettext } from '../../../utils/constants'; import { gettext } from '../../../utils/constants';
import { NoOptionsStyle } from './seahub-select-style';
const NoGroupMessage = (props) => { const NoGroupMessage = (props) => {
return ( return (
<div {...props.innerProps} style={{ margin: '6px 10px', textAlign: 'center', color: 'hsl(0,0%,50%)' }}>{gettext('Group not found')}</div> <div {...props.innerProps} style={NoOptionsStyle}>{gettext('Group not found')}</div>
); );
}; };

View File

@ -87,4 +87,10 @@ const UserSelectStyle = {
}, },
}; };
export { MenuSelectStyle, UserSelectStyle }; const NoOptionsStyle = {
margin: '6px 10px',
textAlign: 'center',
color: 'hsl(0, 0%, 50%)',
};
export { MenuSelectStyle, UserSelectStyle, NoOptionsStyle };

View File

@ -5,7 +5,7 @@ import { gettext, isPro } from '../../utils/constants';
import wikiAPI from '../../utils/wiki-api'; import wikiAPI from '../../utils/wiki-api';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import toaster from '../toast'; import toaster from '../toast';
import { SeahubSelect } from '../common/select'; import { SeahubSelect, NoOptionsStyle } from '../common/select';
const propTypes = { const propTypes = {
toggleCancel: PropTypes.func.isRequired, toggleCancel: PropTypes.func.isRequired,
@ -97,7 +97,7 @@ class AddWikiDialog extends React.Component {
maxMenuHeight={200} maxMenuHeight={200}
value={this.state.selectedOption} value={this.state.selectedOption}
components={{ NoOptionsMessage: ( components={{ NoOptionsMessage: (
<div style={{ margin: '6px 10px', textAlign: 'center', color: 'hsl(0,0%,50%)' }}>{gettext('No department')}</div> <div style={NoOptionsStyle}>{gettext('No department')}</div>
) }} ) }}
noOptionsMessage={() => {return gettext('No options available');}} noOptionsMessage={() => {return gettext('No options available');}}
/> />

View File

@ -0,0 +1,114 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Label } from 'reactstrap';
import { gettext, isPro } from '../../utils/constants';
import wikiAPI from '../../utils/wiki-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import { SeahubSelect, NoOptionsStyle } from '../common/select';
const propTypes = {
toggleCancel: PropTypes.func.isRequired,
convertWiki: PropTypes.func.isRequired,
wiki: PropTypes.object.isRequired,
};
class ConvertWikiDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
isSubmitBtnActive: false,
selectedOption: null,
options: [],
};
}
componentDidMount() {
if (!isPro) return;
wikiAPI.listWikiDepartments().then(res => {
const departments = res.data.sort((a, b) => {
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
});
let options = [];
for (let i = 0 ; i < departments.length; i++) {
let obj = {};
obj.value = departments[i].name;
obj.id = departments[i].id;
obj.email = departments[i].email;
obj.label = departments[i].name;
options.push(obj);
}
this.setState({ options });
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
}
inputNewName = (e) => {
this.setState({
name: e.target.value,
});
};
handleKeyDown = (e) => {
if (e.key === 'Enter') {
this.handleSubmit(e);
}
};
handleSubmit = (e) => {
const wikiName = this.state.name.trim();
const departmentID = this.state.selectedOption ? this.state.selectedOption.id : null;
if (!wikiName) return;
this.props.convertWiki(this.props.wiki, wikiName, departmentID);
this.props.toggleCancel(e);
};
toggle = () => {
this.props.toggleCancel();
};
handleSelectChange = (option) => {
this.setState({ selectedOption: option });
};
render() {
return (
<Modal isOpen={true} autoFocus={false} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{gettext('Convert Wiki')}</ModalHeader>
<ModalBody>
<Label>{gettext('Name')}</Label>
<Input onKeyDown={this.handleKeyDown} autoFocus={true} value={this.state.name} onChange={this.inputNewName}/>
{isPro &&
<>
<Label className='mt-4'>{gettext('Wiki owner')} ({gettext('Optional')})</Label>
<SeahubSelect
onChange={this.handleSelectChange}
options={this.state.options}
hideSelectedOptions={true}
placeholder={gettext('Select a department')}
maxMenuHeight={200}
value={this.state.selectedOption}
components={{ NoOptionsMessage: (
<div style={NoOptionsStyle}>{gettext('No department')}</div>
) }}
noOptionsMessage={() => {return gettext('No options available');}}
/>
</>
}
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.handleSubmit} disabled={!this.state.name.trim()}>{gettext('Submit')}</Button>
</ModalFooter>
</Modal>
);
}
}
ConvertWikiDialog.propTypes = propTypes;
export default ConvertWikiDialog;

View File

@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const propTypes = { const propTypes = {
children: PropTypes.object.isRequired, children: PropTypes.any.isRequired,
}; };
const modalRoot = document.getElementById('modal-wrapper'); const modalRoot = document.getElementById('modal-wrapper');

View File

@ -5,7 +5,7 @@ import { seafileAPI } from '../utils/seafile-api';
import { gettext, enableShowContactEmailWhenSearchUser, enableShowLoginIDWhenSearchUser } from '../utils/constants'; import { gettext, enableShowContactEmailWhenSearchUser, enableShowLoginIDWhenSearchUser } from '../utils/constants';
import { Utils } from '../utils/utils'; import { Utils } from '../utils/utils';
import toaster from './toast'; import toaster from './toast';
import { UserSelectStyle } from './common/select'; import { UserSelectStyle, NoOptionsStyle } from './common/select';
import '../css/user-select.css'; import '../css/user-select.css';
@ -86,7 +86,6 @@ class UserSelect extends React.Component {
render() { render() {
const searchValue = this.state.searchValue; const searchValue = this.state.searchValue;
const style = { margin: '6px 10px', textAlign: 'center', color: 'hsl(0,0%,50%)' };
return ( return (
<AsyncSelect <AsyncSelect
isClearable isClearable
@ -94,7 +93,9 @@ class UserSelect extends React.Component {
components={{ components={{
NoOptionsMessage: (props) => { NoOptionsMessage: (props) => {
return ( return (
<div {...props.innerProps} style={style}>{searchValue ? gettext('User not found') : gettext('Enter characters to start searching')}</div> <div {...props.innerProps} style={NoOptionsStyle}>
{searchValue ? gettext('User not found') : gettext('Enter characters to start searching')}
</div>
); );
} }
}} }}

View File

@ -14,6 +14,7 @@ const propTypes = {
isDepartment: PropTypes.bool.isRequired, isDepartment: PropTypes.bool.isRequired,
isShowAvatar: PropTypes.bool.isRequired, isShowAvatar: PropTypes.bool.isRequired,
renameWiki: PropTypes.func.isRequired, renameWiki: PropTypes.func.isRequired,
convertWiki: PropTypes.func,
toggelAddWikiDialog: PropTypes.func, toggelAddWikiDialog: PropTypes.func,
sidePanelRate: PropTypes.number, sidePanelRate: PropTypes.number,
isSidePanelFolded: PropTypes.bool, isSidePanelFolded: PropTypes.bool,
@ -78,7 +79,10 @@ class WikiCardGroup extends Component {
isDepartment={isDepartment} isDepartment={isDepartment}
isShowAvatar={this.props.isShowAvatar} isShowAvatar={this.props.isShowAvatar}
renameWiki={this.props.renameWiki} renameWiki={this.props.renameWiki}
/> : <WikiCardItem convertWiki={this.props.convertWiki}
/>
:
<WikiCardItem
key={index + wiki.id + wiki.name} key={index + wiki.id + wiki.name}
wiki={wiki} wiki={wiki}
deleteWiki={this.props.deleteWiki} deleteWiki={this.props.deleteWiki}
@ -86,6 +90,7 @@ class WikiCardGroup extends Component {
isDepartment={isDepartment} isDepartment={isDepartment}
isShowAvatar={this.props.isShowAvatar} isShowAvatar={this.props.isShowAvatar}
renameWiki={this.props.renameWiki} renameWiki={this.props.renameWiki}
convertWiki={this.props.convertWiki}
/> />
); );
})} })}

View File

@ -11,6 +11,7 @@ import ShareWikiDialog from '../dialog/share-wiki-dialog';
import PublishWikiDialog from '../dialog/publish-wiki-dialog'; import PublishWikiDialog from '../dialog/publish-wiki-dialog';
import wikiAPI from '../../utils/wiki-api'; import wikiAPI from '../../utils/wiki-api';
import toaster from '../toast'; import toaster from '../toast';
import ConvertWikiDialog from '../dialog/convert-wiki-dialog';
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
@ -20,6 +21,7 @@ const propTypes = {
deleteWiki: PropTypes.func.isRequired, deleteWiki: PropTypes.func.isRequired,
unshareGroupWiki: PropTypes.func.isRequired, unshareGroupWiki: PropTypes.func.isRequired,
renameWiki: PropTypes.func.isRequired, renameWiki: PropTypes.func.isRequired,
convertWiki: PropTypes.func,
isDepartment: PropTypes.bool.isRequired, isDepartment: PropTypes.bool.isRequired,
isShowAvatar: PropTypes.bool.isRequired, isShowAvatar: PropTypes.bool.isRequired,
}; };
@ -33,6 +35,7 @@ class WikiCardItem extends Component {
isItemMenuShow: false, isItemMenuShow: false,
isShowShareDialog: false, isShowShareDialog: false,
isShowPublishDialog: false, isShowPublishDialog: false,
isShowConvertDialog: false,
customUrl: '', customUrl: '',
}; };
} }
@ -56,6 +59,13 @@ class WikiCardItem extends Component {
}); });
}; };
onConvertToggle = (e) => {
e && e.preventDefault();
this.setState({
isShowConvertDialog: !this.state.isShowConvertDialog,
});
};
onPublishToggle = (e) => { onPublishToggle = (e) => {
this.getPublishWikiLink(); this.getPublishWikiLink();
}; };
@ -176,6 +186,7 @@ class WikiCardItem extends Component {
let showLeaveShare = false; let showLeaveShare = false;
let showDropdownMenu = false; let showDropdownMenu = false;
let showPublish = false; let showPublish = false;
let showWikiConvert = false;
if (isDepartment) { if (isDepartment) {
if (isAdmin) { if (isAdmin) {
@ -184,6 +195,9 @@ class WikiCardItem extends Component {
showShare = true; showShare = true;
showRename = true; showRename = true;
showPublish = true; showPublish = true;
if (isOldVersion) {
showWikiConvert = true;
}
} else { } else {
showLeaveShare = true; showLeaveShare = true;
} }
@ -194,6 +208,9 @@ class WikiCardItem extends Component {
showDelete = true; showDelete = true;
showRename = true; showRename = true;
showPublish = true; showPublish = true;
if (isOldVersion) {
showWikiConvert = true;
}
} else { } else {
showLeaveShare = true; showLeaveShare = true;
} }
@ -239,6 +256,9 @@ class WikiCardItem extends Component {
{showDelete && {showDelete &&
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Delete')}</DropdownItem> <DropdownItem onClick={this.onDeleteToggle}>{gettext('Delete')}</DropdownItem>
} }
{showWikiConvert &&
<DropdownItem onClick={this.onConvertToggle}>{gettext('Convert to new Wiki')}</DropdownItem>
}
{showLeaveShare && {showLeaveShare &&
<DropdownItem onClick={this.onDeleteToggle}>{gettext('Leave')}</DropdownItem> <DropdownItem onClick={this.onDeleteToggle}>{gettext('Leave')}</DropdownItem>
} }
@ -327,6 +347,15 @@ class WikiCardItem extends Component {
/> />
</ModalPortal> </ModalPortal>
} }
{this.state.isShowConvertDialog &&
<ModalPortal>
<ConvertWikiDialog
toggleCancel={this.onConvertToggle}
convertWiki={this.props.convertWiki}
wiki={this.props.wiki}
/>
</ModalPortal>
}
</> </>
); );
} }

View File

@ -14,6 +14,7 @@ const propTypes = {
renameWiki: PropTypes.func.isRequired, renameWiki: PropTypes.func.isRequired,
leaveSharedWiki: PropTypes.func.isRequired, leaveSharedWiki: PropTypes.func.isRequired,
unshareGroupWiki: PropTypes.func.isRequired, unshareGroupWiki: PropTypes.func.isRequired,
convertWiki: PropTypes.func.isRequired,
toggelAddWikiDialog: PropTypes.func, toggelAddWikiDialog: PropTypes.func,
sidePanelRate: PropTypes.number, sidePanelRate: PropTypes.number,
isSidePanelFolded: PropTypes.bool, isSidePanelFolded: PropTypes.bool,
@ -132,6 +133,7 @@ class WikiCardView extends Component {
deleteWiki={this.props.deleteWiki} deleteWiki={this.props.deleteWiki}
renameWiki={this.props.renameWiki} renameWiki={this.props.renameWiki}
unshareGroupWiki={this.props.unshareGroupWiki} unshareGroupWiki={this.props.unshareGroupWiki}
convertWiki={this.props.convertWiki}
isSidePanelFolded={isSidePanelFolded} isSidePanelFolded={isSidePanelFolded}
sidePanelRate={sidePanelRate} sidePanelRate={sidePanelRate}
wikis={v1Wikis} wikis={v1Wikis}

View File

@ -249,6 +249,17 @@ class Wikis extends Component {
} }
}; };
convertWiki = (wiki, wikiName, departmentID) => {
wikiAPI.convertWiki(wiki.id, wikiName, departmentID).then((res) => {
this.getWikis();
}).catch((error) => {
if (error.response) {
let errorMsg = error.response.data.error_msg;
toaster.danger(errorMsg);
}
});
};
toggleDropdownMenu = (e) => { toggleDropdownMenu = (e) => {
e.stopPropagation(); e.stopPropagation();
this.setState({ this.setState({
@ -299,6 +310,7 @@ class Wikis extends Component {
leaveSharedWiki={this.leaveSharedWiki} leaveSharedWiki={this.leaveSharedWiki}
unshareGroupWiki={this.unshareGroupWiki} unshareGroupWiki={this.unshareGroupWiki}
renameWiki={this.renameWiki} renameWiki={this.renameWiki}
convertWiki={this.convertWiki}
toggelAddWikiDialog={this.toggelAddWikiDialog} toggelAddWikiDialog={this.toggelAddWikiDialog}
sidePanelRate={this.props.sidePanelRate} sidePanelRate={this.props.sidePanelRate}
isSidePanelFolded={this.props.isSidePanelFolded} isSidePanelFolded={this.props.isSidePanelFolded}

View File

@ -280,6 +280,16 @@ class WikiAPI {
return this.req.delete(url); return this.req.delete(url);
} }
convertWiki(oldWikiId, wikiName, owner) {
const url = this.server + '/api/v2.1/convert-wiki/';
let form = new FormData();
form.append('old_wiki_id', oldWikiId);
form.append('name', wikiName);
if (owner) {
form.append('owner', owner);
}
return this._sendPostRequest(url, form);
}
} }
let wikiAPI = new WikiAPI(); let wikiAPI = new WikiAPI();

View File

@ -25,11 +25,12 @@ from seahub.api2.utils import api_error, is_wiki_repo
from seahub.api2.endpoints.utils import wiki_search from seahub.api2.endpoints.utils import wiki_search
from seahub.utils.db_api import SeafileDB from seahub.utils.db_api import SeafileDB
from seahub.wiki2.models import Wiki2 as Wiki from seahub.wiki2.models import Wiki2 as Wiki
from seahub.wiki.models import Wiki as OldWiki
from seahub.wiki2.models import WikiPageTrash, Wiki2Publish from seahub.wiki2.models import WikiPageTrash, Wiki2Publish
from seahub.wiki2.utils import is_valid_wiki_name, get_wiki_config, WIKI_PAGES_DIR, is_group_wiki, \ from seahub.wiki2.utils import is_valid_wiki_name, get_wiki_config, WIKI_PAGES_DIR, is_group_wiki, \
check_wiki_admin_permission, check_wiki_permission, get_all_wiki_ids, get_and_gen_page_nav_by_id, \ check_wiki_admin_permission, check_wiki_permission, get_all_wiki_ids, get_and_gen_page_nav_by_id, \
get_current_level_page_ids, save_wiki_config, gen_unique_id, gen_new_page_nav_by_id, pop_nav, \ get_current_level_page_ids, save_wiki_config, gen_unique_id, gen_new_page_nav_by_id, pop_nav, \
delete_page, move_nav, revert_nav, get_sub_ids_by_page_id, get_parent_id_stack delete_page, move_nav, revert_nav, get_sub_ids_by_page_id, get_parent_id_stack, add_convert_wiki_task
from seahub.utils import is_org_context, get_user_repos, is_pro_version, is_valid_dirent_name, \ from seahub.utils import is_org_context, get_user_repos, is_pro_version, is_valid_dirent_name, \
get_no_duplicate_obj_name get_no_duplicate_obj_name
@ -37,7 +38,7 @@ 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 from seahub.utils.repo import get_repo_owner, is_valid_repo_id_format, is_group_repo_staff, is_repo_owner
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
@ -51,6 +52,7 @@ from seahub.group.utils import group_id_to_name, is_group_admin
from seahub.utils.rpc import SeafileAPI from seahub.utils.rpc import SeafileAPI
from seahub.constants import PERMISSION_READ_WRITE from seahub.constants import PERMISSION_READ_WRITE
from seaserv import ccnet_api from seaserv import ccnet_api
from seahub.share.utils import is_repo_admin
HTTP_520_OPERATION_FAILED = 520 HTTP_520_OPERATION_FAILED = 520
@ -1311,3 +1313,136 @@ class WikiSearch(APIView):
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error') return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
return Response(resp_json, resp.status_code) return Response(resp_json, resp.status_code)
class WikiConvertView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
def post(self, request):
old_wiki_id = request.data.get('old_wiki_id', None)
if not old_wiki_id:
error_msg = 'old_wiki_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
wiki = OldWiki.objects.get(id=old_wiki_id)
except OldWiki.DoesNotExist:
error_msg = 'Old Wiki not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
username = request.user.username
old_repo_id = wiki.repo_id
# check old wiki permission
is_owner = is_repo_owner(request, old_repo_id, username)
if not is_owner:
repo_admin = is_repo_admin(username, old_repo_id)
if not repo_admin:
is_group_repo_admin = is_group_repo_staff(request, old_repo_id, username)
if not is_group_repo_admin:
error_msg = _('Permission denied.')
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if wiki.username != username:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not request.user.permissions.can_add_repo():
return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to create library.')
wiki_name = request.data.get("name", None)
if not wiki_name:
return api_error(status.HTTP_400_BAD_REQUEST, 'wiki name is required.')
if not is_valid_wiki_name(wiki_name):
msg = _('Name can only contain letters, numbers, blank, hyphen or underscore.')
return api_error(status.HTTP_400_BAD_REQUEST, msg)
old_repo_id = wiki.repo_id
repo = seafile_api.get_repo(old_repo_id)
if not repo:
error_msg = 'Library %s not found.' % old_repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
wiki_owner = request.data.get('owner', 'me')
is_group_owner = False
group_id = ''
if wiki_owner == 'me':
wiki_owner = request.user.username
else:
try:
group_id = int(wiki_owner)
wiki_owner = "%s@seafile_group" % group_id
except:
return api_error(status.HTTP_400_BAD_REQUEST, 'wiki_owner invalid')
is_group_owner = True
org_id = -1
if is_org_context(request):
org_id = request.user.org.org_id
permission = PERMISSION_READ_WRITE
if is_group_owner:
group_id = int(group_id)
# only group admin can create wiki
if not is_group_admin(group_id, request.user.username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
group_quota = seafile_api.get_group_quota(group_id)
group_quota = int(group_quota)
if group_quota <= 0 and group_quota != -2:
error_msg = 'No group quota.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# create group owned repo
group_id = int(group_id)
password = None
if is_pro_version() and ENABLE_STORAGE_CLASSES:
if STORAGE_CLASS_MAPPING_POLICY in ('USER_SELECT', 'ROLE_BASED'):
storage_id = None
repo_id = seafile_api.add_group_owned_repo(group_id,
wiki_name,
permission,
password,
enc_version=ENCRYPTED_LIBRARY_VERSION,
storage_id=storage_id)
else:
# STORAGE_CLASS_MAPPING_POLICY == 'REPO_ID_MAPPING'
repo_id = SeafileAPI.add_group_owned_repo(
group_id, wiki_name, password, permission, org_id=org_id)
else:
repo_id = SeafileAPI.add_group_owned_repo(
group_id, wiki_name, password, permission, org_id=org_id)
else:
if org_id and org_id > 0:
repo_id = seafile_api.create_org_repo(wiki_name, '', wiki_owner, org_id)
else:
repo_id = seafile_api.create_repo(wiki_name, '', wiki_owner)
try:
seafile_db_api = SeafileDB()
seafile_db_api.set_repo_type(repo_id, 'wiki')
except Exception as e:
logger.error(e)
msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, msg)
params = {
'old_repo_id': old_repo_id,
'new_repo_id': repo_id,
'username': request.user.username,
}
try:
task_id = add_convert_wiki_task(params=params)
except Exception as e:
logger.error(e)
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Internal Server Error')
return Response({"task_id": task_id})

View File

@ -210,7 +210,7 @@ from seahub.ocm.settings import OCM_ENDPOINT
from seahub.wiki2.views import wiki_view, wiki_publish_view from seahub.wiki2.views import wiki_view, wiki_publish_view
from seahub.api2.endpoints.wiki2 import Wikis2View, Wiki2View, Wiki2ConfigView, Wiki2PagesView, Wiki2PageView, \ from seahub.api2.endpoints.wiki2 import Wikis2View, Wiki2View, Wiki2ConfigView, Wiki2PagesView, Wiki2PageView, \
Wiki2DuplicatePageView, WikiPageTrashView, Wiki2PublishView, Wiki2PublishConfigView, Wiki2PublishPageView, \ Wiki2DuplicatePageView, WikiPageTrashView, Wiki2PublishView, Wiki2PublishConfigView, Wiki2PublishPageView, \
WikiSearch WikiSearch, WikiConvertView
from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView from seahub.api2.endpoints.subscription import SubscriptionView, SubscriptionPlansView, SubscriptionLogsView
from seahub.api2.endpoints.metadata_manage import MetadataRecords, MetadataManage, MetadataColumns, MetadataRecordInfo, \ from seahub.api2.endpoints.metadata_manage import MetadataRecords, MetadataManage, MetadataColumns, MetadataRecordInfo, \
MetadataViews, MetadataViewsMoveView, MetadataViewsDetailView, MetadataViewsDuplicateView, FacesRecords, \ MetadataViews, MetadataViewsMoveView, MetadataViewsDetailView, MetadataViewsDuplicateView, FacesRecords, \
@ -558,7 +558,7 @@ urlpatterns = [
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/trash/', WikiPageTrashView.as_view(), name='api-v2.1-wiki2-trash'), re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/trash/', WikiPageTrashView.as_view(), name='api-v2.1-wiki2-trash'),
re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/publish/$', Wiki2PublishView.as_view(), name='api-v2.1-wiki2-publish'), re_path(r'^api/v2.1/wiki2/(?P<wiki_id>[-0-9a-f]{36})/publish/$', Wiki2PublishView.as_view(), name='api-v2.1-wiki2-publish'),
re_path(r'^api/v2.1/wiki2/search/$', WikiSearch.as_view(), name='api-v2.1-wiki2-search'), re_path(r'^api/v2.1/wiki2/search/$', WikiSearch.as_view(), name='api-v2.1-wiki2-search'),
re_path(r'^api/v2.1/convert-wiki/$', WikiConvertView.as_view(), name='api-v2.1-wiki-convert'),
## user::drafts ## user::drafts
re_path(r'^api/v2.1/drafts/$', DraftsView.as_view(), name='api-v2.1-drafts'), re_path(r'^api/v2.1/drafts/$', DraftsView.as_view(), name='api-v2.1-drafts'),
re_path(r'^api/v2.1/drafts/(?P<pk>\d+)/$', DraftView.as_view(), name='api-v2.1-draft'), re_path(r'^api/v2.1/drafts/(?P<pk>\d+)/$', DraftView.as_view(), name='api-v2.1-draft'),

View File

@ -8,12 +8,16 @@ import json
import requests import requests
import posixpath import posixpath
import random import random
import jwt
import time
from urllib.parse import urljoin
from seaserv import seafile_api from seaserv import seafile_api
from seahub.constants import PERMISSION_READ_WRITE from seahub.constants import PERMISSION_READ_WRITE
from seahub.utils import gen_inner_file_get_url, gen_file_upload_url from seahub.utils import gen_inner_file_get_url, gen_file_upload_url
from seahub.group.utils import is_group_admin, is_group_member from seahub.group.utils import is_group_admin, is_group_member
from seahub.wiki2.models import WikiPageTrash from seahub.wiki2.models import WikiPageTrash
from seahub.settings import SECRET_KEY, SEAFEVENTS_SERVER_URL
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -318,3 +322,12 @@ def get_parent_id_stack(navigation, page_id):
return_parent_page_id(navigation, page_id, id_list) return_parent_page_id(navigation, page_id, id_list)
return id_list return id_list
def add_convert_wiki_task(params):
payload = {'exp': int(time.time()) + 300, }
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
headers = {"Authorization": "Token %s" % token}
url = urljoin(SEAFEVENTS_SERVER_URL, '/add-convert-wiki-task')
resp = requests.get(url, params=params, headers=headers)
return json.loads(resp.content)['task_id']