Просмотр исходного кода

Merge branch 'imprv/122259-new-password-email' into imprv/122259-123684-fix-design

soumaeda 2 лет назад
Родитель
Сommit
dcc3b217f6

+ 56 - 5
apps/app/src/components/Admin/Users/PasswordResetModal.jsx

@@ -9,7 +9,7 @@ import {
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
-
+import { useIsMailerSetup } from '~/stores/context';
 
 class PasswordResetModal extends React.Component {
 
@@ -22,6 +22,7 @@ class PasswordResetModal extends React.Component {
     };
 
     this.resetPassword = this.resetPassword.bind(this);
+    this.onClickSendNewPasswordButton = this.onClickSendNewPasswordButton.bind(this);
   }
 
   async resetPassword() {
@@ -40,6 +41,21 @@ class PasswordResetModal extends React.Component {
     toastSuccess('Copied Password');
   }
 
+  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;
 
@@ -102,13 +118,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;
@@ -132,7 +180,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} />;
 };
 
 /**
@@ -145,6 +194,8 @@ PasswordResetModal.propTypes = {
   isOpen: PropTypes.bool.isRequired,
   onClose: PropTypes.func.isRequired,
   userForPasswordResetModal: PropTypes.object,
+  onSuccessfullySentNewPasswordEmail: PropTypes.func.isRequired,
+  adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
 
 };
 

+ 41 - 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,29 @@ module.exports = (crowi) => {
     }
   });
 
+  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,
+      };
+
+      const sendEmail = await sendEmailByUser(userInfo);
+
+      return res.apiv3({ user, failedToSendEmail: sendEmail.failedToSendEmailList[0] });
+    }
+    catch (err) {
+      logger.error('Error', err);
+      return res.apiv3Err(new ErrorV3(err));
+    }
+  });
+
   /**
    * @swagger
    *