Преглед изворни кода

122638 add checkbox to reset-password-modal

soumaeda пре 2 година
родитељ
комит
340473bc30

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

@@ -757,7 +757,8 @@
       "password_reset_message": "Let the user know the new password below and strongly recommend to change another one immediately.",
       "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.",
       "send_new_password": "Please send the new password to the user.",
       "target_user": "Target User",
       "target_user": "Target User",
-      "new_password": "New Password"
+      "new_password": "New Password",
+      "send_password_email": "Send the password to user"
     },
     },
     "external_account": "External Account Management",
     "external_account": "External Account Management",
     "external_accounts":"External accounts",
     "external_accounts":"External accounts",

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

@@ -765,7 +765,8 @@
       "password_reset_message": "対象ユーザーに下記のパスワードを伝え、すぐに新しく別のパスワードを設定するよう伝えてください。",
       "password_reset_message": "対象ユーザーに下記のパスワードを伝え、すぐに新しく別のパスワードを設定するよう伝えてください。",
       "send_new_password": "新規発行したパスワードを、対象ユーザーへ連絡してください。",
       "send_new_password": "新規発行したパスワードを、対象ユーザーへ連絡してください。",
       "target_user": "対象ユーザー",
       "target_user": "対象ユーザー",
-      "new_password": "新しいパスワード"
+      "new_password": "新しいパスワード",
+      "send_password_email": "メールを送信する"
     },
     },
     "external_account": "外部アカウントの管理",
     "external_account": "外部アカウントの管理",
     "external_accounts": "外部アカウント",
     "external_accounts": "外部アカウント",

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

@@ -765,7 +765,8 @@
       "password_reset_message": "Let the user know the new password below and strongly recommend to change another one immediately.",
       "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.",
       "send_new_password": "Please send the new password to the user.",
       "target_user": "Target User",
       "target_user": "Target User",
-      "new_password": "New Password"
+      "new_password": "New Password",
+      "send_password_email": "Send the password to user"
     },
     },
     "external_account": "外部账户管理",
     "external_account": "外部账户管理",
     "external_accounts": "外部账户",
     "external_accounts": "外部账户",

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

