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

Merge pull request #2934 from weseek/fix/3545-4158-prevent-opening-sharelink-for-guest-user

Fix/3545 4158 prevent opening sharelink for guest user
Yuki Takei 5 лет назад
Родитель
Сommit
45ea913ea6

+ 1 - 1
src/client/js/app.jsx

@@ -95,7 +95,7 @@ if (pageContainer.state.pageId != null) {
     'page-comments-list': <PageComments />,
     'page-comment-write': <CommentEditorLazyRenderer />,
     'page-management': <PageManagement />,
-    'revision-toc': <TableOfContents />,
+    'revision-toc': <TableOfContents isGuestUserMode={appContainer.currentUser == null} />,
     'seen-user-list': <SeenUserList />,
     'liker-list': <LikerList />,
 

+ 27 - 12
src/client/js/components/PageAccessoriesModal.jsx

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
 import PropTypes from 'prop-types';
 
 import {
-  Modal, ModalBody, ModalHeader, Nav, NavItem, NavLink, TabContent, TabPane,
+  Modal, ModalBody, ModalHeader, Nav, NavItem, NavLink, TabContent, TabPane, UncontrolledTooltip,
 } from 'reactstrap';
 
 import { withTranslation } from 'react-i18next';
@@ -27,31 +27,36 @@ const navTabMapping = {
     icon: <PageListIcon />,
     i18n: 'page_list',
     index: 0,
+    isGuestNotAllowed: false,
   },
   timeline:  {
     icon: <TimeLineIcon />,
     i18n: 'Timeline View',
     index: 1,
+    isGuestNotAllowed: false,
   },
   pageHistory: {
     icon: <RecentChangesIcon />,
     i18n: 'History',
     index: 2,
+    isGuestNotAllowed: false,
   },
   attachment: {
     icon: <AttachmentIcon />,
     i18n: 'attachment_data',
     index: 3,
+    isGuestNotAllowed: false,
   },
   shareLink: {
     icon: <ShareLinkIcon />,
     i18n: 'share_links.share_link_management',
     index: 4,
+    isGuestNotAllowed: true,
   },
 };
 
 const PageAccessoriesModal = (props) => {
-  const { t, pageAccessoriesContainer } = props;
+  const { t, pageAccessoriesContainer, isGuestUserMode } = props;
   const { switchActiveTab } = pageAccessoriesContainer;
   const { activeTab } = pageAccessoriesContainer.state;
 
@@ -102,17 +107,24 @@ const PageAccessoriesModal = (props) => {
   return (
     <React.Fragment>
       <Modal size="xl" isOpen={props.isOpen} toggle={closeModalHandler} className="grw-page-accessories-modal">
-        {/* [TODO: insert a modal header and move nav tabs there  by gw-3890] */}
         <ModalHeader className="p-0" toggle={closeModalHandler}>
           <Nav className="nav-title" id="nav-title">
             {Object.entries(navTabMapping).map(([key, value]) => {
+              const isDisabledNavLink = (isGuestUserMode && value.isGuestNotAllowed);
               return (
-                <NavItem key={key} type="button" className={`p-0 nav-link ${activeTab === key && 'active'}`}>
-                  <NavLink onClick={() => { switchActiveTab(key) }}>
-                    {value.icon}
-                    {t(value.i18n)}
-                  </NavLink>
-                </NavItem>
+                <React.Fragment key={key}>
+                  <NavItem id={key} type="button" className={`p-0 nav-link ${activeTab === key && 'active'}`}>
+                    <NavLink disabled={isDisabledNavLink} onClick={() => { switchActiveTab(key) }}>
+                      {value.icon}
+                      {t(value.i18n)}
+                    </NavLink>
+                  </NavItem>
+                  {(isDisabledNavLink) && (
+                    <UncontrolledTooltip placement="bottom" target={key} fade={false}>
+                      {t('Not available for guest')}
+                    </UncontrolledTooltip>
+                  )}
+                </React.Fragment>
               );
             })}
           </Nav>
@@ -134,9 +146,11 @@ const PageAccessoriesModal = (props) => {
             <TabPane tabId="attachment">
               {pageAccessoriesContainer.state.activeComponents.has('attachment') && <PageAttachment />}
             </TabPane>
-            <TabPane tabId="shareLink">
-              {pageAccessoriesContainer.state.activeComponents.has('shareLink') && <ShareLink />}
-            </TabPane>
+            {!isGuestUserMode && (
+              <TabPane tabId="shareLink">
+                {pageAccessoriesContainer.state.activeComponents.has('shareLink') && <ShareLink />}
+              </TabPane>
+            )}
           </TabContent>
         </ModalBody>
       </Modal>
@@ -153,6 +167,7 @@ PageAccessoriesModal.propTypes = {
   t: PropTypes.func.isRequired, //  i18next
   // pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   pageAccessoriesContainer: PropTypes.instanceOf(PageAccessoriesContainer).isRequired,
+  isGuestUserMode: PropTypes.bool.isRequired,
   isOpen: PropTypes.bool.isRequired,
   onClose: PropTypes.func,
 };

+ 4 - 2
src/client/js/components/TableOfContents.jsx

@@ -20,7 +20,7 @@ const logger = loggerFactory('growi:TableOfContents');
  */
 const TableOfContents = (props) => {
 
-  const { pageContainer, navigationContainer } = props;
+  const { pageContainer, navigationContainer, isGuestUserMode } = props;
 
   const calcViewHeight = useCallback(() => {
     // calculate absolute top of '#revision-toc' element
@@ -42,7 +42,7 @@ const TableOfContents = (props) => {
 
   return (
     <>
-      <TopOfTableContents />
+      <TopOfTableContents isGuestUserMode={isGuestUserMode} />
       <StickyStretchableScroller
         contentsElemSelector=".revision-toc .markdownIt-TOC"
         stickyElemSelector="#revision-toc"
@@ -70,6 +70,8 @@ const TableOfContentsWrapper = withUnstatedContainers(TableOfContents, [PageCont
 TableOfContents.propTypes = {
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
+
+  isGuestUserMode: PropTypes.bool.isRequired,
 };
 
 export default withTranslation()(TableOfContentsWrapper);

+ 25 - 15
src/client/js/components/TopOfTableContents.jsx

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 
 import { withTranslation } from 'react-i18next';
 
+import { UncontrolledTooltip } from 'reactstrap';
 import PageAccessoriesContainer from '../services/PageAccessoriesContainer';
 
 import PageListIcon from './Icons/PageListIcon';
@@ -16,16 +17,15 @@ import PageAccessoriesModal from './PageAccessoriesModal';
 import { withUnstatedContainers } from './UnstatedUtils';
 
 const TopOfTableContents = (props) => {
-  const { pageAccessoriesContainer } = props;
+  const { t, pageAccessoriesContainer, isGuestUserMode } = props;
 
   function renderModal() {
     return (
-      <>
-        <PageAccessoriesModal
-          isOpen={pageAccessoriesContainer.state.isPageAccessoriesModalShown}
-          onClose={pageAccessoriesContainer.closePageAccessoriesModal}
-        />
-      </>
+      <PageAccessoriesModal
+        isGuestUserMode={isGuestUserMode}
+        isOpen={pageAccessoriesContainer.state.isPageAccessoriesModalShown}
+        onClose={pageAccessoriesContainer.closePageAccessoriesModal}
+      />
     );
   }
 
@@ -64,14 +64,20 @@ const TopOfTableContents = (props) => {
           <AttachmentIcon />
         </button>
 
-        <button
-          type="button"
-          className="btn btn-link grw-btn-top-of-table"
-          onClick={() => pageAccessoriesContainer.openPageAccessoriesModal('shareLink')}
-        >
-          <ShareLinkIcon />
-        </button>
-
+        <div id="shareLink-btn-wrapper-for-tooltip">
+          <button
+            type="button"
+            className={`btn btn-link grw-btn-top-of-table ${isGuestUserMode && 'disabled'}`}
+            onClick={() => pageAccessoriesContainer.openPageAccessoriesModal('shareLink')}
+          >
+            <ShareLinkIcon />
+          </button>
+        </div>
+        {isGuestUserMode && (
+          <UncontrolledTooltip placement="top" target="shareLink-btn-wrapper-for-tooltip" fade={false}>
+            {t('Not available for guest')}
+          </UncontrolledTooltip>
+        )}
         <div
           id="seen-user-list"
           data-user-ids-str="{{ page.seenUsers|slice(-15)|default([])|reverse|join(',') }}"
@@ -90,7 +96,11 @@ const TopOfTableContents = (props) => {
 const TopOfTableContentsWrapper = withUnstatedContainers(TopOfTableContents, [PageAccessoriesContainer]);
 
 TopOfTableContents.propTypes = {
+  t: PropTypes.func.isRequired, //  i18next
+
   pageAccessoriesContainer: PropTypes.instanceOf(PageAccessoriesContainer).isRequired,
+
+  isGuestUserMode: PropTypes.bool.isRequired,
 };
 
 export default withTranslation()(TopOfTableContentsWrapper);