file_controller.py 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # coding=utf-8
  2. import typing
  3. import transaction
  4. from depot.io.local import LocalStoredFile
  5. from depot.manager import DepotManager
  6. from pyramid.config import Configurator
  7. from pyramid.response import FileResponse, FileIter
  8. from tracim.exceptions import EmptyLabelNotAllowed
  9. from tracim.models.data import UserRoleInWorkspace
  10. try: # Python 3.5+
  11. from http import HTTPStatus
  12. except ImportError:
  13. from http import client as HTTPStatus
  14. from tracim import TracimRequest
  15. from tracim.extensions import hapic
  16. from tracim.lib.core.content import ContentApi
  17. from tracim.views.controllers import Controller
  18. from tracim.views.core_api.schemas import FileContentSchema
  19. from tracim.views.core_api.schemas import FileRevisionSchema
  20. from tracim.views.core_api.schemas import SetContentStatusSchema
  21. from tracim.views.core_api.schemas import FileContentModifySchema
  22. from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
  23. from tracim.views.core_api.schemas import NoContentSchema
  24. from tracim.lib.utils.authorization import require_content_types
  25. from tracim.lib.utils.authorization import require_workspace_role
  26. from tracim.models.context_models import ContentInContext
  27. from tracim.models.context_models import RevisionInContext
  28. from tracim.models.contents import ContentTypeLegacy as ContentType
  29. from tracim.models.contents import file_type
  30. from tracim.models.revision_protection import new_revision
  31. FILE_ENDPOINTS_TAG = 'Files'
  32. class FileController(Controller):
  33. # File data
  34. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  35. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  36. @require_content_types([file_type])
  37. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  38. #@hapic.input_files()
  39. @hapic.output_file([])
  40. def upload_file(self, context, request: TracimRequest, hapic_data=None):
  41. # TODO - G.M - 2018-07-05 - Do this endpoint
  42. app_config = request.registry.settings['CFG']
  43. api = ContentApi(
  44. current_user=request.current_user,
  45. session=request.dbsession,
  46. config=app_config,
  47. )
  48. content = api.get_one(
  49. hapic_data.path.content_id,
  50. content_type=ContentType.Any
  51. )
  52. file = request.POST['files']
  53. with new_revision(
  54. session=request.dbsession,
  55. tm=transaction.manager,
  56. content=content
  57. ):
  58. api.update_file_data(
  59. content,
  60. new_filename=file.filename,
  61. new_mimetype=file.type,
  62. new_content=file.file,
  63. )
  64. file = DepotManager.get().get(content.depot_file)
  65. response = request.response
  66. response.content_type = file.content_type
  67. response.app_iter = FileIter(file)
  68. return response
  69. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  70. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  71. @require_content_types([file_type])
  72. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  73. @hapic.output_file([])
  74. def download_file(self, context, request: TracimRequest, hapic_data=None):
  75. # TODO - G.M - 2018-07-05 - Do this endpoint
  76. app_config = request.registry.settings['CFG']
  77. api = ContentApi(
  78. current_user=request.current_user,
  79. session=request.dbsession,
  80. config=app_config,
  81. )
  82. content = api.get_one(
  83. hapic_data.path.content_id,
  84. content_type=ContentType.Any
  85. )
  86. file = DepotManager.get().get(content.depot_file)
  87. response = request.response
  88. response.content_type = file.content_type
  89. response.app_iter = FileIter(file)
  90. return response
  91. # Previews
  92. # def get_file_preview(self):
  93. # # TODO - G.M - 2018-07-05 - Do this endpoint
  94. # pass
  95. # File infos
  96. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  97. @require_workspace_role(UserRoleInWorkspace.READER)
  98. @require_content_types([file_type])
  99. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  100. @hapic.output_body(FileContentSchema())
  101. def get_file_infos(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  102. """
  103. Get thread content
  104. """
  105. app_config = request.registry.settings['CFG']
  106. api = ContentApi(
  107. current_user=request.current_user,
  108. session=request.dbsession,
  109. config=app_config,
  110. )
  111. content = api.get_one(
  112. hapic_data.path.content_id,
  113. content_type=ContentType.Any
  114. )
  115. return api.get_content_in_context(content)
  116. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  117. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  118. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  119. @require_content_types([file_type])
  120. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  121. @hapic.input_body(FileContentModifySchema())
  122. @hapic.output_body(FileContentSchema())
  123. def update_file_info(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  124. """
  125. update thread
  126. """
  127. app_config = request.registry.settings['CFG']
  128. api = ContentApi(
  129. current_user=request.current_user,
  130. session=request.dbsession,
  131. config=app_config,
  132. )
  133. content = api.get_one(
  134. hapic_data.path.content_id,
  135. content_type=ContentType.Any
  136. )
  137. with new_revision(
  138. session=request.dbsession,
  139. tm=transaction.manager,
  140. content=content
  141. ):
  142. api.update_content(
  143. item=content,
  144. new_label=hapic_data.body.label,
  145. new_content=hapic_data.body.raw_content,
  146. )
  147. api.save(content)
  148. return api.get_content_in_context(content)
  149. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  150. @require_workspace_role(UserRoleInWorkspace.READER)
  151. @require_content_types([file_type])
  152. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  153. @hapic.output_body(FileRevisionSchema(many=True))
  154. def get_file_revisions(
  155. self,
  156. context,
  157. request: TracimRequest,
  158. hapic_data=None
  159. ) -> typing.List[RevisionInContext]:
  160. """
  161. get file revisions
  162. """
  163. app_config = request.registry.settings['CFG']
  164. api = ContentApi(
  165. current_user=request.current_user,
  166. session=request.dbsession,
  167. config=app_config,
  168. )
  169. content = api.get_one(
  170. hapic_data.path.content_id,
  171. content_type=ContentType.Any
  172. )
  173. revisions = content.revisions
  174. return [
  175. api.get_revision_in_context(revision)
  176. for revision in revisions
  177. ]
  178. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  179. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  180. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  181. @require_content_types([file_type])
  182. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  183. @hapic.input_body(SetContentStatusSchema())
  184. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  185. def set_file_status(self, context, request: TracimRequest, hapic_data=None) -> None: # nopep8
  186. """
  187. set file status
  188. """
  189. app_config = request.registry.settings['CFG']
  190. api = ContentApi(
  191. current_user=request.current_user,
  192. session=request.dbsession,
  193. config=app_config,
  194. )
  195. content = api.get_one(
  196. hapic_data.path.content_id,
  197. content_type=ContentType.Any
  198. )
  199. with new_revision(
  200. session=request.dbsession,
  201. tm=transaction.manager,
  202. content=content
  203. ):
  204. api.set_status(
  205. content,
  206. hapic_data.body.status,
  207. )
  208. api.save(content)
  209. return
  210. def bind(self, configurator: Configurator) -> None:
  211. # Get file info
  212. configurator.add_route(
  213. 'file_info',
  214. '/workspaces/{workspace_id}/files/{content_id}',
  215. request_method='GET'
  216. )
  217. configurator.add_view(self.get_file_infos, route_name='file_info') # nopep8
  218. # update file
  219. configurator.add_route(
  220. 'update_file_info',
  221. '/workspaces/{workspace_id}/files/{content_id}',
  222. request_method='PUT'
  223. ) # nopep8
  224. configurator.add_view(self.update_file_info, route_name='update_file_info') # nopep8
  225. # upload new file data
  226. configurator.add_route(
  227. 'upload_file',
  228. '/workspaces/{workspace_id}/files/{content_id}/file_data', # nopep8
  229. request_method='PUT'
  230. )
  231. configurator.add_view(self.upload_file, route_name='upload_file') # nopep8
  232. # download file data
  233. configurator.add_route(
  234. 'download_file',
  235. '/workspaces/{workspace_id}/files/{content_id}/file_data', # nopep8
  236. request_method='GET'
  237. )
  238. configurator.add_view(self.download_file, route_name='download_file') # nopep8
  239. # get file revisions
  240. configurator.add_route(
  241. 'file_revisions',
  242. '/workspaces/{workspace_id}/files/{content_id}/revisions', # nopep8
  243. request_method='GET'
  244. )
  245. configurator.add_view(self.get_file_revisions, route_name='file_revisions') # nopep8
  246. # get file revisions
  247. configurator.add_route(
  248. 'set_file_status',
  249. '/workspaces/{workspace_id}/files/{content_id}/status', # nopep8
  250. request_method='PUT'
  251. )
  252. configurator.add_view(self.set_file_status, route_name='set_file_status') # nopep8