diff --git a/frontend/src/components/dialog/add-wiki-dialog.js b/frontend/src/components/dialog/add-wiki-dialog.js index e76597ac3b..295bb117b7 100644 --- a/frontend/src/components/dialog/add-wiki-dialog.js +++ b/frontend/src/components/dialog/add-wiki-dialog.js @@ -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, @@ -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, @@ -33,8 +63,9 @@ 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(); }; @@ -42,6 +73,10 @@ class AddWikiDialog extends React.Component { this.props.toggleCancel(); }; + handleSelectChange = (option) => { + this.setState({ selectedOption: option }); + }; + render() { return ( @@ -49,6 +84,18 @@ class AddWikiDialog extends React.Component { + + {gettext('No department')} + ) }} + /> diff --git a/frontend/src/components/dialog/transfer-dialog.js b/frontend/src/components/dialog/transfer-dialog.js index d2eb1d25a6..679893e624 100644 --- a/frontend/src/components/dialog/transfer-dialog.js +++ b/frontend/src/components/dialog/transfer-dialog.js @@ -166,14 +166,11 @@ class TransferDialog extends React.Component { ); }; + render() { - - const { itemName: repoName } = this.props; let title = gettext('Transfer Library {library_name}'); title = title.replace('{library_name}', '' + Utils.HTMLescape(repoName) + ''); - - return ( diff --git a/frontend/src/pages/wikis/wikis.js b/frontend/src/pages/wikis/wikis.js index faa6522e47..c774a35fde 100644 --- a/frontend/src/pages/wikis/wikis.js +++ b/frontend/src/pages/wikis/wikis.js @@ -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'; diff --git a/frontend/src/utils/wiki-api.js b/frontend/src/utils/wiki-api.js index 76de655aeb..3ed5812573 100644 --- a/frontend/src/utils/wiki-api.js +++ b/frontend/src/utils/wiki-api.js @@ -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); } diff --git a/seahub/api2/endpoints/departments.py b/seahub/api2/endpoints/departments.py index 2f8b2b6297..e0159b4ca7 100644 --- a/seahub/api2/endpoints/departments.py +++ b/seahub/api2/endpoints/departments.py @@ -10,13 +10,13 @@ from rest_framework import status 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__) @@ -47,13 +47,24 @@ class Departments(APIView): 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) diff --git a/seahub/api2/endpoints/wiki2.py b/seahub/api2/endpoints/wiki2.py index 54f2cdeb11..798a762602 100644 --- a/seahub/api2/endpoints/wiki2.py +++ b/seahub/api2/endpoints/wiki2.py @@ -23,7 +23,8 @@ from seahub.api2.throttling import UserRateThrottle 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 @@ -32,13 +33,17 @@ from seahub.base.templatetags.seahub_tags import email2nickname 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 @@ -95,6 +100,10 @@ class Wikis2View(APIView): 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}) @@ -115,18 +124,77 @@ class Wikis2View(APIView): 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): @@ -143,23 +211,27 @@ class Wiki2View(APIView): 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() @@ -184,7 +256,7 @@ class Wiki2ConfigView(APIView): 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) @@ -224,7 +296,7 @@ class Wiki2ConfigView(APIView): 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) @@ -327,6 +399,10 @@ class Wiki2PageContentView(APIView): 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.' @@ -424,7 +500,7 @@ class Wiki2PagesView(APIView): 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) @@ -488,7 +564,7 @@ class Wiki2PageView(APIView): 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) diff --git a/seahub/wiki2/models.py b/seahub/wiki2/models.py index b94afbe139..84de5f1377 100644 --- a/seahub/wiki2/models.py +++ b/seahub/wiki2/models.py @@ -4,7 +4,6 @@ from django.utils import timezone from seaserv import seafile_api from seahub.base.fields import LowerCaseCharField -from seahub.base.templatetags.seahub_tags import email2nickname from seahub.utils.timeutils import timestamp_to_isoformat_timestr, datetime_to_isoformat_timestr @@ -13,17 +12,9 @@ class WikiDoesNotExist(Exception): class WikiManager(models.Manager): - def add(self, wiki_name, username, org_id=-1): + def add(self, wiki_name, owner, repo_id): now = timezone.now() - 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) - - repo = seafile_api.get_repo(repo_id) - assert repo is not None - - wiki = self.model(username=username, name=wiki_name, repo_id=repo.id, created_at=now) + wiki = self.model(owner=owner, name=wiki_name, repo_id=repo_id, created_at=now) wiki.save(using=self._db) return wiki @@ -33,7 +24,7 @@ class Wiki2(models.Model): personal wiki. """ - username = LowerCaseCharField(max_length=255) + owner = LowerCaseCharField(max_length=255) name = models.CharField(max_length=255) repo_id = models.CharField(max_length=36, db_index=True) created_at = models.DateTimeField(default=timezone.now, db_index=True) @@ -41,7 +32,7 @@ class Wiki2(models.Model): class Meta: db_table = 'wiki_wiki2' - unique_together = (('username', 'repo_id'),) + unique_together = (('owner', 'repo_id'),) ordering = ["name"] @property @@ -57,8 +48,7 @@ class Wiki2(models.Model): def to_dict(self): return { 'id': self.pk, - 'owner': self.username, - 'owner_nickname': email2nickname(self.username), + 'owner': self.owner, 'name': self.name, 'created_at': datetime_to_isoformat_timestr(self.created_at), 'updated_at': timestamp_to_isoformat_timestr(self.updated_at), diff --git a/seahub/wiki2/utils.py b/seahub/wiki2/utils.py index 20ca636d28..82838e2053 100644 --- a/seahub/wiki2/utils.py +++ b/seahub/wiki2/utils.py @@ -11,6 +11,7 @@ import posixpath from seaserv import seafile_api from seahub.constants import PERMISSION_READ_WRITE from seahub.utils import gen_inner_file_get_url +from seahub.group.utils import is_group_admin, is_group_member logger = logging.getLogger(__name__) @@ -56,8 +57,34 @@ def can_edit_wiki(wiki, username): def get_wiki_config(repo_id, username): config_path = posixpath.join(WIKI_CONFIG_PATH, WIKI_CONFIG_FILE_NAME) file_id = seafile_api.get_file_id_by_path(repo_id, config_path) + if not file_id: + return {} token = seafile_api.get_fileserver_access_token(repo_id, file_id, 'download', username, use_onetime=True) url = gen_inner_file_get_url(token, WIKI_CONFIG_FILE_NAME) resp = requests.get(url) wiki_config = json.loads(resp.content) return wiki_config + + +def is_group_wiki(wiki): + return not ('@' in wiki.owner) + + +def check_wiki_admin_permission(wiki, username): + if is_group_wiki(wiki): + group_id = wiki.owner + return is_group_admin(group_id, username) + else: + if username == wiki.owner: + return True + return False + + +def check_wiki_permission(wiki, username): + if is_group_wiki(wiki): + group_id = wiki.owner + return is_group_member(group_id, username) + else: + if username == wiki.owner: + return True + return False diff --git a/seahub/wiki2/views.py b/seahub/wiki2/views.py index 88212bb9be..b55e0dc885 100644 --- a/seahub/wiki2/views.py +++ b/seahub/wiki2/views.py @@ -18,7 +18,7 @@ from seahub.utils import get_file_type_and_ext, render_permission_error, is_pro_ from seahub.utils.file_types import IMAGE, SEADOC from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token from seahub.auth.decorators import login_required -from seahub.wiki2.utils import can_edit_wiki +from seahub.wiki2.utils import can_edit_wiki, check_wiki_permission 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 @@ -38,6 +38,9 @@ def wiki_view(request, wiki_id, file_path): # perm check req_user = request.user.username + if not check_wiki_permission(wiki, req_user): + return render_permission_error(request, 'Permission denied.') + permission = check_folder_permission(request, wiki.repo_id, '/') if not permission: return render_permission_error(request, 'Permission denied.')