Browse Source

Merge pull request #2230 from weseek/imprv/renamepage-modal-reactify

Imprv/renamepage modal reactify
itizawa 5 years ago
parent
commit
afb996eed6

+ 2 - 2
resource/locales/en-US/translation.json

@@ -270,9 +270,9 @@
       "Redirect": "Redirect"
       "Redirect": "Redirect"
     },
     },
     "help": {
     "help": {
-      "redirect": "Redirect to new page if someone accesses <code>%s</code>",
+      "redirect": "Redirect to new page if someone accesses under this path",
       "metadata": "Remains last update user and updated date as is",
       "metadata": "Remains last update user and updated date as is",
-      "recursive": "Move/Rename children of under <code>%s</code> recursively"
+      "recursive": "Move/Rename children of under this path recursively"
     }
     }
   },
   },
   "Put Back": "Put back",
   "Put Back": "Put back",

+ 2 - 2
resource/locales/ja/translation.json

@@ -268,9 +268,9 @@
       "Redirect": "リダイレクトする"
       "Redirect": "リダイレクトする"
     },
     },
     "help": {
     "help": {
-      "redirect": "<code class='text-break'>%s</code> にアクセスされた際に自動的に新しいページにジャンプします",
+      "redirect": "にアクセスされた際に自動的に新しいページにジャンプします",
       "metadata": "最終更新ユーザー、最終更新日を更新せず維持します",
       "metadata": "最終更新ユーザー、最終更新日を更新せず維持します",
-      "recursive": "<code class='text-break'>%s</code> 配下のページも移動/名前変更します"
+      "recursive": "配下のページも移動/名前変更します"
     }
     }
   },
   },
   "Put Back": "元に戻す",
   "Put Back": "元に戻す",

+ 2 - 0
src/client/js/app.jsx

@@ -23,6 +23,7 @@ import PageTimeline from './components/PageTimeline';
 import CommentEditorLazyRenderer from './components/PageComment/CommentEditorLazyRenderer';
 import CommentEditorLazyRenderer from './components/PageComment/CommentEditorLazyRenderer';
 import PageManagement from './components/Page/PageManagement';
 import PageManagement from './components/Page/PageManagement';
 import PageDuplicateModal from './components/PageDuplicateModal';
 import PageDuplicateModal from './components/PageDuplicateModal';
+import PageRenameModal from './components/PageRenameModal';
 import TrashPageAlert from './components/Page/TrashPageAlert';
 import TrashPageAlert from './components/Page/TrashPageAlert';
 import PageAttachment from './components/PageAttachment';
 import PageAttachment from './components/PageAttachment';
 import PageStatusAlert from './components/PageStatusAlert';
 import PageStatusAlert from './components/PageStatusAlert';
