|
|
@@ -7,11 +7,19 @@ import { useTranslation } from 'react-i18next';
|
|
|
import { apiPost } from '~/client/util/apiv1-client';
|
|
|
import { apiv3Post } from '~/client/util/apiv3-client';
|
|
|
import { usePageDeleteModal } from '~/stores/modal';
|
|
|
+import loggerFactory from '~/utils/logger';
|
|
|
|
|
|
-import { IDeleteSinglePageApiv1Result, IDeleteManyPageApiv3Result } from '~/interfaces/page';
|
|
|
+import {
|
|
|
+ IDeleteSinglePageApiv1Result, IDeleteManyPageApiv3Result, isIPageInfoForOperation, IPageToDeleteWithMeta, IDataWithMeta, IPageInfoForOperation,
|
|
|
+} from '~/interfaces/page';
|
|
|
+import { HasObjectId } from '~/interfaces/has-object-id';
|
|
|
|
|
|
import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
|
|
|
import { isTrashPage } from '^/../core/src/utils/page-path-utils';
|
|
|
+import { useSWRxPageInfoForList } from '~/stores/page';
|
|
|
+
|
|
|
+
|
|
|
+const logger = loggerFactory('growi:cli:PageDeleteModal');
|
|
|
|
|
|
|
|
|
const deleteIconAndKey = {
|
|
|
@@ -34,16 +42,32 @@ const PageDeleteModal: FC = () => {
|
|
|
|
|
|
const isOpened = deleteModalData?.isOpened ?? false;
|
|
|
|
|
|
- const isAbleToDeleteCompletely = useMemo(() => {
|
|
|
- if (deleteModalData != null && deleteModalData.pages != null && deleteModalData.pages.length > 0) {
|
|
|
- return deleteModalData.pages.every(page => page.isAbleToDeleteCompletely);
|
|
|
+ const notOperatablePages: IPageToDeleteWithMeta[] = (deleteModalData?.pages ?? [])
|
|
|
+ .filter(p => !isIPageInfoForOperation(p.meta));
|
|
|
+ const notOperatablePageIds = notOperatablePages.map(p => p.data._id);
|
|
|
+
|
|
|
+ const { injectTo } = useSWRxPageInfoForList(notOperatablePageIds);
|
|
|
+
|
|
|
+ // inject IPageInfo to operate
|
|
|
+ let injectedPages: IDataWithMeta<HasObjectId & { path: string }, IPageInfoForOperation>[] | null = null;
|
|
|
+ if (deleteModalData?.pages != null && notOperatablePageIds.length > 0) {
|
|
|
+ injectedPages = injectTo(deleteModalData?.pages);
|
|
|
+ }
|
|
|
+
|
|
|
+ // calculate conditions to delete
|
|
|
+ const [isDeletable, isAbleToDeleteCompletely] = useMemo(() => {
|
|
|
+ if (injectedPages != null && injectedPages.length > 0) {
|
|
|
+ const isDeletable = injectedPages.every(pageWithMeta => pageWithMeta.meta?.isDeletable);
|
|
|
+ const isAbleToDeleteCompletely = injectedPages.every(pageWithMeta => pageWithMeta.meta?.isAbleToDeleteCompletely);
|
|
|
+ return [isDeletable, isAbleToDeleteCompletely];
|
|
|
}
|
|
|
- return true;
|
|
|
- }, [deleteModalData]);
|
|
|
+ return [true, true];
|
|
|
+ }, [injectedPages]);
|
|
|
|
|
|
+ // calculate condition to determine modal status
|
|
|
const forceDeleteCompletelyMode = useMemo(() => {
|
|
|
if (deleteModalData != null && deleteModalData.pages != null && deleteModalData.pages.length > 0) {
|
|
|
- return deleteModalData.pages.every(page => isTrashPage(page.path));
|
|
|
+ return deleteModalData.pages.every(pageWithMeta => isTrashPage(pageWithMeta.data?.path ?? ''));
|
|
|
}
|
|
|
return false;
|
|
|
}, [deleteModalData]);
|
|
|
@@ -71,6 +95,11 @@ const PageDeleteModal: FC = () => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (!isDeletable) {
|
|
|
+ logger.error('At least one page is not deletable.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* When multiple pages
|
|
|
*/
|
|
|
@@ -80,7 +109,7 @@ const PageDeleteModal: FC = () => {
|
|
|
const isCompletely = isDeleteCompletely === true ? true : undefined;
|
|
|
|
|
|
const pageIdToRevisionIdMap = {};
|
|
|
- deleteModalData.pages.forEach((p) => { pageIdToRevisionIdMap[p.pageId] = p.revisionId });
|
|
|
+ deleteModalData.pages.forEach((p) => { pageIdToRevisionIdMap[p.data._id] = p.data.revision as string });
|
|
|
|
|
|
const { data } = await apiv3Post<IDeleteManyPageApiv3Result>('/pages/delete', {
|
|
|
pageIdToRevisionIdMap,
|
|
|
@@ -92,6 +121,8 @@ const PageDeleteModal: FC = () => {
|
|
|
if (onDeleted != null) {
|
|
|
onDeleted(data.paths, data.isRecursively, data.isCompletely);
|
|
|
}
|
|
|
+
|
|
|
+ closeDeleteModal();
|
|
|
}
|
|
|
catch (err) {
|
|
|
setErrs([err]);
|
|
|
@@ -105,11 +136,11 @@ const PageDeleteModal: FC = () => {
|
|
|
const recursively = isDeleteRecursively === true ? true : undefined;
|
|
|
const completely = forceDeleteCompletelyMode || isDeleteCompletely ? true : undefined;
|
|
|
|
|
|
- const page = deleteModalData.pages[0];
|
|
|
+ const page = deleteModalData.pages[0].data;
|
|
|
|
|
|
const { path, isRecursively, isCompletely } = await apiPost('/pages.remove', {
|
|
|
- page_id: page.pageId,
|
|
|
- revision_id: page.revisionId,
|
|
|
+ page_id: page._id,
|
|
|
+ revision_id: page.revision,
|
|
|
recursively,
|
|
|
completely,
|
|
|
}) as IDeleteSinglePageApiv1Result;
|
|
|
@@ -118,6 +149,8 @@ const PageDeleteModal: FC = () => {
|
|
|
if (onDeleted != null) {
|
|
|
onDeleted(path, isRecursively, isCompletely);
|
|
|
}
|
|
|
+
|
|
|
+ closeDeleteModal();
|
|
|
}
|
|
|
catch (err) {
|
|
|
setErrs([err]);
|
|
|
@@ -126,7 +159,6 @@ const PageDeleteModal: FC = () => {
|
|
|
}
|
|
|
|
|
|
async function deleteButtonHandler() {
|
|
|
- await closeDeleteModal();
|
|
|
await deletePage();
|
|
|
}
|
|
|
|
|
|
@@ -176,8 +208,13 @@ const PageDeleteModal: FC = () => {
|
|
|
}
|
|
|
|
|
|
const renderPagePathsToDelete = () => {
|
|
|
- if (deleteModalData != null && deleteModalData.pages != null) {
|
|
|
- return deleteModalData.pages.map(page => <div key={page.pageId}><code>{ page.path }</code></div>);
|
|
|
+ if (injectedPages != null && injectedPages != null) {
|
|
|
+ return injectedPages.map(page => (
|
|
|
+ <div key={page.data._id}>
|
|
|
+ <code>{ page.data.path }</code>
|
|
|
+ { !page.meta?.isDeletable && <span className="ml-3 text-danger"><strong>(CAN NOT TO DELETE)</strong></span> }
|
|
|
+ </div>
|
|
|
+ ));
|
|
|
}
|
|
|
return <></>;
|
|
|
};
|
|
|
@@ -195,12 +232,17 @@ const PageDeleteModal: FC = () => {
|
|
|
{/* https://redmine.weseek.co.jp/issues/82787 */}
|
|
|
{renderPagePathsToDelete()}
|
|
|
</div>
|
|
|
- {renderDeleteRecursivelyForm()}
|
|
|
- { !forceDeleteCompletelyMode && renderDeleteCompletelyForm() }
|
|
|
+ { isDeletable && renderDeleteRecursivelyForm()}
|
|
|
+ { isDeletable && !forceDeleteCompletelyMode && renderDeleteCompletelyForm() }
|
|
|
</ModalBody>
|
|
|
<ModalFooter>
|
|
|
<ApiErrorMessageList errs={errs} />
|
|
|
- <button type="button" className={`btn btn-${deleteIconAndKey[deleteMode].color}`} onClick={deleteButtonHandler}>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ className={`btn btn-${deleteIconAndKey[deleteMode].color}`}
|
|
|
+ disabled={!isDeletable}
|
|
|
+ onClick={deleteButtonHandler}
|
|
|
+ >
|
|
|
<i className={`icon-${deleteIconAndKey[deleteMode].icon}`} aria-hidden="true"></i>
|
|
|
{ t(`modal_delete.delete_${deleteIconAndKey[deleteMode].translationKey}`) }
|
|
|
</button>
|