Explorar o código

Merge branch 'support/share-link-for-outside-for-merge' into imprv/sharelink-copy-on-modal

# Conflicts:
#	src/client/js/components/OutsideShareLinkModal.jsx
ryohek %!s(int64=5) %!d(string=hai) anos
pai
achega
ccec531228

+ 1 - 1
resource/locales/en_US/admin/admin.json

@@ -223,7 +223,7 @@
     "invite_users": "Invite new users",
     "invite_users": "Invite new users",
     "click_twice_same_checkbox": "You should check at least one checkbox.",
     "click_twice_same_checkbox": "You should check at least one checkbox.",
     "invite_modal": {
     "invite_modal": {
-      "emails": "Emails",
+      "emails": "Emails (Possible to invite multiple people with new lines)",
       "invite_thru_email": "Send invitation email",
       "invite_thru_email": "Send invitation email",
       "valid_email": "Valid email address is required",
       "valid_email": "Valid email address is required",
       "temporary_password": "The created user has a temporary password",
       "temporary_password": "The created user has a temporary password",

+ 18 - 4
resource/locales/en_US/translation.json

@@ -19,6 +19,7 @@
   "Tag": "Tag",
   "Tag": "Tag",
   "Tags": "Tags",
   "Tags": "Tags",
   "New": "New",
   "New": "New",
+  "Close": "Close",
   "Shortcuts": "Shortcuts",
   "Shortcuts": "Shortcuts",
   "eg": "e.g.",
   "eg": "e.g.",
   "add": "Add",
   "add": "Add",
@@ -47,7 +48,6 @@
   "History": "History",
   "History": "History",
   "Presentation Mode": "Presentation",
   "Presentation Mode": "Presentation",
   "Not available for guest": "Not available for guest",
   "Not available for guest": "Not available for guest",
-  "Shere this page link to public": "Shere this page link to public",
   "Create Archive Page": "Create Archive Page",
   "Create Archive Page": "Create Archive Page",
   "File type": "File type",
   "File type": "File type",
   "Include Attachment File": "Include Attachment File",
   "Include Attachment File": "Include Attachment File",
@@ -58,9 +58,6 @@
   "Last updated": "Updated",
   "Last updated": "Updated",
   "Last_Login": "Last login",
   "Last_Login": "Last login",
   "Share": "Share",
   "Share": "Share",
-  "Share Link": "Share Link",
-  "share_link_notice":"remove {{count}} share links",
-  "delete_all_share_links":"Delete all share links",
   "Markdown Link": "Markdown Link",
   "Markdown Link": "Markdown Link",
   "Create/Edit Template": "Create/Edit template page",
   "Create/Edit Template": "Create/Edit template page",
   "Go to this version": "View this version",
   "Go to this version": "View this version",
@@ -198,6 +195,23 @@
     "password_is_not_set": "Password is not set"
     "password_is_not_set": "Password is not set"
   },
   },
   "security_settings": "Security settings",
   "security_settings": "Security settings",
+  "share_links": {
+    "Shere this page link to public": "Shere this page link to public",
+    "share_link_list": "Share link list",
+    "share_link_management": "Share Link Management",
+    "No_share_links":"No share links",
+    "Share Link": "Share Link",
+    "Page Path": "Page Path",
+    "share_link_notice":"remove {{count}} share links",
+    "delete_all_share_links":"Delete all share links",
+    "expire": "Expiration",
+    "Days": "Days",
+    "Custom": "Custom",
+    "description": "description",
+    "enter_desc": "Enter description",
+    "Unlimited": "unlimited",
+    "Issue": "Issue"
+  },
   "API Settings": "API settings",
   "API Settings": "API settings",
   "API Token Settings": "API token settings",
   "API Token Settings": "API token settings",
   "Current API Token": "Current API token",
   "Current API Token": "Current API token",

+ 18 - 4
resource/locales/ja_JP/translation.json

@@ -19,6 +19,7 @@
   "Tag": "タグ",
   "Tag": "タグ",
   "Tags": "タグ",
   "Tags": "タグ",
   "New": "作成",
   "New": "作成",
+  "Close": "閉じる",
   "Shortcuts": "ショートカット",
   "Shortcuts": "ショートカット",
   "eg": "例:",
   "eg": "例:",
   "add": "追加",
   "add": "追加",
