示例#1
0
class ThreadController(Controller):
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_THREAD_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_thread_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(TextBasedContentSchema())
    def get_thread(self,
                   context,
                   request: TracimRequest,
                   hapic_data=None) -> ContentInContext:
        """
        Get thread content
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_THREAD_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder,
                            HTTPStatus.BAD_REQUEST)
    @check_right(is_contributor)
    @check_right(is_thread_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(TextBasedContentModifySchema())
    @hapic.output_body(TextBasedContentSchema())
    def update_thread(self,
                      context,
                      request: TracimRequest,
                      hapic_data=None) -> ContentInContext:
        """
        update thread
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        with new_revision(session=request.dbsession,
                          tm=transaction.manager,
                          content=content):
            api.update_content(
                item=content,
                new_label=hapic_data.body.label,
                new_content=hapic_data.body.raw_content,
            )
            api.save(content)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_THREAD_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_thread_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(TextBasedRevisionSchema(many=True))
    def get_thread_revisions(
            self,
            context,
            request: TracimRequest,
            hapic_data=None) -> typing.List[RevisionInContext]:
        """
        get thread revisions
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        revisions = content.revisions
        return [
            api.get_revision_in_context(revision) for revision in revisions
        ]

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_THREAD_ENDPOINTS])
    @check_right(is_contributor)
    @check_right(is_thread_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(SetContentStatusSchema())
    @hapic.output_body(NoContentSchema(),
                       default_http_code=HTTPStatus.NO_CONTENT)
    @hapic.handle_exception(ContentStatusException, HTTPStatus.BAD_REQUEST)
    def set_thread_status(self,
                          context,
                          request: TracimRequest,
                          hapic_data=None) -> None:
        """
        set thread status
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        if content.status == request.json_body.get("status"):
            raise ContentStatusException(
                "Content id {} already have status {}".format(
                    content.content_id, content.status))
        with new_revision(session=request.dbsession,
                          tm=transaction.manager,
                          content=content):
            api.set_status(content, hapic_data.body.status)
            api.save(content)
        return

    def bind(self, configurator: Configurator) -> None:
        # Get thread
        configurator.add_route(
            "thread",
            "/workspaces/{workspace_id}/threads/{content_id}",
            request_method="GET")
        configurator.add_view(self.get_thread, route_name="thread")

        # update thread
        configurator.add_route(
            "update_thread",
            "/workspaces/{workspace_id}/threads/{content_id}",
            request_method="PUT")
        configurator.add_view(self.update_thread, route_name="update_thread")

        # get thread revisions
        configurator.add_route(
            "thread_revisions",
            "/workspaces/{workspace_id}/threads/{content_id}/revisions",
            request_method="GET",
        )
        configurator.add_view(self.get_thread_revisions,
                              route_name="thread_revisions")

        # get thread revisions
        configurator.add_route(
            "set_thread_status",
            "/workspaces/{workspace_id}/threads/{content_id}/status",
            request_method="PUT",
        )
        configurator.add_view(self.set_thread_status,
                              route_name="set_thread_status")
