threads_controller.py 7.7KB

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