@@ -47,7 +48,6 @@
   "History": "更新履歴",
   "History": "更新履歴",
   "Presentation Mode": "プレゼンテーション",
   "Presentation Mode": "プレゼンテーション",
   "Not available for guest": "ゲストユーザーは利用できません",
   "Not available for guest": "ゲストユーザーは利用できません",
-  "Shere this page link to public": "外部に共有するリンクを発行する",
   "Create Archive Page": "アーカイブページの作成",
   "Create Archive Page": "アーカイブページの作成",
   "File type": "ファイル形式",
   "File type": "ファイル形式",
   "Include Attachment File": "添付ファイルも含める",
   "Include Attachment File": "添付ファイルも含める",
@@ -58,9 +58,6 @@
   "Last updated": "最終更新",
   "Last updated": "最終更新",
   "Last_Login": "最終ログイン",
   "Last_Login": "最終ログイン",
   "Share": "共有",
   "Share": "共有",
-  "Share Link": "共有用リンク",
-  "share_link_notice":"{{count}} 件の共有リンクを削除します",
-  "delete_all_share_links":"全ての共有リンクを削除します",
   "Markdown Link": "Markdown形式のリンク",
   "Markdown Link": "Markdown形式のリンク",
   "Create/Edit Template": "テンプレートページの作成/編集",
   "Create/Edit Template": "テンプレートページの作成/編集",
   "Go to this version": "このバージョンを見る",
   "Go to this version": "このバージョンを見る",
@@ -200,6 +197,23 @@
     "password_is_not_set": "パスワードが設定されていません"
     "password_is_not_set": "パスワードが設定されていません"
   },
   },
   "security_settings": "セキュリティ設定",
   "security_settings": "セキュリティ設定",
+  "share_links": {
+    "Shere this page link to public": "外部に共有するリンクを発行する",
+    "share_link_list": "共有リンクリスト",
+    "share_link_management": "共有リンク管理",
+    "No_share_links":"共有リンクが存在しません",
+    "Share Link": "共有用リンク",
+    "Page Path": "ページパス",
+    "share_link_notice":"{{count}} 件の共有リンクを削除します",
+    "delete_all_share_links":"全ての共有リンクを削除します",
+    "expire": "有効期限",
+    "Days": "日間",
+    "Custom": "カスタム",
+    "description": "概要",
+    "enter_desc": "概要を入力",
+    "Unlimited": "無期限",
+    "Issue": "発行"
+  },
   "API Settings": "API設定",
   "API Settings": "API設定",
   "API Token Settings": "API Token設定",
   "API Token Settings": "API Token設定",
   "Current API Token": "現在のAPI Token",
   "Current API Token": "現在のAPI Token",

+ 22 - 6
resource/locales/zh_CN/translation.json

@@ -19,7 +19,8 @@
 	"administrator": "管理员",
 	"administrator": "管理员",
 	"Tag": "标签",
 	"Tag": "标签",
 	"Tags": "Tags",
 	"Tags": "Tags",
-	"New": "新建",
+  "New": "新建",
+  "Close": "Close",
 	"Shortcuts": "快捷方式",
 	"Shortcuts": "快捷方式",
 	"eg": "e.g.",
 	"eg": "e.g.",
 	"add": "添加",
 	"add": "添加",
@@ -47,15 +48,13 @@
 	"Timeline View": "时间线",
 	"Timeline View": "时间线",
 	"History": "历史",
 	"History": "历史",
 	"Presentation Mode": "演示文稿",
 	"Presentation Mode": "演示文稿",
-	"Not available for guest": "Not available for guest",
+  "Not available for guest": "Not available for guest",
 	"username": "用户名",
 	"username": "用户名",
 	"Created": "创建",
 	"Created": "创建",
 	"Last updated": "上次更新",
 	"Last updated": "上次更新",
-	"Last_Login": "上次登录",
+  "Last_Login": "上次登录",
 	"Share": "分享",
 	"Share": "分享",
   "Share Link": "分享链接",
   "Share Link": "分享链接",
-  "share_link_notice":"remove {{count}} share links",
-  "delete_all_share_links":"Delete all share links",
 	"Markdown Link": "Markdown链接",
 	"Markdown Link": "Markdown链接",
 	"Create/Edit Template": "创建/编辑 模板页面",
 	"Create/Edit Template": "创建/编辑 模板页面",
 	"Unportalize": "未启动",
 	"Unportalize": "未启动",
