Przeglądaj źródła

Merge pull request #7767 from weseek/imprv/122259-123395-send-new-password-email

Imprv: Send new password email
Yuki Takei 2 lat temu
rodzic
commit
1b07a77514

+ 1 - 5
apps/app/public/static/locales/en_US/admin.json

@@ -757,11 +757,7 @@
       "password_reset_message": "Let the user know the new password below and strongly recommend to change another one immediately.",
       "send_new_password": "Please send the new password to the user.",
       "target_user": "Target User",
-      "new_password": "New Password",
-      "send_password_email": "Send the password to user",
-      "mail_setting_link":"<i class='icon-settings mr-2'></i><a href='/admin/app'>Email settings</a>",
-      "show_password": "Click to show password",
-      "hide_password": "hide"
+      "new_password": "New Password"
     },
     "external_account": "External Account Management",
     "external_accounts":"External accounts",

+ 1 - 5
apps/app/public/static/locales/ja_JP/admin.json

@@ -765,11 +765,7 @@
       "password_reset_message": "対象ユーザーに下記のパスワードを伝え、すぐに新しく別のパスワードを設定するよう伝えてください。",
       "send_new_password": "新規発行したパスワードを、対象ユーザーへ連絡してください。",
       "target_user": "対象ユーザー",
-      "new_password": "新しいパスワード",
-      "send_password_email": "メールを送信する",
-      "mail_setting_link": "<i class='icon-settings mr-2'></i><a href='/admin/app'>メールの設定</a>",
-      "show_password": "クリックしてパスワードを表示",
-      "hide_password": "非表示"
+      "new_password": "新しいパスワード"
     },
     "external_account": "外部アカウントの管理",
     "external_accounts": "外部アカウント",

+ 1 - 5
apps/app/public/static/locales/zh_CN/admin.json

@@ -765,11 +765,7 @@
       "password_reset_message": "Let the user know the new password below and strongly recommend to change another one immediately.",
       "send_new_password": "Please send the new password to the user.",
       "target_user": "Target User",
-      "new_password": "New Password",
-      "send_password_email": "Send the password to user",
-      "mail_setting_link":"<i class='icon-settings mr-2'></i><a href='/admin/app'>Email settings</a>",
-      "show_password": "Click to show password",
-      "hide_password": "hide"
+      "new_password": "New Password"
     },
     "external_account": "外部账户管理",
     "external_accounts": "外部账户",

+ 57 - 7
apps/app/src/components/Admin/Users/PasswordResetModal.jsx

@@ -6,9 +6,10 @@ import {
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 
+import AdminUsersContainer from '~/client/services/AdminUsersContainer';
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastError } from '~/client/util/toastr';
-
+import { useIsMailerSetup } from '~/stores/context';
 
 class PasswordResetModal extends React.Component {
 
@@ -21,6 +22,7 @@ class PasswordResetModal extends React.Component {
     };
 
     this.resetPassword = this.resetPassword.bind(this);
+    this.onClickSendNewPasswordButton = this.onClickSendNewPasswordButton.bind(this);
   }
 
   // Reset Password
@@ -36,6 +38,21 @@ class PasswordResetModal extends React.Component {
     }
   }
 
