Procházet zdrojové kódy

improve ExportZipFormModal

Yuki Takei před 6 roky
rodič
revize
4797c3c241

+ 1 - 0
resource/locales/en-US/translation.json

@@ -771,6 +771,7 @@
     "export_collections": "Export Collections",
     "check_all": "Check All",
     "uncheck_all": "Uncheck All",
+    "desc_password_seed": "DO NOT FORGET to set current <code>PASSWORD_SEED</code> to your new GROWI system when restoring user data, or users will NOT be able to login with their password.<br><br><strong>HINT:</strong><br>The current <code>PASSWORD_SEED</code> will be stored in <code>meta.json</code> in exported ZIP.",
     "create_new_exported_data": "Create New Exported Data",
     "export": "Export",
     "cancel": "Cancel",

+ 1 - 0
resource/locales/ja/translation.json

@@ -754,6 +754,7 @@
     "export_collections": "コレクションのエクスポート",
     "check_all": "全てにチェックを付ける",
     "uncheck_all": "全てからチェックを外す",
+    "desc_password_seed": "ユーザーデータをバックアップ/リストアする場合、現在の <code>PASSWORD_SEED</code> を新しい GROWI システムにセットすることを忘れないでください。さもなくば、ユーザーがパスワードでログインできなくなります。<br><br><strong>ヒント:</strong><br>現在の <code>PASSWORD_SEED</code> は、エクスポートされる ZIP 中の <code>meta.json</code> に保存されます。",
     "create_new_exported_data": "エクスポートデータの新規作成",
     "export": "エクスポート",
     "cancel": "キャンセル",

+ 2 - 3
src/client/js/components/Admin/Export/ExportPage.jsx

@@ -45,12 +45,11 @@ class ExportPage extends React.Component {
 
     const { zipFileStats, isExporting, progressList } = status;
     this.setState({
-      collections: ['pages', 'revisions'],
+      collections,
       zipFileStats,
       isExporting,
       progressList,
-    }); // FIXME: delete this line and uncomment the line below
-    // this.setState({ collections, zipFileStats, isExporting });
+    });
 
     this.setupWebsocketEventHandler();
   }

+ 104 - 28
src/client/js/components/Admin/Export/ExportZipFormModal.jsx

@@ -8,13 +8,25 @@ import { createSubscribedElement } from '../../UnstatedUtils';
 import AppContainer from '../../../services/AppContainer';
 // import { toastSuccess, toastError } from '../../../util/apiNotification';
 
