threads_controller.py 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. # coding=utf-8
  2. import typing
  3. import transaction
  4. from pyramid.config import Configurator
  5. from tracim.models.data import UserRoleInWorkspace
  6. try: # Python 3.5+
  7. from http import HTTPStatus
  8. except ImportError:
  9. from http import client as HTTPStatus
  10. from tracim import TracimRequest
  11. from tracim.extensions import hapic
  12. from tracim.lib.core.content import ContentApi
  13. from tracim.views.controllers import Controller
  14. from tracim.views.core_api.schemas import ThreadContentSchema
  15. from tracim.views.core_api.schemas import ThreadRevisionSchema
  16. from tracim.views.core_api.schemas import SetContentStatusSchema
  17. from tracim.views.core_api.schemas import ThreadModifySchema
  18. from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
  19. from tracim.views.core_api.schemas import NoContentSchema
  20. from tracim.lib.utils.authorization import require_content_types
  21. from tracim.lib.utils.authorization import require_workspace_role
  22. from tracim.exceptions import WorkspaceNotFound, ContentTypeNotAllowed
  23. from tracim.exceptions import InsufficientUserWorkspaceRole
  24. from tracim.exceptions import NotAuthenticated
  25. from tracim.exceptions import AuthenticationFailed
  26. from tracim.models.context_models import ContentInContext, RevisionInContext
  27. from tracim.models.contents import ContentTypeLegacy as ContentType
  28. from tracim.models.contents import thread_type
  29. from tracim.models.revision_protection import new_revision
  30. THREAD_ENDPOINTS_TAG = 'Threads'
  31. class ThreadController(Controller):
  32. @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
  33. @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
  34. @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
  35. @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
  36. @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
  37. @hapic.handle_exception(ContentTypeNotAllowed, HTTPStatus.BAD_REQUEST)
  38. @require_workspace_role(UserRoleInWorkspace.READER)
  39. @require_content_types([thread_type])
  40. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  41. @hapic.output_body(ThreadContentSchema())
  42. def get_thread(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  43. """
  44. Get thread content
  45. """
  46. app_config = request.registry.settings['CFG']
  47. api = ContentApi(
  48. current_user=request.current_user,
  49. session=request.dbsession,
  50. config=app_config,
  51. )
  52. content = api.get_one(
  53. hapic_data.path.content_id,
  54. content_type=ContentType.Any
  55. )
  56. return api.get_content_in_context(content)
  57. @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
  58. @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
  59. @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
  60. @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
  61. @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
  62. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  63. @require_content_types([thread_type])
  64. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  65. @hapic.input_body(ThreadModifySchema())
  66. @hapic.output_body(ThreadContentSchema())
  67. def update_thread(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  68. """
  69. update thread
  70. """
  71. app_config = request.registry.settings['CFG']
  72. api = ContentApi(
  73. current_user=request.current_user,
  74. session=request.dbsession,
  75. config=app_config,
  76. )
  77. content = api.get_one(
  78. hapic_data.path.content_id,
  79. content_type=ContentType.Any
  80. )
  81. with new_revision(
  82. session=request.dbsession,
  83. tm=transaction.manager,
  84. content=content
  85. ):
  86. api.update_content(
  87. item=content,
  88. new_label=hapic_data.body.label,
  89. new_content=hapic_data.body.raw_content,
  90. )
  91. api.save(content)
  92. return api.get_content_in_context(content)
  93. @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
  94. @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
  95. @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
  96. @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
  97. @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
  98. @require_workspace_role(UserRoleInWorkspace.READER)
  99. @require_content_types([thread_type])
  100. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  101. @hapic.output_body(ThreadRevisionSchema(many=True))
  102. def get_thread_revisions(
  103. self,
  104. context,
  105. request: TracimRequest,
  106. hapic_data=None
  107. ) -> typing.List[RevisionInContext]:
  108. """
  109. get thread revisions
  110. """
  111. app_config = request.registry.settings['CFG']
  112. api = ContentApi(
  113. current_user=request.current_user,
  114. session=request.dbsession,
  115. config=app_config,
  116. )
  117. content = api.get_one(
  118. hapic_data.path.content_id,
  119. content_type=ContentType.Any
  120. )
  121. revisions = content.revisions
  122. return [
  123. api.get_revision_in_context(revision)
  124. for revision in revisions
  125. ]
  126. @hapic.with_api_doc(tags=[THREAD_ENDPOINTS_TAG])
  127. @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
  128. @hapic.handle_exception(InsufficientUserWorkspaceRole, HTTPStatus.FORBIDDEN)
  129. @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
  130. @hapic.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN)
  131. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  132. @require_content_types([thread_type])
  133. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  134. @hapic.input_body(SetContentStatusSchema())
  135. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  136. def set_thread_status(self, context, request: TracimRequest, hapic_data=None) -> None: # nopep8
  137. """
  138. set thread status
  139. """
  140. app_config = request.registry.settings['CFG']
  141. api = ContentApi(
  142. current_user=request.current_user,
  143. session=request.dbsession,
  144. config=app_config,
  145. )
  146. content = api.get_one(
  147. hapic_data.path.content_id,
  148. content_type=ContentType.Any
  149. )
  150. with new_revision(
  151. session=request.dbsession,
  152. tm=transaction.manager,
  153. content=content
  154. ):
  155. api.set_status(
  156. content,
  157. hapic_data.body.status,
  158. )
  159. api.save(content)
  160. return
  161. def bind(self, configurator: Configurator) -> None:
  162. # Get thread
  163. configurator.add_route(
  164. 'thread',
  165. '/workspaces/{workspace_id}/threads/{content_id}',
  166. request_method='GET'
  167. )
  168. configurator.add_view(self.get_thread, route_name='thread') # nopep8
  169. # update thread
  170. configurator.add_route(
  171. 'update_thread',
  172. '/workspaces/{workspace_id}/threads/{content_id}',
  173. request_method='PUT'
  174. ) # nopep8
  175. configurator.add_view(self.update_thread, route_name='update_thread') # nopep8
  176. # get thread revisions
  177. configurator.add_route(
  178. 'thread_revisions',
  179. '/workspaces/{workspace_id}/threads/{content_id}/revisions', # nopep8
  180. request_method='GET'
  181. )
  182. configurator.add_view(self.get_thread_revisions, route_name='thread_revisions') # nopep8
  183. # get thread revisions
  184. configurator.add_route(
  185. 'set_thread_status',
  186. '/workspaces/{workspace_id}/threads/{content_id}/status', # nopep8
  187. request_method='PUT'
  188. )
  189. configurator.add_view(self.set_thread_status, route_name='set_thread_status') # nopep8