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

Merge branch 'dev/4.2.x' into imprv/codemirror

Yuki Takei 4 лет назад
Родитель
Сommit
856bdcda04

+ 1 - 0
CHANGES.md

@@ -13,6 +13,7 @@
     * validator
     * ws
     * nodemailer
+    * i18next-express-middleware
 
 ## v4.2.20
 

+ 1 - 1
package.json

@@ -117,7 +117,7 @@
     "growi-commons": "^5.0.3",
     "helmet": "^3.13.0",
     "i18next": "^20.3.2",
-    "i18next-express-middleware": "^1.4.1",
+    "i18next-express-middleware": "^2.0.0",
     "i18next-node-fs-backend": "^2.1.0",
     "i18next-sprintf-postprocessor": "^0.2.2",
     "is-iso-date": "^0.0.1",

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

+ 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';

+ 8 - 5
yarn.lock

@@ -4419,6 +4419,7 @@ cookiejar@2.0.6:
 cookies@0.7.1:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b"
+  integrity sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=
   dependencies:
     depd "~1.1.1"
     keygrip "~1.0.2"
@@ -7493,9 +7494,10 @@ i18next-browser-languagedetector@^4.0.1:
   dependencies:
     "@babel/runtime" "^7.5.5"
 
-i18next-express-middleware@^1.4.1:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/i18next-express-middleware/-/i18next-express-middleware-1.4.1.tgz#273c4a490ad688ce246815ce1288689c63fa7de1"
+i18next-express-middleware@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/i18next-express-middleware/-/i18next-express-middleware-2.0.0.tgz#e6ab3be8d2db3c715dc084880d100d235b6fd62e"
+  integrity sha512-TGlSkYsQHikggv4mIp5B+CiXsZzwbpHaZgmOkRNGStLOdKHABH5cHr136g2PC1+p2VPMf3y3UoQZ1TfPfVOrgg==
   dependencies:
     cookies "0.7.1"
 
@@ -8877,8 +8879,9 @@ kareem@2.3.2:
   integrity sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ==
 
 keygrip@~1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc"
+  integrity sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==
 
 keyv@3.0.0:
   version "3.0.0"