Ver código fonte

Merge pull request #2812 from weseek/master

release v4.1.6
Yuki Takei 5 anos atrás
pai
commit
5ff1aa314a

+ 37 - 20
.gitignore

@@ -1,23 +1,27 @@
-# Logs
-logs
-*.log
-npm-debug.log.*
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
 
 
-# OS generated files #
-.DS_Store
-.Trash-*
-ehthumbs.db
-Thumbs.db
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
 
 
-# Node Files #
-/node_modules/
-/bower_components/
-npm-debug.log
-/npm-debug.log.*
-package-lock.json
+# next.js
+/.next/
+/out/
 
 
-# Dist #
+# production
+/build
+
+# dist
+/dist/
 /report/
 /report/
+/public/uploads
+/tmp/
+
+# dist (for GROWI v4.x and below)
 /public/*.chunk.js
 /public/*.chunk.js
 /public/*.chunk.js.LICENSE
 /public/*.chunk.js.LICENSE
 /public/*.bundle.js
 /public/*.bundle.js
@@ -25,17 +29,30 @@ package-lock.json
 /public/dll
 /public/dll
 /public/js
 /public/js
 /public/styles
 /public/styles
-/public/uploads
 /src/*/__build__/
 /src/*/__build__/
 /__build__/**
 /__build__/**
 /src/*/dist/
 /src/*/dist/
 /.awcache
 /.awcache
 .webpack.json
 .webpack.json
 /compiled/
 /compiled/
-/tmp/
 
 
-# Doc #
-/doc/
+# misc
+.DS_Store
+*.pem
+.Trash-*
+ehthumbs.db
+Thumbs.db
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
 
 
 # IDE, dev #
 # IDE, dev #
 .idea
 .idea

+ 4 - 1
CHANGES.md

@@ -2,7 +2,10 @@
 
 
 ## v4.1.6-RC
 ## v4.1.6-RC
 
 
-* 
+* Improvement: Hide Fab at admin pages
+* Fix: Presentation does not work
+* Fix: Update GrantSelector status when uploading a file to a new page
+* Fix: CopyDropdown origin refs draw.io host wrongly
 
 
 ## v4.1.5
 ## v4.1.5
 
 

+ 1 - 0
bin/github-actions/list-branches.js

@@ -17,6 +17,7 @@ const EXCLUDE_PATTERNS = [
   /^feat\/custom-sidebar-2$/,
   /^feat\/custom-sidebar-2$/,
   // https://regex101.com/r/Lnx7Pz/3
   // https://regex101.com/r/Lnx7Pz/3
   /^dev\/[\d.x]*$/,
   /^dev\/[\d.x]*$/,
+  /^release\/.+$/,
 ];
 ];
 const LEGAL_PATTERNS = [
 const LEGAL_PATTERNS = [
   /^master$/,
   /^master$/,

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "growi",
   "name": "growi",
-  "version": "4.1.5-RC",
+  "version": "4.1.6-RC",
   "description": "Team collaboration software using markdown",
   "description": "Team collaboration software using markdown",
   "tags": [
   "tags": [
     "wiki",
     "wiki",

+ 3 - 0
src/client/js/app.jsx

@@ -28,6 +28,7 @@ import MyDraftList from './components/MyDraftList/MyDraftList';
 import SeenUserList from './components/User/SeenUserList';
 import SeenUserList from './components/User/SeenUserList';
 import LikerList from './components/User/LikerList';
 import LikerList from './components/User/LikerList';
 import TableOfContents from './components/TableOfContents';
 import TableOfContents from './components/TableOfContents';
+import Fab from './components/Fab';
 
 
 import PersonalSettings from './components/Me/PersonalSettings';
 import PersonalSettings from './components/Me/PersonalSettings';
 import NavigationContainer from './services/NavigationContainer';
 import NavigationContainer from './services/NavigationContainer';
@@ -81,6 +82,8 @@ Object.assign(componentMappings, {
   'page-timeline': <PageTimeline />,
   'page-timeline': <PageTimeline />,
 
 
   'personal-setting': <PersonalSettings crowi={personalContainer} />,
   'personal-setting': <PersonalSettings crowi={personalContainer} />,
+
+  'grw-fab-container': <Fab />,
 });
 });
 
 
 // additional definitions if data exists
 // additional definitions if data exists

+ 0 - 2
src/client/js/base.jsx

@@ -8,7 +8,6 @@ import GrowiNavbarBottom from './components/Navbar/GrowiNavbarBottom';
 import Sidebar from './components/Sidebar';
 import Sidebar from './components/Sidebar';
 import ShareLinkAlert from './components/Page/ShareLinkAlert';
 import ShareLinkAlert from './components/Page/ShareLinkAlert';
 import HotkeysManager from './components/Hotkeys/HotkeysManager';
 import HotkeysManager from './components/Hotkeys/HotkeysManager';
-import Fab from './components/Fab';
 
 
 import AppContainer from './services/AppContainer';
 import AppContainer from './services/AppContainer';
 import SocketIoContainer from './services/SocketIoContainer';
 import SocketIoContainer from './services/SocketIoContainer';
@@ -46,7 +45,6 @@ const componentMappings = {
 
 
   'grw-sidebar-wrapper': <Sidebar />,
   'grw-sidebar-wrapper': <Sidebar />,
 
 
-  'grw-fab-container': <Fab />,
   'grw-hotkeys-manager': <HotkeysManager />,
   'grw-hotkeys-manager': <HotkeysManager />,
 
 
   'share-link-alert': <ShareLinkAlert />,
   'share-link-alert': <ShareLinkAlert />,

+ 21 - 11
src/client/js/components/Fab.jsx

@@ -4,6 +4,7 @@ import loggerFactory from '@alias/logger';
 
 
 import StickyEvents from 'sticky-events';
 import StickyEvents from 'sticky-events';
 
 
+import AppContainer from '../services/AppContainer';
 import NavigationContainer from '../services/NavigationContainer';
 import NavigationContainer from '../services/NavigationContainer';
 import { withUnstatedContainers } from './UnstatedUtils';
 import { withUnstatedContainers } from './UnstatedUtils';
 import CreatePageIcon from './Icons/CreatePageIcon';
 import CreatePageIcon from './Icons/CreatePageIcon';
@@ -12,7 +13,8 @@ import ReturnTopIcon from './Icons/ReturnTopIcon';
 const logger = loggerFactory('growi:cli:Fab');
 const logger = loggerFactory('growi:cli:Fab');
 
 
 const Fab = (props) => {
 const Fab = (props) => {
-  const { navigationContainer } = props;
+  const { navigationContainer, appContainer } = props;
+  const { currentUser } = appContainer;
 
 
   const [animateClasses, setAnimateClasses] = useState('invisible');
   const [animateClasses, setAnimateClasses] = useState('invisible');
 
 
@@ -39,18 +41,25 @@ const Fab = (props) => {
     };
     };
   }, [stickyChangeHandler]);
   }, [stickyChangeHandler]);
 
 
+  function renderPageCreateButton() {
+    return (
+      <>
+        <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: '2.3rem', right: '4rem' }}>
+          <button
+            type="button"
+            className="btn btn-lg btn-create-page btn-primary rounded-circle p-0 waves-effect waves-light"
+            onClick={navigationContainer.openPageCreateModal}
+          >
+            <CreatePageIcon />
+          </button>
+        </div>
+      </>
+    );
+  }
 
 
   return (
   return (
     <div className="grw-fab d-none d-md-block">
     <div className="grw-fab d-none d-md-block">
-      <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: '2.3rem', right: '4rem' }}>
-        <button
-          type="button"
-          className="btn btn-lg btn-create-page btn-primary rounded-circle p-0 waves-effect waves-light"
-          onClick={navigationContainer.openPageCreateModal}
-        >
-          <CreatePageIcon />
-        </button>
-      </div>
+      {currentUser != null && renderPageCreateButton()}
       <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: 0, right: 0 }}>
       <div className={`rounded-circle position-absolute ${animateClasses}`} style={{ bottom: 0, right: 0 }}>
         <button type="button" className="btn btn-light btn-scroll-to-top rounded-circle p-0" onClick={() => navigationContainer.smoothScrollIntoView()}>
         <button type="button" className="btn btn-light btn-scroll-to-top rounded-circle p-0" onClick={() => navigationContainer.smoothScrollIntoView()}>
           <ReturnTopIcon />
           <ReturnTopIcon />
@@ -62,7 +71,8 @@ const Fab = (props) => {
 };
 };
 
 
 Fab.propTypes = {
 Fab.propTypes = {
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
   navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
 };
 };
 
 
-export default withUnstatedContainers(Fab, [NavigationContainer]);
+export default withUnstatedContainers(Fab, [AppContainer, NavigationContainer]);

+ 1 - 0
src/client/js/components/Page/CopyDropdown.jsx

@@ -75,6 +75,7 @@ class CopyDropdown extends React.Component {
   }
   }
 
 
   generatePermalink() {
   generatePermalink() {
+    const { origin } = window.location;
     const { pageId, isShareLinkMode } = this.props;
     const { pageId, isShareLinkMode } = this.props;
 
 
     if (pageId == null) {
     if (pageId == null) {

+ 2 - 1
src/client/js/components/PageEditor.jsx

@@ -131,7 +131,7 @@ class PageEditor extends React.Component {
    * @param {any} file
    * @param {any} file
    */
    */
   async onUpload(file) {
   async onUpload(file) {
-    const { appContainer, pageContainer } = this.props;
+    const { appContainer, pageContainer, editorContainer } = this.props;
 
 
     try {
     try {
       let res = await appContainer.apiGet('/attachments.limit', {
       let res = await appContainer.apiGet('/attachments.limit', {
@@ -167,6 +167,7 @@ class PageEditor extends React.Component {
       if (res.pageCreated) {
       if (res.pageCreated) {
         logger.info('Page is created', res.page._id);
         logger.info('Page is created', res.page._id);
         pageContainer.updateStateAfterSave(res.page);
         pageContainer.updateStateAfterSave(res.page);
+        editorContainer.setState({ grant: res.page.grant });
       }
       }
     }
     }
     catch (e) {
     catch (e) {

+ 4 - 26
src/client/js/components/SavePageControls/GrantSelector.jsx

@@ -47,24 +47,11 @@ class GrantSelector extends React.Component {
     this.state = {
     this.state = {
       userRelatedGroups: [],
       userRelatedGroups: [],
       isSelectGroupModalShown: false,
       isSelectGroupModalShown: false,
-      grant: this.props.grant,
-      grantGroup: null,
     };
     };
-    if (this.props.grantGroupId != null) {
-      this.state.grantGroup = {
-        _id: this.props.grantGroupId,
-        name: this.props.grantGroupName,
-      };
-    }
-
-    // retrieve xss library from window
-    this.xss = window.xss;
 
 
     this.showSelectGroupModal = this.showSelectGroupModal.bind(this);
     this.showSelectGroupModal = this.showSelectGroupModal.bind(this);
     this.hideSelectGroupModal = this.hideSelectGroupModal.bind(this);
     this.hideSelectGroupModal = this.hideSelectGroupModal.bind(this);
 
 
-    this.getGroupName = this.getGroupName.bind(this);
-
     this.changeGrantHandler = this.changeGrantHandler.bind(this);
     this.changeGrantHandler = this.changeGrantHandler.bind(this);
     this.groupListItemClickHandler = this.groupListItemClickHandler.bind(this);
     this.groupListItemClickHandler = this.groupListItemClickHandler.bind(this);
   }
   }
@@ -78,11 +65,6 @@ class GrantSelector extends React.Component {
     this.setState({ isSelectGroupModalShown: false });
     this.setState({ isSelectGroupModalShown: false });
   }
   }
 
 
-  getGroupName() {
-    const grantGroup = this.state.grantGroup;
-    return grantGroup ? this.xss.process(grantGroup.name) : '';
-  }
-
   /**
   /**
    * Retrieve user-group-relations data from backend
    * Retrieve user-group-relations data from backend
    */
    */
@@ -109,16 +91,12 @@ class GrantSelector extends React.Component {
       return;
       return;
     }
     }
 
 
-    this.setState({ grant, grantGroup: null });
-
     if (this.props.onUpdateGrant != null) {
     if (this.props.onUpdateGrant != null) {
       this.props.onUpdateGrant({ grant, grantGroupId: null, grantGroupName: null });
       this.props.onUpdateGrant({ grant, grantGroupId: null, grantGroupName: null });
     }
     }
   }
   }
 
 
   groupListItemClickHandler(grantGroup) {
   groupListItemClickHandler(grantGroup) {
-    this.setState({ grant: 5, grantGroup });
-
     if (this.props.onUpdateGrant != null) {
     if (this.props.onUpdateGrant != null) {
       this.props.onUpdateGrant({ grant: 5, grantGroupId: grantGroup._id, grantGroupName: grantGroup.name });
       this.props.onUpdateGrant({ grant: 5, grantGroupId: grantGroup._id, grantGroupName: grantGroup.name });
     }
     }
@@ -134,13 +112,13 @@ class GrantSelector extends React.Component {
    */
    */
   renderGrantSelector() {
   renderGrantSelector() {
     const { t } = this.props;
     const { t } = this.props;
-    const { grant: currentGrant, grantGroup } = this.state;
+    const { grant: currentGrant, grantGroupId } = this.props;
 
 
     let dropdownToggleBtnColor = null;
     let dropdownToggleBtnColor = null;
     let dropdownToggleLabelElm = null;
     let dropdownToggleLabelElm = null;
 
 
     const dropdownMenuElems = this.availableGrants.map((opt) => {
     const dropdownMenuElems = this.availableGrants.map((opt) => {
-      const label = (opt.grant === 5 && grantGroup != null)
+      const label = (opt.grant === 5 && grantGroupId != null)
         ? opt.reselectLabel // when grantGroup is selected
         ? opt.reselectLabel // when grantGroup is selected
         : opt.label;
         : opt.label;
 
 
@@ -161,11 +139,11 @@ class GrantSelector extends React.Component {
     });
     });
 
 
     // add specified group option
     // add specified group option
-    if (grantGroup != null) {
+    if (grantGroupId != null) {
       const labelElm = (
       const labelElm = (
         <span>
         <span>
           <i className="icon icon-fw icon-organization"></i>
           <i className="icon icon-fw icon-organization"></i>
-          <span className="label">{this.getGroupName()}</span>
+          <span className="label">{this.props.grantGroupName}</span>
         </span>
         </span>
       );
       );
 
 

+ 11 - 2
src/server/routes/page.js

@@ -229,6 +229,11 @@ module.exports = function(crowi, app) {
     renderVars.hasDraftOnHackmd = page.hasDraftOnHackmd;
     renderVars.hasDraftOnHackmd = page.hasDraftOnHackmd;
   }
   }
 
 
+  function addRenderVarsForPresentation(renderVars, page) {
+    renderVars.page = page;
+    renderVars.revision = page.revision;
+  }
+
   async function addRenderVarsForUserPage(renderVars, page, requestUser) {
   async function addRenderVarsForUserPage(renderVars, page, requestUser) {
     const userData = await User.findUserByUsername(User.getUsernameByPath(page.path));
     const userData = await User.findUserByUsername(User.getUsernameByPath(page.path));
 
 
@@ -286,7 +291,7 @@ module.exports = function(crowi, app) {
 
 
   async function showPageForPresentation(req, res, next) {
   async function showPageForPresentation(req, res, next) {
     const path = getPathFromRequest(req);
     const path = getPathFromRequest(req);
-    const revisionId = req.query.revision;
+    const { revisionId } = req.query;
 
 
     let page = await Page.findByPathAndViewer(path, req.user);
     let page = await Page.findByPathAndViewer(path, req.user);
 
 
@@ -298,7 +303,11 @@ module.exports = function(crowi, app) {
 
 
     // populate
     // populate
     page = await page.populateDataToMakePresentation(revisionId);
     page = await page.populateDataToMakePresentation(revisionId);
-    addRenderVarsForPage(renderVars, page);
+
+    if (page != null) {
+      addRenderVarsForPresentation(renderVars, page);
+    }
+
     return res.render('page_presentation', renderVars);
     return res.render('page_presentation', renderVars);
   }
   }
 
 

+ 1 - 1
src/server/views/widget/page_tabs.html

@@ -50,7 +50,7 @@
   <!-- presentation -->
   <!-- presentation -->
   {% if not page.isTopPage() %}
   {% if not page.isTopPage() %}
     <li class="nav-item d-edit-none">
     <li class="nav-item d-edit-none">
-      <a href="?presentation=1" class="nav-link toggle-presentation">
+      <a href="?presentation=1&revisionId={{revision.id}}" class="nav-link toggle-presentation">
         <i class="icon-film icon-fw"></i><span class="d-none d-sm-inline">{{ t('Presentation Mode') }}</span>
         <i class="icon-film icon-fw"></i><span class="d-none d-sm-inline">{{ t('Presentation Mode') }}</span>
       </a>
       </a>
     </li>
     </li>