file_controller.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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. from preview_generator.manager import PreviewManager
  32. FILE_ENDPOINTS_TAG = 'Files'
  33. class FileController(Controller):
  34. # File data
  35. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  36. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  37. @require_content_types([file_type])
  38. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  39. #@hapic.input_files()
  40. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  41. def upload_file(self, context, request: TracimRequest, hapic_data=None):
  42. # TODO - G.M - 2018-07-05 - Do this endpoint
  43. app_config = request.registry.settings['CFG']
  44. api = ContentApi(
  45. current_user=request.current_user,
  46. session=request.dbsession,
  47. config=app_config,
  48. )
  49. content = api.get_one(
  50. hapic_data.path.content_id,
  51. content_type=ContentType.Any
  52. )
  53. file = request.POST['files']
  54. with new_revision(
  55. session=request.dbsession,
  56. tm=transaction.manager,
  57. content=content
  58. ):
  59. api.update_file_data(
  60. content,
  61. new_filename=file.filename,
  62. new_mimetype=file.type,
  63. new_content=file.file,
  64. )
  65. return
  66. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  67. @require_workspace_role(UserRoleInWorkspace.READER)
  68. @require_content_types([file_type])
  69. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  70. @hapic.output_file([])
  71. def download_file(self, context, request: TracimRequest, hapic_data=None):
  72. # TODO - G.M - 2018-07-05 - Do this endpoint
  73. app_config = request.registry.settings['CFG']
  74. api = ContentApi(
  75. current_user=request.current_user,
  76. session=request.dbsession,
  77. config=app_config,
  78. )
  79. content = api.get_one(
  80. hapic_data.path.content_id,
  81. content_type=ContentType.Any
  82. )
  83. file = DepotManager.get().get(content.depot_file)
  84. response = request.response
  85. response.content_type = file.content_type
  86. response.app_iter = FileIter(file)
  87. return response
  88. # Previews
  89. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  90. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  91. @require_content_types([file_type])
  92. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  93. #@hapic.output_file([])
  94. def get_file_preview_info(self, context, request: TracimRequest, hapic_data=None): # nopep8
  95. # TODO - G.M - 2018-07-05 - Do this endpoint
  96. app_config = request.registry.settings['CFG']
  97. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  98. api = ContentApi(
  99. current_user=request.current_user,
  100. session=request.dbsession,
  101. config=app_config,
  102. )
  103. content = api.get_one(
  104. hapic_data.path.content_id,
  105. content_type=ContentType.Any
  106. )
  107. file_path = api.get_one_revision_filepath(content.revision_id)
  108. return {
  109. 'nb_pages': preview_manager.get_page_nb(file_path),
  110. 'pdf_preview': preview_manager.has_pdf_preview(file_path),
  111. 'mimetype': preview_manager.get_mimetype(file_path),
  112. }
  113. # File infos
  114. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  115. @require_workspace_role(UserRoleInWorkspace.READER)
  116. @require_content_types([file_type])
  117. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  118. @hapic.output_body(FileContentSchema())
  119. def get_file_infos(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  120. """
  121. Get thread content
  122. """
  123. app_config = request.registry.settings['CFG']
  124. api = ContentApi(
  125. current_user=request.current_user,
  126. session=request.dbsession,
  127. config=app_config,
  128. )
  129. content = api.get_one(
  130. hapic_data.path.content_id,
  131. content_type=ContentType.Any
  132. )
  133. return api.get_content_in_context(content)
  134. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  135. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  136. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  137. @require_content_types([file_type])
  138. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  139. @hapic.input_body(FileContentModifySchema())
  140. @hapic.output_body(FileContentSchema())
  141. def update_file_info(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  142. """
  143. update thread
  144. """
  145. app_config = request.registry.settings['CFG']
  146. api = ContentApi(
  147. current_user=request.current_user,
  148. session=request.dbsession,
  149. config=app_config,
  150. )
  151. content = api.get_one(
  152. hapic_data.path.content_id,
  153. content_type=ContentType.Any
  154. )
  155. with new_revision(
  156. session=request.dbsession,
  157. tm=transaction.manager,
  158. content=content
  159. ):
  160. api.update_content(
  161. item=content,
  162. new_label=hapic_data.body.label,
  163. new_content=hapic_data.body.raw_content,
  164. )
  165. api.save(content)
  166. return api.get_content_in_context(content)
  167. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  168. @require_workspace_role(UserRoleInWorkspace.READER)
  169. @require_content_types([file_type])
  170. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  171. @hapic.output_body(FileRevisionSchema(many=True))
  172. def get_file_revisions(
  173. self,
  174. context,
  175. request: TracimRequest,
  176. hapic_data=None
  177. ) -> typing.List[RevisionInContext]:
  178. """
  179. get file revisions
  180. """
  181. app_config = request.registry.settings['CFG']
  182. api = ContentApi(
  183. current_user=request.current_user,
  184. session=request.dbsession,
  185. config=app_config,
  186. )
  187. content = api.get_one(
  188. hapic_data.path.content_id,
  189. content_type=ContentType.Any
  190. )
  191. revisions = content.revisions
  192. return [
  193. api.get_revision_in_context(revision)
  194. for revision in revisions
  195. ]
  196. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  197. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  198. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  199. @require_content_types([file_type])
  200. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  201. @hapic.input_body(SetContentStatusSchema())
  202. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  203. def set_file_status(self, context, request: TracimRequest, hapic_data=None) -> None: # nopep8
  204. """
  205. set file status
  206. """
  207. app_config = request.registry.settings['CFG']
  208. api = ContentApi(
  209. current_user=request.current_user,
  210. session=request.dbsession,
  211. config=app_config,
  212. )
  213. content = api.get_one(
  214. hapic_data.path.content_id,
  215. content_type=ContentType.Any
  216. )
  217. with new_revision(
  218. session=request.dbsession,
  219. tm=transaction.manager,
  220. content=content
  221. ):
  222. api.set_status(
  223. content,
  224. hapic_data.body.status,
  225. )
  226. api.save(content)
  227. return
  228. def bind(self, configurator: Configurator) -> None:
  229. # Get file info
  230. configurator.add_route(
  231. 'file_info',
  232. '/workspaces/{workspace_id}/files/{content_id}',
  233. request_method='GET'
  234. )
  235. configurator.add_view(self.get_file_infos, route_name='file_info') # nopep8
  236. # update file
  237. configurator.add_route(
  238. 'update_file_info',
  239. '/workspaces/{workspace_id}/files/{content_id}',
  240. request_method='PUT'
  241. ) # nopep8
  242. configurator.add_view(self.update_file_info, route_name='update_file_info') # nopep8
  243. # upload new file data
  244. configurator.add_route(
  245. 'upload_file',
  246. '/workspaces/{workspace_id}/files/{content_id}/file_data', # nopep8
  247. request_method='PUT'
  248. )
  249. configurator.add_view(self.upload_file, route_name='upload_file') # nopep8
  250. # download file data
  251. configurator.add_route(
  252. 'download_file',
  253. '/workspaces/{workspace_id}/files/{content_id}/file_data', # nopep8
  254. request_method='GET'
  255. )
  256. configurator.add_view(self.download_file, route_name='download_file') # nopep8
  257. # get file revisions
  258. configurator.add_route(
  259. 'file_revisions',
  260. '/workspaces/{workspace_id}/files/{content_id}/revisions', # nopep8
  261. request_method='GET'
  262. )
  263. configurator.add_view(self.get_file_revisions, route_name='file_revisions') # nopep8
  264. # get file status
  265. configurator.add_route(
  266. 'set_file_status',
  267. '/workspaces/{workspace_id}/files/{content_id}/status', # nopep8
  268. request_method='PUT'
  269. )
  270. configurator.add_view(self.set_file_status, route_name='set_file_status') # nopep8
  271. # get preview info
  272. configurator.add_route(
  273. 'preview_info',
  274. '/workspaces/{workspace_id}/files/{content_id}/preview/info', # nopep8
  275. request_method='GET'
  276. )
  277. configurator.add_view(self.get_file_preview_info, route_name='preview_info') # nopep8