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

Merge branch 'master' into feat/89743-rename-from-pagetree-input

Shun Miyazawa 4 лет назад
Родитель
Сommit
eeae598b85

+ 5 - 5
packages/app/src/client/services/AdminHomeContainer.js

@@ -25,12 +25,12 @@ export default class AdminHomeContainer extends Container {
     this.timer = null;
 
     this.state = {
-      growiVersion: '',
-      nodeVersion: '',
-      npmVersion: '',
-      yarnVersion: '',
+      growiVersion: null,
+      nodeVersion: null,
+      npmVersion: null,
+      yarnVersion: null,
       copyState: this.copyStateValues.DEFAULT,
-      installedPlugins: [],
+      installedPlugins: null,
       isV5Compatible: null,
     };
 

+ 9 - 3
packages/app/src/components/Admin/AdminHome/InstalledPluginTable.jsx

@@ -11,8 +11,14 @@ class InstalledPluginTable extends React.Component {
   render() {
     const { t, adminHomeContainer } = this.props;
 
+    const { installedPlugins } = adminHomeContainer.state;
+
+    if (installedPlugins == null) {
+      return <></>;
+    }
+
     return (
-      <table className="table table-bordered">
+      <table data-testid="admin-installed-plugin-table" className="table table-bordered">
         <thead>
           <tr>
             <th className="text-center">{t('admin:admin_top.package_name')}</th>
@@ -25,8 +31,8 @@ class InstalledPluginTable extends React.Component {
             return (
               <tr key={plugin.name}>
                 <td>{plugin.name}</td>
-                <td className="text-center">{plugin.requiredVersion}</td>
-                <td className="text-center">{plugin.installedVersion}</td>
+                <td data-hide-in-vrt className="text-center">{plugin.requiredVersion}</td>
+                <td data-hide-in-vrt className="text-center">{plugin.installedVersion}</td>
               </tr>
             );
           })}

+ 13 - 5
packages/app/src/components/Admin/AdminHome/SystemInfomationTable.jsx

@@ -11,24 +11,32 @@ class SystemInformationTable extends React.Component {
   render() {
     const { adminHomeContainer } = this.props;
 
+    const {
+      growiVersion, nodeVersion, npmVersion, yarnVersion,
+    } = adminHomeContainer.state;
+
+    if (growiVersion == null || nodeVersion == null || npmVersion == null || yarnVersion == null) {
+      return <></>;
+    }
+
     return (
-      <table className="table table-bordered">
+      <table data-testid="admin-system-information-table" className="table table-bordered">
         <tbody>
           <tr>
             <th>GROWI</th>
-            <td>{ adminHomeContainer.state.growiVersion }</td>
+            <td data-hide-in-vrt>{ growiVersion }</td>
           </tr>
           <tr>
             <th>node.js</th>
-            <td>{ adminHomeContainer.state.nodeVersion }</td>
+            <td>{ nodeVersion }</td>
           </tr>
           <tr>
             <th>npm</th>
-            <td>{ adminHomeContainer.state.npmVersion }</td>
+            <td>{ npmVersion }</td>
           </tr>
           <tr>
             <th>yarn</th>
-            <td>{ adminHomeContainer.state.yarnVersion }</td>
+            <td>{ yarnVersion }</td>
           </tr>
         </tbody>
       </table>

+ 1 - 0
packages/app/src/components/Page/RevisionRenderer.jsx

@@ -192,6 +192,7 @@ const RevisionRenderer = (props) => {
 RevisionRenderer.propTypes = {
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
   markdown: PropTypes.string.isRequired,
+  isRenderable: PropTypes.bool,
   highlightKeywords: PropTypes.arrayOf(PropTypes.string),
   additionalClassName: PropTypes.string,
 };

+ 25 - 14
packages/app/src/components/Sidebar/CustomSidebar.tsx

@@ -31,9 +31,9 @@ const CustomSidebar: FC<Props> = (props: Props) => {
 
   const renderer = appContainer.getRenderer('sidebar');
 
-  const { data: page, mutate } = useSWRxPageByPath('/Sidebar');
+  const { data: page, error, mutate } = useSWRxPageByPath('/Sidebar');
 
-  const isLoading = page === undefined;
+  const isLoading = page === undefined && error == null;
   const markdown = (page?.revision as IRevision | undefined)?.body;
 
   return (
@@ -47,20 +47,31 @@ const CustomSidebar: FC<Props> = (props: Props) => {
           <i className="icon icon-reload"></i>
         </button>
       </div>
-      { !isLoading && markdown == null && <SidebarNotFound /> }
-      {/* eslint-disable-next-line react/no-danger */}
-      { markdown != null && (
-        <div className="p-3">
-          <RevisionRenderer
-            growiRenderer={renderer}
-            markdown={markdown}
-            additionalClassName="grw-custom-sidebar-content"
-          />
-        </div>
-      ) }
+
+      {
+        isLoading && (
+          <div className="text-muted text-center">
+            <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
+          </div>
+        )
+      }
+
+      {
+        !isLoading && markdown != null ? (
+          <div className="p-3">
+            <RevisionRenderer
+              growiRenderer={renderer}
+              markdown={markdown}
+              additionalClassName="grw-custom-sidebar-content"
+              isRenderable
+            />
+          </div>
+        ) : (
+          <SidebarNotFound />
+        )
+      }
     </>
   );
-
 };
 
 /**

+ 3 - 1
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -154,13 +154,15 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
       return;
     }
 
-    // socket
     socket.on(SocketEventName.UpdateDescCount, (data: UpdateDescCountRawData) => {
       // save to global state
       const newData: UpdateDescCountData = new Map(Object.entries(data));
 
       updatePtDescCountMap(newData);
     });
+
+    return () => { socket.off(SocketEventName.UpdateDescCount) };
+
   }, [socket, ptDescCountMap, updatePtDescCountMap]);
 
   const onRenamed = () => {

+ 6 - 1
packages/app/src/server/routes/apiv3/personal-setting.js

@@ -76,7 +76,12 @@ module.exports = (crowi) => {
   const validator = {
     personal: [
       body('name').isString().not().isEmpty(),
-      body('email').isEmail(),
+      body('email')
+        .isEmail()
+        .custom((email) => {
+          if (!User.isEmailValid(email)) throw new Error('email is not included in whitelist');
+          return true;
+        }),
       body('lang').isString().isIn(listLocaleIds()),
       body('isEmailPublished').isBoolean(),
     ],

+ 1 - 1
packages/app/src/server/routes/index.js

@@ -203,7 +203,7 @@ module.exports = function(crowi, app) {
     .use(forgotPassword.handleErrosMiddleware));
 
   app.use('/_private-legacy-pages', express.Router()
-    .get('/', privateLegacyPages.renderPrivateLegacyPages));
+    .get('/', injectUserUISettings, privateLegacyPages.renderPrivateLegacyPages));
   app.use('/user-activation', express.Router()
     .get('/:token', apiLimiter, applicationInstalled, injectUserRegistrationOrderByTokenMiddleware, userActivation.form)
     .use(userActivation.tokenErrorHandlerMiddeware));

+ 2 - 0
packages/app/test/cypress/integration/2-basic-features/access-to-admin-page.spec.ts

@@ -37,6 +37,8 @@ context('Access to Admin page', () => {
   it('/admin is successfully loaded', () => {
     cy.visit('/admin');
     cy.getByTestid('admin-home').should('be.visible');
+    cy.getByTestid('admin-system-information-table').should('be.visible');
+    cy.getByTestid('admin-installed-plugin-table').should('be.visible');
     cy.screenshot(`${ssPrefix}-admin`);
   });
 

+ 0 - 2
packages/app/test/cypress/integration/2-basic-features/access-to-page.spec.ts

@@ -29,8 +29,6 @@ context('Access to page', () => {
 
   it('/Sandbox with anchor hash is successfully loaded', () => {
     cy.visit('/Sandbox#Headers');
-    cy.getByTestid('grw-fab-create-page').should('have.class', 'fadeInUp').should('be.visible');
-    cy.getByTestid('grw-fab-return-to-top').should('have.class', 'fadeInUp').should('be.visible');
     cy.screenshot(`${ssPrefix}-sandbox-headers`, {
       disableTimersAndAnimations: false,
     });

+ 10 - 10
packages/app/test/cypress/integration/2-basic-features/open-presentation-modal.spec.ts

@@ -1,5 +1,5 @@
-
 context('Open presentation modal', () => {
+
   const ssPrefix = 'access-to-presentation-modal-';
 
   let connectSid: string | undefined;
@@ -20,12 +20,12 @@ context('Open presentation modal', () => {
     }
   });
 
-  it('PageCreateModal for "/" is shown successfully', () => {
+  it('PresentationModal for "/" is shown successfully', () => {
     cy.visit('/');
 
     cy.get('#grw-subnav-container').within(() => {
-      cy.getByTestid('open-page-item-control-btn').click({force: true})
-      cy.getByTestid('open-presentation-modal-btn').click({force: true})
+      cy.getByTestid('open-page-item-control-btn').click({force: true});
+      cy.getByTestid('open-presentation-modal-btn').click({force: true});
     });
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
@@ -33,12 +33,12 @@ context('Open presentation modal', () => {
     cy.screenshot(`${ssPrefix}-opne-top`);
   });
 
-  it('PageCreateModal for "/Sandbox/Bootstrap4" is shown successfully', () => {
+  it('PresentationModal for "/Sandbox/Bootstrap4" is shown successfully', () => {
     cy.visit('/Sandbox/Bootstrap4');
 
     cy.get('#grw-subnav-container').within(() => {
-      cy.getByTestid('open-page-item-control-btn').click({force: true})
-      cy.getByTestid('open-presentation-modal-btn').click({force: true})
+      cy.getByTestid('open-page-item-control-btn').click({force: true});
+      cy.getByTestid('open-presentation-modal-btn').click({force: true});
     });
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting
@@ -46,12 +46,12 @@ context('Open presentation modal', () => {
     cy.screenshot(`${ssPrefix}-open-bootstrap4`);
   });
 
-  it('PageCreateModal for /Sandbox/Bootstrap4#Cards" is shown successfully', () => {
+  it('PresentationModal for /Sandbox/Bootstrap4#Cards" is shown successfully', () => {
     cy.visit('/Sandbox/Bootstrap4#Cards');
 
     cy.get('#grw-subnav-container').within(() => {
-      cy.getByTestid('open-page-item-control-btn').click({force: true})
-      cy.getByTestid('open-presentation-modal-btn').click({force: true})
+      cy.getByTestid('open-page-item-control-btn').click({force: true});
+      cy.getByTestid('open-presentation-modal-btn').click({force: true});
     });
 
     // eslint-disable-next-line cypress/no-unnecessary-waiting