示例#2
0
class FileController(Controller):
    """
    Endpoints for File Content
    """

    # File data
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnallowedSubContent, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST)  # nopep8
    @hapic.handle_exception(ParentNotFound, HTTPStatus.BAD_REQUEST)
    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
    @hapic.input_path(WorkspaceIdPathSchema())
    @hapic.output_body(ContentDigestSchema())
    @hapic.input_forms(FileCreationFormSchema())
    @hapic.input_files(SimpleFileSchema())
    def create_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Create a file .This will create 2 new revision.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        _file = hapic_data.files.files
        parent_id = hapic_data.forms.parent_id
        api = ContentApi(
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config
        )

        parent = None  # type: typing.Optional['Content']
        if parent_id:
            try:
                parent = api.get_one(content_id=parent_id, content_type=content_type_list.Any_SLUG)  # nopep8
            except ContentNotFound as exc:
                raise ParentNotFound(
                    'Parent with content_id {} not found'.format(parent_id)
                ) from exc
        content = api.create(
            filename=_file.filename,
            content_type_slug=FILE_TYPE,
            workspace=request.current_workspace,
            parent=parent,
        )
        api.save(content, ActionDescription.CREATION)
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.update_file_data(
                content,
                new_filename=_file.filename,
                new_mimetype=_file.type,
                new_content=_file.file,
            )

        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST)  # nopep8
    @hapic.input_path(FilePathSchema())
    @hapic.input_files(SimpleFileSchema())
    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
    def upload_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Upload a new version of raw file of content. This will create a new
        revision.
        Good pratice for filename is filename is `{label}{file_extension}` or `{filename}`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        _file = hapic_data.files.files
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.update_file_data(
                content,
                new_filename=_file.filename,
                new_mimetype=_file.type,
                new_content=_file.file,
            )
        api.save(content)
        return

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def download_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Download raw file of last revision of content.
        Good pratice for filename is filename is `{label}{file_extension}` or `{filename}`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        file = DepotManager.get().get(content.depot_file)
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = content.file_name
        return HapicFile(
            file_object=file,
            mimetype=file.content_type,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FileRevisionPathSchema())
    @hapic.output_file([])
    def download_revisions_file(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Download raw file for specific revision of content.
        Good pratice for filename is filename is `{label}_r{revision_id}{file_extension}`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id,
            content=content
        )
        file = DepotManager.get().get(revision.depot_file)
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_r{revision_id}{file_extension}".format(
                label=revision.file_name,
                revision_id=revision.revision_id,
                file_extension=revision.file_extension
            )
        return HapicFile(
            file_object=file,
            mimetype=file.content_type,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    # preview
    # pdf
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(TracimUnavailablePreviewType, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.input_query(PageQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
        """
        Obtain a specific page pdf preview of last revision of content.
        Good pratice for filename is filename is `{label}_page_{page_number}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        pdf_preview_path = api.get_pdf_preview_path(
            content.content_id,
            content.revision_id,
            page_number=hapic_data.query.page,
            file_extension=content.file_extension,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_page_{page_number}.pdf".format(
                label=content.label,
                page_number=hapic_data.query.page
            )
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(TracimUnavailablePreviewType, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def preview_pdf_full(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Obtain a full pdf preview (all page) of last revision of content.
        Good pratice for filename is filename is `{label}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        pdf_preview_path = api.get_full_pdf_preview_path(
            content.revision_id,
            file_extension=content.file_extension,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}.pdf".format(label=content.label)
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(TracimUnavailablePreviewType, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FileRevisionPathSchema())
    @hapic.input_query(FileQuerySchema())
    @hapic.output_file([])
    def preview_pdf_full_revision(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Obtain full pdf preview of a specific revision of content.
        Good pratice for filename is filename is `{label}_r{revision_id}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id,
            content=content
        )
        pdf_preview_path = api.get_full_pdf_preview_path(
            revision.revision_id,
            file_extension=revision.file_extension,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_r{revision_id}.pdf".format(
                revision_id=revision.revision_id,
                label=revision.label
            )
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(TracimUnavailablePreviewType, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FileRevisionPathSchema())
    @hapic.input_query(PageQuerySchema())
    @hapic.output_file([])
    def preview_pdf_revision(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Obtain a specific page pdf preview of a specific revision of content.
        Good pratice for filename is filename is `{label}_page_{page_number}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id,
            content=content
        )
        pdf_preview_path = api.get_pdf_preview_path(
            revision.content_id,
            revision.revision_id,
            page_number=hapic_data.query.page,
            file_extension=revision.file_extension,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_page_{page_number}.pdf".format(
                label=content.label,
                page_number=hapic_data.query.page
            )
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    # jpg
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FilePathSchema())
    @hapic.input_query(PageQuerySchema())
    @hapic.output_file([])
    def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
        """
        Obtain normally sized jpg preview of last revision of content.
        Good pratice for filename is `filename is {label}_page_{page_number}.jpg`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        allowed_dim = api.get_jpg_preview_allowed_dim()
        jpg_preview_path = api.get_jpg_preview_path(
            content_id=content.content_id,
            revision_id=content.revision_id,
            page_number=hapic_data.query.page,
            file_extension=content.file_extension,
            width=allowed_dim.dimensions[0].width,
            height=allowed_dim.dimensions[0].height,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_page_{page_number}.jpg".format(
                label=content.label,
                page_number=hapic_data.query.page
            )
        return HapicFile(
            file_path=jpg_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PreviewDimNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.input_query(PageQuerySchema())
    @hapic.input_path(FilePreviewSizedPathSchema())
    @hapic.output_file([])
    def sized_preview_jpg(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Obtain resized jpg preview of last revision of content.
        Good pratice for filename is filename is `{label}_page_{page_number}_{width}x{height}.jpg`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        jpg_preview_path = api.get_jpg_preview_path(
            content_id=content.content_id,
            revision_id=content.revision_id,
            file_extension=content.file_extension,
            page_number=hapic_data.query.page,
            height=hapic_data.path.height,
            width=hapic_data.path.width,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_page_{page_number}_{width}x{height}.jpg".format(
                label=content.label,
                page_number=hapic_data.query.page,
                width=hapic_data.path.width,
                height=hapic_data.path.height
            )
        return HapicFile(
            file_path=jpg_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PreviewDimNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FileRevisionPreviewSizedPathSchema())
    @hapic.input_query(PageQuerySchema())
    @hapic.output_file([])
    def sized_preview_jpg_revision(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Obtain resized jpg preview of a specific revision of content.
        Good pratice for filename is filename is `{label}_r{revision_id}_page_{page_number}_{width}x{height}.jpg`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id,
            content=content
        )
        jpg_preview_path = api.get_jpg_preview_path(
            content_id=content.content_id,
            revision_id=revision.revision_id,
            page_number=hapic_data.query.page,
            height=hapic_data.path.height,
            width=hapic_data.path.width,
            file_extension=revision.file_extension,
        )
        filename = hapic_data.path.filename
        if not filename or filename == 'raw':
            filename = "{label}_r{revision_id}_page_{page_number}_{width}x{height}.jpg".format(  # nopep8
                revision_id=revision.revision_id,
                label=revision.label,
                page_number=hapic_data.query.page,
                width=hapic_data.path.width,
                height=hapic_data.path.height
            )
        return HapicFile(
            file_path=jpg_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(AllowedJpgPreviewDimSchema())
    def allowed_dim_preview_jpg(self, context, request: TracimRequest, hapic_data=None):  # nopep8
        """
        Get allowed dimensions of jpg preview. If restricted is true,
        only those dimensions are strictly accepted.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        return api.get_jpg_preview_allowed_dim()

    # File infos
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(FileContentSchema())
    def get_file_infos(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
        """
        Get thread content
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST)
    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
    @require_content_types([FILE_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(FileContentModifySchema())
    @hapic.output_body(FileContentSchema())
    def update_file_info(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
        """
        update thread
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.update_content(
                item=content,
                new_label=hapic_data.body.label,
                new_content=hapic_data.body.raw_content,

            )
            api.save(content)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FILE_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(FileRevisionSchema(many=True))
    def get_file_revisions(
            self,
            context,
            request: TracimRequest,
            hapic_data=None
    ) -> typing.List[RevisionInContext]:
        """
        get file revisions
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        revisions = content.revisions
        return [
            api.get_revision_in_context(revision)
            for revision in revisions
        ]

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
    @require_content_types([FILE_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(SetContentStatusSchema())
    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
    def set_file_status(self, context, request: TracimRequest, hapic_data=None) -> None:  # nopep8
        """
        set file status
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.set_status(
                content,
                hapic_data.body.status,
            )
            api.save(content)
        return

    def bind(self, configurator: Configurator) -> None:
        """
        Add route to configurator.
        """

        # file info #
        # Get file info
        configurator.add_route(
            'file_info',
            '/workspaces/{workspace_id}/files/{content_id}',
            request_method='GET'
        )
        configurator.add_view(self.get_file_infos, route_name='file_info')  # nopep8
        # update file
        configurator.add_route(
            'update_file_info',
            '/workspaces/{workspace_id}/files/{content_id}',
            request_method='PUT'
        )  # nopep8
        configurator.add_view(self.update_file_info, route_name='update_file_info')  # nopep8

        # raw file #
        # create file
        configurator.add_route(
            'create_file',
            '/workspaces/{workspace_id}/files',  # nopep8
            request_method='POST'
        )
        configurator.add_view(self.create_file, route_name='create_file')  # nopep8
        # upload raw file
        configurator.add_route(
            'upload_file',
            '/workspaces/{workspace_id}/files/{content_id}/raw/{filename:[^/]*}',  # nopep8
            request_method='PUT'
        )
        configurator.add_view(self.upload_file, route_name='upload_file')  # nopep8
        # download raw file
        configurator.add_route(
            'download_file',
            '/workspaces/{workspace_id}/files/{content_id}/raw/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.download_file, route_name='download_file')  # nopep8
        # download raw file of revision
        configurator.add_route(
            'download_revision',
            '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/raw/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.download_revisions_file, route_name='download_revision')  # nopep8

        # previews #
        # get preview pdf full
        configurator.add_route(
            'preview_pdf_full',
            '/workspaces/{workspace_id}/files/{content_id}/preview/pdf/full/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.preview_pdf_full, route_name='preview_pdf_full')  # nopep8
        # get preview pdf
        configurator.add_route(
            'preview_pdf',
            '/workspaces/{workspace_id}/files/{content_id}/preview/pdf/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.preview_pdf, route_name='preview_pdf')  # nopep8
        # get preview jpg allowed dims
        configurator.add_route(
            'allowed_dim_preview_jpg',
            '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/allowed_dims',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.allowed_dim_preview_jpg, route_name='allowed_dim_preview_jpg')  # nopep8
        # get preview jpg
        configurator.add_route(
            'preview_jpg',
            '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.preview_jpg, route_name='preview_jpg')  # nopep8
        # get preview jpg with size
        configurator.add_route(
            'sized_preview_jpg',
            '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/{width}x{height}/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.sized_preview_jpg, route_name='sized_preview_jpg')  # nopep8
        # get jpg preview for revision
        configurator.add_route(
            'sized_preview_jpg_revision',
            '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/jpg/{width}x{height}/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.sized_preview_jpg_revision, route_name='sized_preview_jpg_revision')  # nopep8
        # get full pdf preview for revision
        configurator.add_route(
            'preview_pdf_full_revision',
            '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf/full/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.preview_pdf_full_revision, route_name='preview_pdf_full_revision')  # nopep8
        # get pdf preview for revision
        configurator.add_route(
            'preview_pdf_revision',
            '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf/{filename:[^/]*}',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.preview_pdf_revision, route_name='preview_pdf_revision')  # nopep8
        # others #
        # get file revisions
        configurator.add_route(
            'file_revisions',
            '/workspaces/{workspace_id}/files/{content_id}/revisions',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.get_file_revisions, route_name='file_revisions')  # nopep8

        # get file status
        configurator.add_route(
            'set_file_status',
            '/workspaces/{workspace_id}/files/{content_id}/status',  # nopep8
            request_method='PUT'
        )
        configurator.add_view(self.set_file_status, route_name='set_file_status')  # nopep8
示例#3
0
class FileController(Controller):
    """
    Endpoints for File Content
    """

    # File data
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_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)
    @hapic.handle_exception(FileSizeOverMaxLimitation, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(FileSizeOverWorkspaceEmptySpace,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(FileSizeOverOwnerEmptySpace,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(NoFileValidationError, HTTPStatus.BAD_REQUEST)
    @check_right(can_create_file)
    @hapic.input_path(WorkspaceIdPathSchema())
    @hapic.output_body(ContentDigestSchema())
    @hapic.input_forms(FileCreationFormSchema())
    @hapic.input_files(SimpleFileSchema())
    def create_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Create a file .This will create 2 new revision.
        """
        # INFO - G.M - 2019-09-03 - check validation of file here, because marshmallow
        # required doesn't work correctly with cgi_fieldstorage.
        # check is done with None because cgi_fieldstorage cannot be converted to bool
        if hapic_data.files.files is None:
            raise NoFileValidationError(
                'No file "files" given at input, validation failed.')
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        api.check_upload_size(request.content_length,
                              request.current_workspace)
        _file = hapic_data.files.files
        parent_id = hapic_data.forms.parent_id
        parent = None  # type: typing.Optional['Content']
        if parent_id:
            try:
                parent = api.get_one(content_id=parent_id,
                                     content_type=content_type_list.Any_SLUG)
            except ContentNotFound as exc:
                raise ParentNotFound(
                    "Parent with content_id {} not found".format(
                        parent_id)) from exc
        content = api.create(
            filename=_file.filename,
            content_type_slug=FILE_TYPE,
            workspace=request.current_workspace,
            parent=parent,
        )
        api.save(content, ActionDescription.CREATION)
        with new_revision(session=request.dbsession,
                          tm=transaction.manager,
                          content=content):
            api.update_file_data(
                content,
                new_filename=_file.filename,
                new_mimetype=_file.type,
                new_content=_file.file,
            )
        api.execute_created_content_actions(content)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_contributor)
    @check_right(is_file_content)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(FileSizeOverMaxLimitation, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(FileSizeOverWorkspaceEmptySpace,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(FileSizeOverOwnerEmptySpace,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(NoFileValidationError, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FilePathSchema())
    @hapic.input_files(SimpleFileSchema())
    @hapic.output_body(NoContentSchema(),
                       default_http_code=HTTPStatus.NO_CONTENT)
    def upload_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Upload a new version of raw file of content. This will create a new
        revision.
        Good pratice for filename is filename is `{label}{file_extension}` or `{filename}`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        # INFO - G.M - 2019-09-03 - check validation of file here, because marshmallow
        # required doesn't work correctly with cgi_fieldstorage.
        # check is done with None because cgi_fieldstorage cannot be converted to bool
        if hapic_data.files.files is None:
            raise NoFileValidationError(
                'No file "files" given at input, validation failed.')
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        api.check_upload_size(request.content_length, content.workspace)
        _file = hapic_data.files.files
        with new_revision(session=request.dbsession,
                          tm=transaction.manager,
                          content=content):
            api.update_file_data(
                content,
                new_filename=_file.filename,
                new_mimetype=_file.type,
                new_content=_file.file,
            )

        api.save(content)
        api.execute_update_content_actions(content)
        return

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def download_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Download raw file of last revision of content.
        Good pratice for filename is filename is `{label}{file_extension}` or `{filename}`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        try:
            file = DepotManager.get().get(content.depot_file)
        except IOError as exc:
            raise TracimFileNotFound(
                "file related to revision {} of content {} not found in depot."
                .format(content.cached_revision_id,
                        content.content_id)) from exc
        filename = hapic_data.path.filename

        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will be original file one.
        if not filename or filename == "raw":
            filename = content.file_name
        return HapicFile(
            file_object=file,
            mimetype=file.content_type,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
            content_length=file.content_length,
            last_modified=content.updated,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FileRevisionPathSchema())
    @hapic.output_file([])
    def download_revisions_file(self,
                                context,
                                request: TracimRequest,
                                hapic_data=None):
        """
        Download raw file for specific revision of content.
        Good pratice for filename is filename is `{label}_r{revision_id}{file_extension}`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id, content=content)
        try:
            file = DepotManager.get().get(revision.depot_file)
        except IOError as exc:
            raise TracimFileNotFound(
                "file related to revision {} of content {} not found in depot."
                .format(revision.revision_id, revision.content_id)) from exc

        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will be a custom one.
        if not filename or filename == "raw":
            filename = "{label}_r{revision_id}{file_extension}".format(
                label=revision.file_name,
                revision_id=revision.revision_id,
                file_extension=revision.file_extension,
            )
        return HapicFile(
            file_object=file,
            mimetype=file.content_type,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
            content_length=file.content_length,
            last_modified=revision.updated,
        )

    # preview
    # pdf
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(TracimUnavailablePreviewType,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.input_query(PageQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
        """
        Obtain a specific page pdf preview of last revision of content.
        Good pratice for filename is filename is `{label}_page_{page_number}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        pdf_preview_path = api.get_pdf_preview_path(
            content.content_id,
            content.cached_revision_id,
            page_number=hapic_data.query.page,
            file_extension=content.file_extension,
        )
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will a custom one.
        if not filename or filename == "raw":
            filename = "{label}_page_{page_number}.pdf".format(
                label=content.label, page_number=hapic_data.query.page)
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(TracimUnavailablePreviewType,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def preview_pdf_full(self,
                         context,
                         request: TracimRequest,
                         hapic_data=None):
        """
        Obtain a full pdf preview (all page) of last revision of content.
        Good pratice for filename is filename is `{label}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        pdf_preview_path = api.get_full_pdf_preview_path(
            content.cached_revision_id, file_extension=content.file_extension)
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will be a custom one.
        if not filename or filename == "raw":
            filename = "{label}.pdf".format(label=content.label)
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(TracimUnavailablePreviewType,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FileRevisionPathSchema())
    @hapic.input_query(FileQuerySchema())
    @hapic.output_file([])
    def preview_pdf_full_revision(self,
                                  context,
                                  request: TracimRequest,
                                  hapic_data=None):
        """
        Obtain full pdf preview of a specific revision of content.
        Good pratice for filename is filename is `{label}_r{revision_id}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id, content=content)
        pdf_preview_path = api.get_full_pdf_preview_path(
            revision.revision_id, file_extension=revision.file_extension)
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will be a custom one.
        if not filename or filename == "raw":
            filename = "{label}_r{revision_id}.pdf".format(
                revision_id=revision.revision_id, label=revision.label)
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(TracimUnavailablePreviewType,
                            HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FileRevisionPathSchema())
    @hapic.input_query(PageQuerySchema())
    @hapic.output_file([])
    def preview_pdf_revision(self,
                             context,
                             request: TracimRequest,
                             hapic_data=None):
        """
        Obtain a specific page pdf preview of a specific revision of content.
        Good pratice for filename is filename is `{label}_page_{page_number}.pdf`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id, content=content)
        pdf_preview_path = api.get_pdf_preview_path(
            revision.content_id,
            revision.revision_id,
            page_number=hapic_data.query.page,
            file_extension=revision.file_extension,
        )
        filename = hapic_data.path.filename

        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will a custom one.
        if not filename or filename == "raw":
            filename = "{label}_page_{page_number}.pdf".format(
                label=content.label, page_number=hapic_data.query.page)
        return HapicFile(
            file_path=pdf_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    # jpg
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FilePathSchema())
    @hapic.input_query(PageQuerySchema())
    @hapic.output_file([])
    def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
        """
        Obtain normally sized jpg preview of last revision of content.
        Good pratice for filename is `filename is {label}_page_{page_number}.jpg`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        allowed_dim = api.get_jpg_preview_allowed_dim()
        jpg_preview_path = api.get_jpg_preview_path(
            content_id=content.content_id,
            revision_id=content.cached_revision_id,
            page_number=hapic_data.query.page,
            file_extension=content.file_extension,
            width=allowed_dim.dimensions[0].width,
            height=allowed_dim.dimensions[0].height,
        )
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will a custom one.
        if not filename or filename == "raw":
            filename = "{label}_page_{page_number}.jpg".format(
                label=content.label, page_number=hapic_data.query.page)
        return HapicFile(
            file_path=jpg_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PreviewDimNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.input_query(PageQuerySchema())
    @hapic.input_path(FilePreviewSizedPathSchema())
    @hapic.output_file([])
    def sized_preview_jpg(self,
                          context,
                          request: TracimRequest,
                          hapic_data=None):
        """
        Obtain resized jpg preview of last revision of content.
        Good pratice for filename is filename is `{label}_page_{page_number}_{width}x{height}.jpg`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        jpg_preview_path = api.get_jpg_preview_path(
            content_id=content.content_id,
            revision_id=content.cached_revision_id,
            file_extension=content.file_extension,
            page_number=hapic_data.query.page,
            height=hapic_data.path.height,
            width=hapic_data.path.width,
        )
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will a custom one.
        if not filename or filename == "raw":
            filename = "{label}_page_{page_number}_{width}x{height}.jpg".format(
                label=content.label,
                page_number=hapic_data.query.page,
                width=hapic_data.path.width,
                height=hapic_data.path.height,
            )
        return HapicFile(
            file_path=jpg_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.handle_exception(UnavailablePreview, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(PreviewDimNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.input_path(FileRevisionPreviewSizedPathSchema())
    @hapic.input_query(PageQuerySchema())
    @hapic.output_file([])
    def sized_preview_jpg_revision(self,
                                   context,
                                   request: TracimRequest,
                                   hapic_data=None):
        """
        Obtain resized jpg preview of a specific revision of content.
        Good pratice for filename is filename is `{label}_r{revision_id}_page_{page_number}_{width}x{height}.jpg`.
        Default filename value is 'raw' (without file extension) or nothing.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        revision = api.get_one_revision(
            revision_id=hapic_data.path.revision_id, content=content)
        jpg_preview_path = api.get_jpg_preview_path(
            content_id=content.content_id,
            revision_id=revision.revision_id,
            page_number=hapic_data.query.page,
            height=hapic_data.path.height,
            width=hapic_data.path.width,
            file_extension=revision.file_extension,
        )
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will a custom one.
        if not filename or filename == "raw":
            filename = "{label}_r{revision_id}_page_{page_number}_{width}x{height}.jpg".format(
                revision_id=revision.revision_id,
                label=revision.label,
                page_number=hapic_data.query.page,
                width=hapic_data.path.width,
                height=hapic_data.path.height,
            )
        return HapicFile(
            file_path=jpg_preview_path,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(AllowedJpgPreviewDimSchema())
    def allowed_dim_preview_jpg(self,
                                context,
                                request: TracimRequest,
                                hapic_data=None):
        """
        Get allowed dimensions of jpg preview. If restricted is true,
        only those dimensions are strictly accepted.
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        return api.get_jpg_preview_allowed_dim()

    # File infos
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(FileContentSchema())
    def get_file_infos(self,
                       context,
                       request: TracimRequest,
                       hapic_data=None) -> ContentInContext:
        """
        Get thread content
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder,
                            HTTPStatus.BAD_REQUEST)
    @check_right(is_contributor)
    @check_right(is_file_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(FileContentModifySchema())
    @hapic.output_body(FileContentSchema())
    def update_file_info(self,
                         context,
                         request: TracimRequest,
                         hapic_data=None) -> ContentInContext:
        """
        update thread
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        with new_revision(session=request.dbsession,
                          tm=transaction.manager,
                          content=content):
            api.update_content(
                item=content,
                new_label=hapic_data.body.label,
                new_content=hapic_data.body.raw_content,
            )
            api.save(content)
            api.execute_update_content_actions(content)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_file_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(FileRevisionSchema(many=True))
    def get_file_revisions(self,
                           context,
                           request: TracimRequest,
                           hapic_data=None) -> typing.List[RevisionInContext]:
        """
        get file revisions
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        revisions = content.revisions
        return [
            api.get_revision_in_context(revision) for revision in revisions
        ]

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FILE_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @check_right(is_contributor)
    @check_right(is_file_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(SetContentStatusSchema())
    @hapic.output_body(NoContentSchema(),
                       default_http_code=HTTPStatus.NO_CONTENT)
    @hapic.handle_exception(ContentStatusException, HTTPStatus.BAD_REQUEST)
    def set_file_status(self,
                        context,
                        request: TracimRequest,
                        hapic_data=None) -> None:
        """
        set file status
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id,
                              content_type=content_type_list.Any_SLUG)
        if content.status == request.json_body.get("status"):
            raise ContentStatusException(
                "Content id {} already have status {}".format(
                    content.content_id, content.status))
        with new_revision(session=request.dbsession,
                          tm=transaction.manager,
                          content=content):
            api.set_status(content, hapic_data.body.status)
            api.save(content)
            api.execute_update_content_actions(content)
        return

    def bind(self, configurator: Configurator) -> None:
        """
        Add route to configurator.
        """

        # file info #
        # Get file info
        configurator.add_route("file_info",
                               "/workspaces/{workspace_id}/files/{content_id}",
                               request_method="GET")
        configurator.add_view(self.get_file_infos, route_name="file_info")
        # update file
        configurator.add_route(
            "update_file_info",
            "/workspaces/{workspace_id}/files/{content_id}",
            request_method="PUT",
        )
        configurator.add_view(self.update_file_info,
                              route_name="update_file_info")

        # raw file #
        # create file
        configurator.add_route("create_file",
                               "/workspaces/{workspace_id}/files",
                               request_method="POST")
        configurator.add_view(self.create_file, route_name="create_file")

        # upload raw file
        configurator.add_route(
            "upload_file",
            "/workspaces/{workspace_id}/files/{content_id}/raw/{filename:[^/]*}",
            request_method="PUT",
        )
        configurator.add_view(self.upload_file, route_name="upload_file")
        # download raw file
        configurator.add_route(
            "download_file",
            "/workspaces/{workspace_id}/files/{content_id}/raw/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.download_file, route_name="download_file")
        # download raw file of revision
        configurator.add_route(
            "download_revision",
            "/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/raw/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.download_revisions_file,
                              route_name="download_revision")

        # previews #
        # get preview pdf full
        configurator.add_route(
            "preview_pdf_full",
            "/workspaces/{workspace_id}/files/{content_id}/preview/pdf/full/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.preview_pdf_full,
                              route_name="preview_pdf_full")
        # get preview pdf
        configurator.add_route(
            "preview_pdf",
            "/workspaces/{workspace_id}/files/{content_id}/preview/pdf/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.preview_pdf, route_name="preview_pdf")
        # get preview jpg allowed dims
        configurator.add_route(
            "allowed_dim_preview_jpg",
            "/workspaces/{workspace_id}/files/{content_id}/preview/jpg/allowed_dims",
            request_method="GET",
        )
        configurator.add_view(self.allowed_dim_preview_jpg,
                              route_name="allowed_dim_preview_jpg")
        # get preview jpg
        configurator.add_route(
            "preview_jpg",
            "/workspaces/{workspace_id}/files/{content_id}/preview/jpg/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.preview_jpg, route_name="preview_jpg")
        # get preview jpg with size
        configurator.add_route(
            "sized_preview_jpg",
            "/workspaces/{workspace_id}/files/{content_id}/preview/jpg/{width}x{height}/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.sized_preview_jpg,
                              route_name="sized_preview_jpg")
        # get jpg preview for revision
        configurator.add_route(
            "sized_preview_jpg_revision",
            "/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/jpg/{width}x{height}/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.sized_preview_jpg_revision,
                              route_name="sized_preview_jpg_revision")
        # get full pdf preview for revision
        configurator.add_route(
            "preview_pdf_full_revision",
            "/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf/full/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.preview_pdf_full_revision,
                              route_name="preview_pdf_full_revision")
        # get pdf preview for revision
        configurator.add_route(
            "preview_pdf_revision",
            "/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.preview_pdf_revision,
                              route_name="preview_pdf_revision")
        # others #
        # get file revisions
        configurator.add_route(
            "file_revisions",
            "/workspaces/{workspace_id}/files/{content_id}/revisions",
            request_method="GET",
        )
        configurator.add_view(self.get_file_revisions,
                              route_name="file_revisions")

        # get file status
        configurator.add_route(
            "set_file_status",
            "/workspaces/{workspace_id}/files/{content_id}/status",
            request_method="PUT",
        )
        configurator.add_view(self.set_file_status,
                              route_name="set_file_status")
示例#4
0
class FolderController(Controller):

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FOLDER_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FOLDER_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(TextBasedContentSchema())
    def get_folder(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
        """
        Get folder info
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FOLDER_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST)
    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
    @require_content_types([FOLDER_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(FolderContentModifySchema())
    @hapic.output_body(TextBasedContentSchema())
    def update_folder(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext:  # nopep8
        """
        update folder
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.update_content(
                item=content,
                new_label=hapic_data.body.label,
                new_content=hapic_data.body.raw_content,

            )
            api.set_allowed_content(
                content=content,
                allowed_content_type_slug_list=hapic_data.body.sub_content_types  # nopep8
            )
            api.save(content)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FOLDER_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.READER)
    @require_content_types([FOLDER_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(TextBasedRevisionSchema(many=True))
    def get_folder_revisions(
            self,
            context,
            request: TracimRequest,
            hapic_data=None
    ) -> typing.List[RevisionInContext]:
        """
        get folder revisions
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        revisions = content.revisions
        return [
            api.get_revision_in_context(revision)
            for revision in revisions
        ]

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_FOLDER_ENDPOINTS])
    @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
    @require_content_types([FOLDER_TYPE])
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(SetContentStatusSchema())
    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
    def set_folder_status(self, context, request: TracimRequest, hapic_data=None) -> None:  # nopep8
        """
        set folder status
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(
            hapic_data.path.content_id,
            content_type=content_type_list.Any_SLUG
        )
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.set_status(
                content,
                hapic_data.body.status,
            )
            api.save(content)
        return

    def bind(self, configurator: Configurator) -> None:
        # Get folder
        configurator.add_route(
            'folder',
            '/workspaces/{workspace_id}/folders/{content_id}',
            request_method='GET'
        )
        configurator.add_view(self.get_folder, route_name='folder')  # nopep8

        # update folder
        configurator.add_route(
            'update_folder',
            '/workspaces/{workspace_id}/folders/{content_id}',
            request_method='PUT'
        )  # nopep8
        configurator.add_view(self.update_folder, route_name='update_folder')  # nopep8

        # get folder revisions
        configurator.add_route(
            'folder_revisions',
            '/workspaces/{workspace_id}/folders/{content_id}/revisions',  # nopep8
            request_method='GET'
        )
        configurator.add_view(self.get_folder_revisions, route_name='folder_revisions')  # nopep8

        # get folder revisions
        configurator.add_route(
            'set_folder_status',
            '/workspaces/{workspace_id}/folders/{content_id}/status',  # nopep8
            request_method='PUT'
        )
        configurator.add_view(self.set_folder_status, route_name='set_folder_status')  # nopep8
示例#5
0
class HTMLDocumentController(Controller):
    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_HTML_DOCUMENT_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_html_document_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(TextBasedContentSchema())
    def get_html_document(
        self, context, request: TracimRequest, hapic_data=None
    ) -> ContentInContext:
        """
        Get html document content
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id, content_type=content_type_list.Any_SLUG)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_HTML_DOCUMENT_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_html_document_content)
    @hapic.input_query(FileQuerySchema())
    @hapic.input_path(FilePathSchema())
    @hapic.output_file([])
    def get_html_document_preview(
        self, context, request: TracimRequest, hapic_data=None
    ) -> HapicFile:
        """
           Download preview of html document
           Good pratice for filename is filename is `{label}{file_extension}` or `{filename}`.
           Default filename value is 'raw' (without file extension) or nothing.
           """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id, content_type=content_type_list.Any_SLUG)
        file = BytesIO()
        byte_size = file.write(content.description.encode("utf-8"))
        file.seek(0)
        filename = hapic_data.path.filename
        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", where filename returned will be original file one.
        if not filename or filename == "raw":
            filename = content.file_name
        return HapicFile(
            file_object=file,
            mimetype=CONTENT_TYPE_TEXT_HTML,
            filename=filename,
            as_attachment=hapic_data.query.force_download,
            content_length=byte_size,
            last_modified=content.updated,
        )

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_HTML_DOCUMENT_ENDPOINTS])
    @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(ContentFilenameAlreadyUsedInFolder, HTTPStatus.BAD_REQUEST)
    @hapic.handle_exception(UserNotMemberOfWorkspace, HTTPStatus.BAD_REQUEST)
    @check_right(is_contributor)
    @check_right(is_html_document_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(TextBasedContentModifySchema())
    @hapic.output_body(TextBasedContentSchema())
    def update_html_document(
        self, context, request: TracimRequest, hapic_data=None
    ) -> ContentInContext:
        """
        update_html_document
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id, content_type=content_type_list.Any_SLUG)
        with new_revision(session=request.dbsession, tm=transaction.manager, content=content):
            api.update_content(
                item=content,
                new_label=hapic_data.body.label,
                new_content=hapic_data.body.raw_content,
            )
            api.save(content)
            api.execute_update_content_actions(content)
        return api.get_content_in_context(content)

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_HTML_DOCUMENT_ENDPOINTS])
    @check_right(is_reader)
    @check_right(is_html_document_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.output_body(TextBasedRevisionSchema(many=True))
    def get_html_document_revisions(
        self, context, request: TracimRequest, hapic_data=None
    ) -> typing.List[RevisionInContext]:
        """
        get html_document revisions
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id, content_type=content_type_list.Any_SLUG)
        revisions = content.revisions
        return [api.get_revision_in_context(revision) for revision in revisions]

    @hapic.with_api_doc(tags=[SWAGGER_TAG__CONTENT_HTML_DOCUMENT_ENDPOINTS])
    @check_right(is_contributor)
    @check_right(is_html_document_content)
    @hapic.input_path(WorkspaceAndContentIdPathSchema())
    @hapic.input_body(SetContentStatusSchema())
    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)
    @hapic.handle_exception(ContentStatusException, HTTPStatus.BAD_REQUEST)
    def set_html_document_status(self, context, request: TracimRequest, hapic_data=None) -> None:
        """
        set html_document status
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        content = api.get_one(hapic_data.path.content_id, content_type=content_type_list.Any_SLUG)
        if content.status == request.json_body.get("status"):
            raise ContentStatusException(
                "Content id {} already have status {}".format(content.content_id, content.status)
            )
        with new_revision(session=request.dbsession, tm=transaction.manager, content=content):
            api.set_status(content, hapic_data.body.status)
            api.save(content)
            api.execute_update_content_actions(content)
        return

    def bind(self, configurator: Configurator) -> None:
        # Get html-document
        configurator.add_route(
            "html_document",
            "/workspaces/{workspace_id}/html-documents/{content_id}",
            request_method="GET",
        )
        configurator.add_view(self.get_html_document, route_name="html_document")

        # get html-document preview
        configurator.add_route(
            "preview_html",
            "/workspaces/{workspace_id}/html-documents/{content_id}/preview/html/{filename:[^/]*}",
            request_method="GET",
        )
        configurator.add_view(self.get_html_document_preview, route_name="preview_html")

        # update html-document
        configurator.add_route(
            "update_html_document",
            "/workspaces/{workspace_id}/html-documents/{content_id}",
            request_method="PUT",
        )
        configurator.add_view(self.update_html_document, route_name="update_html_document")

        # get html document revisions
        configurator.add_route(
            "html_document_revisions",
            "/workspaces/{workspace_id}/html-documents/{content_id}/revisions",
            request_method="GET",
        )
        configurator.add_view(
            self.get_html_document_revisions, route_name="html_document_revisions"
        )

        # get html document revisions
        configurator.add_route(
            "set_html_document_status",
            "/workspaces/{workspace_id}/html-documents/{content_id}/status",
            request_method="PUT",
        )
        configurator.add_view(self.set_html_document_status, route_name="set_html_document_status")