+
+const GROUPS_PAGE = [
+  'pages', 'revisions', 'tags', 'pagetagrelations',
+];
+const GROUPS_USER = [
+  'users', 'externalaccounts', 'usergroups', 'usergrouprelations',
+];
+const GROUPS_CONFIG = [
+  'configs', 'updateposts', 'globalnotificationsettings',
+];
+const ALL_GROUPED_COLLECTIONS = GROUPS_PAGE.concat(GROUPS_USER).concat(GROUPS_CONFIG);
+
 class ExportZipFormModal extends React.Component {
 
   constructor(props) {
     super(props);
 
     this.state = {
-      collections: new Set(),
+      selectedCollections: new Set(),
     };
 
     this.toggleCheckbox = this.toggleCheckbox.bind(this);
@@ -29,24 +41,24 @@ class ExportZipFormModal extends React.Component {
     const { name, checked } = target;
 
     this.setState((prevState) => {
-      const collections = new Set(prevState.collections);
+      const selectedCollections = new Set(prevState.selectedCollections);
       if (checked) {
-        collections.add(name);
+        selectedCollections.add(name);
       }
       else {
-        collections.delete(name);
+        selectedCollections.delete(name);
       }
 
-      return { collections };
+      return { selectedCollections };
     });
   }
 
   checkAll() {
-    this.setState({ collections: new Set(this.props.collections) });
+    this.setState({ selectedCollections: new Set(this.props.collections) });
   }
 
   uncheckAll() {
-    this.setState({ collections: new Set() });
+    this.setState({ selectedCollections: new Set() });
   }
 
   async export(e) {
@@ -54,7 +66,7 @@ class ExportZipFormModal extends React.Component {
 
     try {
       // TODO: use appContainer.apiv3.post
-      const result = await this.props.appContainer.apiPost('/v3/export', { collections: Array.from(this.state.collections) });
+      const result = await this.props.appContainer.apiPost('/v3/export', { collections: Array.from(this.state.selectedCollections) });
       // TODO: toastSuccess, toastError
 
       if (!result.ok) {
@@ -90,7 +102,66 @@ class ExportZipFormModal extends React.Component {
   }
 
   validateForm() {
-    return this.state.collections.size > 0;
+    return this.state.selectedCollections.size > 0;
+  }
+
+  renderWarnForUser() {
+    // whether this.state.selectedCollections includes one of GROUPS_USER
+    const isUserRelatedDataSelected = GROUPS_USER.some((collectionName) => {
+      return this.state.selectedCollections.has(collectionName);
+    });
+
+    if (!isUserRelatedDataSelected) {
+      return <></>;
+    }
+
+    const html = this.props.t('export_management.desc_password_seed');
+
+    // eslint-disable-next-line react/no-danger
+    return <div className="well well-sm" dangerouslySetInnerHTML={{ __html: html }}></div>;
+  }
+
+  renderGroups(groupList, color) {
+    const collectionNames = groupList.filter((collectionName) => {
+      return this.props.collections.includes(collectionName);
+    });
+
+    return this.renderCheckboxes(collectionNames, color);
+  }
+
+  renderOthers() {
+    const collectionNames = this.props.collections.filter((collectionName) => {
+      return !ALL_GROUPED_COLLECTIONS.includes(collectionName);
+    });
+
+    return this.renderCheckboxes(collectionNames);
+  }
+
+  renderCheckboxes(collectionNames, color) {
+    const checkboxColor = color ? `checkbox-${color}` : 'checkbox-info';
+
+    return (
+      <div className={`row checkbox ${checkboxColor}`}>
+        {collectionNames.map((collectionName) => {
+          return (
+            <div className="col-xs-6 my-1" key={collectionName}>
+              <input
+                type="checkbox"
+                id={collectionName}
+                name={collectionName}
+                className="form-check-input"
+                value={collectionName}
+                checked={this.state.selectedCollections.has(collectionName)}
+                onChange={this.toggleCheckbox}
+              />
+              <label className="text-capitalize form-check-label ml-3" htmlFor={collectionName}>
+                {collectionName}
+              </label>
+            </div>
+          );
+        })}
+      </div>
+    );
   }
 
   render() {
@@ -114,25 +185,30 @@ class ExportZipFormModal extends React.Component {
                 </button>
               </div>
             </div>
-            <div className="checkbox checkbox-info">
-              {this.props.collections.map((collectionName) => {
-                return (
-                  <div className="my-1" key={collectionName}>
-                    <input
-                      type="checkbox"
-                      id={collectionName}
-                      name={collectionName}
-                      className="form-check-input"
-                      value={collectionName}
-                      checked={this.state.collections.has(collectionName)}
-                      onChange={this.toggleCheckbox}
-                    />
-                    <label className="text-capitalize form-check-label ml-3" htmlFor={collectionName}>
-                      {collectionName}
-                    </label>
-                  </div>
-                );
-              })}
+            <div className="row mt-4">
+              <div className="col-xs-12">
+                <legend>Page Collections</legend>
+                { this.renderGroups(GROUPS_PAGE) }
+              </div>
+            </div>
+            <div className="row mt-4">
+              <div className="col-xs-12">
+                <legend>User Collections</legend>
+                { this.renderGroups(GROUPS_USER, 'danger') }
+                { this.renderWarnForUser() }
+              </div>
+            </div>
+            <div className="row mt-4">
+              <div className="col-xs-12">
+                <legend>Config Collections</legend>
+                { this.renderGroups(GROUPS_CONFIG) }
+              </div>
+            </div>
+            <div className="row mt-4">
+              <div className="col-xs-12">
+                <legend>Other Collections</legend>
+                { this.renderOthers() }
+              </div>
             </div>
           </Modal.Body>