@@ -7,7 +7,8 @@ import {
 } from 'reactstrap';
 } from 'reactstrap';
 
 
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Put } from '~/client/util/apiv3-client';
-import { toastError } from '~/client/util/toastr';
+import { toastSuccess, toastError } from '~/client/util/toastr';
+import { useIsMailerSetup } from '~/stores/context';
 
 
 
 
 class PasswordResetModal extends React.Component {
 class PasswordResetModal extends React.Component {
@@ -18,9 +19,12 @@ class PasswordResetModal extends React.Component {
     this.state = {
     this.state = {
       temporaryPassword: [],
       temporaryPassword: [],
       isPasswordResetDone: false,
       isPasswordResetDone: false,
+      sendEmail: false,
+      isCreateUserButtonPushed: false,
     };
     };
 
 
     this.resetPassword = this.resetPassword.bind(this);
     this.resetPassword = this.resetPassword.bind(this);
+    this.handleCheckBox = this.handleCheckBox.bind(this);
   }
   }
 
 
   async resetPassword() {
   async resetPassword() {
@@ -28,6 +32,15 @@ class PasswordResetModal extends React.Component {
     try {
     try {
       const res = await apiv3Put('/users/reset-password', { id: userForPasswordResetModal._id });
       const res = await apiv3Put('/users/reset-password', { id: userForPasswordResetModal._id });
       const { newPassword } = res.data;
       const { newPassword } = res.data;
+      const { failedToSendEmail } = res.data;
+      if (failedToSendEmail == null) {
+        const msg = `Email has been sent<br>・${userForPasswordResetModal.email}`;
+        toastSuccess(msg);
+      }
+      else {
+        const msg = { message: `email: ${failedToSendEmail.email}<br>reason: ${failedToSendEmail.reason}` };
+        toastError(msg);
+      }
       this.setState({ temporaryPassword: newPassword, isPasswordResetDone: true });
       this.setState({ temporaryPassword: newPassword, isPasswordResetDone: true });
     }
     }
     catch (err) {
     catch (err) {
@@ -68,16 +81,40 @@ class PasswordResetModal extends React.Component {
   }
   }
 
 
   returnModalFooterBeforeReset() {
   returnModalFooterBeforeReset() {
-    const { t } = this.props;
+    const { t, isMailerSetup } = this.props;
     return (
     return (
-      <button type="submit" className="btn btn-danger" onClick={this.resetPassword}>
-        {t('user_management.reset_password')}
-      </button>
+      <>
+        <div className="col text-left custom-control custom-checkbox custom-checkbox-info text-left" onChange={this.handleCheckBox}>
+          <input
+            type="checkbox"
+            id="sendEmail"
+            className="custom-control-input"
+            name="sendEmail"
+            defaultChecked={this.state.sendEmail}
+            disabled={!isMailerSetup}
+          />
+          <label className="custom-control-label" htmlFor="sendEmail">
+            {t('admin:user_management.reset_password_modal.send_password_email')}
+          </label>
+          {isMailerSetup
+            // eslint-disable-next-line react/no-danger
+            ? <p className="form-text text-muted" dangerouslySetInnerHTML={{ __html: t('admin:user_management.invite_modal.mail_setting_link') }} />
+            // eslint-disable-next-line react/no-danger
+            : <p className="form-text text-muted" dangerouslySetInnerHTML={{ __html: t('admin:mailer_setup_required') }} />
+          }
+        </div>
+        <div>
+          <button type="submit" className="btn btn-danger" onClick={this.resetPassword}>
+            {t('user_management.reset_password')}
+          </button>
+        </div>
+      </>
     );
     );
   }
   }
 
 
   returnModalFooterAfterReset() {
   returnModalFooterAfterReset() {
-    const { t } = this.props;
+    const { t, isMailerSetup } = this.props;
+    const { isCreateUserButtonPushed } = this.state;
 
 
     return (
     return (
       <button type="submit" className="btn btn-primary" onClick={this.props.onClose}>
       <button type="submit" className="btn btn-primary" onClick={this.props.onClose}>
@@ -87,6 +124,10 @@ class PasswordResetModal extends React.Component {
   }
   }
 
 
 
 
+  handleCheckBox() {
+    this.setState({ sendEmail: !this.state.sendEmail });
+  }
+
   render() {
   render() {
     const { t } = this.props;
     const { t } = this.props;
 
 
@@ -109,7 +150,8 @@ class PasswordResetModal extends React.Component {
 
 
 const PasswordResetModalWrapperFC = (props) => {
 const PasswordResetModalWrapperFC = (props) => {
   const { t } = useTranslation('admin');
   const { t } = useTranslation('admin');
-  return <PasswordResetModal t={t} {...props} />;
+  const { data: isMailerSetup } = useIsMailerSetup();
+  return <PasswordResetModal t={t} isMailerSetup={isMailerSetup ?? false} {...props} />;
 };
 };
 
 
 /**
 /**

+ 2 - 2
apps/app/src/components/PageRenameModal.tsx

@@ -255,7 +255,7 @@ const PageRenameModal = (): JSX.Element => {
             <div className="custom-control custom-radio custom-radio-warning">
             <div className="custom-control custom-radio custom-radio-warning">
               <input
               <input
                 className="custom-control-input"
                 className="custom-control-input"
-                name="recursively"
+                name="withoutExistRecursively"
                 id="cbRenameThisPageOnly"
                 id="cbRenameThisPageOnly"
                 type="radio"
                 type="radio"
                 checked={!isRenameRecursively}
                 checked={!isRenameRecursively}
@@ -268,7 +268,7 @@ const PageRenameModal = (): JSX.Element => {
             <div className="custom-control custom-radio custom-radio-warning mt-1">
             <div className="custom-control custom-radio custom-radio-warning mt-1">
               <input
               <input
                 className="custom-control-input"
                 className="custom-control-input"
-                name="withoutExistRecursively"
+                name="recursively"
                 id="cbForceRenameRecursively"
                 id="cbForceRenameRecursively"
                 type="radio"
                 type="radio"
                 checked={isRenameRecursively}
                 checked={isRenameRecursively}

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

@@ -181,6 +181,38 @@ module.exports = (crowi) => {
     return { failedToSendEmailList };
     return { failedToSendEmailList };
   };
   };
 
 
+
+  const sendEmailByUser = async(user) => {
+    const { appService, mailService } = crowi;
+    const appTitle = appService.getAppTitle();
+    const failedToSendEmail = [];
+
+    try {
+      // eslint-disable-next-line no-await-in-loop
+      await mailService.send({
+        to: user.email,
+        subject: `NewPassword for ${appTitle}`,
+        template: path.join(crowi.localeDir, 'en_US/admin/userResetPassword.txt'),
+        vars: {
+          email: user.email,
+          password: user.password,
+          url: crowi.appService.getSiteUrl(),
+          appTitle,
+        },
+      });
+      // eslint-disable-next-line no-await-in-loop
+    }
+    catch (err) {
+      logger.error(err);
+      failedToSendEmail.push({
+        email: user.email,
+        reason: err.message,
+      });
+    }
+
+    return { failedToSendEmail };
+  };
+
   /**
   /**
    * @swagger
    * @swagger
    *
    *
@@ -956,9 +988,17 @@ module.exports = (crowi) => {
       const [newPassword, user] = await Promise.all([
       const [newPassword, user] = await Promise.all([
         await User.resetPasswordByRandomString(id),
         await User.resetPasswordByRandomString(id),
         await User.findById(id)]);
         await User.findById(id)]);
+      const userInfo = {
+        email: user.email,
+        password: newPassword,
+        user: { id },
+      };
+
+      const sendEmail = await sendEmailByUser(userInfo);
 
 
       activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ADMIN_USERS_PASSWORD_RESET });
       activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ADMIN_USERS_PASSWORD_RESET });
-      return res.apiv3({ newPassword, user });
+      activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ADMIN_USERS_SEND_INVITATION_EMAIL });
+      return res.apiv3({ newPassword, user, failedToSendEmail: sendEmail.failedToSendEmail[0] });
     }
     }
     catch (err) {
     catch (err) {
       logger.error('Error', err);
       logger.error('Error', err);