Quellcode durchsuchen

Merge branch 'master' into feat/3176-grid-edit-modal-for-master-merge

yusuketk vor 5 Jahren
Ursprung
Commit
8325ce0bdf

+ 13 - 0
CHANGES.md

@@ -1,5 +1,14 @@
 # CHANGES
 
+## v4.2.6-RC
+
+* 
+
+## v4.2.5-RC
+
+* Improvement: Invoke garbage collection when reindex all pages by elasticsearch
+* Fix: MathJax rendering does not work
+
 ## v4.2.4
 
 * Fix: Fixed an error when creating a new page with `Ctrl-S`
@@ -48,6 +57,10 @@
     * migrate-mongo
     * mongoose
 
+## v4.1.13
+
+* Fix: MathJax rendering does not work
+
 ## v4.1.12
 
 * Fix: Adjust line-height for pre under li

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "4.2.4-RC",
+  "version": "4.2.6-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -58,7 +58,7 @@
     "i18n-json-merge:noTran": "rs-i18n -lan --",
     "i18n-json-merge": "npm run i18n-json-merge:withTran --",
     "server:nolazy": "env-cmd -f config/env.dev.js node-dev --nolazy --inspect src/server/app.js",
-    "server:dev": "env-cmd -f config/env.dev.js node-dev --inspect src/server/app.js",
+    "server:dev": "env-cmd -f config/env.dev.js node-dev --expose_gc --inspect src/server/app.js",
     "server:prod:ci": "npm run server:prod -- --ci",
     "server:prod": "env-cmd -f config/env.prod.js node src/server/app.js",
     "server": "npm run server:dev",

+ 1 - 1
resource/cdn-manifests.js

@@ -39,7 +39,7 @@ module.exports = {
     },
     {
       name: 'mathjax',
-      url: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js',
+      url: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js',
       args: {
         async: true,
         integrity: '',

+ 17 - 0
resource/locales/en_US/translation.json

@@ -17,6 +17,7 @@
   "Unlinked": "Unlinked",
   "Like!": "Like!",
   "Seen by": "Seen by",
+  "Done": "Done",
   "Cancel": "Cancel",
   "Create": "Create",
   "Admin": "Admin",
@@ -400,6 +401,22 @@
       "Post": "Post"
     }
   },
+  "link_edit": {
+    "edit_link": "Edit Link",
+    "set_link_and_label": "Set link and label",
+    "link": "Link",
+    "placeholder_of_link_input": "Input page path or URL",
+    "label": "Label",
+    "path_format": "Path format",
+    "use_relative_path": "Use relative path",
+    "use_permanent_link": "Use permanent link",
+    "notation": "Notation",
+    "markdown": "Markdown",
+    "GROWI_original": "GROWI original",
+    "pukiwiki": "Pukiwiki",
+    "preview": "Preview",
+    "page_not_found_in_preview": "\"{{path}}\" is not a GROWI page."
+  },
   "toaster": {
     "update_successed": "Succeeded to update {{target}}",
     "initialize_successed": "Succeeded to initialize {{target}}",

+ 17 - 0
resource/locales/ja_JP/translation.json

@@ -17,6 +17,7 @@
   "Unlinked": "リダイレクト削除",
   "Like!": "いいね!",
   "Seen by": "見た人",
+  "Done": "完了",
   "Cancel": "キャンセル",
   "Create": "作成",
   "Admin": "管理",
@@ -402,6 +403,22 @@
       "Post": "投稿"
     }
   },
