Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add wiki support owner department #6113

Merged
merged 2 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions frontend/src/components/dialog/add-wiki-dialog.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Label } from 'reactstrap';
import { gettext } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
import { SeahubSelect } from '../common/select';

const propTypes = {
toggleCancel: PropTypes.func.isRequired,
Expand All @@ -15,9 +19,35 @@ class AddWikiDialog extends React.Component {
this.state = {
name: '',
isSubmitBtnActive: false,
selectedOption: null,
options: [],
};
}

componentDidMount() {
this.listDepartments();
}

listDepartments = () => {
seafileAPI.listDepartments().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.label = departments[i].name;
options.push(obj);
}
this.setState({options: options});
}).catch(error => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
});
};

inputNewName = (e) => {
this.setState({
name: e.target.value,
Expand All @@ -33,22 +63,39 @@ class AddWikiDialog extends React.Component {

handleSubmit = () => {
const wikiName = this.state.name.trim();
const departmentID = this.state.selectedOption ? this.state.selectedOption.id : null;
if (!wikiName) return;
this.props.addWiki(wikiName);
this.props.addWiki(wikiName, departmentID);
this.props.toggleCancel();
};

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('Add Wiki')}</ModalHeader>
<ModalBody>
<Label>{gettext('Name')}</Label>
<Input onKeyDown={this.handleKeyDown} autoFocus={true} value={this.state.name} onChange={this.inputNewName}/>
<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={{margin: '6px 10px', textAlign: 'center', color: 'hsl(0,0%,50%)'}}>{gettext('No department')}</div>
) }}
/>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
Expand Down
5 changes: 1 addition & 4 deletions frontend/src/components/dialog/transfer-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,11 @@ class TransferDialog extends React.Component {
</Fragment>
);
};
render() {


render() {
const { itemName: repoName } = this.props;
let title = gettext('Transfer Library {library_name}');
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');


return (
<Modal isOpen={true} style={{maxWidth: '720px'}} toggle={this.props.toggleDialog} className="transfer-dialog">
<ModalHeader toggle={this.props.toggleDialog}>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/wikis/wikis.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ class Wikis extends Component {
this.setState({isShowAddDialog: !this.state.isShowAddDialog});
};

addWiki = (wikiName) => {
wikiAPI.addWiki2(wikiName).then((res) => {
addWiki = (wikiName, departmentID) => {
wikiAPI.addWiki2(wikiName, departmentID).then((res) => {
let wikis = this.state.wikis.slice(0);
let new_wiki = res.data;
new_wiki['version'] = 'v2';
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/utils/wiki-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,13 @@ class WikiAPI {
});
}

addWiki2(wikiName) {
addWiki2(wikiName, owner) {
const url = this.server + '/api/v2.1/wikis2/';
let form = new FormData();
form.append('name', wikiName);
if (owner) {
form.append('owner', owner);
}
return this._sendPostRequest(url, form);
}

Expand Down
19 changes: 15 additions & 4 deletions seahub/api2/endpoints/departments.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
import seaserv
from seaserv import ccnet_api

from seahub.api2.utils import api_error
from seahub.api2.utils import api_error, to_python_boolean
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, get_default_group_avatar_url
from seahub.utils import is_pro_version
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.group.utils import is_group_member
from seahub.group.utils import is_group_member, is_group_admin
from seahub.avatar.settings import GROUP_AVATAR_DEFAULT_SIZE

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -47,13 +47,24 @@ def get(self, request):
except ValueError:
avatar_size = GROUP_AVATAR_DEFAULT_SIZE

can_admin = request.GET.get('can_admin', 'false')

try:
can_admin = to_python_boolean(can_admin)
except:
return api_error(status.HTTP_400_BAD_REQUEST, 'can_admin invalid')

result = []
for department in departments:
department = seaserv.get_group(department.id)

username = request.user.username
if not is_group_member(department.id, username):
continue
if can_admin:
if not is_group_admin(department.id, username):
continue
else:
if not is_group_member(department.id, username):
continue

try:
avatar_url, is_default, date_uploaded = api_grp_avatar_url(department.id, avatar_size)
Expand Down
116 changes: 96 additions & 20 deletions seahub/api2/endpoints/wiki2.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
from seahub.api2.utils import api_error, to_python_boolean
from seahub.wiki2.models import Wiki2 as Wiki
from seahub.wiki2.utils import is_valid_wiki_name, can_edit_wiki, get_wiki_dirs_by_path, \
get_wiki_config, WIKI_PAGES_DIR, WIKI_CONFIG_PATH, WIKI_CONFIG_FILE_NAME
get_wiki_config, WIKI_PAGES_DIR, WIKI_CONFIG_PATH, WIKI_CONFIG_FILE_NAME, is_group_wiki, \
check_wiki_admin_permission, check_wiki_permission
from seahub.utils import is_org_context, get_user_repos, gen_inner_file_get_url, gen_file_upload_url, \
normalize_dir_path, is_pro_version, check_filename_with_rename, is_valid_dirent_name
from seahub.views import check_folder_permission
Expand All @@ -32,13 +33,17 @@
from seahub.utils.file_op import check_file_lock, ONLINE_OFFICE_LOCK_OWNER, if_locked_by_online_office
from seahub.utils.repo import parse_repo_perm
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token
from seahub.settings import SEADOC_SERVER_URL
from seahub.settings import SEADOC_SERVER_URL, ENABLE_STORAGE_CLASSES, STORAGE_CLASS_MAPPING_POLICY, \
ENCRYPTED_LIBRARY_VERSION
from seahub.seadoc.sdoc_server_api import SdocServerAPI
from seahub.utils.timeutils import timestamp_to_isoformat_timestr
from seahub.tags.models import FileUUIDMap
from seahub.seadoc.models import SeadocHistoryName, SeadocDraft, SeadocCommentReply
from seahub.base.models import FileComment
from seahub.api2.views import HTTP_447_TOO_MANY_FILES_IN_LIBRARY
from seahub.group.utils import group_id_to_name, is_group_admin
from seahub.utils.rpc import SeafileAPI
from seahub.constants import PERMISSION_READ_WRITE

HTTP_520_OPERATION_FAILED = 520

Expand Down Expand Up @@ -95,6 +100,10 @@ def get(self, request, format=None):
wiki_list = []
for wiki in wikis:
wiki_info = wiki.to_dict()
if is_group_wiki(wiki):
wiki_info['owner_nickname'] = group_id_to_name(wiki.owner)
else:
wiki_info['owner_nickname'] = email2nickname(wiki.owner)
wiki_list.append(wiki_info)

return Response({'wikis': wiki_list})
Expand All @@ -115,18 +124,77 @@ def post(self, request, format=None):
msg = _('Name can only contain letters, numbers, blank, hyphen or underscore.')
return api_error(status.HTTP_400_BAD_REQUEST, 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)
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, '', username, org_id)
else:
repo_id = seafile_api.create_repo(wiki_name, '', username)

try:
wiki = Wiki.objects.add(wiki_name=wiki_name, username=username, org_id=org_id)
wiki = Wiki.objects.add(wiki_name=wiki_name, owner=wiki_owner, repo_id=repo_id)
except Exception as e:
logger.error(e)
msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, msg)

return Response(wiki.to_dict())
wiki_info = wiki.to_dict()
if not is_group_owner:
wiki_info['owner_nickname'] = email2nickname(wiki.owner)
else:
wiki_info['owner_nickname'] = group_id_to_name(wiki.owner)

return Response(wiki_info)


class Wiki2View(APIView):
Expand All @@ -143,23 +211,27 @@ def delete(self, request, wiki_id):
except Wiki.DoesNotExist:
error_msg = 'Wiki not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
owner = wiki.username
if owner != username:

if not check_wiki_admin_permission(wiki, username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

wiki.delete()

repo_id = wiki.repo_id
file_name = WIKI_CONFIG_FILE_NAME
try:
seafile_api.del_file(repo_id, WIKI_CONFIG_PATH,
json.dumps([file_name]),
request.user.username)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
org_id = -1
if is_org_context(request):
org_id = request.user.org.org_id

if is_group_wiki(wiki):
group_id = int(wiki.owner)
try:
SeafileAPI.delete_group_owned_repo(group_id, wiki.repo_id, org_id)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
else:
seafile_api.remove_repo(wiki.repo_id)

return Response()

Expand All @@ -184,7 +256,7 @@ def put(self, request, wiki_id):
error_msg = "Wiki not found."
return api_error(status.HTTP_404_NOT_FOUND, error_msg)

if not can_edit_wiki(wiki, request.user.username):
if not check_wiki_permission(wiki, request.user.username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

Expand Down Expand Up @@ -224,7 +296,7 @@ def get(self, request, wiki_id):
error_msg = "Wiki not found."
return api_error(status.HTTP_404_NOT_FOUND, error_msg)

if not can_edit_wiki(wiki, request.user.username):
if not check_wiki_permission(wiki, request.user.username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

Expand Down Expand Up @@ -327,6 +399,10 @@ def get(self, request, wiki_id):
error_msg = "Wiki not found."
return api_error(status.HTTP_404_NOT_FOUND, error_msg)

if not check_wiki_permission(wiki, request.user.username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

permission = check_folder_permission(request, wiki.repo_id, '/')
if not permission:
error_msg = 'Permission denied.'
Expand Down Expand Up @@ -424,7 +500,7 @@ def post(self, request, wiki_id):
error_msg = "Wiki not found."
return api_error(status.HTTP_404_NOT_FOUND, error_msg)

if not can_edit_wiki(wiki, request.user.username):
if not check_wiki_permission(wiki, request.user.username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

Expand Down Expand Up @@ -488,7 +564,7 @@ def delete(self, request, wiki_id, page_id):
return api_error(status.HTTP_404_NOT_FOUND, error_msg)

username = request.user.username
if not can_edit_wiki(wiki, username):
if not check_wiki_permission(wiki, username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)

Expand Down
Loading
Loading