123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- # coding=utf-8
- import typing
-
- import transaction
- from depot.manager import DepotManager
- from preview_generator.exception import UnavailablePreviewType
- from pyramid.config import Configurator
- from pyramid.response import FileResponse, FileIter
-
- try: # Python 3.5+
- from http import HTTPStatus
- except ImportError:
- from http import client as HTTPStatus
-
- from tracim_backend.lib.utils.request import TracimRequest
- from tracim_backend.extensions import hapic
- from tracim_backend.lib.core.content import ContentApi
- from tracim_backend.views.controllers import Controller
- from tracim_backend.views.core_api.schemas import FileContentSchema
- from tracim_backend.views.core_api.schemas import AllowedJpgPreviewDimSchema
- from tracim_backend.views.core_api.schemas import ContentPreviewSizedPathSchema
- from tracim_backend.views.core_api.schemas import RevisionPreviewSizedPathSchema
- from tracim_backend.views.core_api.schemas import PageQuerySchema
- from tracim_backend.views.core_api.schemas import WorkspaceAndContentRevisionIdPathSchema # nopep8
- from tracim_backend.views.core_api.schemas import FileRevisionSchema
- from tracim_backend.views.core_api.schemas import SetContentStatusSchema
- from tracim_backend.views.core_api.schemas import FileContentModifySchema
- from tracim_backend.views.core_api.schemas import WorkspaceAndContentIdPathSchema
- from tracim_backend.views.core_api.schemas import NoContentSchema
- from tracim_backend.lib.utils.authorization import require_content_types
- from tracim_backend.lib.utils.authorization import require_workspace_role
- from tracim_backend.models.data import UserRoleInWorkspace
- from tracim_backend.models.context_models import ContentInContext
- from tracim_backend.models.context_models import RevisionInContext
- from tracim_backend.app_models.contents import CONTENT_TYPES
- from tracim_backend.app_models.contents import FILE_TYPE
- from tracim_backend.models.revision_protection import new_revision
- from tracim_backend.exceptions import EmptyLabelNotAllowed
- from tracim_backend.exceptions import PageOfPreviewNotFound
- from tracim_backend.exceptions import PreviewDimNotAllowed
-
- SWAGGER_TAG__FILE_ENDPOINTS = 'Files'
-
-
- class FileController(Controller):
- """
- Endpoints for File Content
- """
-
- # File data
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
- @require_content_types([FILE_TYPE])
- @hapic.input_path(WorkspaceAndContentIdPathSchema())
- # TODO - G.M - 2018-07-24 - Use hapic for input file
- @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.
- """
- 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_TYPES.Any_SLUG
- )
- file = request.POST['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,
- )
-
- return
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.input_path(WorkspaceAndContentIdPathSchema())
- @hapic.output_file([])
- def download_file(self, context, request: TracimRequest, hapic_data=None):
- """
- Download raw file of last revision of 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_TYPES.Any_SLUG
- )
- file = DepotManager.get().get(content.depot_file)
- response = request.response
- response.content_type = file.content_type
- response.app_iter = FileIter(file)
- return response
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.input_path(WorkspaceAndContentRevisionIdPathSchema())
- @hapic.output_file([])
- def download_revisions_file(self, context, request: TracimRequest, hapic_data=None): # nopep8
- """
- Download raw file for specific revision of 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_TYPES.Any_SLUG
- )
- revision = api.get_one_revision(
- revision_id=hapic_data.path.revision_id,
- content=content
- )
- file = DepotManager.get().get(revision.depot_file)
- response = request.response
- response.content_type = file.content_type
- response.app_iter = FileIter(file)
- return response
-
- # preview
- # pdf
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.handle_exception(UnavailablePreviewType, HTTPStatus.BAD_REQUEST)
- @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
- @hapic.input_query(PageQuerySchema())
- @hapic.input_path(WorkspaceAndContentIdPathSchema())
- @hapic.output_file([])
- def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
- """
- Obtain a specific page pdf preview of last revision of 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_TYPES.Any_SLUG
- )
- pdf_preview_path = api.get_pdf_preview_path(
- content.content_id,
- content.revision_id,
- page=hapic_data.query.page
- )
- return FileResponse(pdf_preview_path)
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.handle_exception(UnavailablePreviewType, HTTPStatus.BAD_REQUEST)
- @hapic.input_path(WorkspaceAndContentIdPathSchema())
- @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.
- """
- 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_TYPES.Any_SLUG
- )
- pdf_preview_path = api.get_full_pdf_preview_path(content.revision_id)
- return FileResponse(pdf_preview_path)
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.handle_exception(UnavailablePreviewType, HTTPStatus.BAD_REQUEST)
- @hapic.input_path(WorkspaceAndContentRevisionIdPathSchema())
- @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.
- """
- 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_TYPES.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=hapic_data.query.page
- )
- return FileResponse(pdf_preview_path)
-
- # jpg
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
- @hapic.input_path(WorkspaceAndContentIdPathSchema())
- @hapic.input_query(PageQuerySchema())
- @hapic.output_file([])
- def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
- """
- Obtain normally sied jpg preview of last revision of 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_TYPES.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=hapic_data.query.page,
- width=allowed_dim.dimensions[0].width,
- height=allowed_dim.dimensions[0].height,
- )
- return FileResponse(jpg_preview_path)
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
- @hapic.handle_exception(PreviewDimNotAllowed, HTTPStatus.BAD_REQUEST)
- @hapic.input_query(PageQuerySchema())
- @hapic.input_path(ContentPreviewSizedPathSchema())
- @hapic.output_file([])
- def sized_preview_jpg(self, context, request: TracimRequest, hapic_data=None): # nopep8
- """
- Obtain resized jpg preview of last revision of 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_TYPES.Any_SLUG
- )
- jpg_preview_path = api.get_jpg_preview_path(
- content_id=content.content_id,
- revision_id=content.revision_id,
- page=hapic_data.query.page,
- height=hapic_data.path.height,
- width=hapic_data.path.width,
- )
- return FileResponse(jpg_preview_path)
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__FILE_ENDPOINTS])
- @require_workspace_role(UserRoleInWorkspace.READER)
- @require_content_types([FILE_TYPE])
- @hapic.handle_exception(PageOfPreviewNotFound, HTTPStatus.BAD_REQUEST)
- @hapic.handle_exception(PreviewDimNotAllowed, HTTPStatus.BAD_REQUEST)
- @hapic.input_path(RevisionPreviewSizedPathSchema())
- @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.
- """
- 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_TYPES.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=hapic_data.query.page,
- height=hapic_data.path.height,
- width=hapic_data.path.width,
- )
- return FileResponse(jpg_preview_path)
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__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__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_TYPES.Any_SLUG
- )
- return api.get_content_in_context(content)
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__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(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_TYPES.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__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_TYPES.Any_SLUG
- )
- revisions = content.revisions
- return [
- api.get_revision_in_context(revision)
- for revision in revisions
- ]
-
- @hapic.with_api_doc(tags=[SWAGGER_TAG__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_TYPES.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 #
- # upload raw file
- configurator.add_route(
- 'upload_file',
- '/workspaces/{workspace_id}/files/{content_id}/raw', # 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', # 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', # 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', # 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', # 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', # 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}', # 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}', # nopep8
- request_method='GET'
- )
- configurator.add_view(self.sized_preview_jpg_revision, route_name='sized_preview_jpg_revision') # nopep8
- # get jpg preview for revision
- configurator.add_route(
- 'preview_pdf_revision',
- '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf', # 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
|