file_controller.py 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  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 ContentPreviewSizedPathSchema
  20. from tracim.views.core_api.schemas import RevisionPreviewSizedPathSchema
  21. from tracim.views.core_api.schemas import PageQuerySchema
  22. from tracim.views.core_api.schemas import WorkspaceAndContentRevisionIdPathSchema # nopep8
  23. from tracim.views.core_api.schemas import FileRevisionSchema
  24. from tracim.views.core_api.schemas import SetContentStatusSchema
  25. from tracim.views.core_api.schemas import FileContentModifySchema
  26. from tracim.views.core_api.schemas import WorkspaceAndContentIdPathSchema
  27. from tracim.views.core_api.schemas import NoContentSchema
  28. from tracim.lib.utils.authorization import require_content_types
  29. from tracim.lib.utils.authorization import require_workspace_role
  30. from tracim.models.context_models import ContentInContext
  31. from tracim.models.context_models import RevisionInContext
  32. from tracim.models.contents import ContentTypeLegacy as ContentType
  33. from tracim.models.contents import file_type
  34. from tracim.models.revision_protection import new_revision
  35. from preview_generator.manager import PreviewManager
  36. FILE_ENDPOINTS_TAG = 'Files'
  37. class FileController(Controller):
  38. # File data
  39. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  40. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  41. @require_content_types([file_type])
  42. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  43. #@hapic.input_files()
  44. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  45. def upload_file(self, context, request: TracimRequest, hapic_data=None):
  46. app_config = request.registry.settings['CFG']
  47. api = ContentApi(
  48. current_user=request.current_user,
  49. session=request.dbsession,
  50. config=app_config,
  51. )
  52. content = api.get_one(
  53. hapic_data.path.content_id,
  54. content_type=ContentType.Any
  55. )
  56. file = request.POST['files']
  57. with new_revision(
  58. session=request.dbsession,
  59. tm=transaction.manager,
  60. content=content
  61. ):
  62. api.update_file_data(
  63. content,
  64. new_filename=file.filename,
  65. new_mimetype=file.type,
  66. new_content=file.file,
  67. )
  68. return
  69. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  70. @require_workspace_role(UserRoleInWorkspace.READER)
  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. app_config = request.registry.settings['CFG']
  76. api = ContentApi(
  77. current_user=request.current_user,
  78. session=request.dbsession,
  79. config=app_config,
  80. )
  81. content = api.get_one(
  82. hapic_data.path.content_id,
  83. content_type=ContentType.Any
  84. )
  85. file = DepotManager.get().get(content.depot_file)
  86. response = request.response
  87. response.content_type = file.content_type
  88. response.app_iter = FileIter(file)
  89. return response
  90. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  91. @require_workspace_role(UserRoleInWorkspace.READER)
  92. @require_content_types([file_type])
  93. @hapic.input_path(WorkspaceAndContentRevisionIdPathSchema())
  94. @hapic.output_file([])
  95. def download_revisions_file(self, context, request: TracimRequest, hapic_data=None): # nopep8
  96. app_config = request.registry.settings['CFG']
  97. api = ContentApi(
  98. current_user=request.current_user,
  99. session=request.dbsession,
  100. config=app_config,
  101. )
  102. content = api.get_one(
  103. hapic_data.path.content_id,
  104. content_type=ContentType.Any
  105. )
  106. revision = api.get_one_revision(
  107. revision_id=hapic_data.path.revision_id,
  108. content=content
  109. )
  110. file = DepotManager.get().get(revision.depot_file)
  111. response = request.response
  112. response.content_type = file.content_type
  113. response.app_iter = FileIter(file)
  114. return response
  115. # preview
  116. # pdf
  117. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  118. @require_workspace_role(UserRoleInWorkspace.READER)
  119. @require_content_types([file_type])
  120. @hapic.input_query(PageQuerySchema())
  121. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  122. @hapic.output_file([])
  123. def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
  124. app_config = request.registry.settings['CFG']
  125. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  126. api = ContentApi(
  127. current_user=request.current_user,
  128. session=request.dbsession,
  129. config=app_config,
  130. )
  131. content = api.get_one(
  132. hapic_data.path.content_id,
  133. content_type=ContentType.Any
  134. )
  135. file_path = api.get_one_revision_filepath(content.revision_id)
  136. if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
  137. raise Exception('page {page} of content {content_id} does not exist'.format(
  138. page=hapic_data.query.page,
  139. content_id=content.content_id),
  140. )
  141. pdf_preview_path = preview_manager.get_pdf_preview(file_path, page=hapic_data.query.page) # nopep8
  142. return FileResponse(pdf_preview_path)
  143. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  144. @require_workspace_role(UserRoleInWorkspace.READER)
  145. @require_content_types([file_type])
  146. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  147. @hapic.output_file([])
  148. def preview_pdf_full(self, context, request: TracimRequest, hapic_data=None):
  149. app_config = request.registry.settings['CFG']
  150. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  151. api = ContentApi(
  152. current_user=request.current_user,
  153. session=request.dbsession,
  154. config=app_config,
  155. )
  156. content = api.get_one(
  157. hapic_data.path.content_id,
  158. content_type=ContentType.Any
  159. )
  160. file_path = api.get_one_revision_filepath(content.revision_id)
  161. pdf_preview_path = preview_manager.get_pdf_preview(file_path)
  162. return FileResponse(pdf_preview_path)
  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(WorkspaceAndContentRevisionIdPathSchema())
  167. @hapic.input_query(PageQuerySchema())
  168. @hapic.output_file([])
  169. def preview_pdf_revision(self, context, request: TracimRequest, hapic_data=None):
  170. app_config = request.registry.settings['CFG']
  171. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  172. api = ContentApi(
  173. current_user=request.current_user,
  174. session=request.dbsession,
  175. config=app_config,
  176. )
  177. content = api.get_one(
  178. hapic_data.path.content_id,
  179. content_type=ContentType.Any
  180. )
  181. revision = api.get_one_revision(
  182. revision_id=hapic_data.path.revision_id,
  183. content=content
  184. )
  185. file_path = api.get_one_revision_filepath(revision.revision_id)
  186. if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
  187. raise Exception('page {page} of content {content_id} does not exist'.format(
  188. page=hapic_data.query.page,
  189. content_id=content.content_id),
  190. )
  191. pdf_preview_path = preview_manager.get_pdf_preview(file_path, page=hapic_data.query.page) # nopep8
  192. return FileResponse(pdf_preview_path)
  193. # jpg
  194. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  195. @require_workspace_role(UserRoleInWorkspace.READER)
  196. @require_content_types([file_type])
  197. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  198. @hapic.input_query(PageQuerySchema())
  199. @hapic.output_file([])
  200. def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
  201. app_config = request.registry.settings['CFG']
  202. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  203. api = ContentApi(
  204. current_user=request.current_user,
  205. session=request.dbsession,
  206. config=app_config,
  207. )
  208. content = api.get_one(
  209. hapic_data.path.content_id,
  210. content_type=ContentType.Any
  211. )
  212. file_path = api.get_one_revision_filepath(content.revision_id)
  213. if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
  214. raise Exception('page {page} of content {content_id} does not exist'.format(
  215. page=hapic_data.query.page,
  216. content_id=content.content_id),
  217. )
  218. jpg_preview_path = preview_manager.get_jpeg_preview(file_path, page=hapic_data.query.page) # nopep8
  219. return FileResponse(jpg_preview_path)
  220. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  221. @require_workspace_role(UserRoleInWorkspace.READER)
  222. @require_content_types([file_type])
  223. @hapic.input_query(PageQuerySchema())
  224. @hapic.input_path(ContentPreviewSizedPathSchema())
  225. @hapic.output_file([])
  226. def sized_preview_jpg(self, context, request: TracimRequest, hapic_data=None):
  227. app_config = request.registry.settings['CFG']
  228. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  229. api = ContentApi(
  230. current_user=request.current_user,
  231. session=request.dbsession,
  232. config=app_config,
  233. )
  234. content = api.get_one(
  235. hapic_data.path.content_id,
  236. content_type=ContentType.Any
  237. )
  238. file_path = api.get_one_revision_filepath(content.revision_id)
  239. if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
  240. raise Exception('page {page} of content {content_id} does not exist'.format(
  241. page=hapic_data.query.page,
  242. content_id=content.content_id),
  243. )
  244. jpg_preview_path = preview_manager.get_jpeg_preview(
  245. file_path,
  246. page=hapic_data.query.page,
  247. width=hapic_data.path.width,
  248. height=hapic_data.path.height,
  249. )
  250. return FileResponse(jpg_preview_path)
  251. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  252. @require_workspace_role(UserRoleInWorkspace.READER)
  253. @require_content_types([file_type])
  254. @hapic.input_path(RevisionPreviewSizedPathSchema())
  255. @hapic.input_query(PageQuerySchema())
  256. @hapic.output_file([])
  257. def sized_preview_jpg_revision(self, context, request: TracimRequest, hapic_data=None):
  258. app_config = request.registry.settings['CFG']
  259. preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  260. api = ContentApi(
  261. current_user=request.current_user,
  262. session=request.dbsession,
  263. config=app_config,
  264. )
  265. content = api.get_one(
  266. hapic_data.path.content_id,
  267. content_type=ContentType.Any
  268. )
  269. revision = api.get_one_revision(
  270. revision_id=hapic_data.path.revision_id,
  271. content=content
  272. )
  273. file_path = api.get_one_revision_filepath(revision.revision_id)
  274. if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
  275. raise Exception('page {page} of content {content_id} does not exist'.format(
  276. page=hapic_data.query.page,
  277. content_id=content.content_id),
  278. )
  279. jpg_preview_path = preview_manager.get_jpeg_preview(
  280. file_path,
  281. page=hapic_data.query.page,
  282. width=hapic_data.path.width,
  283. height=hapic_data.path.height,
  284. )
  285. return FileResponse(jpg_preview_path)
  286. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  287. @require_workspace_role(UserRoleInWorkspace.READER)
  288. @require_content_types([file_type])
  289. @hapic.output_file([])
  290. def allowed_dim_preview_jpg(self, context, request: TracimRequest, hapic_data=None):
  291. raise NotImplemented()
  292. # @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  293. # @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  294. # @require_content_types([file_type])
  295. # @hapic.input_path(WorkspaceAndContentIdPathSchema())
  296. # #@hapic.output_file([])
  297. # def get_file_preview_info(self, context, request: TracimRequest, hapic_data=None): # nopep8
  298. # # TODO - G.M - 2018-07-05 - Do this endpoint
  299. # app_config = request.registry.settings['CFG']
  300. # preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True) # nopep8
  301. # api = ContentApi(
  302. # current_user=request.current_user,
  303. # session=request.dbsession,
  304. # config=app_config,
  305. # )
  306. # content = api.get_one(
  307. # hapic_data.path.content_id,
  308. # content_type=ContentType.Any
  309. # )
  310. # file_path = api.get_one_revision_filepath(content.revision_id)
  311. # return {
  312. # 'nb_pages': preview_manager.get_page_nb(file_path),
  313. # 'pdf_preview': preview_manager.has_pdf_preview(file_path),
  314. # 'mimetype': preview_manager.get_mimetype(file_path),
  315. # }
  316. # File infos
  317. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  318. @require_workspace_role(UserRoleInWorkspace.READER)
  319. @require_content_types([file_type])
  320. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  321. @hapic.output_body(FileContentSchema())
  322. def get_file_infos(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  323. """
  324. Get thread content
  325. """
  326. app_config = request.registry.settings['CFG']
  327. api = ContentApi(
  328. current_user=request.current_user,
  329. session=request.dbsession,
  330. config=app_config,
  331. )
  332. content = api.get_one(
  333. hapic_data.path.content_id,
  334. content_type=ContentType.Any
  335. )
  336. return api.get_content_in_context(content)
  337. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  338. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  339. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  340. @require_content_types([file_type])
  341. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  342. @hapic.input_body(FileContentModifySchema())
  343. @hapic.output_body(FileContentSchema())
  344. def update_file_info(self, context, request: TracimRequest, hapic_data=None) -> ContentInContext: # nopep8
  345. """
  346. update thread
  347. """
  348. app_config = request.registry.settings['CFG']
  349. api = ContentApi(
  350. current_user=request.current_user,
  351. session=request.dbsession,
  352. config=app_config,
  353. )
  354. content = api.get_one(
  355. hapic_data.path.content_id,
  356. content_type=ContentType.Any
  357. )
  358. with new_revision(
  359. session=request.dbsession,
  360. tm=transaction.manager,
  361. content=content
  362. ):
  363. api.update_content(
  364. item=content,
  365. new_label=hapic_data.body.label,
  366. new_content=hapic_data.body.raw_content,
  367. )
  368. api.save(content)
  369. return api.get_content_in_context(content)
  370. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  371. @require_workspace_role(UserRoleInWorkspace.READER)
  372. @require_content_types([file_type])
  373. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  374. @hapic.output_body(FileRevisionSchema(many=True))
  375. def get_file_revisions(
  376. self,
  377. context,
  378. request: TracimRequest,
  379. hapic_data=None
  380. ) -> typing.List[RevisionInContext]:
  381. """
  382. get file revisions
  383. """
  384. app_config = request.registry.settings['CFG']
  385. api = ContentApi(
  386. current_user=request.current_user,
  387. session=request.dbsession,
  388. config=app_config,
  389. )
  390. content = api.get_one(
  391. hapic_data.path.content_id,
  392. content_type=ContentType.Any
  393. )
  394. revisions = content.revisions
  395. return [
  396. api.get_revision_in_context(revision)
  397. for revision in revisions
  398. ]
  399. @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
  400. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  401. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  402. @require_content_types([file_type])
  403. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  404. @hapic.input_body(SetContentStatusSchema())
  405. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  406. def set_file_status(self, context, request: TracimRequest, hapic_data=None) -> None: # nopep8
  407. """
  408. set file status
  409. """
  410. app_config = request.registry.settings['CFG']
  411. api = ContentApi(
  412. current_user=request.current_user,
  413. session=request.dbsession,
  414. config=app_config,
  415. )
  416. content = api.get_one(
  417. hapic_data.path.content_id,
  418. content_type=ContentType.Any
  419. )
  420. with new_revision(
  421. session=request.dbsession,
  422. tm=transaction.manager,
  423. content=content
  424. ):
  425. api.set_status(
  426. content,
  427. hapic_data.body.status,
  428. )
  429. api.save(content)
  430. return
  431. def bind(self, configurator: Configurator) -> None:
  432. # file info #
  433. # Get file info
  434. configurator.add_route(
  435. 'file_info',
  436. '/workspaces/{workspace_id}/files/{content_id}',
  437. request_method='GET'
  438. )
  439. configurator.add_view(self.get_file_infos, route_name='file_info') # nopep8
  440. # update file
  441. configurator.add_route(
  442. 'update_file_info',
  443. '/workspaces/{workspace_id}/files/{content_id}',
  444. request_method='PUT'
  445. ) # nopep8
  446. configurator.add_view(self.update_file_info, route_name='update_file_info') # nopep8
  447. # raw file #
  448. # upload raw file
  449. configurator.add_route(
  450. 'upload_file',
  451. '/workspaces/{workspace_id}/files/{content_id}/raw', # nopep8
  452. request_method='PUT'
  453. )
  454. configurator.add_view(self.upload_file, route_name='upload_file') # nopep8
  455. # download raw file
  456. configurator.add_route(
  457. 'download_file',
  458. '/workspaces/{workspace_id}/files/{content_id}/raw', # nopep8
  459. request_method='GET'
  460. )
  461. configurator.add_view(self.download_file, route_name='download_file') # nopep8
  462. # download raw file of revision
  463. configurator.add_route(
  464. 'download_revision',
  465. '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/raw', # nopep8
  466. request_method='GET'
  467. )
  468. configurator.add_view(self.download_revisions_file, route_name='download_revision') # nopep8
  469. # previews #
  470. # get preview pdf full
  471. configurator.add_route(
  472. 'preview_pdf_full',
  473. '/workspaces/{workspace_id}/files/{content_id}/preview/pdf/full', # nopep8
  474. request_method='GET'
  475. )
  476. configurator.add_view(self.preview_pdf_full, route_name='preview_pdf_full') # nopep8
  477. # get preview pdf
  478. configurator.add_route(
  479. 'preview_pdf',
  480. '/workspaces/{workspace_id}/files/{content_id}/preview/pdf', # nopep8
  481. request_method='GET'
  482. )
  483. configurator.add_view(self.preview_pdf, route_name='preview_pdf') # nopep8
  484. # get preview jpg allowed dims
  485. configurator.add_route(
  486. 'allowed_dim_preview_jpg',
  487. '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/allowed_dims', # nopep8
  488. request_method='GET'
  489. )
  490. configurator.add_view(self.allowed_dim_preview_jpg, route_name='allowed_dim_preview_jpg') # nopep8
  491. # get preview jpg
  492. configurator.add_route(
  493. 'preview_jpg',
  494. '/workspaces/{workspace_id}/files/{content_id}/preview/jpg', # nopep8
  495. request_method='GET'
  496. )
  497. configurator.add_view(self.preview_jpg, route_name='preview_jpg') # nopep8
  498. # get preview jpg with size
  499. configurator.add_route(
  500. 'sized_preview_jpg',
  501. '/workspaces/{workspace_id}/files/{content_id}/preview/jpg/{width}x{height}', # nopep8
  502. request_method='GET'
  503. )
  504. configurator.add_view(self.sized_preview_jpg, route_name='sized_preview_jpg') # nopep8
  505. # get jpg preview for revision
  506. configurator.add_route(
  507. 'sized_preview_jpg_revision',
  508. '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/jpg/{width}x{height}', # nopep8
  509. request_method='GET'
  510. )
  511. configurator.add_view(self.sized_preview_jpg_revision, route_name='sized_preview_jpg_revision') # nopep8
  512. # get jpg preview for revision
  513. configurator.add_route(
  514. 'preview_pdf_revision',
  515. '/workspaces/{workspace_id}/files/{content_id}/revisions/{revision_id}/preview/pdf', # nopep8
  516. request_method='GET'
  517. )
  518. configurator.add_view(self.preview_pdf_revision, route_name='preview_pdf_revision') # nopep8
  519. # others #
  520. # get file revisions
  521. configurator.add_route(
  522. 'file_revisions',
  523. '/workspaces/{workspace_id}/files/{content_id}/revisions', # nopep8
  524. request_method='GET'
  525. )
  526. configurator.add_view(self.get_file_revisions, route_name='file_revisions') # nopep8
  527. # get file status
  528. configurator.add_route(
  529. 'set_file_status',
  530. '/workspaces/{workspace_id}/files/{content_id}/status', # nopep8
  531. request_method='PUT'
  532. )
  533. configurator.add_view(self.set_file_status, route_name='set_file_status') # nopep8