PageDeleteRightsSettings.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import React, { useCallback } from 'react';
  2. import { Collapse } from 'reactstrap';
  3. import type AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
  4. import {
  5. PageDeleteConfigValue,
  6. type IPageDeleteConfigValue,
  7. type IPageDeleteConfigValueToProcessValidation,
  8. } from '~/interfaces/page-delete-config';
  9. import { validateDeleteConfigs, prepareDeleteConfigValuesForCalc } from '~/utils/page-delete-config';
  10. import {
  11. DeletionType,
  12. type DeletionTypeValue,
  13. getDeletionTypeForT,
  14. getDeleteConfigValueForT,
  15. isRecursiveDeletion,
  16. isTypeDeletion,
  17. } from './types';
  18. type Props = {
  19. adminGeneralSecurityContainer: AdminGeneralSecurityContainer;
  20. t: (key: string) => string;
  21. };
  22. export const PageDeleteRightsSettings: React.FC<Props> = ({ adminGeneralSecurityContainer, t }) => {
  23. const {
  24. currentPageDeletionAuthority,
  25. currentPageCompleteDeletionAuthority,
  26. currentPageRecursiveDeletionAuthority,
  27. currentPageRecursiveCompleteDeletionAuthority,
  28. } = adminGeneralSecurityContainer.state;
  29. const getRecursiveDeletionConfigState = useCallback((deletionType: DeletionTypeValue) => {
  30. if (isTypeDeletion(deletionType)) {
  31. return [
  32. adminGeneralSecurityContainer.state.currentPageRecursiveDeletionAuthority,
  33. adminGeneralSecurityContainer.changePageRecursiveDeletionAuthority,
  34. ] as const;
  35. }
  36. return [
  37. adminGeneralSecurityContainer.state.currentPageRecursiveCompleteDeletionAuthority,
  38. adminGeneralSecurityContainer.changePageRecursiveCompleteDeletionAuthority,
  39. ] as const;
  40. }, [adminGeneralSecurityContainer]);
  41. const previousPageRecursiveAuthorityState = useCallback((deletionType: DeletionTypeValue) => {
  42. return isTypeDeletion(deletionType)
  43. ? adminGeneralSecurityContainer.state.previousPageRecursiveDeletionAuthority
  44. : adminGeneralSecurityContainer.state.previousPageRecursiveCompleteDeletionAuthority;
  45. }, [adminGeneralSecurityContainer]);
  46. const setPagePreviousRecursiveAuthorityState = useCallback((deletionType: DeletionTypeValue, previousState: IPageDeleteConfigValue | null) => {
  47. if (isTypeDeletion(deletionType)) {
  48. adminGeneralSecurityContainer.changePreviousPageRecursiveDeletionAuthority(previousState);
  49. return;
  50. }
  51. adminGeneralSecurityContainer.changePreviousPageRecursiveCompleteDeletionAuthority(previousState);
  52. }, [adminGeneralSecurityContainer]);
  53. const expandDeleteOptionsState = useCallback((deletionType: DeletionTypeValue) => {
  54. return isTypeDeletion(deletionType)
  55. ? adminGeneralSecurityContainer.state.expandOtherOptionsForDeletion
  56. : adminGeneralSecurityContainer.state.expandOtherOptionsForCompleteDeletion;
  57. }, [adminGeneralSecurityContainer]);
  58. const setExpandOtherDeleteOptionsState = useCallback((deletionType: DeletionTypeValue, bool: boolean) => {
  59. if (isTypeDeletion(deletionType)) {
  60. adminGeneralSecurityContainer.switchExpandOtherOptionsForDeletion(bool);
  61. return;
  62. }
  63. adminGeneralSecurityContainer.switchExpandOtherOptionsForCompleteDeletion(bool);
  64. }, [adminGeneralSecurityContainer]);
  65. const setDeletionConfigState = useCallback((
  66. newState: IPageDeleteConfigValue,
  67. setState: (value: IPageDeleteConfigValue) => void,
  68. deletionType: DeletionTypeValue,
  69. ) => {
  70. setState(newState);
  71. if (previousPageRecursiveAuthorityState(deletionType) !== null) {
  72. setPagePreviousRecursiveAuthorityState(deletionType, null);
  73. }
  74. if (isRecursiveDeletion(deletionType)) {
  75. return;
  76. }
  77. const [recursiveState, setRecursiveState] = getRecursiveDeletionConfigState(deletionType);
  78. const calculableValue = prepareDeleteConfigValuesForCalc(
  79. newState as IPageDeleteConfigValueToProcessValidation,
  80. recursiveState as IPageDeleteConfigValueToProcessValidation,
  81. );
  82. const shouldForceUpdate = !validateDeleteConfigs(calculableValue[0], calculableValue[1]);
  83. if (shouldForceUpdate) {
  84. setRecursiveState(newState);
  85. setPagePreviousRecursiveAuthorityState(deletionType, recursiveState);
  86. setExpandOtherDeleteOptionsState(deletionType, true);
  87. }
  88. }, [
  89. getRecursiveDeletionConfigState,
  90. previousPageRecursiveAuthorityState,
  91. setPagePreviousRecursiveAuthorityState,
  92. setExpandOtherDeleteOptionsState,
  93. ]);
  94. const renderPageDeletePermissionDropdown = useCallback((
  95. currentState: IPageDeleteConfigValue,
  96. setState: (value: IPageDeleteConfigValue) => void,
  97. deletionType: DeletionTypeValue,
  98. isButtonDisabled: boolean,
  99. ) => {
  100. return (
  101. <div className="dropdown">
  102. <button
  103. className="btn btn-outline-secondary dropdown-toggle text-end"
  104. type="button"
  105. id="dropdownMenuButton"
  106. data-bs-toggle="dropdown"
  107. aria-haspopup="true"
  108. aria-expanded="true"
  109. >
  110. <span className="float-start">{t(getDeleteConfigValueForT(currentState))}</span>
  111. </button>
  112. <div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
  113. {isRecursiveDeletion(deletionType)
  114. ? (
  115. <button
  116. className="dropdown-item"
  117. type="button"
  118. onClick={() => { setDeletionConfigState(PageDeleteConfigValue.Inherit, setState, deletionType) }}
  119. >
  120. {t('security_settings.inherit')}
  121. </button>
  122. )
  123. : (
  124. <button
  125. className="dropdown-item"
  126. type="button"
  127. onClick={() => { setDeletionConfigState(PageDeleteConfigValue.Anyone, setState, deletionType) }}
  128. >
  129. {t('security_settings.anyone')}
  130. </button>
  131. )}
  132. <button
  133. className={`dropdown-item ${isButtonDisabled ? 'disabled' : ''}`}
  134. type="button"
  135. onClick={() => { setDeletionConfigState(PageDeleteConfigValue.AdminAndAuthor, setState, deletionType) }}
  136. >
  137. {t('security_settings.admin_and_author')}
  138. </button>
  139. <button
  140. className="dropdown-item"
  141. type="button"
  142. onClick={() => { setDeletionConfigState(PageDeleteConfigValue.AdminOnly, setState, deletionType) }}
  143. >
  144. {t('security_settings.admin_only')}
  145. </button>
  146. </div>
  147. <p className="form-text text-muted small">{t(`security_settings.${getDeletionTypeForT(deletionType)}_explanation`)}</p>
  148. </div>
  149. );
  150. }, [t, setDeletionConfigState]);
  151. const renderPageDeletePermission = useCallback((
  152. currentState: IPageDeleteConfigValue,
  153. setState: (value: IPageDeleteConfigValue) => void,
  154. deletionType: DeletionTypeValue,
  155. isButtonDisabled: boolean,
  156. ) => {
  157. const expandDeleteOptions = expandDeleteOptionsState(deletionType);
  158. return (
  159. <div key={`page-delete-permission-dropdown-${deletionType}`} className="row">
  160. <div className="col-md-4 text-md-end">
  161. {!isRecursiveDeletion(deletionType) && isTypeDeletion(deletionType) && <strong>{t('security_settings.page_delete')}</strong>}
  162. {!isRecursiveDeletion(deletionType) && !isTypeDeletion(deletionType) && <strong>{t('security_settings.page_delete_completely')}</strong>}
  163. </div>
  164. <div className="col-md-8">
  165. {!isRecursiveDeletion(deletionType)
  166. ? (
  167. <>
  168. {renderPageDeletePermissionDropdown(currentState, setState, deletionType, isButtonDisabled)}
  169. {currentState === PageDeleteConfigValue.Anyone && deletionType === DeletionType.CompleteDeletion && (
  170. <>
  171. <input
  172. id="isAllGroupMembershipRequiredForPageCompleteDeletionCheckbox"
  173. className="form-check-input"
  174. type="checkbox"
  175. checked={adminGeneralSecurityContainer.state.isAllGroupMembershipRequiredForPageCompleteDeletion}
  176. onChange={() => { adminGeneralSecurityContainer.switchIsAllGroupMembershipRequiredForPageCompleteDeletion() }}
  177. />
  178. <label className="form-check-label" htmlFor="isAllGroupMembershipRequiredForPageCompleteDeletionCheckbox">
  179. {t('security_settings.is_all_group_membership_required_for_page_complete_deletion')}
  180. </label>
  181. <p className="form-text text-muted small mt-2">
  182. {t('security_settings.is_all_group_membership_required_for_page_complete_deletion_explanation')}
  183. </p>
  184. </>
  185. )}
  186. </>
  187. )
  188. : (
  189. <>
  190. <button
  191. type="button"
  192. className="btn btn-link p-0 mb-4"
  193. aria-expanded="false"
  194. onClick={() => setExpandOtherDeleteOptionsState(deletionType, !expandDeleteOptions)}
  195. >
  196. <span className={`material-symbols-outlined me-1 ${expandDeleteOptions ? 'rotate-90' : ''}`}>navigate_next</span>
  197. {t('security_settings.other_options')}
  198. </button>
  199. <Collapse isOpen={expandDeleteOptions}>
  200. <div className="pb-4">
  201. <p className="card custom-card bg-warning-sublte">
  202. <span className="text-warning">
  203. <span className="material-symbols-outlined">info</span>
  204. {/* eslint-disable-next-line react/no-danger */}
  205. <span dangerouslySetInnerHTML={{ __html: t('security_settings.page_delete_rights_caution') }} />
  206. </span>
  207. </p>
  208. {previousPageRecursiveAuthorityState(deletionType) !== null && (
  209. <div className="mb-3">
  210. <strong>{t('security_settings.forced_update_desc')}</strong>
  211. <code>{t(getDeleteConfigValueForT(previousPageRecursiveAuthorityState(deletionType)))}</code>
  212. </div>
  213. )}
  214. {renderPageDeletePermissionDropdown(currentState, setState, deletionType, isButtonDisabled)}
  215. </div>
  216. </Collapse>
  217. </>
  218. )}
  219. </div>
  220. </div>
  221. );
  222. }, [
  223. adminGeneralSecurityContainer,
  224. expandDeleteOptionsState,
  225. previousPageRecursiveAuthorityState,
  226. renderPageDeletePermissionDropdown,
  227. setExpandOtherDeleteOptionsState,
  228. t,
  229. ]);
  230. const isButtonDisabledForDeletion = !validateDeleteConfigs(currentPageDeletionAuthority, PageDeleteConfigValue.AdminAndAuthor);
  231. const isButtonDisabledForCompleteDeletion = !validateDeleteConfigs(currentPageCompleteDeletionAuthority, PageDeleteConfigValue.AdminAndAuthor);
  232. return (
  233. <>
  234. <h4 className="mb-3">{t('security_settings.page_delete_rights')}</h4>
  235. {[
  236. [currentPageDeletionAuthority, adminGeneralSecurityContainer.changePageDeletionAuthority, DeletionType.Deletion, false],
  237. [
  238. currentPageRecursiveDeletionAuthority,
  239. adminGeneralSecurityContainer.changePageRecursiveDeletionAuthority,
  240. DeletionType.RecursiveDeletion,
  241. isButtonDisabledForDeletion,
  242. ],
  243. ].map(arr => renderPageDeletePermission(
  244. arr[0] as IPageDeleteConfigValue,
  245. arr[1] as (value: IPageDeleteConfigValue) => void,
  246. arr[2] as DeletionTypeValue,
  247. arr[3] as boolean,
  248. ))}
  249. {[
  250. [currentPageCompleteDeletionAuthority, adminGeneralSecurityContainer.changePageCompleteDeletionAuthority, DeletionType.CompleteDeletion, false],
  251. [
  252. currentPageRecursiveCompleteDeletionAuthority,
  253. adminGeneralSecurityContainer.changePageRecursiveCompleteDeletionAuthority,
  254. DeletionType.RecursiveCompleteDeletion,
  255. isButtonDisabledForCompleteDeletion,
  256. ],
  257. ].map(arr => renderPageDeletePermission(
  258. arr[0] as IPageDeleteConfigValue,
  259. arr[1] as (value: IPageDeleteConfigValue) => void,
  260. arr[2] as DeletionTypeValue,
  261. arr[3] as boolean,
  262. ))}
  263. </>
  264. );
  265. };