+  renderButtons() {
+    const { t, isMailerSetup } = this.props;
+
+    return (
+      <>
+        <button type="submit" className="btn btn-primary" onClick={this.onClickSendNewPasswordButton} disabled={!isMailerSetup}>
+          {t('Send')}
+        </button>
+        <button type="submit" className="btn btn-danger" onClick={this.props.onClose}>
+          {t('Close')}
+        </button>
+      </>
+    );
+  }
+
   renderModalBodyBeforeReset() {
     const { t, userForPasswordResetModal } = this.props;
 
@@ -87,15 +104,45 @@ class PasswordResetModal extends React.Component {
   }
 
   returnModalFooterAfterReset() {
-    const { t } = this.props;
-
+    const { t, isMailerSetup, userForPasswordResetModal } = this.props;
+
+    if (!isMailerSetup) {
+      return (
+        <>
+          <div>
+            <label className="form-text text-muted" dangerouslySetInnerHTML={{ __html: t('admin:mailer_setup_required') }} />
+          </div>
+          {this.renderButtons()}
+        </>
+      );
+    }
     return (
-      <button type="submit" className="btn btn-primary" onClick={this.props.onClose}>
-        {t('Close')}
-      </button>
+      <>
+        <p className="mb-4 mt-1">To:</p>
+        <div className="mr-3">
+          <p className="mb-0">{userForPasswordResetModal.username}</p>
+          <p className="mb-0">{userForPasswordResetModal.email}</p>
+        </div>
+        {this.renderButtons()}
+      </>
     );
   }
 
+  async onClickSendNewPasswordButton() {
+
+    const {
+      userForPasswordResetModal,
+    } = this.props;
+
+
+    try {
+      await apiv3Put('/users/reset-password-email', { id: userForPasswordResetModal._id, newPassword: this.state.temporaryPassword });
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }
+
 
   render() {
     const { t } = this.props;
@@ -119,7 +166,8 @@ class PasswordResetModal extends React.Component {
 
 const PasswordResetModalWrapperFC = (props) => {
   const { t } = useTranslation('admin');
-  return <PasswordResetModal t={t} {...props} />;
+  const { data: isMailerSetup } = useIsMailerSetup();
+  return <PasswordResetModal t={t} isMailerSetup={isMailerSetup ?? false} {...props} />;
 };
 
 /**
@@ -132,6 +180,8 @@ PasswordResetModal.propTypes = {
   isOpen: PropTypes.bool.isRequired,
   onClose: PropTypes.func.isRequired,
   userForPasswordResetModal: PropTypes.object,
+  onSuccessfullySentNewPasswordEmail: PropTypes.func.isRequired,
+  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
 
 };
 

+ 64 - 1
apps/app/src/server/routes/apiv3/users.js

@@ -181,6 +181,23 @@ module.exports = (crowi) => {
     return { failedToSendEmailList };
   };
 
+  const sendEmailByUser = async(user) => {
+    const { appService, mailService } = crowi;
+    const appTitle = appService.getAppTitle();
+
+    await mailService.send({
+      to: user.email,
+      subject: `New password for ${appTitle}`,
+      template: path.join(crowi.localeDir, 'en_US/admin/userResetPassword.txt'),
+      vars: {
+        email: user.email,
+        password: user.password,
+        url: crowi.appService.getSiteUrl(),
+        appTitle,
+      },
+    });
+  };
+
   /**
    * @swagger
    *
@@ -947,7 +964,7 @@ module.exports = (crowi) => {
    *                    description: user id for reset password
    *        responses:
    *          200:
-   *            description: success resrt password
+   *            description: success reset password
    */
   router.put('/reset-password', loginRequiredStrictly, adminRequired, addActivity, async(req, res) => {
     const { id } = req.body;
@@ -966,6 +983,52 @@ module.exports = (crowi) => {
     }
   });
 
+  /**
+   * @swagger
+   *
+   *  paths:
+   *    /users/reset-password-email:
+   *      put:
+   *        tags: [Users]
+   *        operationId: resetPasswordEmail
+   *        summary: /users/reset-password-email
+   *        description: send new password email
+   *        requestBody:
+   *          content:
+   *            application/json:
+   *              schema:
+   *                properties:
+   *                  newPassword:
+   *                    type: string
+   *                  user:
+   *                    type: string
+   *                    description: user id for send new password email
+   *        responses:
+   *          200:
+   *            description: success send new password email
+   */
+  router.put('/reset-password-email', loginRequiredStrictly, adminRequired, addActivity, async(req, res) => {
+    const { id } = req.body;
+
+    try {
+      const user = await User.findById(id);
+      if (user == null) {
+        throw new Error('User not found');
+      }
+      const userInfo = {
+        email: user.email,
+        password: req.body.newPassword,
+      };
+
+      await sendEmailByUser(userInfo);
+    }
+    catch (err) {
+      const msg = err.message;
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(msg));
+    }
+  });
+
   /**
    * @swagger
    *