+  "link_edit": {
+    "edit_link": "リンク編集",
+    "set_link_and_label": "リンク情報",
+    "link": "リンク",
+    "placeholder_of_link_input": "ページパスまたはURLを入力してください",
+    "label": "ラベル",
+    "path_format": "ページパス設定",
+    "use_relative_path": "相対パスを使う",
+    "use_permanent_link": "パーマリンクを使う",
+    "notation": "リンクの形式",
+    "markdown": "マークダウン 記法",
+    "GROWI_original": "GROWI 独自記法",
+    "pukiwiki": "Pukiwiki 記法",
+    "preview": "プレビュー",
+    "page_not_found_in_preview": "\"{{path}}\" というページはありません。"
+  },
   "toaster": {
     "update_successed": "{{target}}を更新しました",
     "initialize_successed": "{{target}}を初期化しました",

+ 18 - 1
resource/locales/zh_CN/translation.json

@@ -18,7 +18,8 @@
 	"Unlinked": "Unlinked",
 	"Like!": "Like!",
 	"Seen by": "Seen by",
-	"Cancel": "取消",
+  "Done": "Done",
+  "Cancel": "取消",
 	"Create": "创建",
 	"Admin": "管理",
 	"administrator": "管理员",
@@ -380,6 +381,22 @@
 			"Post": "提交"
 		}
 	},
