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

Merge branch 'master' into dependabot/npm_and_yarn/striptags-3.2.0

itizawa 4 лет назад
Родитель
Сommit
3aad117794

+ 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 - 4
package.json

@@ -116,7 +116,7 @@
     "graceful-fs": "^4.1.11",
     "growi-commons": "^5.0.3",
     "helmet": "^3.13.0",
-    "i18next": "^19.0.0",
+    "i18next": "^20.3.2",
     "i18next-express-middleware": "^1.4.1",
     "i18next-node-fs-backend": "^2.1.0",
     "i18next-sprintf-postprocessor": "^0.2.2",
@@ -133,7 +133,7 @@
     "mongoose-unique-validator": "^2.0.3",
     "multer": "~1.4.0",
     "multer-autoreap": "^1.0.3",
-    "nodemailer": "^6.0.0",
+    "nodemailer": "^6.6.2",
     "nodemailer-ses-transport": "~1.5.0",
     "npm-run-all": "^4.1.2",
     "openid-client": "=2.5.0",
@@ -160,8 +160,8 @@
     "universal-bunyan": "^0.9.2",
     "unzipper": "^0.10.5",
     "url-join": "^4.0.0",
-    "validator": "^12.0.0",
-    "ws": "^7.3.1",
+    "validator": "^13.6.0",
+    "ws": "^7.4.6",
     "xss": "^1.0.6"
   },
   "devDependencies": {

+ 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()}

+ 6 - 2
src/client/js/components/PageEditor/LinkEditModal.jsx

@@ -152,6 +152,7 @@ class LinkEditModal extends React.PureComponent {
     const { t } = this.props;
     const path = this.state.linkInputValue;
     let markdown = '';
+    let permalink = '';
     let previewError = '';
 
     if (path.startsWith('/')) {
@@ -162,6 +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;
+        permalink = page.id;
       }
       catch (err) {
         previewError = err.message;
@@ -170,7 +172,7 @@ class LinkEditModal extends React.PureComponent {
     else {
       previewError = t('link_edit.page_not_found_in_preview', { path });
     }
-    this.setState({ markdown, previewError });
+    this.setState({ markdown, previewError, permalink });
   }
 
   getLinkForPreview() {
@@ -228,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';

+ 29 - 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.16"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.16.tgz#673e7b84c1f6287d96d483fa65cd3db792d53e22"
@@ -7503,12 +7510,12 @@ i18next-sprintf-postprocessor@^0.2.2:
   version "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==
+i18next@^20.3.2:
+  version "20.3.2"
+  resolved "https://registry.yarnpkg.com/i18next/-/i18next-20.3.2.tgz#5195e76b9e0848a1c198001bf6c7fc72995a55f1"
+  integrity sha512-e8CML2R9Ng2sSQOM80wb/PrM2j8mDm84o/T4Amzn9ArVyNX5/ENWxxAXkRpZdTQNDaxKImF93Wep4mAoozFrKw==
   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"
@@ -10397,9 +10404,10 @@ nodemailer-ses-transport@~1.5.0:
   dependencies:
     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"
+nodemailer@^6.6.2:
+  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"
@@ -13335,7 +13343,7 @@ rxjs@^6.4.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"
 
@@ -13344,7 +13352,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==
@@ -15573,10 +15581,10 @@ validator@^11.0.0:
   resolved "https://registry.yarnpkg.com/validator/-/validator-11.1.0.tgz#ac18cac42e0aa5902b603d7a5d9b7827e2346ac4"
   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==
+validator@^13.6.0:
+  version "13.6.0"
+  resolved "https://registry.yarnpkg.com/validator/-/validator-13.6.0.tgz#1e71899c14cdc7b2068463cb24c1cc16f6ec7059"
+  integrity sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg==
 
 validator@^2.1.0:
   version "2.1.0"
@@ -15968,21 +15976,16 @@ 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.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.0.0, ws@^7.1.2, ws@^7.4.6:
+  version "7.4.6"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
+  integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
 
 ws@~3.3.1:
   version "3.3.3"
@@ -15992,12 +15995,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"