file_controller.py 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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 WorkspaceAndContentRevisionIdPathSchema # nopep8
  20. from tracim.views.core_api.schemas import FileRevisionSchema
  21. from tracim.views.core_api.schemas import SetContentStatusSchema
  22. from tracim.views.core_api.schemas import FileContentModifySchema
  23. from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
  24. from tracim.views.core_api.schemas import NoContentSchema
  25. from tracim.lib.utils.authorization import require_content_types
  26. from tracim.lib.utils.authorization import require_workspace_role
  27. from tracim.models.context_models import ContentInContext
  28. from tracim.models.context_models import RevisionInContext
  29. from tracim.models.contents import ContentTypeLegacy as ContentType
  30. from tracim.models.contents import file_type
  31. from tracim.models.revision_protection import new_revision
  32. from preview_generator.manager import PreviewManager
  33. FILE_ENDPOINTS_TAG = 'Files'
  34. class FileController(Controller):
  35. # File data
  36. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  37. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  38. @require_content_types([file_type])
  39. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  40. #@hapic.input_files()
  41. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  42. def upload_file(self, context, request: TracimRequest, hapic_data=None):
  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. app_config = request.registry.settings['CFG']
  73. api = ContentApi(
  74. current_user=request.current_user,
  75. session=request.dbsession,
  76. config=app_config,
  77. )
  78. content = api.get_one(
  79. hapic_data.path.content_id,
  80. content_type=ContentType.Any
  81. )
  82. file = DepotManager.get().get(content.depot_file)
  83. response = request.response
  84. response.content_type = file.content_type
  85. response.app_iter = FileIter(file)
  86. return response
  87. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  88. @require_workspace_role(UserRoleInWorkspace.READER)
  89. @require_content_types([file_type])
  90. @hapic.input_path(WorkspaceAndContentRevisionIdPathSchema())
  91. @hapic.output_file([])
  92. def download_revisions_file(self, context, request: TracimRequest, hapic_data=None): # nopep8
  93. app_config = request.registry.settings['CFG']
  94. api = ContentApi(
  95. current_user=request.current_user,
  96. session=request.dbsession,
  97. config=app_config,
  98. )
  99. content = api.get_one(
  100. hapic_data.path.content_id,
  101. content_type=ContentType.Any
  102. )
  103. revision = api.get_one_revision(
  104. revision_id=hapic_data.path.revision_id,
  105. content=content
  106. )
  107. file = DepotManager.get().get(revision.depot_file)
  108. response = request.response
  109. response.content_type = file.content_type
  110. response.app_iter = FileIter(file)
  111. return response
  112. # preview
  113. # pdf
  114. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  115. @require_workspace_role(UserRoleInWorkspace.READER)
  116. @require_content_types([file_type])
  117. @hapic.output_file([])
  118. def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
  119. raise NotImplemented()
  120. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  121. @require_workspace_role(UserRoleInWorkspace.READER)
  122. @require_content_types([file_type])
  123. @hapic.output_file([])
  124. def preview_pdf_full(self, context, request: TracimRequest, hapic_data=None):
  125. raise NotImplemented()
  126. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  127. @require_workspace_role(UserRoleInWorkspace.READER)
  128. @require_content_types([file_type])
  129. @hapic.output_file([])
  130. def preview_pdf_revision(self, context, request: TracimRequest, hapic_data=None):
  131. raise NotImplemented()
  132. # jpg
  133. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  134. @require_workspace_role(UserRoleInWorkspace.READER)
  135. @require_content_types([file_type])
  136. @hapic.output_file([])
  137. def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
  138. raise NotImplemented()
  139. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  140. @require_workspace_role(UserRoleInWorkspace.READER)
  141. @require_content_types([file_type])
  142. @hapic.output_file([])
  143. def sized_preview_jpg(self, context, request: TracimRequest, hapic_data=None):
  144. raise NotImplemented()
  145. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  146. @require_workspace_role(UserRoleInWorkspace.READER)
  147. @require_content_types([file_type])
  148. @hapic.output_file([])
  149. def sized_preview_jpg_revision(self, context, request: TracimRequest, hapic_data=None):
  150. raise NotImplemented()
  151. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  152. @require_workspace_role(UserRoleInWorkspace.READER)
  153. @require_content_types([file_type])
  154. @hapic.output_file([])
  155. def allowed_dim_preview_jpg(self, context, request: TracimRequest, hapic_data=None):
  156. raise NotImplemented()
  157. # @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  158. # @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  159. # @require_content_types([file_type])
  160. # @hapic.input_path(WorkspaceAndContentIdPathSchema())
  161. # #@hapic.output_file([])
  162. # def get_file_preview_info(self, context, request: TracimRequest, hapic_data=None): # nopep8
  163. # # TODO - G.M - 2018-07-05 - Do this endpoint
  164. # app_config = request.registry.settings['CFG']
  165. # preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  166. # api = ContentApi(
  167. # current_user=request.current_user,
  168. # session=request.dbsession,
  169. # config=app_config,
  170. # )
  171. # content = api.get_one(
  172. # hapic_data.path.content_id,
  173. # content_type=ContentType.Any
  174. # )
  175. # file_path = api.get_one_revision_filepath(content.revision_id)
  176. # return {
  177. # 'nb_pages': preview_manager.get_page_nb(file_path),
  178. # 'pdf_preview': preview_manager.has_pdf_preview(file_path),
  179. # 'mimetype': preview_manager.get_mimetype(file_path),
  180. # }
  181. # File infos
  182. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  183. @require_workspace_role(UserRoleInWorkspace.READER)
  184. @require_content_types([file_type])
  185. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  186. @hapic.output_body(FileContentSchema())
  187. def get_file_infos(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  188. """
  189. Get thread content
  190. """
  191. app_config = request.registry.settings['CFG']
  192. api = ContentApi(
  193. current_user=request.current_user,
  194. session=request.dbsession,
  195. config=app_config,
  196. )
  197. content = api.get_one(
  198. hapic_data.path.content_id,
  199. content_type=ContentType.Any
  200. )
  201. return api.get_content_in_context(content)
  202. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  203. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  204. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  205. @require_content_types([file_type])
  206. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  207. @hapic.input_body(FileContentModifySchema())
  208. @hapic.output_body(FileContentSchema())
  209. def update_file_info(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  210. """
  211. update thread
  212. """
  213. app_config = request.registry.settings['CFG']
  214. api = ContentApi(
  215. current_user=request.current_user,
  216. session=request.dbsession,
  217. config=app_config,
  218. )
  219. content = api.get_one(
  220. hapic_data.path.content_id,
  221. content_type=ContentType.Any
  222. )
  223. with new_revision(
  224. session=request.dbsession,
  225. tm=transaction.manager,
  226. content=content
  227. ):
  228. api.update_content(
  229. item=content,
  230. new_label=hapic_data.body.label,
  231. new_content=hapic_data.body.raw_content,
  232. )
  233. api.save(content)
  234. return api.get_content_in_context(content)
  235. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  236. @require_workspace_role(UserRoleInWorkspace.READER)
  237. @require_content_types([file_type])
  238. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  239. @hapic.output_body(FileRevisionSchema(many=True))
  240. def get_file_revisions(
  241. self,
  242. context,
  243. request: TracimRequest,
  244. hapic_data=None
  245. ) -> typing.List[RevisionInContext]:
  246. """
  247. get file revisions
  248. """
  249. app_config = request.registry.settings['CFG']
  250. api = ContentApi(
  251. current_user=request.current_user,
  252. session=request.dbsession,
  253. config=app_config,
  254. )
  255. content = api.get_one(
  256. hapic_data.path.content_id,
  257. content_type=ContentType.Any
  258. )
  259. revisions = content.revisions
  260. return [
  261. api.get_revision_in_context(revision)
  262. for revision in revisions
  263. ]
  264. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  265. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  266. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  267. @require_content_types([file_type])
  268. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  269. @hapic.input_body(SetContentStatusSchema())
  270. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  271. def set_file_status(self, context, request: TracimRequest, hapic_data=None) -> None: # nopep8
  272. """
  273. set file status
  274. """
  275. app_config = request.registry.settings['CFG']
  276. api = ContentApi(
  277. current_user=request.current_user,
  278. session=request.dbsession,
  279. config=app_config,
  280. )
  281. content = api.get_one(
  282. hapic_data.path.content_id,
  283. content_type=ContentType.Any
  284. )
  285. with new_revision(
  286. session=request.dbsession,
  287. tm=transaction.manager,
  288. content=content
  289. ):
  290. api.set_status(
  291. content,
  292. hapic_data.body.status,
  293. )
  294. api.save(content)
  295. return
  296. def bind(self, configurator: Configurator) -> None:
  297. # file info #
  298. # Get file info
  299. configurator.add_route(
  300. 'file_info',
  301. '/workspaces/{workspace_id}/files/{content_id}',
  302. request_method='GET'
  303. )
  304. configurator.add_view(self.get_file_infos, route_name='file_info') # nopep8
  305. # update file
  306. configurator.add_route(
  307. 'update_file_info',
  308. '/workspaces/{workspace_id}/files/{content_id}',
  309. request_method='PUT'
  310. ) # nopep8
  311. configurator.add_view(self.update_file_info, route_name='update_file_info') # nopep8
  312. # raw file #
  313. # upload raw file
  314. configurator.add_route(
  315. 'upload_file',
  316. '/workspaces/{workspace_id}/files/{content_id}/raw', # nopep8
  317. request_method='PUT'
  318. )
  319. configurator.add_view(self.upload_file, route_name='upload_file') # nopep8
  320. # download raw file
  321. configurator.add_route(
  322. 'download_file',
  323. '/workspaces/{workspace_id}/files/{content_id}/raw', # nopep8
  324. request_method='GET'
  325. )
  326. configurator.add_view(self.download_file, route_name='download_file') # nopep8
  327. # download raw file of revision
  328. configurator.add_route(
  329. 'download_revision',
  330. '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/raw', # nopep8
  331. request_method='GET'
  332. )
  333. configurator.add_view(self.download_revisions_file, route_name='download_revision') # nopep8
  334. # previews #
  335. # get preview pdf full
  336. configurator.add_route(
  337. 'preview_pdf_full',
  338. '/workspaces/{workspace_id}/files/{content_id}/preview/pdf/full', # nopep8
  339. request_method='GET'
  340. )
  341. configurator.add_view(self.preview_pdf_full, route_name='preview_pdf_full') # nopep8
  342. # get preview pdf
  343. configurator.add_route(
  344. 'preview_pdf',
  345. '/workspaces/{workspace_id}/files/{content_id}/preview/pdf', # nopep8
  346. request_method='GET'
  347. )
  348. configurator.add_view(self.preview_pdf, route_name='preview_pdf') # nopep8
  349. # get preview jpg allowed dims
  350. configurator.add_route(
  351. 'allowed_dim_preview_jpg',
  352. '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/allowed_dims', # nopep8
  353. request_method='GET'
  354. )
  355. configurator.add_view(self.allowed_dim_preview_jpg, route_name='allowed_dim_preview_jpg') # nopep8
  356. # get preview jpg
  357. configurator.add_route(
  358. 'preview_jpg',
  359. '/workspaces/{workspace_id}/files/{content_id}/preview/jpg', # nopep8
  360. request_method='GET'
  361. )
  362. configurator.add_view(self.preview_jpg, route_name='preview_jpg') # nopep8
  363. # get preview jpg with size
  364. configurator.add_route(
  365. 'sized_preview_jpg',
  366. '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/{width}x{height}', # nopep8
  367. request_method='GET'
  368. )
  369. configurator.add_view(self.sized_preview_jpg, route_name='sized_preview_jpg') # nopep8
  370. # get jpg preview for revision
  371. configurator.add_route(
  372. 'sized_preview_jpg_revision',
  373. '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/jpg/{width}x{height}', # nopep8
  374. request_method='GET'
  375. )
  376. configurator.add_view(self.sized_preview_jpg_revision, route_name='sized_preview_jpg_revision') # nopep8
  377. # get jpg preview for revision
  378. configurator.add_route(
  379. 'preview_pdf_revision',
  380. '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf', # nopep8
  381. request_method='GET'
  382. )
  383. configurator.add_view(self.preview_pdf_revision, route_name='preview_pdf_revision') # nopep8
  384. # others #
  385. # get file revisions
  386. configurator.add_route(
  387. 'file_revisions',
  388. '/workspaces/{workspace_id}/files/{content_id}/revisions', # nopep8
  389. request_method='GET'
  390. )
  391. configurator.add_view(self.get_file_revisions, route_name='file_revisions') # nopep8
  392. # get file status
  393. configurator.add_route(
  394. 'set_file_status',
  395. '/workspaces/{workspace_id}/files/{content_id}/status', # nopep8
  396. request_method='PUT'
  397. )
  398. configurator.add_view(self.set_file_status, route_name='set_file_status') # nopep8