@@ -93,6 +94,7 @@ if (pageContainer.state.pageId != null) {
     'page-comment-write': <CommentEditorLazyRenderer />,
     'page-comment-write': <CommentEditorLazyRenderer />,
     'page-management': <PageManagement />,
     'page-management': <PageManagement />,
     'page-duplicate-modal': <PageDuplicateModal />,
     'page-duplicate-modal': <PageDuplicateModal />,
+    'page-rename-modal': <PageRenameModal />,
 
 
     'revision-toc': <TableOfContents />,
     'revision-toc': <TableOfContents />,
     'seen-user-list': <UserPictureList userIds={pageContainer.state.seenUserIds} />,
     'seen-user-list': <UserPictureList userIds={pageContainer.state.seenUserIds} />,

+ 2 - 1
src/client/js/components/Page/PageManagement.jsx

@@ -12,6 +12,7 @@ import PageDeleteModal from '../PageDeleteModal';
 const PageManagement = (props) => {
 const PageManagement = (props) => {
   const { t, appContainer, pageContainer } = props;
   const { t, appContainer, pageContainer } = props;
   const { path, isDeletable, isAbleToDeleteCompletely } = pageContainer.state;
   const { path, isDeletable, isAbleToDeleteCompletely } = pageContainer.state;
+
   const { currentUser } = appContainer;
   const { currentUser } = appContainer;
   const isTopPagePath = isTopPage(path);
   const isTopPagePath = isTopPage(path);
 
 
@@ -28,7 +29,7 @@ const PageManagement = (props) => {
   function renderDropdownItemForNotTopPage() {
   function renderDropdownItemForNotTopPage() {
     return (
     return (
       <>
       <>
-        <a className="dropdown-item" href="#" data-target="#renamePage" data-toggle="modal">
+        <a className="dropdown-item" type="button" onClick={pageContainer.openPageRenameModal}>
           <i className="icon-fw icon-action-redo"></i> { t('Move/Rename') }
           <i className="icon-fw icon-action-redo"></i> { t('Move/Rename') }
         </a>
         </a>
         <a className="dropdown-item" type="button" onClick={pageContainer.openPageDuplicateModal}>
         <a className="dropdown-item" type="button" onClick={pageContainer.openPageDuplicateModal}>

+ 159 - 0
src/client/js/components/PageRenameModal.jsx

@@ -0,0 +1,159 @@
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+
+import {
+  Modal, ModalHeader, ModalBody, ModalFooter,
+} from 'reactstrap';
+
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from './UnstatedUtils';
+
+import AppContainer from '../services/AppContainer';
+import PageContainer from '../services/PageContainer';
+import ApiErrorMessage from './PageManagement/ApiErrorMessage';
+
+const PageRenameModal = (props) => {
+  const {
+    t, appContainer, pageContainer,
+  } = props;
+
+  const { path } = pageContainer.state;
+
+  const { crowi } = appContainer.config;
+
+  const [pageNameInput, setPageNameInput] = useState(path);
+  const [errorCode, setErrorCode] = useState(null);
+  const [errorMessage, setErrorMessage] = useState(null);
+
+  const [isRenameRecursively, SetIsRenameRecursively] = useState(true);
+  const [isRenameRedirect, SetIsRenameRedirect] = useState(true);
+  const [isRenameMetadata, SetIsRenameMetadata] = useState(true);
+
+  function changeIsRenameRecursivelyHandler() {
+    SetIsRenameRecursively(!isRenameRecursively);
+  }
+
+  /**
+   * change pageNameInput
+   * @param {string} value
+   */
+  function inputChangeHandler(value) {
+    setPageNameInput(value);
+  }
+
+  async function rename() {
+    try {
+      setErrorCode(null);
+      setErrorMessage(null);
+
+      const response = await pageContainer.rename(
+        pageNameInput,
+        isRenameRedirect,
+        isRenameMetadata,
+        isRenameRecursively,
+      );
+      const { page } = response;
+      window.location.href = encodeURI(`${page.path}?rename=${path}`);
+    }
+    catch (err) {
+      setErrorCode(err.code);
+      setErrorMessage(err.message);
+    }
+  }
+
+  return (
+    <Modal isOpen={pageContainer.state.isPageRenameModalShown} toggle={pageContainer.closePageRenameModal} className="grw-create-page">
+      <ModalHeader tag="h4" toggle={pageContainer.closePageRenameModal} className="bg-primary text-light">
+        { t('modal_rename.label.Move/Rename page') }
+      </ModalHeader>
+      <ModalBody>
+        <div className="form-group">
+          <label>{ t('modal_rename.label.Current page name') }</label><br />
+          <code>{ path }</code>
+        </div>
+        <div className="form-group">
+          <label htmlFor="newPageName">{ t('modal_rename.label.New page name') }</label><br />
+          <div className="input-group">
+            <div className="input-group-prepend">
+              <span className="input-group-text">{crowi.url}</span>
+            </div>
+            <div className="flex-fill">
+              <input
+                type="text"
+                value={pageNameInput}
+                className="form-control"
+                onChange={e => inputChangeHandler(e.target.value)}
+                required
+              />
+            </div>
+          </div>
+        </div>
+        <div className="custom-control custom-checkbox custom-checkbox-warning">
+          <input
+            className="custom-control-input"
+            name="recursively"
+            id="cbRenameRecursively"
+            type="checkbox"
+            checked={isRenameRecursively}
+            onChange={changeIsRenameRecursivelyHandler}
+          />
+          <label className="custom-control-label" htmlFor="cbRenameRecursively">
+            { t('modal_rename.label.Recursively') }
+            <p className="form-text text-muted mt-0">{ t('modal_rename.help.recursive') }</p>
+          </label>
+        </div>
+
+        <div className="custom-control custom-checkbox custom-checkbox-success">
+          <input
+            className="custom-control-input"
+            name="create_redirect"
+            id="cbRenameRedirect"
+            type="checkbox"
+            onChange={SetIsRenameRedirect}
+          />
+          <label className="custom-control-label" htmlFor="cbRenameRedirect">
+            { t('modal_rename.label.Redirect') }
+            <p className="form-text text-muted mt-0">{ t('modal_rename.help.redirect') }</p>
+          </label>
+        </div>
+
+        <div className="custom-control custom-checkbox custom-checkbox-primary">
+          <input
+            className="custom-control-input"
+            name="remain_metadata"
+            id="cbRenameMetadata"
+            type="checkbox"
+            onChange={SetIsRenameMetadata}
+          />
+          <label className="custom-control-label" htmlFor="cbRenameMetadata">
+            { t('modal_rename.label.Do not update metadata') }
+            <p className="form-text text-muted mt-0">{ t('modal_rename.help.metadata') }</p>
+          </label>
+        </div>
+      </ModalBody>
+      <ModalFooter>
+        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} linkPath={path} />
+        <button type="button" className="btn btn-primary" onClick={rename}>Rename</button>
+      </ModalFooter>
+    </Modal>
+  );
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const PageRenameModalWrapper = (props) => {
+  return createSubscribedElement(PageRenameModal, props, [AppContainer, PageContainer]);
+};
+
+
+PageRenameModal.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
+
+  path: PropTypes.string.isRequired,
+};
+
+export default withTranslation()(PageRenameModalWrapper);

+ 26 - 0
src/client/js/services/PageContainer.js

@@ -65,6 +65,7 @@ export default class PageContainer extends Container {
 
 
       isPageDuplicateModalShown: false,
       isPageDuplicateModalShown: false,
       isCreateTemplatePageModalShown: false,
       isCreateTemplatePageModalShown: false,
+      isPageRenameModalShown: false,
 
 
       isHeaderSticky: false,
       isHeaderSticky: false,
       isSubnavCompact: false,
       isSubnavCompact: false,
@@ -109,6 +110,8 @@ export default class PageContainer extends Container {
     this.closePageDuplicateModal = this.closePageDuplicateModal.bind(this);
     this.closePageDuplicateModal = this.closePageDuplicateModal.bind(this);
     this.openCreateTemplatePageModal = this.openCreateTemplatePageModal.bind(this);
     this.openCreateTemplatePageModal = this.openCreateTemplatePageModal.bind(this);
     this.closeCreateTemplatePageModal = this.closeCreateTemplatePageModal.bind(this);
     this.closeCreateTemplatePageModal = this.closeCreateTemplatePageModal.bind(this);
+    this.openPageRenameModal = this.openPageRenameModal.bind(this);
+    this.closePageRenameModal = this.closePageRenameModal.bind(this);
   }
   }
 
 
   /**
   /**
@@ -348,6 +351,21 @@ export default class PageContainer extends Container {
     });
     });
   }
   }
 
 
+  rename(pageNameInput, isRenameRedirect, isRenameMetadata, isRenameRecursively) {
+    const websocketContainer = this.appContainer.getContainer('WebsocketContainer');
+    const recursively = isRenameRecursively ? true : null;
+
+    return this.appContainer.apiPost('/pages.rename', {
+      recursively,
+      page_id: this.state.pageId,
+      revision_id: this.state.revisionId,
+      new_path: pageNameInput,
+      create_redirect: isRenameRedirect,
+      remain_metadata: isRenameMetadata,
+      socketClientId: websocketContainer.getSocketClientId(),
+    });
+  }
+
   showSuccessToastr() {
   showSuccessToastr() {
     toastr.success(undefined, 'Saved successfully', {
     toastr.success(undefined, 'Saved successfully', {
       closeButton: true,
       closeButton: true,
@@ -456,4 +474,12 @@ export default class PageContainer extends Container {
     this.setState({ isCreateTemplatePageModalShown: false });
     this.setState({ isCreateTemplatePageModalShown: false });
   }
   }
 
 
+  openPageRenameModal() {
+    this.setState({ isPageRenameModalShown: true });
+  }
+
+  closePageRenameModal() {
+    this.setState({ isPageRenameModalShown: false });
+  }
+
 }
 }

+ 1 - 77
src/server/views/modal/rename.html

@@ -1,77 +1 @@
-  <div class="modal" id="renamePage">
-    <div class="modal-dialog">
-      <div class="modal-content">
-
-      <form role="form" id="renamePageForm" onsubmit="return false;">
-
-        <div class="modal-header bg-primary text-light">
-          <div class="modal-title">{{ t('modal_rename.label.Move/Rename page') }}</div>
-          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-        </div>
-        <div class="modal-body">
-          <div class="form-group">
-            <label for="">{{ t('modal_rename.label.Current page name') }}</label><br>
-            <code>{{ page.path }}</code>
-          </div>
-          <div class="form-group">
-            <label for="newPageName">{{ t('modal_rename.label.New page name') }}</label><br>
-            <div class="input-group">
-              <div class="input-group-prepend">
-                <span class="input-group-text">{{ baseUrl }}</span>
-              </div>
-              {% if isSearchServiceConfigured() %}
-              <div id="rename-page-name-input" class="page-name-input flex-fill"></div>
-              {% else %}
-              <input type="text" class="form-control" name="new_path" id="newPageName" value="{{ page.path }}">
-              {% endif %}
-            </div>
-          </div>
-
-          <hr>
-
-          {% if page.grant != 2 %}
-          <div class="custom-control custom-checkbox custom-checkbox-warning">
-            <input class="custom-control-input" name="recursively" id="cbRenameRecursively" value="1" type="checkbox" checked >
-            <label class="custom-control-label" for="cbRenameRecursively">
-              {{ t('modal_rename.label.Recursively') }}
-              <p class="form-text text-muted mt-0">{{ t('modal_rename.help.recursive', page.path) }}</p>
-            </label>
-          </div>
-          {% endif %}
-
-          <div class="custom-control custom-checkbox custom-checkbox-success">
-            <input class="custom-control-input" name="create_redirect" id="cbRenameRedirect" value="1" type="checkbox">
-            <label class="custom-control-label" for="cbRenameRedirect">
-              {{ t('modal_rename.label.Redirect') }}
-              <p class="form-text text-muted mt-0">{{ t('modal_rename.help.redirect', page.path) }}</p>
-            </label>
-          </div>
-
-          <div class="custom-control custom-checkbox custom-checkbox-primary">
-            <input class="custom-control-input" name="remain_metadata" id="cbRenameMetadata" value="1" type="checkbox">
-            <label class="custom-control-label" for="cbRenameMetadata">
-              {{ t('modal_rename.label.Do not update metadata') }}
-              <p class="form-text text-muted mt-0">{{ t('modal_rename.help.metadata') }}</p>
-            </label>
-          </div>
-
-        </div>
-        <div class="modal-footer">
-          <div class="d-flex justify-content-between">
-            {% include '../widget/modal/page-api-error-messages.html' %}
-            <div>
-              <input type="hidden" name="_csrf" value="{{ csrf() }}">
-              <input type="hidden" name="path" value="{{ page.path }}">
-              <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
-              <input type="hidden" name="revision_id" value="{{ page.revision._id.toString() }}">
-            </div>
-          </div>
-          <div class="d-flex justify-content-end">
-            <button type="submit" class="btn btn-primary">Rename</button>
-          </div>
-        </div>
-
-      </form>
-      </div><!-- /.modal-content -->
-    </div><!-- /.modal-dialog -->
-  </div><!-- /.modal -->
+<div id ="page-rename-modal"></div>