workspace_controller.py 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  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. with_notif=hapic_data.body.do_notify
  211. )
  212. return rapi.get_user_role_workspace_with_context(role)
  213. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  214. @require_workspace_role(UserRoleInWorkspace.WORKSPACE_MANAGER)
  215. @hapic.input_path(WorkspaceAndUserIdPathSchema())
  216. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  217. def delete_workspaces_members_role(
  218. self,
  219. context,
  220. request: TracimRequest,
  221. hapic_data=None
  222. ) -> None:
  223. app_config = request.registry.settings['CFG']
  224. rapi = RoleApi(
  225. current_user=request.current_user,
  226. session=request.dbsession,
  227. config=app_config,
  228. )
  229. rapi.delete_one(
  230. user_id=hapic_data.path.user_id,
  231. workspace_id=hapic_data.path.workspace_id,
  232. )
  233. return
  234. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  235. @hapic.handle_exception(UserCreationFailed, HTTPStatus.BAD_REQUEST)
  236. @require_workspace_role(UserRoleInWorkspace.WORKSPACE_MANAGER)
  237. @hapic.input_path(WorkspaceIdPathSchema())
  238. @hapic.input_body(WorkspaceMemberInviteSchema())
  239. @hapic.output_body(WorkspaceMemberCreationSchema())
  240. def create_workspaces_members_role(
  241. self,
  242. context,
  243. request: TracimRequest,
  244. hapic_data=None
  245. ) -> UserRoleWorkspaceInContext:
  246. """
  247. Add Members to this workspace
  248. """
  249. newly_created = False
  250. email_sent = False
  251. app_config = request.registry.settings['CFG']
  252. rapi = RoleApi(
  253. current_user=request.current_user,
  254. session=request.dbsession,
  255. config=app_config,
  256. )
  257. uapi = UserApi(
  258. current_user=request.current_user,
  259. session=request.dbsession,
  260. config=app_config,
  261. )
  262. try:
  263. _, user = uapi.find(
  264. user_id=hapic_data.body.user_id,
  265. email=hapic_data.body.user_email_or_public_name,
  266. public_name=hapic_data.body.user_email_or_public_name
  267. )
  268. except UserDoesNotExist:
  269. try:
  270. # TODO - G.M - 2018-07-05 - [UserCreation] Reenable email
  271. # notification for creation
  272. user = uapi.create_user(
  273. email=hapic_data.body.user_email_or_public_name,
  274. password=password_generator(),
  275. do_notify=True
  276. ) # nopep8
  277. newly_created = True
  278. if app_config.EMAIL_NOTIFICATION_ACTIVATED and \
  279. app_config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == 'sync':
  280. email_sent = True
  281. except EmailValidationFailed:
  282. raise UserCreationFailed('no valid mail given')
  283. role = rapi.create_one(
  284. user=user,
  285. workspace=request.current_workspace,
  286. role_level=WorkspaceRoles.get_role_from_slug(hapic_data.body.role).level, # nopep8
  287. with_notif=hapic_data.body.do_notify or False, # nopep8, default value as false
  288. flush=True,
  289. )
  290. return rapi.get_user_role_workspace_with_context(
  291. role,
  292. newly_created=newly_created,
  293. email_sent=email_sent,
  294. )
  295. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  296. @require_workspace_role(UserRoleInWorkspace.READER)
  297. @hapic.input_path(WorkspaceIdPathSchema())
  298. @hapic.input_query(FilterContentQuerySchema())
  299. @hapic.output_body(ContentDigestSchema(many=True))
  300. def workspace_content(
  301. self,
  302. context,
  303. request: TracimRequest,
  304. hapic_data=None,
  305. ) -> typing.List[ContentInContext]:
  306. """
  307. return list of contents found in the workspace
  308. """
  309. app_config = request.registry.settings['CFG']
  310. content_filter = hapic_data.query
  311. api = ContentApi(
  312. current_user=request.current_user,
  313. session=request.dbsession,
  314. config=app_config,
  315. show_archived=content_filter.show_archived,
  316. show_deleted=content_filter.show_deleted,
  317. show_active=content_filter.show_active,
  318. )
  319. contents = api.get_all(
  320. parent_id=content_filter.parent_id,
  321. workspace=request.current_workspace,
  322. content_type=content_filter.content_type or CONTENT_TYPES.Any_SLUG,
  323. )
  324. contents = [
  325. api.get_content_in_context(content) for content in contents
  326. ]
  327. return contents
  328. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  329. @require_workspace_role(UserRoleInWorkspace.CONTRIBUTOR)
  330. @hapic.handle_exception(EmptyLabelNotAllowed, HTTPStatus.BAD_REQUEST)
  331. @hapic.handle_exception(UnallowedSubContent, HTTPStatus.BAD_REQUEST)
  332. @hapic.input_path(WorkspaceIdPathSchema())
  333. @hapic.input_body(ContentCreationSchema())
  334. @hapic.output_body(ContentDigestSchema())
  335. def create_generic_empty_content(
  336. self,
  337. context,
  338. request: TracimRequest,
  339. hapic_data=None,
  340. ) -> ContentInContext:
  341. """
  342. create a generic empty content
  343. """
  344. app_config = request.registry.settings['CFG']
  345. creation_data = hapic_data.body
  346. api = ContentApi(
  347. current_user=request.current_user,
  348. session=request.dbsession,
  349. config=app_config
  350. )
  351. parent = None
  352. if creation_data.parent_id:
  353. try:
  354. parent = api.get_one(content_id=creation_data.parent_id, content_type=CONTENT_TYPES.Any_SLUG) # nopep8
  355. except ContentNotFound as exc:
  356. raise ParentNotFound(
  357. 'Parent with content_id {} not found'.format(creation_data.parent_id)
  358. ) from exc
  359. content = api.create(
  360. label=creation_data.label,
  361. content_type_slug=creation_data.content_type,
  362. workspace=request.current_workspace,
  363. parent=parent,
  364. )
  365. api.save(content, ActionDescription.CREATION)
  366. content = api.get_content_in_context(content)
  367. return content
  368. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  369. @require_workspace_role(UserRoleInWorkspace.READER)
  370. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  371. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) # nopep8
  372. def get_content_from_workspace(
  373. self,
  374. context,
  375. request: TracimRequest,
  376. hapic_data=None,
  377. ) -> None:
  378. """
  379. redirect to correct content file endpoint
  380. """
  381. app_config = request.registry.settings['CFG']
  382. content = request.current_content
  383. content_type = CONTENT_TYPES.get_one_by_slug(content.type).slug
  384. # TODO - G.M - 2018-08-03 - Jsonify redirect response ?
  385. raise HTTPFound(
  386. "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}".format(
  387. base_url=BASE_API_V2,
  388. workspace_id=content.workspace_id,
  389. content_type=content_type,
  390. content_id=content.content_id,
  391. )
  392. )
  393. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  394. @hapic.input_path(ContentIdPathSchema())
  395. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.FOUND) # nopep8
  396. def get_content(
  397. self,
  398. context,
  399. request: TracimRequest,
  400. hapic_data=None,
  401. ) -> None:
  402. """
  403. redirect to correct content file endpoint
  404. """
  405. app_config = request.registry.settings['CFG']
  406. api = ContentApi(
  407. current_user=request.current_user,
  408. session=request.dbsession,
  409. config=app_config,
  410. )
  411. content = api.get_one(
  412. content_id=hapic_data.path['content_id'],
  413. content_type=CONTENT_TYPES.Any_SLUG
  414. )
  415. content_type = CONTENT_TYPES.get_one_by_slug(content.type).slug
  416. # TODO - G.M - 2018-08-03 - Jsonify redirect response ?
  417. raise HTTPFound(
  418. "{base_url}workspaces/{workspace_id}/{content_type}s/{content_id}".format(
  419. base_url=BASE_API_V2,
  420. workspace_id=content.workspace_id,
  421. content_type=content_type,
  422. content_id=content.content_id,
  423. )
  424. )
  425. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  426. @hapic.handle_exception(WorkspacesDoNotMatch, HTTPStatus.BAD_REQUEST)
  427. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  428. @require_candidate_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  429. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  430. @hapic.input_body(ContentMoveSchema())
  431. @hapic.output_body(ContentDigestSchema())
  432. def move_content(
  433. self,
  434. context,
  435. request: TracimRequest,
  436. hapic_data=None,
  437. ) -> ContentInContext:
  438. """
  439. move a content
  440. """
  441. app_config = request.registry.settings['CFG']
  442. path_data = hapic_data.path
  443. move_data = hapic_data.body
  444. api = ContentApi(
  445. show_archived=True,
  446. show_deleted=True,
  447. current_user=request.current_user,
  448. session=request.dbsession,
  449. config=app_config,
  450. )
  451. content = api.get_one(
  452. path_data.content_id,
  453. content_type=CONTENT_TYPES.Any_SLUG
  454. )
  455. new_parent = api.get_one(
  456. move_data.new_parent_id, content_type=CONTENT_TYPES.Any_SLUG
  457. )
  458. new_workspace = request.candidate_workspace
  459. with new_revision(
  460. session=request.dbsession,
  461. tm=transaction.manager,
  462. content=content
  463. ):
  464. api.move(
  465. content,
  466. new_parent=new_parent,
  467. new_workspace=new_workspace,
  468. must_stay_in_same_workspace=False,
  469. )
  470. updated_content = api.get_one(
  471. path_data.content_id,
  472. content_type=CONTENT_TYPES.Any_SLUG
  473. )
  474. return api.get_content_in_context(updated_content)
  475. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  476. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  477. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  478. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  479. def delete_content(
  480. self,
  481. context,
  482. request: TracimRequest,
  483. hapic_data=None,
  484. ) -> None:
  485. """
  486. delete a content
  487. """
  488. app_config = request.registry.settings['CFG']
  489. path_data = hapic_data.path
  490. api = ContentApi(
  491. show_archived=True,
  492. show_deleted=True,
  493. current_user=request.current_user,
  494. session=request.dbsession,
  495. config=app_config,
  496. )
  497. content = api.get_one(
  498. path_data.content_id,
  499. content_type=CONTENT_TYPES.Any_SLUG
  500. )
  501. with new_revision(
  502. session=request.dbsession,
  503. tm=transaction.manager,
  504. content=content
  505. ):
  506. api.delete(content)
  507. return
  508. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  509. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  510. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  511. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  512. def undelete_content(
  513. self,
  514. context,
  515. request: TracimRequest,
  516. hapic_data=None,
  517. ) -> None:
  518. """
  519. undelete a content
  520. """
  521. app_config = request.registry.settings['CFG']
  522. path_data = hapic_data.path
  523. api = ContentApi(
  524. current_user=request.current_user,
  525. session=request.dbsession,
  526. config=app_config,
  527. show_deleted=True,
  528. show_archived=True,
  529. )
  530. content = api.get_one(
  531. path_data.content_id,
  532. content_type=CONTENT_TYPES.Any_SLUG
  533. )
  534. with new_revision(
  535. session=request.dbsession,
  536. tm=transaction.manager,
  537. content=content
  538. ):
  539. api.undelete(content)
  540. return
  541. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  542. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  543. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  544. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  545. def archive_content(
  546. self,
  547. context,
  548. request: TracimRequest,
  549. hapic_data=None,
  550. ) -> None:
  551. """
  552. archive a content
  553. """
  554. app_config = request.registry.settings['CFG']
  555. path_data = hapic_data.path
  556. api = ContentApi(
  557. show_archived=True,
  558. show_deleted=True,
  559. current_user=request.current_user,
  560. session=request.dbsession,
  561. config=app_config,
  562. )
  563. content = api.get_one(path_data.content_id, content_type=CONTENT_TYPES.Any_SLUG) # nopep8
  564. with new_revision(
  565. session=request.dbsession,
  566. tm=transaction.manager,
  567. content=content
  568. ):
  569. api.archive(content)
  570. return
  571. @hapic.with_api_doc(tags=[SWAGGER_TAG_WORKSPACE_ENDPOINTS])
  572. @require_workspace_role(UserRoleInWorkspace.CONTENT_MANAGER)
  573. @hapic.input_path(WorkspaceAndContentIdPathSchema())
  574. @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT) # nopep8
  575. def unarchive_content(
  576. self,
  577. context,
  578. request: TracimRequest,
  579. hapic_data=None,
  580. ) -> None:
  581. """
  582. unarchive a content
  583. """
  584. app_config = request.registry.settings['CFG']
  585. path_data = hapic_data.path
  586. api = ContentApi(
  587. current_user=request.current_user,
  588. session=request.dbsession,
  589. config=app_config,
  590. show_archived=True,
  591. show_deleted=True,
  592. )
  593. content = api.get_one(
  594. path_data.content_id,
  595. content_type=CONTENT_TYPES.Any_SLUG
  596. )
  597. with new_revision(
  598. session=request.dbsession,
  599. tm=transaction.manager,
  600. content=content
  601. ):
  602. api.unarchive(content)
  603. return
  604. def bind(self, configurator: Configurator) -> None:
  605. """
  606. Create all routes and views using
  607. pyramid configurator for this controller
  608. """
  609. # Workspace
  610. configurator.add_route('workspace', '/workspaces/{workspace_id}', request_method='GET') # nopep8
  611. configurator.add_view(self.workspace, route_name='workspace')
  612. # Create workspace
  613. configurator.add_route('create_workspace', '/workspaces', request_method='POST') # nopep8
  614. configurator.add_view(self.create_workspace, route_name='create_workspace') # nopep8
  615. # Delete/Undelete workpace
  616. configurator.add_route('delete_workspace', '/workspaces/{workspace_id}/delete', request_method='PUT') # nopep8
  617. configurator.add_view(self.delete_workspace, route_name='delete_workspace') # nopep8
  618. configurator.add_route('undelete_workspace', '/workspaces/{workspace_id}/undelete', request_method='PUT') # nopep8
  619. configurator.add_view(self.undelete_workspace, route_name='undelete_workspace') # nopep8
  620. # Update Workspace
  621. configurator.add_route('update_workspace', '/workspaces/{workspace_id}', request_method='PUT') # nopep8
  622. configurator.add_view(self.update_workspace, route_name='update_workspace') # nopep8
  623. # Workspace Members (Roles)
  624. configurator.add_route('workspace_members', '/workspaces/{workspace_id}/members', request_method='GET') # nopep8
  625. configurator.add_view(self.workspaces_members, route_name='workspace_members') # nopep8
  626. # Update Workspace Members roles
  627. configurator.add_route('update_workspace_member', '/workspaces/{workspace_id}/members/{user_id}', request_method='PUT') # nopep8
  628. configurator.add_view(self.update_workspaces_members_role, route_name='update_workspace_member') # nopep8
  629. # Create Workspace Members roles
  630. configurator.add_route('create_workspace_member', '/workspaces/{workspace_id}/members', request_method='POST') # nopep8
  631. configurator.add_view(self.create_workspaces_members_role, route_name='create_workspace_member') # nopep8
  632. # Delete Workspace Members roles
  633. configurator.add_route('delete_workspace_member', '/workspaces/{workspace_id}/members/{user_id}', request_method='DELETE') # nopep8
  634. configurator.add_view(self.delete_workspaces_members_role, route_name='delete_workspace_member') # nopep8
  635. # Workspace Content
  636. configurator.add_route('workspace_content', '/workspaces/{workspace_id}/contents', request_method='GET') # nopep8
  637. configurator.add_view(self.workspace_content, route_name='workspace_content') # nopep8
  638. # Create Generic Content
  639. configurator.add_route('create_generic_content', '/workspaces/{workspace_id}/contents', request_method='POST') # nopep8
  640. configurator.add_view(self.create_generic_empty_content, route_name='create_generic_content') # nopep8
  641. # Get Content
  642. configurator.add_route('get_content', '/contents/{content_id}', request_method='GET') # nopep8
  643. configurator.add_view(self.get_content, route_name='get_content')
  644. # Get Content From workspace
  645. configurator.add_route('get_content_from_workspace', '/workspaces/{workspace_id}/contents/{content_id}', request_method='GET') # nopep8
  646. configurator.add_view(self.get_content_from_workspace, route_name='get_content_from_workspace') # nopep8
  647. # Move Content
  648. configurator.add_route('move_content', '/workspaces/{workspace_id}/contents/{content_id}/move', request_method='PUT') # nopep8
  649. configurator.add_view(self.move_content, route_name='move_content') # nopep8
  650. # Delete/Undelete Content
  651. configurator.add_route('delete_content', '/workspaces/{workspace_id}/contents/{content_id}/delete', request_method='PUT') # nopep8
  652. configurator.add_view(self.delete_content, route_name='delete_content') # nopep8
  653. configurator.add_route('undelete_content', '/workspaces/{workspace_id}/contents/{content_id}/undelete', request_method='PUT') # nopep8
  654. configurator.add_view(self.undelete_content, route_name='undelete_content') # nopep8
  655. # # Archive/Unarchive Content
  656. configurator.add_route('archive_content', '/workspaces/{workspace_id}/contents/{content_id}/archive', request_method='PUT') # nopep8
  657. configurator.add_view(self.archive_content, route_name='archive_content') # nopep8
  658. configurator.add_route('unarchive_content', '/workspaces/{workspace_id}/contents/{content_id}/unarchive', request_method='PUT') # nopep8
  659. configurator.add_view(self.unarchive_content, route_name='unarchive_content') # nopep8