Przeglądaj źródła

Merge pull request #2691 from weseek/feat/Convert-duplicate-page-to-a-new-path-name

Feat/convert duplicate page to a new path name
itizawa 5 lat temu
rodzic
commit
3e60628b1f

+ 2 - 0
resource/locales/en_US/translation.json

@@ -134,6 +134,8 @@
   "Disassociate": "Disassociate",
   "Recent Created": "Recent Created",
   "Recent Changes": "Recent Changes",
+  "original_path":"Original path",
+  "new_path":"New path",
   "personal_dropdown": {
     "home": "Home",
     "settings": "Settings",

+ 2 - 0
resource/locales/ja_JP/translation.json

@@ -137,6 +137,8 @@
   "Sidebar mode on Editor": "サイドバーモード(編集時)",
   "Recent Created": "最新の作成",
   "Recent Changes": "最新の変更",
+  "original_path":"元のパス",
+  "new_path":"新しいパス",
   "personal_dropdown": {
     "home": "ホーム",
     "settings": "設定",

+ 3 - 1
resource/locales/zh_CN/translation.json

@@ -136,7 +136,9 @@
 	"Sign out": "退出",
 	"Disassociate": "解除关联",
 	"Recent Created": "最新创建",
-	"Recent Changes": "最新修改",
+  "Recent Changes": "最新修改",
+  "original_path":"Original path",
+  "new_path":"New path",
 	"form_validation": {
 		"error_message": "有些值不正确",
 		"required": "%s 是必需的",

+ 60 - 0
src/client/js/components/ComparePathsTable.jsx

@@ -0,0 +1,60 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { withTranslation } from 'react-i18next';
+import { withUnstatedContainers } from './UnstatedUtils';
+
+import PageContainer from '../services/PageContainer';
+import { convertToNewAffiliationPath } from '../../../lib/util/path-utils';
+
+function ComparePathsTable(props) {
+  const {
+    subordinatedPages, pageContainer, newPagePath, t,
+  } = props;
+  const { path } = pageContainer.state;
+
+  return (
+    <table className="table table-bordered">
+      <thead>
+        <tr>
+          <th className="w-50">{t('original_path')}</th>
+          <th className="w-50">{t('new_path')}</th>
+        </tr>
+      </thead>
+      <tbody>
+        {subordinatedPages.map((subordinatedPage) => {
+          const convertedPath = convertToNewAffiliationPath(path, newPagePath, subordinatedPage.path);
+          return (
+            <tr key={subordinatedPage._id}>
+              <td className="text-break">
+                <a href={subordinatedPage.path}>
+                  {subordinatedPage.path}
+                </a>
+              </td>
+              <td className="text-break">
+                {convertedPath}
+              </td>
+            </tr>
+          );
+        })}
+      </tbody>
+    </table>
+  );
+}
+
+
+/**
+ * Wrapper component for using unstated
+ */
+const PageDuplicateModallWrapper = withUnstatedContainers(ComparePathsTable, [PageContainer]);
+
+ComparePathsTable.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+
+  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
+  subordinatedPages: PropTypes.array.isRequired,
+  newPagePath: PropTypes.string.isRequired,
+};
+
+
+export default withTranslation()(PageDuplicateModallWrapper);

+ 27 - 37
src/client/js/components/PageDuplicateModal.jsx

@@ -7,11 +7,13 @@ import {
 
 import { withTranslation } from 'react-i18next';
 import { withUnstatedContainers } from './UnstatedUtils';
+import { toastError } from '../util/apiNotification';
 
 import AppContainer from '../services/AppContainer';
 import PageContainer from '../services/PageContainer';
 import PagePathAutoComplete from './PagePathAutoComplete';
 import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
+import ComparePathsTable from './ComparePathsTable';
 
 
 const PageDuplicateModal = (props) => {
@@ -26,15 +28,9 @@ const PageDuplicateModal = (props) => {
 
   const [errs, setErrs] = useState(null);
 
-  // ToDo: subordinatedPaths is not used yet so commented. Will use when all the child of { path } is needed.
-  // const [subordinatedPaths, setSubordinatedPaths] = useState([]);
-  // for now we use the code below.
-  const [setSubordinatedPaths] = useState([]);
-  const [getSubordinatedError, setGetSuborinatedError] = useState(null);
-  const [isDuplicateRecursively, setIsDuplicateRecursively] = useState(true);
-  const [isDuplicateRecursivelyWithoutExistPath, setIsDuplicateRecursivelyWithoutExistPath] = useState(true);
-  const [isDuplicateExistList, setIsDuplicateExistList] = useState([]);
-
+  const [subordinatedPages, setSubordinatedPages] = useState([]);
+  const [isDuplicateRecursively, setIsDuplicateRecursively] = useState(false);
+  const [isDuplicateRecursivelyWithoutExistPath, setIsDuplicateRecursivelyWithoutExistPath] = useState(false);
 
   function getSubordinatedDuplicateList(value) {
 
@@ -46,7 +42,6 @@ const PageDuplicateModal = (props) => {
     // setIsDuplicateExist(duplicatedList);
 
     // ToDo: for now we use dummy path
-    setIsDuplicateExistList(['/test146/test147', value]);
   }
 
   /**
@@ -74,10 +69,12 @@ const PageDuplicateModal = (props) => {
   const getSubordinatedList = useCallback(async() => {
     try {
       const res = await appContainer.apiv3Get('/pages/subordinated-list', { path });
-      setSubordinatedPaths(res.data.resultPaths);
+      const { subordinatedPaths } = res.data;
+      setSubordinatedPages(subordinatedPaths);
     }
     catch (err) {
-      setGetSuborinatedError(t('modal_duplicate.label.Fail to get subordinated pages'));
+      setErrs(err);
+      toastError(t('modal_duplicate.label.Fail to get subordinated pages'));
     }
   }, [appContainer, path, t]);
 
@@ -143,7 +140,7 @@ const PageDuplicateModal = (props) => {
             </div>
           </div>
         </div>
-        <div className="custom-control custom-checkbox custom-checkbox-warning">
+        <div className="custom-control custom-checkbox custom-checkbox-warning mb-3">
           <input
             className="custom-control-input"
             name="recursively"
@@ -156,30 +153,23 @@ const PageDuplicateModal = (props) => {
             { t('modal_duplicate.label.Duplicate with child') }
           </label>
         </div>
-
-        <div
-          className="custom-control custom-checkbox custom-checkbox-warning"
-          style={{ display: (isDuplicateExistList.length !== 0) && isDuplicateRecursively ? '' : 'none' }}
-        >
-          <input
-            className="custom-control-input"
-            name="withoutExistRecursively"
-            id="cbDuplicatewithoutExistRecursively"
-            type="checkbox"
-            checked={isDuplicateRecursivelyWithoutExistPath}
-            onChange={changeIsDuplicateRecursivelyWithoutExistPathHandler}
-          />
-          <label className="custom-control-label" htmlFor="cbDuplicatewithoutExistRecursively">
-            { t('modal_duplicate.label.Duplicate without exist path') }
-          </label>
-        </div>
-
-        <div>
-          <ul className="duplicate-name">
-            {isDuplicateRecursively && isDuplicateExistList.length !== 0 && isDuplicateExistList}
-          </ul>
-        </div>
-        <div> {getSubordinatedError} </div>
+        {isDuplicateRecursively && <ComparePathsTable subordinatedPages={subordinatedPages} newPagePath={pageNameInput} />}
+
+        {isDuplicateRecursively && (
+          <div className="custom-control custom-checkbox custom-checkbox-warning">
+            <input
+              className="custom-control-input"
+              name="withoutExistRecursively"
+              id="cbDuplicatewithoutExistRecursively"
+              type="checkbox"
+              checked={isDuplicateRecursivelyWithoutExistPath}
+              onChange={changeIsDuplicateRecursivelyWithoutExistPathHandler}
+            />
+            <label className="custom-control-label" htmlFor="cbDuplicatewithoutExistRecursively">
+              { t('modal_duplicate.label.Duplicate without exist path') }
+            </label>
+          </div>
+        )}
       </ModalBody>
       <ModalFooter>
         <ApiErrorMessageList errs={errs} targetPath={pageNameInput} />

+ 16 - 0
src/lib/util/path-utils.js

@@ -1,3 +1,5 @@
+const escapeStringRegexp = require('escape-string-regexp');
+
 /**
  * Whether path is the top page
  * @param {string} path
@@ -47,9 +49,23 @@ const userPageRoot = (user) => {
   return `/user/${user.username}`;
 };
 
+/**
+ * return user path
+ * @param {string} parentPath
+ * @param {string} childPath
+ * @param {string} newPath
+ *
+ * @return {string}
+ */
+const convertToNewAffiliationPath = (oldPath, newPath, childPath) => {
+  const pathRegExp = new RegExp(`^${escapeStringRegexp(oldPath)}`, 'i');
+  return childPath.replace(pathRegExp, newPath);
+};
+
 module.exports = {
   isTopPage,
   isTrashPage,
   isUserPage,
   userPageRoot,
+  convertToNewAffiliationPath,
 };

+ 1 - 4
src/server/routes/apiv3/pages.js

@@ -561,12 +561,9 @@ module.exports = (crowi) => {
 
     try {
       const pageData = await Page.findByPath(path);
-
       const result = await Page.findManageableListWithDescendants(pageData, req.user);
 
-      const resultPaths = result.map(element => element.path);
-
-      return res.apiv3({ resultPaths });
+      return res.apiv3({ subordinatedPaths: result });
     }
     catch (err) {
       return res.apiv3Err(new ErrorV3('Failed to update page.', 'unknown'), 500);