workspace_controller.py 29KB

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