workspace_controller.py 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. import typing
  2. import transaction
  3. from pyramid.config import Configurator
  4. from pyramid.httpexceptions import HTTPFound
  5. from tracim_backend.lib.core.user import UserApi
  6. from tracim_backend.models.roles import WorkspaceRoles
  7. try: # Python 3.5+
  8. from http import HTTPStatus
  9. except ImportError:
  10. from http import client as HTTPStatus
  11. from tracim_backend import hapic
  12. from tracim_backend.lib.utils.request import TracimRequest
  13. from tracim_backend import BASE_API_V2
  14. from tracim_backend.lib.core.workspace import WorkspaceApi
  15. from tracim_backend.lib.core.content import ContentApi
  16. from tracim_backend.lib.core.userworkspace import RoleApi
  17. from tracim_backend.lib.utils.authorization import require_workspace_role
  18. from tracim_backend.lib.utils.authorization import require_profile_or_other_profile_with_workspace_role
  19. from tracim_backend.lib.utils.authorization import require_profile
  20. from tracim_backend.models import Group
  21. from tracim_backend.lib.utils.authorization import require_candidate_workspace_role
  22. from tracim_backend.models.data import UserRoleInWorkspace
  23. from tracim_backend.models.data import ActionDescription
  24. from tracim_backend.models.context_models import UserRoleWorkspaceInContext
  25. from tracim_backend.models.context_models import ContentInContext
  26. from tracim_backend.exceptions import EmptyLabelNotAllowed
  27. from tracim_backend.exceptions import UnallowedSubContent
  28. from tracim_backend.exceptions import EmailValidationFailed
  29. from tracim_backend.exceptions import UserCreationFailed
  30. from tracim_backend.exceptions import UserDoesNotExist
  31. from tracim_backend.exceptions import ContentNotFound
  32. from tracim_backend.exceptions import WorkspacesDoNotMatch
  33. from tracim_backend.exceptions import ParentNotFound
  34. from tracim_backend.views.controllers import Controller
  35. from tracim_backend.lib.utils.utils import password_generator
  36. from tracim_backend.views.core_api.schemas import FilterContentQuerySchema
  37. from tracim_backend.views.core_api.schemas import ContentIdPathSchema
  38. from tracim_backend.views.core_api.schemas import WorkspaceMemberCreationSchema
  39. from tracim_backend.views.core_api.schemas import WorkspaceMemberInviteSchema
  40. from tracim_backend.views.core_api.schemas import RoleUpdateSchema
  41. from tracim_backend.views.core_api.schemas import WorkspaceCreationSchema
  42. from tracim_backend.views.core_api.schemas import WorkspaceModifySchema
  43. from tracim_backend.views.core_api.schemas import WorkspaceAndUserIdPathSchema
  44. from tracim_backend.views.core_api.schemas import ContentMoveSchema
  45. from tracim_backend.views.core_api.schemas import NoContentSchema
  46. from tracim_backend.views.core_api.schemas import ContentCreationSchema
  47. from tracim_backend.views.core_api.schemas import WorkspaceAndContentIdPathSchema
  48. from tracim_backend.views.core_api.schemas import ContentDigestSchema
  49. from tracim_backend.views.core_api.schemas import WorkspaceSchema
  50. from tracim_backend.views.core_api.schemas import WorkspaceIdPathSchema
  51. from tracim_backend.views.core_api.schemas import WorkspaceMemberSchema
  52. from tracim_backend.app_models.contents import CONTENT_TYPES
  53. from tracim_backend.models.revision_protection import new_revision
  54. SWAGGER_TAG_WORKSPACE_ENDPOINTS = 'Workspaces'
  55. class WorkspaceController(Controller):
  56. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  57. @require_workspace_role(UserRoleInWorkspace.READER)
  58. @hapic.input_path(WorkspaceIdPathSchema())
  59. @hapic.output_body(WorkspaceSchema())
  60. def workspace(self, context, request: TracimRequest, hapic_data=None):
  61. """
  62. Get workspace informations
  63. """
  64. app_config = request.registry.settings['CFG']
  65. wapi = WorkspaceApi(
  66. current_user=request.current_user, # User
  67. session=request.dbsession,
  68. config=app_config,
  69. )
  70. return wapi.get_workspace_with_context(request.current_workspace)
  71. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  72. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  73. @require_workspace_role(UserRoleInWorkspace.WORKSPACE_MANAGER)
  74. @hapic.input_path(WorkspaceIdPathSchema())
  75. @hapic.input_body(WorkspaceModifySchema())
  76. @hapic.output_body(WorkspaceSchema())
  77. def update_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8
  78. """
  79. Update workspace informations
  80. """
  81. app_config = request.registry.settings['CFG']
  82. wapi = WorkspaceApi(
  83. current_user=request.current_user, # User
  84. session=request.dbsession,
  85. config=app_config,
  86. )
  87. wapi.update_workspace(
  88. request.current_workspace,
  89. label=hapic_data.body.label,
  90. description=hapic_data.body.description,
  91. save_now=True,
  92. )
  93. return wapi.get_workspace_with_context(request.current_workspace)
  94. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  95. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  96. @require_profile(Group.TIM_MANAGER)
  97. @hapic.input_body(WorkspaceCreationSchema())
  98. @hapic.output_body(WorkspaceSchema())
  99. def create_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8
  100. """
  101. create workspace
  102. """
  103. app_config = request.registry.settings['CFG']
  104. wapi = WorkspaceApi(
  105. current_user=request.current_user, # User
  106. session=request.dbsession,
  107. config=app_config,
  108. )
  109. workspace = wapi.create_workspace(
  110. label=hapic_data.body.label,
  111. description=hapic_data.body.description,
  112. save_now=True,
  113. )
  114. return wapi.get_workspace_with_context(workspace)
  115. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  116. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  117. @require_profile_or_other_profile_with_workspace_role(
  118. Group.TIM_ADMIN,
  119. Group.TIM_MANAGER,
  120. UserRoleInWorkspace.WORKSPACE_MANAGER,
  121. )
  122. @hapic.input_path(WorkspaceIdPathSchema())
  123. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  124. def delete_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8
  125. """
  126. delete workspace
  127. """
  128. app_config = request.registry.settings['CFG']
  129. wapi = WorkspaceApi(
  130. current_user=request.current_user, # User
  131. session=request.dbsession,
  132. config=app_config,
  133. )
  134. wapi.delete(request.current_workspace, flush=True)
  135. return
  136. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  137. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  138. @require_profile_or_other_profile_with_workspace_role(
  139. Group.TIM_ADMIN,
  140. Group.TIM_MANAGER,
  141. UserRoleInWorkspace.WORKSPACE_MANAGER,
  142. )
  143. @hapic.input_path(WorkspaceIdPathSchema())
  144. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  145. def undelete_workspace(self, context, request: TracimRequest, hapic_data=None): # nopep8
  146. """
  147. restore deleted workspace
  148. """
  149. app_config = request.registry.settings['CFG']
  150. wapi = WorkspaceApi(
  151. current_user=request.current_user, # User
  152. session=request.dbsession,
  153. config=app_config,
  154. show_deleted=True,
  155. )
  156. wapi.undelete(request.current_workspace, flush=True)
  157. return
  158. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  159. @require_workspace_role(UserRoleInWorkspace.READER)
  160. @hapic.input_path(WorkspaceIdPathSchema())
  161. @hapic.output_body(WorkspaceMemberSchema(many=True))
  162. def workspaces_members(
  163. self,
  164. context,
  165. request: TracimRequest,
  166. hapic_data=None
  167. ) -> typing.List[UserRoleWorkspaceInContext]:
  168. """
  169. Get Members of this workspace
  170. """
  171. app_config = request.registry.settings['CFG']
  172. rapi = RoleApi(
  173. current_user=request.current_user,
  174. session=request.dbsession,
  175. config=app_config,
  176. )
  177. roles = rapi.get_all_for_workspace(request.current_workspace)
  178. return [
  179. rapi.get_user_role_workspace_with_context(user_role)
  180. for user_role in roles
  181. ]
  182. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  183. @require_workspace_role(UserRoleInWorkspace.WORKSPACE_MANAGER)
  184. @hapic.input_path(WorkspaceAndUserIdPathSchema())
  185. @hapic.input_body(RoleUpdateSchema())
  186. @hapic.output_body(WorkspaceMemberSchema())
  187. def update_workspaces_members_role(
  188. self,
  189. context,
  190. request: TracimRequest,
  191. hapic_data=None
  192. ) -> UserRoleWorkspaceInContext:
  193. """
  194. Update Members to this workspace
  195. """
  196. app_config = request.registry.settings['CFG']
  197. rapi = RoleApi(
  198. current_user=request.current_user,
  199. session=request.dbsession,
  200. config=app_config,
  201. )
  202. role = rapi.get_one(
  203. user_id=hapic_data.path.user_id,
  204. workspace_id=hapic_data.path.workspace_id,
  205. )
  206. workspace_role = WorkspaceRoles.get_role_from_slug(hapic_data.body.role)
  207. role = rapi.update_role(
  208. role,
  209. role_level=workspace_role.level
  210. )
  211. return rapi.get_user_role_workspace_with_context(role)
  212. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  213. @require_workspace_role(UserRoleInWorkspace.WORKSPACE_MANAGER)
  214. @hapic.input_path(WorkspaceAndUserIdPathSchema())
  215. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  216. def delete_workspaces_members_role(
  217. self,
  218. context,
  219. request: TracimRequest,
  220. hapic_data=None
  221. ) -> None:
  222. app_config = request.registry.settings['CFG']
  223. rapi = RoleApi(
  224. current_user=request.current_user,
  225. session=request.dbsession,
  226. config=app_config,
  227. )
  228. rapi.delete_one(
  229. user_id=hapic_data.path.user_id,
  230. workspace_id=hapic_data.path.workspace_id,
  231. )
  232. return
  233. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  234. @hapic.handle_exception(UserCreationFailed, HTTPStatus.BAD_REQUEST)
  235. @require_workspace_role(UserRoleInWorkspace.WORKSPACE_MANAGER)
  236. @hapic.input_path(WorkspaceIdPathSchema())
  237. @hapic.input_body(WorkspaceMemberInviteSchema())
  238. @hapic.output_body(WorkspaceMemberCreationSchema())
  239. def create_workspaces_members_role(
  240. self,
  241. context,
  242. request: TracimRequest,
  243. hapic_data=None
  244. ) -> UserRoleWorkspaceInContext:
  245. """
  246. Add Members to this workspace
  247. """
  248. newly_created = False
  249. email_sent = False
  250. app_config = request.registry.settings['CFG']
  251. rapi = RoleApi(
  252. current_user=request.current_user,
  253. session=request.dbsession,
  254. config=app_config,
  255. )
  256. uapi = UserApi(
  257. current_user=request.current_user,
  258. session=request.dbsession,
  259. config=app_config,
  260. )
  261. try:
  262. _, user = uapi.find(
  263. user_id=hapic_data.body.user_id,
  264. email=hapic_data.body.user_email_or_public_name,
  265. public_name=hapic_data.body.user_email_or_public_name
  266. )
  267. except UserDoesNotExist:
  268. try:
  269. # TODO - G.M - 2018-07-05 - [UserCreation] Reenable email
  270. # notification for creation
  271. user = uapi.create_user(
  272. email=hapic_data.body.user_email_or_public_name,
  273. password=password_generator(),
  274. do_notify=True
  275. ) # nopep8
  276. newly_created = True
  277. if app_config.EMAIL_NOTIFICATION_ACTIVATED and \
  278. app_config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == 'sync':
  279. email_sent = True
  280. except EmailValidationFailed:
  281. raise UserCreationFailed('no valid mail given')
  282. role = rapi.create_one(
  283. user=user,
  284. workspace=request.current_workspace,
  285. role_level=WorkspaceRoles.get_role_from_slug(hapic_data.body.role).level, # nopep8
  286. with_notif=False,
  287. flush=True,
  288. )
  289. return rapi.get_user_role_workspace_with_context(
  290. role,
  291. newly_created=newly_created,
  292. email_sent=email_sent,
  293. )
  294. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  295. @require_workspace_role(UserRoleInWorkspace.READER)
  296. @hapic.input_path(WorkspaceIdPathSchema())
  297. @hapic.input_query(FilterContentQuerySchema())
  298. @hapic.output_body(ContentDigestSchema(many=True))
  299. def workspace_content(
  300. self,
  301. context,
  302. request: TracimRequest,
  303. hapic_data=None,
  304. ) -> typing.List[ContentInContext]:
  305. """
  306. return list of contents found in the workspace
  307. """
  308. app_config = request.registry.settings['CFG']
  309. content_filter = hapic_data.query
  310. api = ContentApi(
  311. current_user=request.current_user,
  312. session=request.dbsession,
  313. config=app_config,
  314. show_archived=content_filter.show_archived,
  315. show_deleted=content_filter.show_deleted,
  316. show_active=content_filter.show_active,
  317. )
  318. contents = api.get_all(
  319. parent_id=content_filter.parent_id,
  320. workspace=request.current_workspace,
  321. content_type=content_filter.content_type or CONTENT_TYPES.Any_SLUG,
  322. )
  323. contents = [
  324. api.get_content_in_context(content) for content in contents
  325. ]
  326. return contents
  327. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  328. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  329. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  330. @hapic.handle_exception(UnallowedSubContent, HTTPStatus.BAD_REQUEST)
  331. @hapic.input_path(WorkspaceIdPathSchema())
  332. @hapic.input_body(ContentCreationSchema())
  333. @hapic.output_body(ContentDigestSchema())
  334. def create_generic_empty_content(
  335. self,
  336. context,
  337. request: TracimRequest,
  338. hapic_data=None,
  339. ) -> ContentInContext:
  340. """
  341. create a generic empty content
  342. """
  343. app_config = request.registry.settings['CFG']
  344. creation_data = hapic_data.body
  345. api = ContentApi(
  346. current_user=request.current_user,
  347. session=request.dbsession,
  348. config=app_config
  349. )
  350. parent = None
  351. if creation_data.parent_id:
  352. try:
  353. parent = api.get_one(content_id=creation_data.parent_id, content_type=CONTENT_TYPES.Any_SLUG) # nopep8
  354. except ContentNotFound as exc:
  355. raise ParentNotFound(
  356. 'Parent with content_id {} not found'.format(creation_data.parent_id)
  357. ) from exc
  358. content = api.create(
  359. label=creation_data.label,
  360. content_type_slug=creation_data.content_type,
  361. workspace=request.current_workspace,
  362. parent=parent,
  363. )
  364. api.save(content, ActionDescription.CREATION)
  365. content = api.get_content_in_context(content)
  366. return content
  367. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  368. @require_workspace_role(UserRoleInWorkspace.READER)
  369. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  370. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) # nopep8
  371. def get_content_from_workspace(
  372. self,
  373. context,
  374. request: TracimRequest,
  375. hapic_data=None,
  376. ) -> None:
  377. """
  378. redirect to correct content file endpoint
  379. """
  380. app_config = request.registry.settings['CFG']
  381. content = request.current_content
  382. content_type = CONTENT_TYPES.get_one_by_slug(content.type).slug
  383. # TODO - G.M - 2018-08-03 - Jsonify redirect response ?
  384. raise HTTPFound(
  385. "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}".format(
  386. base_url=BASE_API_V2,
  387. workspace_id=content.workspace_id,
  388. content_type=content_type,
  389. content_id=content.content_id,
  390. )
  391. )
  392. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  393. @hapic.input_path(ContentIdPathSchema())
  394. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) # nopep8
  395. def get_content(
  396. self,
  397. context,
  398. request: TracimRequest,
  399. hapic_data=None,
  400. ) -> None:
  401. """
  402. redirect to correct content file endpoint
  403. """
  404. app_config = request.registry.settings['CFG']
  405. api = ContentApi(
  406. current_user=request.current_user,
  407. session=request.dbsession,
  408. config=app_config,
  409. )
  410. content = api.get_one(
  411. content_id=hapic_data.path['content_id'],
  412. content_type=CONTENT_TYPES.Any_SLUG
  413. )
  414. content_type = CONTENT_TYPES.get_one_by_slug(content.type).slug
  415. # TODO - G.M - 2018-08-03 - Jsonify redirect response ?
  416. raise HTTPFound(
  417. "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}".format(
  418. base_url=BASE_API_V2,
  419. workspace_id=content.workspace_id,
  420. content_type=content_type,
  421. content_id=content.content_id,
  422. )
  423. )
  424. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  425. @hapic.handle_exception(WorkspacesDoNotMatch, HTTPStatus.BAD_REQUEST)
  426. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  427. @require_candidate_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  428. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  429. @hapic.input_body(ContentMoveSchema())
  430. @hapic.output_body(ContentDigestSchema())
  431. def move_content(
  432. self,
  433. context,
  434. request: TracimRequest,
  435. hapic_data=None,
  436. ) -> ContentInContext:
  437. """
  438. move a content
  439. """
  440. app_config = request.registry.settings['CFG']
  441. path_data = hapic_data.path
  442. move_data = hapic_data.body
  443. api = ContentApi(
  444. show_archived=True,
  445. show_deleted=True,
  446. current_user=request.current_user,
  447. session=request.dbsession,
  448. config=app_config,
  449. )
  450. content = api.get_one(
  451. path_data.content_id,
  452. content_type=CONTENT_TYPES.Any_SLUG
  453. )
  454. new_parent = api.get_one(
  455. move_data.new_parent_id, content_type=CONTENT_TYPES.Any_SLUG
  456. )
  457. new_workspace = request.candidate_workspace
  458. with new_revision(
  459. session=request.dbsession,
  460. tm=transaction.manager,
  461. content=content
  462. ):
  463. api.move(
  464. content,
  465. new_parent=new_parent,
  466. new_workspace=new_workspace,
  467. must_stay_in_same_workspace=False,
  468. )
  469. updated_content = api.get_one(
  470. path_data.content_id,
  471. content_type=CONTENT_TYPES.Any_SLUG
  472. )
  473. return api.get_content_in_context(updated_content)
  474. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  475. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  476. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  477. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  478. def delete_content(
  479. self,
  480. context,
  481. request: TracimRequest,
  482. hapic_data=None,
  483. ) -> None:
  484. """
  485. delete a content
  486. """
  487. app_config = request.registry.settings['CFG']
  488. path_data = hapic_data.path
  489. api = ContentApi(
  490. show_archived=True,
  491. show_deleted=True,
  492. current_user=request.current_user,
  493. session=request.dbsession,
  494. config=app_config,
  495. )
  496. content = api.get_one(
  497. path_data.content_id,
  498. content_type=CONTENT_TYPES.Any_SLUG
  499. )
  500. with new_revision(
  501. session=request.dbsession,
  502. tm=transaction.manager,
  503. content=content
  504. ):
  505. api.delete(content)
  506. return
  507. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  508. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  509. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  510. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  511. def undelete_content(
  512. self,
  513. context,
  514. request: TracimRequest,
  515. hapic_data=None,
  516. ) -> None:
  517. """
  518. undelete a content
  519. """
  520. app_config = request.registry.settings['CFG']
  521. path_data = hapic_data.path
  522. api = ContentApi(
  523. current_user=request.current_user,
  524. session=request.dbsession,
  525. config=app_config,
  526. show_deleted=True,
  527. show_archived=True,
  528. )
  529. content = api.get_one(
  530. path_data.content_id,
  531. content_type=CONTENT_TYPES.Any_SLUG
  532. )
  533. with new_revision(
  534. session=request.dbsession,
  535. tm=transaction.manager,
  536. content=content
  537. ):
  538. api.undelete(content)
  539. return
  540. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  541. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  542. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  543. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  544. def archive_content(
  545. self,
  546. context,
  547. request: TracimRequest,
  548. hapic_data=None,
  549. ) -> None:
  550. """
  551. archive a content
  552. """
  553. app_config = request.registry.settings['CFG']
  554. path_data = hapic_data.path
  555. api = ContentApi(
  556. show_archived=True,
  557. show_deleted=True,
  558. current_user=request.current_user,
  559. session=request.dbsession,
  560. config=app_config,
  561. )
  562. content = api.get_one(path_data.content_id, content_type=CONTENT_TYPES.Any_SLUG) # nopep8
  563. with new_revision(
  564. session=request.dbsession,
  565. tm=transaction.manager,
  566. content=content
  567. ):
  568. api.archive(content)
  569. return
  570. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  571. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  572. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  573. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  574. def unarchive_content(
  575. self,
  576. context,
  577. request: TracimRequest,
  578. hapic_data=None,
  579. ) -> None:
  580. """
  581. unarchive a content
  582. """
  583. app_config = request.registry.settings['CFG']
  584. path_data = hapic_data.path
  585. api = ContentApi(
  586. current_user=request.current_user,
  587. session=request.dbsession,
  588. config=app_config,
  589. show_archived=True,
  590. show_deleted=True,
  591. )
  592. content = api.get_one(
  593. path_data.content_id,
  594. content_type=CONTENT_TYPES.Any_SLUG
  595. )
  596. with new_revision(
  597. session=request.dbsession,
  598. tm=transaction.manager,
  599. content=content
  600. ):
  601. api.unarchive(content)
  602. return
  603. def bind(self, configurator: Configurator) -> None:
  604. """
  605. Create all routes and views using
  606. pyramid configurator for this controller
  607. """
  608. # Workspace
  609. configurator.add_route('workspace', '/workspaces/{workspace_id}', request_method='GET') # nopep8
  610. configurator.add_view(self.workspace, route_name='workspace')
  611. # Create workspace
  612. configurator.add_route('create_workspace', '/workspaces', request_method='POST') # nopep8
  613. configurator.add_view(self.create_workspace, route_name='create_workspace') # nopep8
  614. # Delete/Undelete workpace
  615. configurator.add_route('delete_workspace', '/workspaces/{workspace_id}/delete', request_method='PUT') # nopep8
  616. configurator.add_view(self.delete_workspace, route_name='delete_workspace') # nopep8
  617. configurator.add_route('undelete_workspace', '/workspaces/{workspace_id}/undelete', request_method='PUT') # nopep8
  618. configurator.add_view(self.undelete_workspace, route_name='undelete_workspace') # nopep8
  619. # Update Workspace
  620. configurator.add_route('update_workspace', '/workspaces/{workspace_id}', request_method='PUT') # nopep8
  621. configurator.add_view(self.update_workspace, route_name='update_workspace') # nopep8
  622. # Workspace Members (Roles)
  623. configurator.add_route('workspace_members', '/workspaces/{workspace_id}/members', request_method='GET') # nopep8
  624. configurator.add_view(self.workspaces_members, route_name='workspace_members') # nopep8
  625. # Update Workspace Members roles
  626. configurator.add_route('update_workspace_member', '/workspaces/{workspace_id}/members/{user_id}', request_method='PUT') # nopep8
  627. configurator.add_view(self.update_workspaces_members_role, route_name='update_workspace_member') # nopep8
  628. # Create Workspace Members roles
  629. configurator.add_route('create_workspace_member', '/workspaces/{workspace_id}/members', request_method='POST') # nopep8
  630. configurator.add_view(self.create_workspaces_members_role, route_name='create_workspace_member') # nopep8
  631. # Delete Workspace Members roles
  632. configurator.add_route('delete_workspace_member', '/workspaces/{workspace_id}/members/{user_id}', request_method='DELETE') # nopep8
  633. configurator.add_view(self.delete_workspaces_members_role, route_name='delete_workspace_member') # nopep8
  634. # Workspace Content
  635. configurator.add_route('workspace_content', '/workspaces/{workspace_id}/contents', request_method='GET') # nopep8
  636. configurator.add_view(self.workspace_content, route_name='workspace_content') # nopep8
  637. # Create Generic Content
  638. configurator.add_route('create_generic_content', '/workspaces/{workspace_id}/contents', request_method='POST') # nopep8
  639. configurator.add_view(self.create_generic_empty_content, route_name='create_generic_content') # nopep8
  640. # Get Content
  641. configurator.add_route('get_content', '/contents/{content_id}', request_method='GET') # nopep8
  642. configurator.add_view(self.get_content, route_name='get_content')
  643. # Get Content From workspace
  644. configurator.add_route('get_content_from_workspace', '/workspaces/{workspace_id}/contents/{content_id}', request_method='GET') # nopep8
  645. configurator.add_view(self.get_content_from_workspace, route_name='get_content_from_workspace') # nopep8
  646. # Move Content
  647. configurator.add_route('move_content', '/workspaces/{workspace_id}/contents/{content_id}/move', request_method='PUT') # nopep8
  648. configurator.add_view(self.move_content, route_name='move_content') # nopep8
  649. # Delete/Undelete Content
  650. configurator.add_route('delete_content', '/workspaces/{workspace_id}/contents/{content_id}/delete', request_method='PUT') # nopep8
  651. configurator.add_view(self.delete_content, route_name='delete_content') # nopep8
  652. configurator.add_route('undelete_content', '/workspaces/{workspace_id}/contents/{content_id}/undelete', request_method='PUT') # nopep8
  653. configurator.add_view(self.undelete_content, route_name='undelete_content') # nopep8
  654. # # Archive/Unarchive Content
  655. configurator.add_route('archive_content', '/workspaces/{workspace_id}/contents/{content_id}/archive', request_method='PUT') # nopep8
  656. configurator.add_view(self.archive_content, route_name='archive_content') # nopep8
  657. configurator.add_route('unarchive_content', '/workspaces/{workspace_id}/contents/{content_id}/unarchive', request_method='PUT') # nopep8
  658. configurator.add_view(self.unarchive_content, route_name='unarchive_content') # nopep8