file_controller.py 16KB

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