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

Merge branch 'master' into feat/growi-bot

itizawa 4 лет назад
Родитель
Сommit
15b0bd66f6

+ 2 - 2
.github/workflows/list-unhealthy-branches.yml

@@ -37,7 +37,7 @@ jobs:
         status: custom
         payload: |
           {
-            text: '<!channel> There is some *illegal named branches* on GitHub.',
+            text: '<!channel> There is some branches *with illegal names* on GitHub.',
             channel: '#ci',
             attachments: ${{ steps.list-branches.outputs.SLACK_ATTACHMENTS_ILLEGAL }}
           }
@@ -51,7 +51,7 @@ jobs:
         status: custom
         payload: |
           {
-            text: '<!channel> There is some *illegal named branches* on GitHub.',
+            text: '<!channel> There is some branches *that are no longer updated* on GitHub.',
             channel: '#ci',
             attachments: ${{ steps.list-branches.outputs.SLACK_ATTACHMENTS_INACTIVE }}
           }

+ 4 - 1
CHANGES.md

@@ -5,10 +5,13 @@
 * Improvement: Upgrade mongodb driver to fix [NODE-2784](https://jira.mongodb.org/browse/NODE-2784)
 * Support: Upgrade libs
     * connect-mongo
+    * i18next
     * migrate-mongo
     * mongoose
     * stream-to-promise
-
+    * validator
+    * ws
+    * nodemailer
 
 ## v4.2.20
 

+ 4 - 0
resource/Contributor.js

@@ -43,6 +43,9 @@ const contributors = [
           { name: 'oshikishintaro' },
           { name: 'makotoshiraishi' },
           { name: 'yamagai' },
+          { name: 'stevenfukase' },
+          { name: 'miya' },
+          { name: 'kaho819' },
         ],
       },
     ],
@@ -109,6 +112,7 @@ const contributors = [
           { position: 'Flatt Security', name: 'Azara/Norihide Saito' },
           { position: 'CyberAgent, Inc.', name: 'Daisuke Takahashi' },
           { position: 'Mitsui Bussan Secure Directions, Inc.', name: 'Yuji Tounai' },
+          { name: 'yy0931' },
         ],
       },
     ],

+ 33 - 17
src/client/js/components/PageCreateModal.jsx

@@ -6,14 +6,16 @@ import { Modal, ModalHeader, ModalBody } from 'reactstrap';
 
 import { withTranslation } from 'react-i18next';
 import { format } from 'date-fns';
-import urljoin from 'url-join';
 
-import { userPageRoot, isCreatablePage } from '@commons/util/path-utils';
+import {
+  userPageRoot, isCreatablePage, generateEditorPath,
+} from '@commons/util/path-utils';
 import { pathUtils } from 'growi-commons';
 
 import AppContainer from '../services/AppContainer';
 import NavigationContainer from '../services/NavigationContainer';
 import { withUnstatedContainers } from './UnstatedUtils';
+import { toastError } from '../util/apiNotification';
 
 import PagePathAutoComplete from './PagePathAutoComplete';
 
@@ -70,6 +72,20 @@ const PageCreateModal = (props) => {
     setTemplate(value);
   }
 