@@ -409,7 +408,24 @@
 		"someone_editing": "Someone editing this page on HackMD",
 		"someone_editing": "Someone editing this page on HackMD",
 		"this_page_has_draft": "This page has a draft on HackMD"
 		"this_page_has_draft": "This page has a draft on HackMD"
 	},
 	},
-	"security_settings": "安全设置",
+  "security_settings": "安全设置",
+  "share_links": {
+    "Shere this page link to public": "Shere this page link to public",
+    "share_link_list": "Share link list",
+    "share_link_management": "Share Link Management",
+    "No_share_links":"No share links",
+    "Share Link": "Share Link",
+    "Page Path": "Page Path",
+    "share_link_notice":"remove {{count}} share links",
+    "delete_all_share_links":"Delete all share links",
+    "expire": "Expiration",
+    "Days": "Days",
+    "Custom": "Custom",
+    "description": "description",
+    "enter_desc": "Enter description",
+    "Unlimited": "unlimited",
+    "Issue": "Issue"
+  },
 	"security_setting": {
 	"security_setting": {
 		"Security settings": "安全设置",
 		"Security settings": "安全设置",
 		"Guest Users Access": "来宾用户访问",
 		"Guest Users Access": "来宾用户访问",

+ 4 - 4
src/client/js/components/Admin/Security/DeleteAllShareLinksModal.jsx

@@ -37,17 +37,17 @@ const DeleteAllShareLinksModal = React.memo((props) => {
       <ModalHeader tag="h4" toggle={closeButtonHandler} className="bg-danger text-light">
       <ModalHeader tag="h4" toggle={closeButtonHandler} className="bg-danger text-light">
         <span>
         <span>
           <i className="icon-fw icon-fire"></i>
           <i className="icon-fw icon-fire"></i>
-          {t('delete_all_share_links')}
+          {t('share_links.delete_all_share_links')}
         </span>
         </span>
       </ModalHeader>
       </ModalHeader>
       <ModalBody>
       <ModalBody>
-        { t('share_link_notice', { count: props.count })}
+        { t('share_links.share_link_notice', { count: props.count })}
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
-        <Button onClick={closeButtonHandler}>Cancel</Button>
+        <Button onClick={closeButtonHandler}>{t('Cancel')}</Button>
         <Button color="danger" onClick={deleteAllLinkHandler}>
         <Button color="danger" onClick={deleteAllLinkHandler}>
           <i className="icon icon-fire"></i>
           <i className="icon icon-fire"></i>
-            Delete
+          {t('Delete')}
         </Button>
         </Button>
       </ModalFooter>
       </ModalFooter>
     </Modal>
     </Modal>

+ 10 - 10
src/client/js/components/Admin/Security/ShareLinkSetting.jsx

@@ -81,7 +81,7 @@ class ShareLinkSetting extends React.Component {
 
 
 
 
   render() {
   render() {
-    const { adminGeneralSecurityContainer } = this.props;
+    const { t, adminGeneralSecurityContainer } = this.props;
 
 
     const pager = (
     const pager = (
       <div className="pull-right my-3">
       <div className="pull-right my-3">
@@ -102,11 +102,11 @@ class ShareLinkSetting extends React.Component {
             type="button"
             type="button"
             onClick={this.showDeleteConfirmModal}
             onClick={this.showDeleteConfirmModal}
           >
           >
-            Delete all links
+            {t('share_links.delete_all_share_links')}
           </button>
           </button>
         )
         )
         : (
         : (
-          <p className="pull-right mr-2">No share links</p>
+          <p className="pull-right mr-2">{t('share_links.No_share_links')}</p>
         )
         )
     );
     );
 
 
@@ -114,7 +114,7 @@ class ShareLinkSetting extends React.Component {
       <Fragment>
       <Fragment>
         <div className="mb-3">
         <div className="mb-3">
           {deleteAllButton}
           {deleteAllButton}
-          <h2 className="alert-anchor border-bottom">Shared Link List</h2>
+          <h2 className="alert-anchor border-bottom">{t('share_links.share_link_management')}</h2>
         </div>
         </div>
 
 
         {pager}
         {pager}
@@ -122,11 +122,11 @@ class ShareLinkSetting extends React.Component {
           <table className="table table-bordered">
           <table className="table table-bordered">
             <thead>
             <thead>
               <tr>
               <tr>
-                <th>Link</th>
-                <th>PagePath</th>
-                <th>Expiration</th>
-                <th>Description</th>
-                <th>Order</th>
+                <th>{t('share_links.Share Link')}</th>
+                <th>{t('share_links.Page Path')}</th>
+                <th>{t('share_links.expire')}</th>
+                <th>{t('share_links.description')}</th>
+                <th></th>
               </tr>
               </tr>
             </thead>
             </thead>
             <tbody>
             <tbody>
@@ -144,7 +144,7 @@ class ShareLinkSetting extends React.Component {
                         shareLinks={sharelink._id}
                         shareLinks={sharelink._id}
                         onClick={() => { this.deleteLinkById(sharelink._id) }}
                         onClick={() => { this.deleteLinkById(sharelink._id) }}
                       >
                       >
-                        <i className="icon-trash mr-2"></i>Delete
+                        <i className="icon-trash mr-2"></i>{t('Delete')}
                       </button>
                       </button>
                     </td>
                     </td>
                   </tr>
                   </tr>

+ 1 - 1
src/client/js/components/Admin/Users/UserInviteModal.jsx

@@ -49,7 +49,7 @@ class UserInviteModal extends React.Component {
         <label> {t('admin:user_management.invite_modal.emails')}</label>
         <label> {t('admin:user_management.invite_modal.emails')}</label>
         <textarea
         <textarea
           className="form-control"
           className="form-control"
-          placeholder="e.g. user@growi.org"
+          placeholder="e.g.&#13;&#10;user1@growi.org&#13;&#10;user2@growi.org"
           style={{ height: '200px' }}
           style={{ height: '200px' }}
           value={this.state.emailInputValue}
           value={this.state.emailInputValue}
           onChange={this.handleInput}
           onChange={this.handleInput}

+ 6 - 5
src/client/js/components/OutsideShareLinkModal.jsx

@@ -87,15 +87,16 @@ class OutsideShareLinkModal extends React.Component {
   }
   }
 
 
   render() {
   render() {
+    const { t } = this.props;
     return (
     return (
-      <Modal size="xl" isOpen={this.props.isOpen} toggle={this.props.onClose}>
-        <ModalHeader tag="h4" toggle={this.props.onClose} className="bg-primary text-light">Title
+      <Modal size="lg" isOpen={this.props.isOpen} toggle={this.props.onClose}>
+        <ModalHeader tag="h4" toggle={this.props.onClose} className="bg-primary text-light">{t('share_links.Shere this page link to public')}
         </ModalHeader>
         </ModalHeader>
         <ModalBody>
         <ModalBody>
           <div className="container">
           <div className="container">
             <div className="form-inline mb-3">
             <div className="form-inline mb-3">
-              <h4>Shared Link List</h4>
-              <button className="ml-auto btn btn-danger" type="button" onClick={this.deleteAllLinksButtonHandler}>Delete all links</button>
+              <h4>{t('share_links.share_link_list')}</h4>
+              <button className="ml-auto btn btn-danger" type="button" onClick={this.deleteAllLinksButtonHandler}>{t('delete_all')}</button>
             </div>
             </div>
 
 
             <div>
             <div>
@@ -108,7 +109,7 @@ class OutsideShareLinkModal extends React.Component {
                 type="button"
                 type="button"
                 onClick={this.toggleShareLinkFormHandler}
                 onClick={this.toggleShareLinkFormHandler}
               >
               >
-                {this.state.isOpenShareLinkForm ? 'Close' : 'New'}
+                {this.state.isOpenShareLinkForm ? t('Close') : t('New')}
               </button>
               </button>
               {this.state.isOpenShareLinkForm && <ShareLinkForm onCloseForm={this.toggleShareLinkFormHandler} />}
               {this.state.isOpenShareLinkForm && <ShareLinkForm onCloseForm={this.toggleShareLinkFormHandler} />}
             </div>
             </div>

+ 1 - 1
src/client/js/components/Page/PageShareManagement.jsx

@@ -71,7 +71,7 @@ const PageShareManagement = (props) => {
       {currentUser == null ? renderGuestUser() : renderCurrentUser()}
       {currentUser == null ? renderGuestUser() : renderCurrentUser()}
       <div className="dropdown-menu dropdown-menu-right">
       <div className="dropdown-menu dropdown-menu-right">
         <button className="dropdown-item" type="button" onClick={openOutsideShareLinkModalHandler}>
         <button className="dropdown-item" type="button" onClick={openOutsideShareLinkModalHandler}>
-          <i className="icon-fw icon-link"></i>{t('Shere this page link to public')}
+          <i className="icon-fw icon-link"></i>{t('share_links.Shere this page link to public')}
           <span className="ml-2 badge badge-info badge-pill">{pageContainer.state.shareLinksNumber}</span>
           <span className="ml-2 badge badge-info badge-pill">{pageContainer.state.shareLinksNumber}</span>
         </button>
         </button>
       </div>
       </div>

+ 1 - 1
src/client/js/components/PageDeleteModal.jsx

@@ -124,7 +124,7 @@ const PageDeleteModal = (props) => {
         {!isDeleteCompletelyModal && renderDeleteCompletelyForm()}
         {!isDeleteCompletelyModal && renderDeleteCompletelyForm()}
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
-        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} linkPath={path} />
+        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} />
         <button type="button" className={`btn btn-${deleteIconAndKey[deleteMode].color}`} onClick={deleteButtonHandler}>
         <button type="button" className={`btn btn-${deleteIconAndKey[deleteMode].color}`} onClick={deleteButtonHandler}>
           <i className={`icon-${deleteIconAndKey[deleteMode].icon}`} aria-hidden="true"></i>
           <i className={`icon-${deleteIconAndKey[deleteMode].icon}`} aria-hidden="true"></i>
           { t(`modal_delete.delete_${deleteIconAndKey[deleteMode].translationKey}`) }
           { t(`modal_delete.delete_${deleteIconAndKey[deleteMode].translationKey}`) }

+ 1 - 1
src/client/js/components/PageDuplicateModal.jsx

@@ -100,7 +100,7 @@ const PageDuplicateModal = (props) => {
         </div>
         </div>
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
-        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} linkPath={path} />
+        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} targetPath={pageNameInput} />
         <button type="button" className="btn btn-primary" onClick={duplicate}>Duplicate page</button>
         <button type="button" className="btn btn-primary" onClick={duplicate}>Duplicate page</button>
       </ModalFooter>
       </ModalFooter>
     </Modal>
     </Modal>

+ 3 - 3
src/client/js/components/PageManagement/ApiErrorMessage.jsx

@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next';
 
 
 const ApiErrorMessage = (props) => {
 const ApiErrorMessage = (props) => {
   const {
   const {
-    t, errorCode, errorMessage, linkPath,
+    t, errorCode, errorMessage, targetPath,
   } = props;
   } = props;
 
 
   function reload() {
   function reload() {
@@ -18,7 +18,7 @@ const ApiErrorMessage = (props) => {
         return (
         return (
           <>
           <>
             <strong><i className="icon-fw icon-ban"></i>{ t('page_api_error.already_exists') }</strong>
             <strong><i className="icon-fw icon-ban"></i>{ t('page_api_error.already_exists') }</strong>
-            <small><a href={linkPath}>{linkPath} <i className="icon-login"></i></a></small>
+            <small><a href={targetPath}>{targetPath} <i className="icon-login"></i></a></small>
           </>
           </>
         );
         );
       case 'notfound_or_forbidden':
       case 'notfound_or_forbidden':
@@ -77,7 +77,7 @@ ApiErrorMessage.propTypes = {
 
 
   errorCode:    PropTypes.string,
   errorCode:    PropTypes.string,
   errorMessage: PropTypes.string,
   errorMessage: PropTypes.string,
-  linkPath:     PropTypes.string,
+  targetPath:   PropTypes.string,
 };
 };
 
 
 export default withTranslation()(ApiErrorMessage);
 export default withTranslation()(ApiErrorMessage);

+ 1 - 1
src/client/js/components/PageRenameModal.jsx

@@ -150,7 +150,7 @@ const PageRenameModal = (props) => {
         </div>
         </div>
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
-        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} linkPath={path} />
+        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} targetPath={pageNameInput} />
         <button type="button" className="btn btn-primary" onClick={rename}>Rename</button>
         <button type="button" className="btn btn-primary" onClick={rename}>Rename</button>
       </ModalFooter>
       </ModalFooter>
     </Modal>
     </Modal>

+ 1 - 1
src/client/js/components/PutbackPageModal.jsx

@@ -73,7 +73,7 @@ const PutBackPageModal = (props) => {
         </div>
         </div>
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
-        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} linkPath={path} />
+        <ApiErrorMessage errorCode={errorCode} errorMessage={errorMessage} />
         <button type="button" className="btn btn-info" onClick={putbackPageButtonHandler}>
         <button type="button" className="btn btn-info" onClick={putbackPageButtonHandler}>
           <i className="icon-action-undo mr-2" aria-hidden="true"></i> { t('Put Back') }
           <i className="icon-action-undo mr-2" aria-hidden="true"></i> { t('Put Back') }
         </button>
         </button>

+ 10 - 7
src/client/js/components/ShareLinkForm.jsx

@@ -133,6 +133,7 @@ class ShareLinkForm extends React.Component {
 
 
   renderExpirationTypeOptions() {
   renderExpirationTypeOptions() {
     const { expirationType } = this.state;
     const { expirationType } = this.state;
+    const { t } = this.props;
 
 
     return (
     return (
       <div className="form-group">
       <div className="form-group">
@@ -146,7 +147,7 @@ class ShareLinkForm extends React.Component {
             checked={expirationType === 'unlimited'}
             checked={expirationType === 'unlimited'}
             onChange={() => { this.handleChangeExpirationType('unlimited') }}
             onChange={() => { this.handleChangeExpirationType('unlimited') }}
           />
           />
-          <label className="custom-control-label" htmlFor="customRadio1">Unlimited</label>
+          <label className="custom-control-label" htmlFor="customRadio1">{t('share_links.Unlimited')}</label>
         </div>
         </div>
 
 
         <div className="custom-control custom-radio offset-4 mb-2">
         <div className="custom-control custom-radio offset-4 mb-2">
@@ -170,7 +171,7 @@ class ShareLinkForm extends React.Component {
                 onFocus={() => { this.handleChangeExpirationType('numberOfDays') }}
                 onFocus={() => { this.handleChangeExpirationType('numberOfDays') }}
                 onChange={e => this.handleChangeNumberOfDays(Number(e.target.value))}
                 onChange={e => this.handleChangeNumberOfDays(Number(e.target.value))}
               />
               />
-              <span className="col-auto">Days</span>
+              <span className="col-auto">{t('share_links.Days')}</span>
             </div>
             </div>
           </label>
           </label>
         </div>
         </div>
@@ -186,7 +187,7 @@ class ShareLinkForm extends React.Component {
             onChange={() => { this.handleChangeExpirationType('custom') }}
             onChange={() => { this.handleChangeExpirationType('custom') }}
           />
           />
           <label className="custom-control-label" htmlFor="customRadio3">
           <label className="custom-control-label" htmlFor="customRadio3">
-            Custom
+            {t('share_links.Custom')}
           </label>
           </label>
           <input
           <input
             type="date"
             type="date"
@@ -210,15 +211,16 @@ class ShareLinkForm extends React.Component {
   }
   }
 
 
   renderDescriptionForm() {
   renderDescriptionForm() {
+    const { t } = this.props;
     return (
     return (
       <div className="form-group row">
       <div className="form-group row">
-        <label htmlFor="inputDesc" className="col-md-4 col-form-label">Description</label>
+        <label htmlFor="inputDesc" className="col-md-4 col-form-label">{t('share_links.description')}</label>
         <div className="col-md-4">
         <div className="col-md-4">
           <input
           <input
             type="text"
             type="text"
             className="form-control"
             className="form-control"
             id="inputDesc"
             id="inputDesc"
-            placeholder="Enter description"
+            placeholder={t('share_links.enter_desc')}
             value={this.state.description}
             value={this.state.description}
             onChange={e => this.handleChangeDescription(e.target.value)}
             onChange={e => this.handleChangeDescription(e.target.value)}
           />
           />
@@ -228,15 +230,16 @@ class ShareLinkForm extends React.Component {
   }
   }
 
 
   render() {
   render() {
+    const { t } = this.props;
     return (
     return (
       <div className="share-link-form border p-3">
       <div className="share-link-form border p-3">
-        <h4>Expiration Date</h4>
+        <h4>{t('share_links.expire')}</h4>
         {this.renderExpirationTypeOptions()}
         {this.renderExpirationTypeOptions()}
         <hr />
         <hr />
         {this.renderDescriptionForm()}
         {this.renderDescriptionForm()}
         <div className="text-right">
         <div className="text-right">
           <button type="button" className="btn btn-primary" onClick={this.handleIssueShareLink}>
           <button type="button" className="btn btn-primary" onClick={this.handleIssueShareLink}>
-            Issue
+            {t('share_links.Issue')}
           </button>
           </button>
         </div>
         </div>
       </div>
       </div>

+ 6 - 5
src/client/js/components/ShareLinkList.jsx

@@ -12,6 +12,7 @@ import CopyDropdown from './Page/CopyDropdown';
 
 
 const ShareLinkList = (props) => {
 const ShareLinkList = (props) => {
 
 
+  const { t } = props;
   function deleteLinkHandler(shareLinkId) {
   function deleteLinkHandler(shareLinkId) {
     if (props.onClickDeleteButton == null) {
     if (props.onClickDeleteButton == null) {
       return;
       return;
@@ -32,7 +33,7 @@ const ShareLinkList = (props) => {
             <td>{shareLink.description}</td>
             <td>{shareLink.description}</td>
             <td>
             <td>
               <button className="btn btn-outline-warning" type="button" onClick={() => deleteLinkHandler(shareLink._id)}>
               <button className="btn btn-outline-warning" type="button" onClick={() => deleteLinkHandler(shareLink._id)}>
-                <i className="icon-trash"></i>Delete
+                <i className="icon-trash"></i>{t('Delete')}
               </button>
               </button>
             </td>
             </td>
           </tr>
           </tr>
@@ -46,10 +47,10 @@ const ShareLinkList = (props) => {
       <table className="table table-bordered">
       <table className="table table-bordered">
         <thead>
         <thead>
           <tr>
           <tr>
-            <th>Link</th>
-            <th>Expiration</th>
-            <th>Description</th>
-            <th>Order</th>
+            <th>{t('share_links.Share Link')}</th>
+            <th>{t('share_links.expire')}</th>
+            <th>{t('share_links.description')}</th>
+            <th></th>
           </tr>
           </tr>
         </thead>
         </thead>
         <tbody>
         <tbody>

+ 3 - 4
src/server/models/page.js

@@ -1185,18 +1185,17 @@ module.exports = function(crowi) {
   /**
   /**
    * Delete Bookmarks, Attachments, Revisions, Pages and emit delete
    * Delete Bookmarks, Attachments, Revisions, Pages and emit delete
    */
    */
-  pageSchema.statics.completelyDeletePageRecursively = async function(pageData, user, options = {}) {
-    const path = pageData.path;
+  pageSchema.statics.completelyDeletePageRecursively = async function(pagePath, user, options = {}) {
 
 
     const findOpts = { includeRedirect: true, includeTrashed: true };
     const findOpts = { includeRedirect: true, includeTrashed: true };
-    const result = await this.findListWithDescendants(path, user, findOpts);
+    const result = await this.findListWithDescendants(pagePath, user, findOpts);
     const pages = result.pages;
     const pages = result.pages;
 
 
     await Promise.all(pages.map((page) => {
     await Promise.all(pages.map((page) => {
       return this.completelyDeletePage(page, user, options);
       return this.completelyDeletePage(page, user, options);
     }));
     }));
 
 
-    return pageData;
+    return pagePath;
   };
   };
 
 
   pageSchema.statics.removeByPath = function(path) {
   pageSchema.statics.removeByPath = function(path) {

+ 1 - 3
src/server/routes/apiv3/pages.js

@@ -72,9 +72,7 @@ module.exports = (crowi) => {
   */
   */
   router.delete('/empty-trash', loginRequired, adminRequired, csrf, async(req, res) => {
   router.delete('/empty-trash', loginRequired, adminRequired, csrf, async(req, res) => {
     try {
     try {
-      const pages = await Page.deleteMany({
-        path: { $in: /^\/trash/ },
-      });
+      const pages = await Page.completelyDeletePageRecursively('/trash', req.user);
       return res.apiv3({ pages });
       return res.apiv3({ pages });
     }
     }
     catch (err) {
     catch (err) {

+ 1 - 1
src/server/routes/page.js

@@ -1283,7 +1283,7 @@ module.exports = function(crowi, app) {
           return res.json(ApiResponse.error('You can not delete completely', 'user_not_admin'));
           return res.json(ApiResponse.error('You can not delete completely', 'user_not_admin'));
         }
         }
         if (isRecursively) {
         if (isRecursively) {
-          page = await Page.completelyDeletePageRecursively(page, req.user, options);
+          await Page.completelyDeletePageRecursively(page.path, req.user, options);
         }
         }
         else {
         else {
           page = await Page.completelyDeletePage(page, req.user, options);
           page = await Page.completelyDeletePage(page, req.user, options);