+  "link_edit": {
+    "edit_link": "Edit Link",
+    "set_link_and_label": "Set link and label",
+    "link": "Link",
+    "placeholder_of_link_input": "Input page path or URL",
+    "label": "Label",
+    "path_format": "Path format",
+    "use_relative_path": "Use relative path",
+    "use_permanent_link": "Use permanent link",
+    "notation": "Notation",
+    "markdown": "Markdown",
+    "GROWI_original": "GROWI original",
+    "pukiwiki": "Pukiwiki",
+    "preview": "Preview",
+    "page_not_found_in_preview": "\"{{path}}\" is not a GROWI page."
+  },
 	"toaster": {
 		"update_successed": "Succeeded to update {{target}}",
     "initialize_successed": "Succeeded to initialize {{target}}",

+ 1 - 1
src/client/js/components/Navbar/GrowiSubNavigation.jsx

@@ -44,7 +44,7 @@ const PagePathNav = ({
   }
 
   const copyDropdownId = `copydropdown${isCompactMode ? '-subnav-compact' : ''}-${pageId}`;
-  const copyDropdownToggleClassName = 'd-block text-muted bg-transparent btn-copy border-0';
+  const copyDropdownToggleClassName = 'd-block text-muted bg-transparent btn-copy border-0 py-0';
 
   return (
     <div className="grw-page-path-nav">

+ 2 - 2
src/client/js/components/Page/RevisionBody.jsx

@@ -42,8 +42,8 @@ export default class RevisionBody extends React.PureComponent {
     //   So, before MathJax is initialized, execute renderMathJaxWithDebounce again.
     //   Avoiding initialization of MathJax of draw.io solves the problem.
     //   refs: https://github.com/jgraph/drawio/pull/831
-    if (MathJax != null && MathJax.Hub != null) {
-      MathJax.Hub.Queue(['Typeset', MathJax.Hub, this.element]);
+    if (MathJax != null) {
+      MathJax.typesetPromise([this.element]);
     }
     else {
       this.renderMathJaxWithDebounce();

+ 23 - 17
src/client/js/components/PageEditor/LinkEditModal.jsx

@@ -11,6 +11,7 @@ import {
 
 import path from 'path';
 import validator from 'validator';
+import { withTranslation } from 'react-i18next';
 import PreviewWithSuspense from './PreviewWithSuspense';
 import PagePreviewIcon from '../Icons/PagePreviewIcon';
 
@@ -147,6 +148,7 @@ class LinkEditModal extends React.PureComponent {
   }
 
   async setMarkdown() {
+    const { t } = this.props;
     const path = this.state.linkInputValue;
     let markdown = '';
     let previewError = '';
@@ -168,7 +170,7 @@ class LinkEditModal extends React.PureComponent {
       }
     }
     else {
-      previewError = `'${path}' is not a GROWI page.`;
+      previewError = t('link_edit.page_not_found_in_preview', { path });
     }
     this.setState({ markdown, previewError, permalink });
   }
@@ -283,20 +285,21 @@ class LinkEditModal extends React.PureComponent {
   }
 
   renderLinkAndLabelForm() {
+    const { t } = this.props;
     return (
       <>
-        <h3 className="grw-modal-head">Set link and label</h3>
+        <h3 className="grw-modal-head">{t('link_edit.set_link_and_label')}</h3>
         <form className="form-group">
           <div className="form-gorup my-3">
             <div className="input-group flex-nowrap">
               <div className="input-group-prepend">
-                <span className="input-group-text">link</span>
+                <span className="input-group-text">{t('link_edit.link')}</span>
               </div>
               <SearchTypeahead
                 onChange={this.handleChangeTypeahead}
                 onInputChange={this.handleChangeLinkInput}
                 inputName="link"
-                placeholder="Input page path or URL"
+                placeholder={t('link_edit.placeholder_of_link_input')}
                 keywordOnInit={this.state.linkInputValue}
                 behaviorOfResetBtn="clear"
                 autoFocus
@@ -316,7 +319,7 @@ class LinkEditModal extends React.PureComponent {
           <div className="form-gorup my-3">
             <div className="input-group flex-nowrap">
               <div className="input-group-prepend">
-                <span className="input-group-text">label</span>
+                <span className="input-group-text">{t('link_edit.label')}</span>
               </div>
               <input
                 type="text"
@@ -335,11 +338,12 @@ class LinkEditModal extends React.PureComponent {
   }
 
   renderPathFormatForm() {
+    const { t } = this.props;
     return (
       <div className="card well pt-3">
         <form className="form-group mb-0">
           <div className="form-group row">
-            <label className="col-sm-3">Path format</label>
+            <label className="col-sm-3">{t('link_edit.path_format')}</label>
             <div className="col-sm-9">
               <div className="custom-control custom-checkbox custom-checkbox-info custom-control-inline">
                 <input
@@ -351,7 +355,7 @@ class LinkEditModal extends React.PureComponent {
                   disabled={!this.state.linkInputValue.startsWith('/') || this.state.linkerType === Linker.types.growiLink}
                 />
                 <label className="custom-control-label" htmlFor="relativePath">
-                  Use relative path
+                  {t('link_edit.use_relative_path')}
                 </label>
               </div>
               <div className="custom-control custom-checkbox custom-checkbox-info custom-control-inline">
@@ -364,13 +368,13 @@ class LinkEditModal extends React.PureComponent {
                   disabled={this.state.permalink === '' || this.state.linkerType === Linker.types.growiLink}
                 />
                 <label className="custom-control-label" htmlFor="permanentLink">
-                  Use permanent link
+                  {t('link_edit.use_permanent_link')}
                 </label>
               </div>
             </div>
           </div>
           <div className="form-group row mb-0">
-            <label className="col-sm-3">Notation</label>
+            <label className="col-sm-3">{t('link_edit.notation')}</label>
             <div className="col-sm-9">
               <div className="custom-control custom-radio custom-control-inline">
                 <input
@@ -382,7 +386,7 @@ class LinkEditModal extends React.PureComponent {
                   onChange={e => this.handleSelecteLinkerType(e.target.value)}
                 />
                 <label className="custom-control-label" htmlFor="markdownType">
-                  Markdown
+                  {t('link_edit.markdown')}
                 </label>
               </div>
               <div className="custom-control custom-radio custom-control-inline">
@@ -395,7 +399,7 @@ class LinkEditModal extends React.PureComponent {
                   onChange={e => this.handleSelecteLinkerType(e.target.value)}
                 />
                 <label className="custom-control-label" htmlFor="growiType">
-                  Growi original
+                  {t('link_edit.GROWI_original')}
                 </label>
               </div>
               {this.isApplyPukiwikiLikeLinkerPlugin && (
@@ -409,7 +413,7 @@ class LinkEditModal extends React.PureComponent {
                     onChange={e => this.handleSelecteLinkerType(e.target.value)}
                   />
                   <label className="custom-control-label" htmlFor="pukiwikiType">
-                    Pukiwiki
+                    {t('link_edit.pukiwiki')}
                   </label>
                 </div>
               )}
@@ -421,10 +425,11 @@ class LinkEditModal extends React.PureComponent {
   }
 
   render() {
+    const { t } = this.props;
     return (
       <Modal className="link-edit-modal" isOpen={this.state.show} toggle={this.cancel} size="lg" autoFocus={false}>
         <ModalHeader tag="h4" toggle={this.cancel} className="bg-primary text-light">
-          Edit Links
+          {t('link_edit.edit_link')}
         </ModalHeader>
 
         <ModalBody className="container">
@@ -436,17 +441,17 @@ class LinkEditModal extends React.PureComponent {
           </div>
           <div className="row">
             <div className="col-12">
-              <h3 className="grw-modal-head">Preview</h3>
+              <h3 className="grw-modal-head">{t('link_edit.preview')}</h3>
               {this.renderLinkPreview()}
             </div>
           </div>
           <div className="row">
             <div className="col-12 text-center">
               <button type="button" className="btn btn-sm btn-outline-secondary mx-1" onClick={this.hide}>
-                Cancel
+                {t('Cancel')}
               </button>
               <button type="submit" className="btn btn-sm btn-primary mx-1" onClick={this.save}>
-                Done
+                {t('Done')}
               </button>
             </div>
           </div>
@@ -458,6 +463,7 @@ class LinkEditModal extends React.PureComponent {
 }
 
 LinkEditModal.propTypes = {
+  t: PropTypes.func.isRequired,
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   onSave: PropTypes.func,
@@ -468,4 +474,4 @@ LinkEditModal.propTypes = {
  */
 const LinkEditModalWrapper = withUnstatedContainers(LinkEditModal, [AppContainer, PageContainer]);
 
-export default LinkEditModalWrapper;
+export default withTranslation('translation', { withRef: true })(LinkEditModalWrapper);

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

@@ -75,7 +75,7 @@ class SearchForm extends React.Component {
     }
 
     if (!isShownHelp) {
-      return null;
+      return <></>;
     }
 
     return (

+ 2 - 1
src/client/styles/scss/_on-edit.scss

@@ -139,7 +139,8 @@ body.on-edit {
       .grw-tag-labels.form-inline {
         flex-flow: row nowrap;
         width: 100%;
-        overflow-x: scroll;
+        overflow-x: auto;
+        scrollbar-width: thin;
       }
     }
   }

+ 14 - 2
src/server/service/search-delegator/elasticsearch.js

@@ -344,7 +344,7 @@ class ElasticsearchDelegator {
 
   addAllPages() {
     const Page = mongoose.model('Page');
-    return this.updateOrInsertPages(() => Page.find(), true);
+    return this.updateOrInsertPages(() => Page.find(), { isEmittingProgressEvent: true, invokeGarbageCollection: true });
   }
 
   updateOrInsertPageById(pageId) {
@@ -355,7 +355,9 @@ class ElasticsearchDelegator {
   /**
    * @param {function} queryFactory factory method to generate a Mongoose Query instance
    */
-  async updateOrInsertPages(queryFactory, isEmittingProgressEvent = false) {
+  async updateOrInsertPages(queryFactory, option = {}) {
+    const { isEmittingProgressEvent = false, invokeGarbageCollection = false } = option;
+
     const Page = mongoose.model('Page');
     const { PageQueryBuilder } = Page;
     const Bookmark = mongoose.model('Bookmark');
@@ -465,6 +467,16 @@ class ElasticsearchDelegator {
           logger.error('addAllPages error on add anyway: ', err);
         }
 
+        if (invokeGarbageCollection) {
+          try {
+            // First aid to prevent unexplained memory leaks
+            global.gc();
+          }
+          catch (err) {
+            logger.error('fail garbage collection: ', err);
+          }
+        }
+
         callback();
       },
       final(callback) {