+  /**
+   * join path, check if creatable, then redirect
+   * @param {string} paths
+   */
+  async function redirectToEditor(...paths) {
+    try {
+      const editorPath = await generateEditorPath(...paths);
+      window.location.href = editorPath;
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }
+
   /**
    * access today page
    */
@@ -78,14 +94,14 @@ const PageCreateModal = (props) => {
     if (tmpTodayInput1 === '') {
       tmpTodayInput1 = t('Memo');
     }
-    window.location.href = encodeURI(urljoin(userPageRootPath, tmpTodayInput1, now, todayInput2, '#edit'));
+    redirectToEditor(userPageRootPath, tmpTodayInput1, now, todayInput2);
   }
 
   /**
    * access input page
    */
   function createInputPage() {
-    window.location.href = encodeURI(urljoin(pageNameInput, '#edit'));
+    redirectToEditor(pageNameInput);
   }
 
   function ppacInputChangeHandler(value) {
@@ -101,14 +117,14 @@ const PageCreateModal = (props) => {
    */
   function createTemplatePage(e) {
     const pageName = (template === 'children') ? '_template' : '__template';
-    window.location.href = encodeURI(urljoin(pathname, pageName, '#edit'));
+    redirectToEditor(pathname, pageName);
   }
 
   function renderCreateTodayForm() {
     return (
       <div className="row">
         <fieldset className="col-12 mb-4">
-          <h3 className="grw-modal-head pb-2">{ t("Create today's") }</h3>
+          <h3 className="grw-modal-head pb-2">{t("Create today's")}</h3>
 
           <div className="d-sm-flex align-items-center justify-items-between">
 
@@ -139,7 +155,7 @@ const PageCreateModal = (props) => {
 
             <div className="d-flex justify-content-end mt-1 mt-sm-0">
               <button type="button" className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ml-3" onClick={createTodayPage}>
-                <i className="icon-fw icon-doc"></i>{ t('Create') }
+                <i className="icon-fw icon-doc"></i>{t('Create')}
               </button>
             </div>
 
@@ -154,7 +170,7 @@ const PageCreateModal = (props) => {
     return (
       <div className="row">
         <fieldset className="col-12 mb-4">
-          <h3 className="grw-modal-head pb-2">{ t('Create under') }</h3>
+          <h3 className="grw-modal-head pb-2">{t('Create under')}</h3>
 
           <div className="d-sm-flex align-items-center justify-items-between">
 
@@ -185,7 +201,7 @@ const PageCreateModal = (props) => {
 
             <div className="d-flex justify-content-end mt-1 mt-sm-0">
               <button type="button" className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ml-3" onClick={createInputPage}>
-                <i className="icon-fw icon-doc"></i>{ t('Create') }
+                <i className="icon-fw icon-doc"></i>{t('Create')}
               </button>
             </div>
 
@@ -202,7 +218,7 @@ const PageCreateModal = (props) => {
         <fieldset className="col-12">
 
           <h3 className="grw-modal-head pb-2">
-            { t('template.modal_label.Create template under')}<br />
+            {t('template.modal_label.Create template under')}<br />
             <code className="h6">{pathname}</code>
           </h3>
 
@@ -210,18 +226,18 @@ const PageCreateModal = (props) => {
 
             <div id="dd-template-type" className="dropdown flex-fill">
               <button id="template-type" type="button" className="btn btn-secondary btn dropdown-toggle w-100" data-toggle="dropdown">
-                {template == null && t('template.option_label.select') }
+                {template == null && t('template.option_label.select')}
                 {template === 'children' && t('template.children.label')}
                 {template === 'decendants' && t('template.decendants.label')}
               </button>
               <div className="dropdown-menu" aria-labelledby="userMenu">
                 <button className="dropdown-item" type="button" onClick={() => onChangeTemplateHandler('children')}>
-                  { t('template.children.label') } (_template)<br className="d-block d-md-none" />
-                  <small className="text-muted text-wrap">- { t('template.children.desc') }</small>
+                  {t('template.children.label')} (_template)<br className="d-block d-md-none" />
+                  <small className="text-muted text-wrap">- {t('template.children.desc')}</small>
                 </button>
                 <button className="dropdown-item" type="button" onClick={() => onChangeTemplateHandler('decendants')}>
-                  { t('template.decendants.label') } (__template) <br className="d-block d-md-none" />
-                  <small className="text-muted">- { t('template.decendants.desc') }</small>
+                  {t('template.decendants.label')} (__template) <br className="d-block d-md-none" />
+                  <small className="text-muted">- {t('template.decendants.desc')}</small>
                 </button>
               </div>
             </div>
@@ -232,7 +248,7 @@ const PageCreateModal = (props) => {
                 className={`grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ml-3 ${template == null && 'disabled'}`}
                 onClick={createTemplatePage}
               >
-                <i className="icon-fw icon-doc"></i>{ t('Edit') }
+                <i className="icon-fw icon-doc"></i>{t('Edit')}
               </button>
             </div>
 
@@ -252,7 +268,7 @@ const PageCreateModal = (props) => {
       autoFocus={false}
     >
       <ModalHeader tag="h4" toggle={navigationContainer.closePageCreateModal} className="bg-primary text-light">
-        { t('New Page') }
+        {t('New Page')}
       </ModalHeader>
       <ModalBody>
         {renderCreateTodayForm()}

+ 7 - 5
src/client/js/components/PageEditor/LinkEditModal.jsx

@@ -152,8 +152,8 @@ class LinkEditModal extends React.PureComponent {
     const { t } = this.props;
     const path = this.state.linkInputValue;
     let markdown = '';
-    let previewError = '';
     let permalink = '';
+    let previewError = '';
 
     if (path.startsWith('/')) {
       const pathWithoutFragment = new URL(path, 'http://dummy').pathname;
@@ -163,8 +163,7 @@ class LinkEditModal extends React.PureComponent {
       try {
         const { page } = await this.props.appContainer.apiGet('/pages.get', { path: pathWithoutFragment, page_id: pageId });
         markdown = page.revision.body;
-        // create permanent link only if path isn't permanent link because checkbox for isUsePermanentLink is disabled when permalink is ''.
-        permalink = !isPermanentLink ? `${window.location.origin}/${page.id}` : '';
+        permalink = page.id;
       }
       catch (err) {
         previewError = err.message;
@@ -217,7 +216,8 @@ class LinkEditModal extends React.PureComponent {
   handleChangeTypeahead(selected) {
     const page = selected[0];
     if (page != null) {
-      this.setState({ linkInputValue: page.path });
+      const permalink = `${window.location.origin}/${page.id}`;
+      this.setState({ linkInputValue: page.path, permalink });
     }
   }
 
@@ -230,7 +230,9 @@ class LinkEditModal extends React.PureComponent {
     if (!this.state.linkInputValue.startsWith('/') || this.state.linkerType === Linker.types.growiLink) {
       isUseRelativePath = false;
     }
-    this.setState({ linkInputValue: link, isUseRelativePath, isUsePermanentLink: false });
+    this.setState({
+      linkInputValue: link, isUseRelativePath, isUsePermanentLink: false, permalink: '',
+    });
   }
 
   handleSelecteLinkerType(linkerType) {

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

@@ -41,7 +41,7 @@ const PagePathAutoComplete = (props) => {
       onChange={inputChangeHandler}
       onInputChange={props.onInputChange}
       inputName="new_path"
-      emptyLabelExceptError={null}
+      behaviorOfResetBtn="clear"
       placeholder="Input page path"
       keywordOnInit={getKeywordOnInit(initializedPath)}
       autoFocus={props.autoFocus}

+ 23 - 1
src/lib/util/path-utils.js

@@ -38,7 +38,7 @@ const isUserPage = (path) => {
 };
 
 const forbiddenPages = [
-  /\^|\$|\*|\+|#|%/,
+  /\^|\$|\*|\+|#|%|\?/,
   /^\/-\/.*/,
   /^\/_r\/.*/,
   /^\/_apix?(\/.*)?/,
@@ -111,6 +111,27 @@ function encodeSpaces(path) {
   return path.replace(/ /g, '%20').replace(/\u3000/g, '%E3%80%80');
 }
 
+/**
+ * Generate editor path
+ * @param {string} paths
+ * @returns {string}
+ */
+function generateEditorPath(...paths) {
+  const joinedPath = [...paths].join('/');
+
+  if (!isCreatablePage(joinedPath)) {
+    throw new Error('Invalid characters on path');
+  }
+
+  try {
+    const url = new URL(joinedPath, 'https://dummy');
+    return `${url.pathname}#edit`;
+  }
+  catch (err) {
+    throw new Error('Invalid path format');
+  }
+}
+
 module.exports = {
   isTopPage,
   isTrashPage,
@@ -119,4 +140,5 @@ module.exports = {
   userPageRoot,
   convertToNewAffiliationPath,
   encodeSpaces,
+  generateEditorPath,
 };

+ 0 - 7
src/server/models/user.js

@@ -335,13 +335,6 @@ module.exports = function(crowi) {
     return this.save();
   };
 
-  userSchema.methods.updateGoogleId = function(googleId, callback) {
-    this.googleId = googleId;
-    this.save((err, userData) => {
-      return callback(err, userData);
-    });
-  };
-
   userSchema.statics.getUserStatusLabels = function() {
     const userStatus = {};
     userStatus[STATUS_REGISTERED] = 'Approval Pending';

+ 34 - 32
yarn.lock

@@ -1123,6 +1123,13 @@
   dependencies:
     regenerator-runtime "^0.12.0"
 
+"@babel/runtime@^7.12.0":
+  version "7.14.6"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
+  integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/runtime@^7.12.5":
   version "7.13.17"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.17.tgz#8966d1fc9593bf848602f0662d6b4d0069e3a7ec"
@@ -7772,7 +7779,7 @@ etag@1.8.1, etag@^1.8.1, etag@~1.8.1:
 
 event-stream@^3.3.2, event-stream@~3.3.0:
   version "3.3.4"
-  resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
+  resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
   dependencies:
     duplexer "~0.1.1"
     from "~0"
@@ -8060,7 +8067,7 @@ express@^4.16.1:
 
 express@^4.16.3:
   version "4.16.3"
-  resolved "http://registry.npmjs.org/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
+  resolved "https://registry.npmjs.org/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
   dependencies:
     accepts "~1.3.5"
     array-flatten "1.1.1"
@@ -8356,7 +8363,7 @@ finalhandler@1.1.0:
 
 finalhandler@1.1.1:
   version "1.1.1"
-  resolved "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
+  resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
   dependencies:
     debug "2.6.9"
     encodeurl "~1.0.2"
@@ -9744,11 +9751,11 @@ i18next-sprintf-postprocessor@^0.2.2:
   resolved "https://registry.yarnpkg.com/i18next-sprintf-postprocessor/-/i18next-sprintf-postprocessor-0.2.2.tgz#2e409f1043579382698b6a2da70cdaa551d67ea4"
 
 i18next@^19.0.0:
-  version "19.0.0"
-  resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.0.0.tgz#5418207d7286128e6cfe558e659fa8c60d89794b"
-  integrity sha512-xxNKNOqLdGP/M+/fzzBOhcc9hCAqv6gDhHq0xbYz/Vlz5PlMfr9P1LbBvmk7RkZjYoh/kyM1tnfSl+sJ2VaD0Q==
+  version "19.9.2"
+  resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.9.2.tgz#ea5a124416e3c5ab85fddca2c8e3c3669a8da397"
+  integrity sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg==
   dependencies:
-    "@babel/runtime" "^7.3.1"
+    "@babel/runtime" "^7.12.0"
 
 iconv-lite@0.4.19, iconv-lite@~0.4.13:
   version "0.4.19"
@@ -13210,8 +13217,9 @@ nodemailer-ses-transport@~1.5.0:
     aws-sdk "^2.2.36"
 
 nodemailer@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.0.0.tgz#d9761128771739dc87c1fdd747f569b7f135cb02"
+  version "6.6.2"
+  resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.2.tgz#e184c9ed5bee245a3e0bcabc7255866385757114"
+  integrity sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q==
 
 noop6@^1.0.1:
   version "1.0.8"
@@ -14465,7 +14473,7 @@ pify@^5.0.0:
 
 pikaday@1.5.1:
   version "1.5.1"
-  resolved "http://registry.npmjs.org/pikaday/-/pikaday-1.5.1.tgz#0a48549bc1a14ea1d08c44074d761bc2f2bfcfd3"
+  resolved "https://registry.npmjs.org/pikaday/-/pikaday-1.5.1.tgz#0a48549bc1a14ea1d08c44074d761bc2f2bfcfd3"
   optionalDependencies:
     moment "2.x"
 
@@ -16675,7 +16683,7 @@ rxjs@^6.6.0:
   dependencies:
     tslib "^1.9.0"
 
-safe-buffer@5.1.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1:
+safe-buffer@5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
 
@@ -16684,7 +16692,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
-safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2:
+safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -19424,9 +19432,9 @@ validator@^11.0.0:
   integrity sha512-qiQ5ktdO7CD6C/5/mYV4jku/7qnqzjrxb3C/Q5wR3vGGinHTgJZN/TdFT3ZX4vXhX2R1PXx42fB1cn5W+uJ4lg==
 
 validator@^12.0.0:
-  version "12.0.0"
-  resolved "https://registry.yarnpkg.com/validator/-/validator-12.0.0.tgz#fb33221f5320abe2422cda2f517dc3838064e813"
-  integrity sha512-r5zA1cQBEOgYlesRmSEwc9LkbfNLTtji+vWyaHzRZUxCTHdsX3bd+sdHfs5tGZ2W6ILGGsxWxCNwT/h3IY/3ng==
+  version "12.2.0"
+  resolved "https://registry.yarnpkg.com/validator/-/validator-12.2.0.tgz#660d47e96267033fd070096c3b1a6f2db4380a0a"
+  integrity sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ==
 
 validator@^2.1.0:
   version "2.1.0"
@@ -19907,21 +19915,21 @@ write@1.0.3:
   dependencies:
     mkdirp "^0.5.1"
 
-ws@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-6.0.0.tgz#eaa494aded00ac4289d455bac8d84c7c651cef35"
+ws@^6.0.0, ws@~6.1.0:
+  version "6.1.4"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
   dependencies:
     async-limiter "~1.0.0"
 
-ws@^7.0.0:
-  version "7.2.1"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e"
-  integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==
+ws@^7.0.0, ws@^7.1.2:
+  version "7.4.6"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
+  integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
 
-ws@^7.1.2, ws@^7.3.1:
-  version "7.3.1"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
-  integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
+ws@^7.3.1:
+  version "7.5.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"
+  integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==
 
 ws@~3.3.1:
   version "3.3.3"
@@ -19931,12 +19939,6 @@ ws@~3.3.1:
     safe-buffer "~5.1.0"
     ultron "~1.1.0"
 
-ws@~6.1.0:
-  version "6.1.4"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
-  dependencies:
-    async-limiter "~1.0.0"
-
 x-is-string@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"