class WorkspaceController(Controller): @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.READER, allow_superadmin=True) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(WorkspaceSchema()) def workspace(self, context, request: TracimRequest, hapic_data=None): """ Get workspace informations """ app_config = request.registry.settings['CFG'] wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, ) return wapi.get_workspace_with_context(request.current_workspace) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @require_profile(Group.TIM_ADMIN) @hapic.output_body( WorkspaceSchema(many=True), ) def workspaces(self, context, request: TracimRequest, hapic_data=None): """ Returns the list of all workspaces. This route is for admin only. Standard users must use their own route: /api/v2/users/me/workspaces """ app_config = request.registry.settings['CFG'] wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, ) workspaces = wapi.get_all() return [ wapi.get_workspace_with_context(workspace) for workspace in workspaces ] @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.WORKSPACE_MANAGER, allow_superadmin=True) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_body(WorkspaceModifySchema()) @hapic.output_body(WorkspaceSchema()) def update_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8 """ Update a workspace. This route is for trusted users and administrators. Note : a trusted user can only update spaces on which he/she is space manager """ app_config = request.registry.settings['CFG'] wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, ) wapi.update_workspace( request.current_workspace, label=hapic_data.body.label, description=hapic_data.body.description, save_now=True, ) return wapi.get_workspace_with_context(request.current_workspace) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(WorkspaceLabelAlreadyUsed, HTTPStatus.BAD_REQUEST) @require_profile(Group.TIM_MANAGER) @hapic.input_body(WorkspaceCreationSchema()) @hapic.output_body(WorkspaceSchema()) def create_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8 """ Create a workspace. This route is for trusted users and administrators. """ app_config = request.registry.settings['CFG'] wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, ) workspace = wapi.create_workspace( label=hapic_data.body.label, description=hapic_data.body.description, save_now=True, ) return wapi.get_workspace_with_context(workspace) @hapic.with_api_doc( tags=[SWAGGER_TAG__WORKSPACE_TRASH_AND_RESTORE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @require_profile_and_workspace_role( minimal_profile=Group.TIM_MANAGER, minimal_required_role=UserRoleInWorkspace.WORKSPACE_MANAGER, allow_superadmin=True) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def delete_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8 """ Delete a workspace. This route is for trusted users and administrators. Note : a trusted user can only delete spaces on which he/she is space manager """ app_config = request.registry.settings['CFG'] wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, ) wapi.delete(request.current_workspace, flush=True) return @hapic.with_api_doc( tags=[SWAGGER_TAG__WORKSPACE_TRASH_AND_RESTORE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @require_profile_and_workspace_role( minimal_profile=Group.TIM_MANAGER, minimal_required_role=UserRoleInWorkspace.WORKSPACE_MANAGER, allow_superadmin=True) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def undelete_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8 """ Restore a deleted space. Note : a trusted user can only restore spaces on which he/she is space manager """ app_config = request.registry.settings['CFG'] wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, show_deleted=True, ) wapi.undelete(request.current_workspace, flush=True) return @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.READER, allow_superadmin=True) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(WorkspaceMemberSchema(many=True)) def workspaces_members( self, context, request: TracimRequest, hapic_data=None) -> typing.List[UserRoleWorkspaceInContext]: """ Returns the list of space members with their role, avatar, etc. """ app_config = request.registry.settings['CFG'] rapi = RoleApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) roles = rapi.get_all_for_workspace(request.current_workspace) return [ rapi.get_user_role_workspace_with_context(user_role) for user_role in roles ] @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.READER, allow_superadmin=True) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.output_body(WorkspaceMemberSchema()) def workspaces_member_role(self, context, request: TracimRequest, hapic_data=None) -> UserRoleWorkspaceInContext: """ Returns given space member with its role, avatar, etc. """ app_config = request.registry.settings['CFG'] rapi = RoleApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) role = rapi.get_one( user_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id, ) return rapi.get_user_role_workspace_with_context(role) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @hapic.handle_exception(UserRoleNotFound, HTTPStatus.BAD_REQUEST) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.WORKSPACE_MANAGER, allow_superadmin=True) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.input_body(RoleUpdateSchema()) @hapic.output_body(WorkspaceMemberSchema()) def update_workspaces_members_role( self, context, request: TracimRequest, hapic_data=None) -> UserRoleWorkspaceInContext: """ Update role of the given space member. This feature is for workspace managers, trusted users and administrators. """ app_config = request.registry.settings['CFG'] rapi = RoleApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) role = rapi.get_one( user_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id, ) workspace_role = WorkspaceRoles.get_role_from_slug( hapic_data.body.role) role = rapi.update_role(role, role_level=workspace_role.level) return rapi.get_user_role_workspace_with_context(role) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.WORKSPACE_MANAGER, allow_superadmin=True) @hapic.handle_exception(UserRoleNotFound, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserCantRemoveHisOwnRoleInWorkspace, HTTPStatus.BAD_REQUEST) # nopep8 @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def delete_workspaces_members_role(self, context, request: TracimRequest, hapic_data=None) -> None: """ Remove the user from the space. This feature is for workspace managers and administrators. """ app_config = request.registry.settings['CFG'] rapi = RoleApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) rapi.delete_one( user_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id, ) return @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @hapic.handle_exception(EmailValidationFailed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserDoesNotExist, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserIsNotActive, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserIsDeleted, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(RoleAlreadyExistError, HTTPStatus.BAD_REQUEST) @require_profile_and_workspace_role( minimal_profile=Group.TIM_USER, minimal_required_role=UserRoleInWorkspace.WORKSPACE_MANAGER, allow_superadmin=True) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_body(WorkspaceMemberInviteSchema()) @hapic.output_body(WorkspaceMemberCreationSchema()) def create_workspaces_members_role( self, context, request: TracimRequest, hapic_data=None) -> UserRoleWorkspaceInContext: """ Add a member to this workspace. This feature is for workspace managers and administrators. """ newly_created = False email_sent = False app_config = request.registry.settings['CFG'] rapi = RoleApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) uapi = UserApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_deactivated=True, show_deleted=True, ) try: _, user = uapi.find(user_id=hapic_data.body.user_id, email=hapic_data.body.user_email, public_name=hapic_data.body.user_public_name) if user.is_deleted: raise UserIsDeleted( 'This user has been deleted. Unable to invite him.' ) # nopep8 if not user.is_active: raise UserIsNotActive( 'This user is not activated. Unable to invite him' ) # nopep8 except UserDoesNotExist as exc: if not uapi.allowed_to_invite_new_user(hapic_data.body.user_email): raise exc user = uapi.create_user(email=hapic_data.body.user_email, password=password_generator(), do_notify=True) newly_created = True if app_config.EMAIL_NOTIFICATION_ACTIVATED and \ app_config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == 'sync': email_sent = True role = rapi.create_one( user=user, workspace=request.current_workspace, role_level=WorkspaceRoles.get_role_from_slug( hapic_data.body.role).level, # nopep8 with_notif=False, flush=True, ) return rapi.get_user_role_workspace_with_context( role, newly_created=newly_created, email_sent=email_sent, ) @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @require_workspace_role(UserRoleInWorkspace.READER) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_query(FilterContentQuerySchema()) @hapic.output_body(ContentDigestSchema(many=True)) def workspace_content( self, context, request: TracimRequest, hapic_data=None, ) -> typing.List[ContentInContext]: """ return a list of contents of the space. This is NOT the full content list: by default, returned contents are the ones at root level. In order to get contents in a given folder, then use parent_id query filter. You can also show.hide archived/deleted contents. """ app_config = request.registry.settings['CFG'] content_filter = hapic_data.query api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_archived=content_filter.show_archived, show_deleted=content_filter.show_deleted, show_active=content_filter.show_active, ) contents = api.get_all(parent_id=content_filter.parent_id, workspace=request.current_workspace, content_type=content_filter.content_type or content_type_list.Any_SLUG, label=content_filter.label, order_by_properties=[Content.label]) contents = [ api.get_content_in_context(content) for content in contents ] return contents @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UnallowedSubContent, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ParentNotFound, HTTPStatus.BAD_REQUEST) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_body(ContentCreationSchema()) @hapic.output_body(ContentDigestSchema()) def create_generic_empty_content( self, context, request: TracimRequest, hapic_data=None, ) -> ContentInContext: """ Creates a generic empty content. The minimum viable content has a label and a content type. Creating a content generally starts with a request to this endpoint. For specific contents like files, it is recommended to use the dedicated endpoint. This feature is accessible to contributors and higher role only. """ app_config = request.registry.settings['CFG'] creation_data = hapic_data.body api = ContentApi(current_user=request.current_user, session=request.dbsession, config=app_config) parent = None if creation_data.parent_id: try: parent = api.get_one( content_id=creation_data.parent_id, content_type=content_type_list.Any_SLUG) # nopep8 except ContentNotFound as exc: raise ParentNotFound( 'Parent with content_id {} not found'.format( creation_data.parent_id)) from exc content = api.create( label=creation_data.label, content_type_slug=creation_data.content_type, workspace=request.current_workspace, parent=parent, ) api.save(content, ActionDescription.CREATION) content = api.get_content_in_context(content) return content @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @require_workspace_role(UserRoleInWorkspace.READER) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) # nopep8 def get_content_from_workspace( self, context, request: TracimRequest, hapic_data=None, ) -> None: """ Convenient route allowing to get detail about a content without to known routes associated to its content type. This route generate a HTTP 302 with the right url """ app_config = request.registry.settings['CFG'] content = request.current_content content_type = content_type_list.get_one_by_slug(content.type).slug # TODO - G.M - 2018-08-03 - Jsonify redirect response ? raise HTTPFound( "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}". format( base_url=BASE_API_V2, workspace_id=content.workspace_id, content_type=content_type, content_id=content.content_id, )) @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @hapic.input_path(ContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) # nopep8 def get_content( self, context, request: TracimRequest, hapic_data=None, ) -> None: """ Convenient route allowing to get detail about a content without to known routes associated to its content type. This route generate a HTTP 302 with the right url """ app_config = request.registry.settings['CFG'] api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one(content_id=hapic_data.path['content_id'], content_type=content_type_list.Any_SLUG) content_type = content_type_list.get_one_by_slug(content.type).slug # TODO - G.M - 2018-08-03 - Jsonify redirect response ? raise HTTPFound( "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}". format( base_url=BASE_API_V2, workspace_id=content.workspace_id, content_type=content_type, content_id=content.content_id, )) @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @hapic.handle_exception(WorkspacesDoNotMatch, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST) @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER) @require_candidate_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.input_body(ContentMoveSchema()) @hapic.output_body(ContentDigestSchema()) def move_content( self, context, request: TracimRequest, hapic_data=None, ) -> ContentInContext: """ Move a content to specified new place. This requires to be content manager in both input and output spaces (which may be the same) """ app_config = request.registry.settings['CFG'] path_data = hapic_data.path move_data = hapic_data.body api = ContentApi( show_archived=True, show_deleted=True, current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) new_parent = api.get_one(move_data.new_parent_id, content_type=content_type_list.Any_SLUG) new_workspace = request.candidate_workspace with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.move( content, new_parent=new_parent, new_workspace=new_workspace, must_stay_in_same_workspace=False, ) updated_content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) return api.get_content_in_context(updated_content) @hapic.with_api_doc( tags=[SWAGGER_TAG__CONTENT_ALL_TRASH_AND_RESTORE_ENDPOINTS]) # nopep8 @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def delete_content( self, context, request: TracimRequest, hapic_data=None, ) -> None: """ Move a content to the trash. After that, the content will be invisible by default. This action requires the user to be a content manager. Note: the content is still accessible but becomes read-only. """ app_config = request.registry.settings['CFG'] path_data = hapic_data.path api = ContentApi( show_archived=True, show_deleted=True, current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.delete(content) return @hapic.with_api_doc( tags=[SWAGGER_TAG__CONTENT_ALL_TRASH_AND_RESTORE_ENDPOINTS]) # nopep8 @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def undelete_content( self, context, request: TracimRequest, hapic_data=None, ) -> None: """ Restore a content from the trash. The content will be visible and editable again. """ app_config = request.registry.settings['CFG'] path_data = hapic_data.path api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_deleted=True, show_archived=True, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.undelete(content) return @hapic.with_api_doc(tags=[ SWAGGER_TAG__CONTENT_ALL_ARCHIVE_AND_RESTORE_ENDPOINTS ]) # nopep8 @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def archive_content( self, context, request: TracimRequest, hapic_data=None, ) -> None: """ Archives a content. The content will be invisible but still available. Difference with delete is that optimizing workspace will not delete archived contents This action requires the user to be a content manager. Note: the content is still accessible but becomes read-only. the difference with delete is that optimizing workspace will not delete archived contents """ app_config = request.registry.settings['CFG'] path_data = hapic_data.path api = ContentApi( show_archived=True, show_deleted=True, current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one( path_data.content_id, content_type=content_type_list.Any_SLUG) # nopep8 with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.archive(content) return @hapic.with_api_doc(tags=[ SWAGGER_TAG__CONTENT_ALL_ARCHIVE_AND_RESTORE_ENDPOINTS ]) # nopep8 @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8 def unarchive_content( self, context, request: TracimRequest, hapic_data=None, ) -> None: """ Restore a content from archive. The content will be visible and editable again. """ app_config = request.registry.settings['CFG'] path_data = hapic_data.path api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_archived=True, show_deleted=True, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.unarchive(content) return def bind(self, configurator: Configurator) -> None: """ Create all routes and views using pyramid configurator for this controller """ # Workspaces configurator.add_route('workspaces', '/workspaces', request_method='GET') # nopep8 configurator.add_view(self.workspaces, route_name='workspaces') # Workspace configurator.add_route('workspace', '/workspaces/{workspace_id}', request_method='GET') # nopep8 configurator.add_view(self.workspace, route_name='workspace') # Create workspace configurator.add_route('create_workspace', '/workspaces', request_method='POST') # nopep8 configurator.add_view(self.create_workspace, route_name='create_workspace') # nopep8 # Delete/Undelete workpace configurator.add_route('delete_workspace', '/workspaces/{workspace_id}/trashed', request_method='PUT') # nopep8 configurator.add_view(self.delete_workspace, route_name='delete_workspace') # nopep8 configurator.add_route('undelete_workspace', '/workspaces/{workspace_id}/trashed/restore', request_method='PUT') # nopep8 configurator.add_view(self.undelete_workspace, route_name='undelete_workspace') # nopep8 # Update Workspace configurator.add_route('update_workspace', '/workspaces/{workspace_id}', request_method='PUT') # nopep8 configurator.add_view(self.update_workspace, route_name='update_workspace') # nopep8 # Workspace Members (Roles) configurator.add_route('workspace_members', '/workspaces/{workspace_id}/members', request_method='GET') # nopep8 configurator.add_view(self.workspaces_members, route_name='workspace_members') # nopep8 # Workspace Members (Role) Individual configurator.add_route('workspace_member_role', '/workspaces/{workspace_id}/members/{user_id}', request_method='GET') # nopep8 configurator.add_view(self.workspaces_member_role, route_name='workspace_member_role') # nopep8 # Update Workspace Members roles configurator.add_route('update_workspace_member', '/workspaces/{workspace_id}/members/{user_id}', request_method='PUT') # nopep8 configurator.add_view(self.update_workspaces_members_role, route_name='update_workspace_member') # nopep8 # Create Workspace Members roles configurator.add_route('create_workspace_member', '/workspaces/{workspace_id}/members', request_method='POST') # nopep8 configurator.add_view(self.create_workspaces_members_role, route_name='create_workspace_member') # nopep8 # Delete Workspace Members roles configurator.add_route('delete_workspace_member', '/workspaces/{workspace_id}/members/{user_id}', request_method='DELETE') # nopep8 configurator.add_view(self.delete_workspaces_members_role, route_name='delete_workspace_member') # nopep8 # Workspace Content configurator.add_route('workspace_content', '/workspaces/{workspace_id}/contents', request_method='GET') # nopep8 configurator.add_view(self.workspace_content, route_name='workspace_content') # nopep8 # Create Generic Content configurator.add_route('create_generic_content', '/workspaces/{workspace_id}/contents', request_method='POST') # nopep8 configurator.add_view(self.create_generic_empty_content, route_name='create_generic_content') # nopep8 # Get Content configurator.add_route('get_content', '/contents/{content_id}', request_method='GET') # nopep8 configurator.add_view(self.get_content, route_name='get_content') # Get Content From workspace configurator.add_route( 'get_content_from_workspace', '/workspaces/{workspace_id}/contents/{content_id}', request_method='GET') # nopep8 configurator.add_view( self.get_content_from_workspace, route_name='get_content_from_workspace') # nopep8 # Move Content configurator.add_route( 'move_content', '/workspaces/{workspace_id}/contents/{content_id}/move', request_method='PUT') # nopep8 configurator.add_view(self.move_content, route_name='move_content') # nopep8 # Delete/Undelete Content configurator.add_route( 'delete_content', '/workspaces/{workspace_id}/contents/{content_id}/trashed', request_method='PUT') # nopep8 configurator.add_view(self.delete_content, route_name='delete_content') # nopep8 configurator.add_route( 'undelete_content', '/workspaces/{workspace_id}/contents/{content_id}/trashed/restore', request_method='PUT') # nopep8 configurator.add_view(self.undelete_content, route_name='undelete_content') # nopep8 # # Archive/Unarchive Content configurator.add_route( 'archive_content', '/workspaces/{workspace_id}/contents/{content_id}/archived', request_method='PUT') # nopep8 configurator.add_view(self.archive_content, route_name='archive_content') # nopep8 configurator.add_route( 'unarchive_content', '/workspaces/{workspace_id}/contents/{content_id}/archived/restore', request_method='PUT') # nopep8 configurator.add_view(self.unarchive_content, route_name='unarchive_content') # nopep8
class WorkspaceController(Controller): @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @check_right(can_see_workspace_information) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(WorkspaceSchema()) def workspace(self, context, request: TracimRequest, hapic_data=None): """ Get workspace informations """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, session=request.dbsession, config=app_config # User ) return wapi.get_workspace_with_context(request.current_workspace) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @check_right(can_see_workspace_information) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(WorkspaceDiskSpaceSchema()) def workspace_disk_space(self, context, request: TracimRequest, hapic_data=None): """ Get workspace space info """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, session=request.dbsession, config=app_config # User ) return wapi.get_workspace_with_context(request.current_workspace) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @check_right(is_administrator) @hapic.input_query(WorkspaceFilterQuerySchema()) @hapic.output_body(WorkspaceSchema(many=True)) def workspaces(self, context, request: TracimRequest, hapic_data=None): """ Returns the list of all workspaces. This route is for admin only. Standard users must use their own route: /api/users/me/workspaces Filtering by parent_ids is possible through this endpoint """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, session=request.dbsession, config=app_config # User ) workspaces = wapi.get_all_children( parent_ids=hapic_data.query.parent_ids) return [ wapi.get_workspace_with_context(workspace) for workspace in workspaces ] @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(DisallowedWorkspaceAccessType, HTTPStatus.BAD_REQUEST) @check_right(can_modify_workspace) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_body(WorkspaceModifySchema()) @hapic.output_body(WorkspaceSchema()) def update_workspace(self, context, request: TracimRequest, hapic_data=None): """ Update a workspace. This route is for trusted users and administrators. Note : a trusted user can only update spaces on which he/she is space manager """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, session=request.dbsession, config=app_config # User ) wapi.update_workspace( request.current_workspace, label=hapic_data.body.label, description=hapic_data.body.description, agenda_enabled=hapic_data.body.agenda_enabled, public_download_enabled=hapic_data.body.public_download_enabled, public_upload_enabled=hapic_data.body.public_upload_enabled, default_user_role=hapic_data.body.default_user_role, save_now=True, ) wapi.execute_update_workspace_actions(request.current_workspace) return wapi.get_workspace_with_context(request.current_workspace) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(DisallowedWorkspaceAccessType, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserNotAllowedToCreateMoreWorkspace, HTTPStatus.BAD_REQUEST) @check_right(is_trusted_user) @hapic.input_body(WorkspaceCreationSchema()) @hapic.output_body(WorkspaceSchema()) def create_workspace(self, context, request: TracimRequest, hapic_data=None): """ Create a workspace. This route is for trusted users and administrators. """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, session=request.dbsession, config=app_config # User ) parent = None if hapic_data.body.parent_id: parent = wapi.get_one(workspace_id=hapic_data.body.parent_id) workspace = wapi.create_workspace( label=hapic_data.body.label, description=hapic_data.body.description, access_type=hapic_data.body.access_type, save_now=True, agenda_enabled=hapic_data.body.agenda_enabled, public_download_enabled=hapic_data.body.public_download_enabled, public_upload_enabled=hapic_data.body.public_upload_enabled, default_user_role=hapic_data.body.default_user_role, parent=parent, ) wapi.execute_created_workspace_actions(workspace) return wapi.get_workspace_with_context(workspace) @hapic.with_api_doc( tags=[SWAGGER_TAG__WORKSPACE_TRASH_AND_RESTORE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @check_right(can_delete_workspace) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def delete_workspace(self, context, request: TracimRequest, hapic_data=None): """ Delete a workspace. This route is for trusted users and administrators. Note : a trusted user can only delete spaces on which he/she is space manager """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, session=request.dbsession, config=app_config # User ) wapi.delete(request.current_workspace, flush=True) return @hapic.with_api_doc( tags=[SWAGGER_TAG__WORKSPACE_TRASH_AND_RESTORE_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @check_right(can_delete_workspace) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def undelete_workspace(self, context, request: TracimRequest, hapic_data=None): """ Restore a deleted space. Note : a trusted user can only restore spaces on which he/she is space manager """ app_config = request.registry.settings["CFG"] # type: CFG wapi = WorkspaceApi( current_user=request.current_user, # User session=request.dbsession, config=app_config, show_deleted=True, ) wapi.undelete(request.current_workspace, flush=True) return @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @check_right(can_see_workspace_information) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_query(WorkspaceMemberFilterQuerySchema()) @hapic.output_body(WorkspaceMemberSchema(many=True)) def workspaces_members( self, context, request: TracimRequest, hapic_data=None) -> typing.List[UserRoleWorkspaceInContext]: """ Returns the list of space members with their role, avatar, etc. """ app_config = request.registry.settings["CFG"] # type: CFG rapi = RoleApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_disabled_user=hapic_data.query.show_disabled_user, ) roles = rapi.get_all_for_workspace(workspace=request.current_workspace) return [ rapi.get_user_role_workspace_with_context(user_role) for user_role in roles ] @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @check_right(can_see_workspace_information) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.output_body(WorkspaceMemberSchema()) def workspaces_member_role(self, context, request: TracimRequest, hapic_data=None) -> UserRoleWorkspaceInContext: """ Returns given space member with its role, avatar, etc. """ app_config = request.registry.settings["CFG"] # type: CFG rapi = RoleApi(current_user=request.current_user, session=request.dbsession, config=app_config) role = rapi.get_one(user_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id) return rapi.get_user_role_workspace_with_context(role) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @hapic.handle_exception(UserRoleNotFound, HTTPStatus.BAD_REQUEST) @check_right(can_modify_workspace) @hapic.handle_exception(LastWorkspaceManagerRoleCantBeModified, HTTPStatus.BAD_REQUEST) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.input_body(RoleUpdateSchema()) @hapic.output_body(WorkspaceMemberSchema()) def update_workspaces_members_role( self, context, request: TracimRequest, hapic_data=None) -> UserRoleWorkspaceInContext: """ Update role of the given space member. This feature is for workspace managers, trusted users and administrators. """ app_config = request.registry.settings["CFG"] # type: CFG rapi = RoleApi(current_user=request.current_user, session=request.dbsession, config=app_config) role = rapi.get_one(user_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id) role = rapi.update_role(role, role_level=hapic_data.body.role.level) return rapi.get_user_role_workspace_with_context(role) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @check_right(can_leave_workspace) @hapic.handle_exception(UserRoleNotFound, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(LastWorkspaceManagerRoleCantBeModified, HTTPStatus.BAD_REQUEST) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def delete_workspaces_members_role(self, context, request: TracimRequest, hapic_data=None) -> None: """ Remove the user from the space. This feature is for workspace managers and administrators. """ app_config = request.registry.settings["CFG"] # type: CFG rapi = RoleApi(current_user=request.current_user, session=request.dbsession, config=app_config) rapi.delete_one(user_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id) return @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_MEMBERS_ENDPOINTS]) @hapic.handle_exception(EmailValidationFailed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserIsNotActive, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UserIsDeleted, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(RoleAlreadyExistError, HTTPStatus.BAD_REQUEST) @check_right(can_modify_workspace) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_body(WorkspaceMemberInviteSchema()) @hapic.output_body(WorkspaceMemberCreationSchema()) def create_workspaces_members_role( self, context, request: TracimRequest, hapic_data=None) -> UserRoleWorkspaceInContext: """ Add a member to this workspace. This feature is for workspace managers and administrators. """ newly_created = False email_sent = False app_config = request.registry.settings["CFG"] # type: CFG rapi = RoleApi(current_user=request.current_user, session=request.dbsession, config=app_config) uapi = UserApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_deactivated=True, show_deleted=True, ) try: if hapic_data.body.user_id: user = uapi.get_one(hapic_data.body.user_id) elif hapic_data.body.user_email: user = uapi.get_one_by_email(hapic_data.body.user_email) else: user = uapi.get_one_by_username(hapic_data.body.user_username) if user.is_deleted: raise UserIsDeleted( "This user has been deleted. Unable to invite him.") if not user.is_active: raise UserIsNotActive( "This user is not activated. Unable to invite him") except UserDoesNotExist as exc: if not uapi.allowed_to_invite_new_user(hapic_data.body.user_email): raise exc if app_config.NEW_USER__INVITATION__DO_NOTIFY: user = uapi.create_user( auth_type=AuthType.UNKNOWN, email=hapic_data.body.user_email, password=password_generator(), do_notify=True, ) if (app_config.EMAIL__NOTIFICATION__ACTIVATED and app_config.NEW_USER__INVITATION__DO_NOTIFY and app_config.JOBS__PROCESSING_MODE == app_config.CST.SYNC): email_sent = True else: user = uapi.create_user( auth_type=AuthType.UNKNOWN, email=hapic_data.body.user_email, password=None, do_notify=False, ) uapi.execute_created_user_actions(user) newly_created = True role = rapi.create_one( user=user, workspace=request.current_workspace, role_level=WorkspaceRoles.get_role_from_slug( hapic_data.body.role).level, with_notif=app_config.EMAIL__NOTIFICATION__ENABLED_ON_INVITATION, flush=True, ) return rapi.get_user_role_workspace_with_context( role, newly_created=newly_created, email_sent=email_sent) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_SUBSCRIPTION_ENDPOINTS]) @check_right(can_modify_workspace) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.output_body(WorkspaceSubscriptionSchema(many=True)) def workspace_subscriptions( self, context, request: TracimRequest, hapic_data=None) -> typing.List[WorkspaceSubscription]: subscription_lib = SubscriptionLib( current_user=request.current_user, session=request.dbsession, config=request.app_config, ) return subscription_lib.get_workspace_subscriptions( request.current_workspace.workspace_id) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_SUBSCRIPTION_ENDPOINTS]) @check_right(can_modify_workspace) @hapic.handle_exception(RoleAlreadyExistError, HTTPStatus.BAD_REQUEST) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.input_body(RoleUpdateSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def accept_subscription( self, context, request: TracimRequest, hapic_data=None) -> typing.List[WorkspaceSubscription]: subscription_lib = SubscriptionLib( current_user=request.current_user, session=request.dbsession, config=request.app_config, ) subscription = subscription_lib.get_one( author_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id) subscription_lib.accept_subscription(subscription=subscription, user_role=hapic_data.body.role) @hapic.with_api_doc(tags=[SWAGGER_TAG__WORKSPACE_SUBSCRIPTION_ENDPOINTS]) @check_right(can_modify_workspace) @hapic.input_path(WorkspaceAndUserIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def reject_subscription( self, context, request: TracimRequest, hapic_data=None) -> typing.List[WorkspaceSubscription]: subscription_lib = SubscriptionLib( current_user=request.current_user, session=request.dbsession, config=request.app_config, ) subscription = subscription_lib.get_one( author_id=hapic_data.path.user_id, workspace_id=hapic_data.path.workspace_id) subscription_lib.reject_subscription(subscription=subscription) @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @check_right(is_reader) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_query(FilterContentQuerySchema()) @hapic.output_body(ContentDigestSchema(many=True)) def workspace_content(self, context, request: TracimRequest, hapic_data=None) -> typing.List[ContentInContext]: """ return a list of contents of the space. This is NOT the full content list: by default, returned contents are the ones at root level. In order to get contents in a given folder, then use parent_id query filter. You can also show.hide archived/deleted contents. """ app_config = request.registry.settings["CFG"] # type: CFG content_filter = hapic_data.query api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, namespaces_filter=content_filter.namespaces_filter or [ContentNamespaces.CONTENT], show_archived=content_filter.show_archived, show_deleted=content_filter.show_deleted, show_active=content_filter.show_active, ) contents = api.get_all( parent_ids=content_filter.parent_ids, complete_path_to_id=content_filter.complete_path_to_id, workspace=request.current_workspace, content_type=content_filter.content_type or content_type_list.Any_SLUG, label=content_filter.label, order_by_properties=[Content.label], ) contents = [ api.get_content_in_context(content) for content in contents ] return contents @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UnallowedSubContent, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ParentNotFound, HTTPStatus.BAD_REQUEST) @check_right(can_create_content) @hapic.input_path(WorkspaceIdPathSchema()) @hapic.input_body(ContentCreationSchema()) @hapic.output_body(ContentDigestSchema()) def create_generic_empty_content(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: """ Creates a generic empty content. The minimum viable content has a label and a content type. Creating a content generally starts with a request to this endpoint. For specific contents like files, it is recommended to use the dedicated endpoint. This feature is accessible to contributors and higher role only. """ app_config = request.registry.settings["CFG"] # type: CFG creation_data = hapic_data.body api = ContentApi(current_user=request.current_user, session=request.dbsession, config=app_config) parent = None if creation_data.parent_id: try: parent = api.get_one(content_id=creation_data.parent_id, content_type=content_type_list.Any_SLUG) except ContentNotFound as exc: raise ParentNotFound( "Parent with content_id {} not found".format( creation_data.parent_id)) from exc content = api.create( label=creation_data.label, content_type_slug=creation_data.content_type, workspace=request.current_workspace, parent=parent, ) api.save(content, ActionDescription.CREATION) api.execute_created_content_actions(content) content = api.get_content_in_context(content) return content @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @check_right(is_reader) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) def get_content_from_workspace(self, context, request: TracimRequest, hapic_data=None) -> None: """ Convenient route allowing to get detail about a content without to known routes associated to its content type. This route generate a HTTP 302 with the right url """ content = request.current_content content_type = content_type_list.get_one_by_slug(content.type).slug # TODO - G.M - 2018-08-03 - Jsonify redirect response ? raise HTTPFound( "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}". format( base_url=BASE_API, workspace_id=content.workspace_id, content_type=content_type, content_id=content.content_id, )) @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @hapic.input_path(ContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) def get_content(self, context, request: TracimRequest, hapic_data=None) -> None: """ Convenient route allowing to get detail about a content without to known routes associated to its content type. This route generate a HTTP 302 with the right url """ app_config = request.registry.settings["CFG"] # type: CFG api = ContentApi(current_user=request.current_user, session=request.dbsession, config=app_config) content = api.get_one(content_id=hapic_data.path["content_id"], content_type=content_type_list.Any_SLUG) content_type = content_type_list.get_one_by_slug(content.type).slug # TODO - G.M - 2018-08-03 - Jsonify redirect response ? raise HTTPFound( "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}". format( base_url=BASE_API, workspace_id=content.workspace_id, content_type=content_type, content_id=content.content_id, )) @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_ENDPOINTS]) @hapic.handle_exception(WorkspacesDoNotMatch, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(UnallowedSubContent, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ConflictingMoveInItself, HTTPStatus.BAD_REQUEST) @hapic.handle_exception(ConflictingMoveInChild, HTTPStatus.BAD_REQUEST) @check_right(can_move_content) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.input_body(ContentMoveSchema()) @hapic.output_body(ContentDigestSchema()) def move_content(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: """ Move a content to specified new place. This requires to be content manager in both input and output spaces (which may be the same) """ app_config = request.registry.settings["CFG"] # type: CFG path_data = hapic_data.path move_data = hapic_data.body api = ContentApi( show_archived=True, show_deleted=True, current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) new_parent = api.get_one(move_data.new_parent_id, content_type=content_type_list.Any_SLUG) new_workspace = request.candidate_workspace with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.move( content, new_parent=new_parent, new_workspace=new_workspace, new_content_namespace=ContentNamespaces.CONTENT, must_stay_in_same_workspace=False, ) api.execute_update_content_actions(content) updated_content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) return api.get_content_in_context(updated_content) @hapic.with_api_doc( tags=[SWAGGER_TAG__CONTENT_ALL_TRASH_AND_RESTORE_ENDPOINTS]) @check_right(is_content_manager) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def delete_content(self, context, request: TracimRequest, hapic_data=None) -> None: """ Move a content to the trash. After that, the content will be invisible by default. This action requires the user to be a content manager. Note: the content is still accessible but becomes read-only. """ app_config = request.registry.settings["CFG"] # type: CFG path_data = hapic_data.path api = ContentApi( show_archived=True, show_deleted=True, current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.delete(content) api.execute_update_content_actions(content) return @hapic.with_api_doc( tags=[SWAGGER_TAG__CONTENT_ALL_TRASH_AND_RESTORE_ENDPOINTS]) @check_right(is_content_manager) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def undelete_content(self, context, request: TracimRequest, hapic_data=None) -> None: """ Restore a content from the trash. The content will be visible and editable again. """ app_config = request.registry.settings["CFG"] # type: CFG path_data = hapic_data.path api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_deleted=True, show_archived=True, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.undelete(content) api.execute_update_content_actions(content) return @hapic.with_api_doc( tags=[SWAGGER_TAG__CONTENT_ALL_ARCHIVE_AND_RESTORE_ENDPOINTS]) @check_right(is_content_manager) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def archive_content(self, context, request: TracimRequest, hapic_data=None) -> None: """ Archives a content. The content will be invisible but still available. Difference with delete is that optimizing workspace will not delete archived contents This action requires the user to be a content manager. Note: the content is still accessible but becomes read-only. the difference with delete is that optimizing workspace will not delete archived contents """ app_config = request.registry.settings["CFG"] # type: CFG path_data = hapic_data.path api = ContentApi( show_archived=True, show_deleted=True, current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.archive(content) api.execute_update_content_actions(content) return @hapic.with_api_doc( tags=[SWAGGER_TAG__CONTENT_ALL_ARCHIVE_AND_RESTORE_ENDPOINTS]) @check_right(is_content_manager) @hapic.input_path(WorkspaceAndContentIdPathSchema()) @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) def unarchive_content(self, context, request: TracimRequest, hapic_data=None) -> None: """ Restore a content from archive. The content will be visible and editable again. """ app_config = request.registry.settings["CFG"] # type: CFG path_data = hapic_data.path api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, show_archived=True, show_deleted=True, ) content = api.get_one(path_data.content_id, content_type=content_type_list.Any_SLUG) with new_revision(session=request.dbsession, tm=transaction.manager, content=content): api.unarchive(content) api.execute_update_content_actions(content) return def bind(self, configurator: Configurator) -> None: """ Create all routes and views using pyramid configurator for this controller """ # Workspaces configurator.add_route("workspaces", "/workspaces", request_method="GET") configurator.add_view(self.workspaces, route_name="workspaces") # Workspace configurator.add_route("workspace", "/workspaces/{workspace_id}", request_method="GET") configurator.add_view(self.workspace, route_name="workspace") # Workspace space configurator.add_route("workspace_disk_space", "/workspaces/{workspace_id}/disk_space", request_method="GET") configurator.add_view(self.workspace_disk_space, route_name="workspace_disk_space") # Create workspace configurator.add_route("create_workspace", "/workspaces", request_method="POST") configurator.add_view(self.create_workspace, route_name="create_workspace") # Delete/Undelete workpace configurator.add_route("delete_workspace", "/workspaces/{workspace_id}/trashed", request_method="PUT") configurator.add_view(self.delete_workspace, route_name="delete_workspace") configurator.add_route("undelete_workspace", "/workspaces/{workspace_id}/trashed/restore", request_method="PUT") configurator.add_view(self.undelete_workspace, route_name="undelete_workspace") # Update Workspace configurator.add_route("update_workspace", "/workspaces/{workspace_id}", request_method="PUT") configurator.add_view(self.update_workspace, route_name="update_workspace") # Workspace Members (Roles) configurator.add_route("workspace_members", "/workspaces/{workspace_id}/members", request_method="GET") configurator.add_view(self.workspaces_members, route_name="workspace_members") # Workspace Members (Role) Individual configurator.add_route( "workspace_member_role", "/workspaces/{workspace_id}/members/{user_id}", request_method="GET", ) configurator.add_view(self.workspaces_member_role, route_name="workspace_member_role") # Update Workspace Members roles configurator.add_route( "update_workspace_member", "/workspaces/{workspace_id}/members/{user_id}", request_method="PUT", ) configurator.add_view(self.update_workspaces_members_role, route_name="update_workspace_member") # Create Workspace Members roles configurator.add_route("create_workspace_member", "/workspaces/{workspace_id}/members", request_method="POST") configurator.add_view(self.create_workspaces_members_role, route_name="create_workspace_member") # Delete Workspace Members roles configurator.add_route( "delete_workspace_member", "/workspaces/{workspace_id}/members/{user_id}", request_method="DELETE", ) configurator.add_view(self.delete_workspaces_members_role, route_name="delete_workspace_member") # Workspace Content configurator.add_route("workspace_content", "/workspaces/{workspace_id}/contents", request_method="GET") configurator.add_view(self.workspace_content, route_name="workspace_content") # Create Generic Content configurator.add_route("create_generic_content", "/workspaces/{workspace_id}/contents", request_method="POST") configurator.add_view(self.create_generic_empty_content, route_name="create_generic_content") # Get Content configurator.add_route("get_content", "/contents/{content_id}", request_method="GET") configurator.add_view(self.get_content, route_name="get_content") # Get Content From workspace configurator.add_route( "get_content_from_workspace", "/workspaces/{workspace_id}/contents/{content_id}", request_method="GET", ) configurator.add_view(self.get_content_from_workspace, route_name="get_content_from_workspace") # Move Content configurator.add_route( "move_content", "/workspaces/{workspace_id}/contents/{content_id}/move", request_method="PUT", ) configurator.add_view(self.move_content, route_name="move_content") # Delete/Undelete Content configurator.add_route( "delete_content", "/workspaces/{workspace_id}/contents/{content_id}/trashed", request_method="PUT", ) configurator.add_view(self.delete_content, route_name="delete_content") configurator.add_route( "undelete_content", "/workspaces/{workspace_id}/contents/{content_id}/trashed/restore", request_method="PUT", ) configurator.add_view(self.undelete_content, route_name="undelete_content") # # Archive/Unarchive Content configurator.add_route( "archive_content", "/workspaces/{workspace_id}/contents/{content_id}/archived", request_method="PUT", ) configurator.add_view(self.archive_content, route_name="archive_content") configurator.add_route( "unarchive_content", "/workspaces/{workspace_id}/contents/{content_id}/archived/restore", request_method="PUT", ) configurator.add_view(self.unarchive_content, route_name="unarchive_content") # Subscriptions configurator.add_route( "workspace_subscriptions", "/workspaces/{workspace_id}/subscriptions", request_method="GET", ) configurator.add_view(self.workspace_subscriptions, route_name="workspace_subscriptions") configurator.add_route( "accept_subscription", "/workspaces/{workspace_id}/subscriptions/{user_id}/accept", request_method="PUT", ) configurator.add_view(self.accept_subscription, route_name="accept_subscription") configurator.add_route( "reject_subscription", "/workspaces/{workspace_id}/subscriptions/{user_id}/reject", request_method="PUT", ) configurator.add_view(self.reject_subscription, route_name="reject_subscription")