Преглед изворни кода

Merge branch 'master' into fix-layout-in-case-of-long-toc-in-user-page

zahmis пре 5 година
родитељ
комит
6a4e0e9f49
31 измењених фајлова са 214 додато и 186 уклоњено
  1. 2 2
      bin/github-actions/update-readme.sh
  2. 5 5
      docker/README.md
  3. 10 2
      src/client/js/components/Hotkeys/Subscribers/EditPage.jsx
  4. 5 0
      src/client/js/components/Icons/LooockIcon.jsx
  5. 5 0
      src/client/js/components/Icons/PaperPlaneIcon.jsx
  6. 5 0
      src/client/js/components/Icons/ShareAltIcon.jsx
  7. 5 0
      src/client/js/components/Icons/UserIcon.jsx
  8. 1 1
      src/client/js/components/LikeButton.jsx
  9. 36 42
      src/client/js/components/Me/PersonalSettings.jsx
  10. 4 4
      src/client/js/components/MyDraftList/Draft.jsx
  11. 2 3
      src/client/js/components/MyDraftList/MyDraftList.jsx
  12. 3 3
      src/client/js/components/Navbar/GrowiSubNavigation.jsx
  13. 1 1
      src/client/js/components/Page/ShareLinkAlert.jsx
  14. 2 2
      src/client/js/components/PageContentFooter.jsx
  15. 1 1
      src/client/js/components/User/UserInfo.jsx
  16. 5 8
      src/client/js/services/PageContainer.js
  17. 0 10
      src/client/styles/scss/_page_list.scss
  18. 1 1
      src/client/styles/scss/_subnav.scss
  19. 10 0
      src/client/styles/scss/_user.scss
  20. 1 1
      src/client/styles/scss/atoms/_buttons.scss
  21. 0 11
      src/client/styles/scss/theme/_apply-colors-dark.scss
  22. 0 11
      src/client/styles/scss/theme/_apply-colors-light.scss
  23. 4 0
      src/client/styles/scss/theme/spring.scss
  24. 43 36
      src/server/routes/apiv3/bookmarks.js
  25. 1 5
      src/server/views/layout-growi/forbidden.html
  26. 1 5
      src/server/views/layout-growi/not_creatable.html
  27. 1 5
      src/server/views/layout-growi/not_found.html
  28. 28 0
      src/server/views/layout-growi/user_page.html
  29. 15 12
      src/server/views/me/drafts.html
  30. 15 13
      src/server/views/me/index.html
  31. 2 2
      src/server/views/widget/page_content.html

+ 2 - 2
bin/github-actions/update-readme.sh

@@ -2,5 +2,5 @@
 
 cd docker
 
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.1\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASE_VERSION}\2\3${RELEASE_VERSION}\4/" README.md
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.1-nocdn\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASE_VERSION}-nocdn\2\3${RELEASE_VERSION}\4/" README.md
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.2\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASE_VERSION}\2\3${RELEASE_VERSION}\4/" README.md
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.2-nocdn\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASE_VERSION}-nocdn\2\3${RELEASE_VERSION}\4/" README.md

+ 5 - 5
docker/README.md

@@ -10,10 +10,10 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 
-* [`4.1.0`, `4.1`, `4`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v4.1.0/docker/Dockerfile)
-* [`4.1.0-nocdn`, `4.1-nocdn`, `4-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.1.0/docker/Dockerfile)
-* [`4.0.11`, `4.0`(Dockerfile)](https://github.com/weseek/growi/blob/v4.0.11/docker/Dockerfile)
-* [`4.0.11-nocdn`, `4.0-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.0.11/docker/Dockerfile)
+* [`4.2.0`, `4.2`, `4`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v4.2.0/docker/Dockerfile)
+* [`4.2.0-nocdn`, `4.2-nocdn`, `4-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.2.0/docker/Dockerfile)
+* [`4.1.10`, `4.1` (Dockerfile)](https://github.com/weseek/growi/blob/v4.1.10/docker/Dockerfile)
+* [`4.1.10-nocdn`, `4.1-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.1.10/docker/Dockerfile)
 * [`3.8.0`, `3.8`, `3` (Dockerfile)](https://github.com/weseek/growi/blob/v3.8.0/docker/Dockerfile)
 * [`3.8.0-nocdn`, `3.8-nocdn`, `3-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v3.8.0/docker/Dockerfile)
 
@@ -39,7 +39,7 @@ The GROWI official docker image for production use which concludes several offic
 Requirements
 -------------
 
-* MongoDB (>= 3.6)
+* MongoDB (>= 4.4)
 
 ### Optional Dependencies
 

+ 10 - 2
src/client/js/components/Hotkeys/Subscribers/EditPage.jsx

@@ -1,6 +1,9 @@
 import React, { useEffect } from 'react';
 import PropTypes from 'prop-types';
 
+import NavigationContainer from '../../../services/NavigationContainer';
+import { withUnstatedContainers } from '../../UnstatedUtils';
+
 const EditPage = (props) => {
 
   // setup effect
@@ -10,6 +13,8 @@ const EditPage = (props) => {
       return;
     }
 
+    props.navigationContainer.setEditorMode('edit');
+
     // remove this
     props.onDeleteRender(this);
   }, [props]);
@@ -18,11 +23,14 @@ const EditPage = (props) => {
 };
 
 EditPage.propTypes = {
+  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
   onDeleteRender: PropTypes.func.isRequired,
 };
 
-EditPage.getHotkeyStrokes = () => {
+const EditPageWrapper = withUnstatedContainers(EditPage, [NavigationContainer]);
+
+EditPageWrapper.getHotkeyStrokes = () => {
   return [['e']];
 };
 
-export default EditPage;
+export default EditPageWrapper;

+ 5 - 0
src/client/js/components/Icons/LooockIcon.jsx

@@ -0,0 +1,5 @@
+import React from 'react';
+
+const LockIcon = () => <i className="icon-fw icon-lock"></i>;
+
+export default LockIcon;

+ 5 - 0
src/client/js/components/Icons/PaperPlaneIcon.jsx

@@ -0,0 +1,5 @@
+import React from 'react';
+
+const PaperPlaneIcon = () => <i className="icon-fw icon-paper-plane"></i>;
+
+export default PaperPlaneIcon;

+ 5 - 0
src/client/js/components/Icons/ShareAltIcon.jsx

@@ -0,0 +1,5 @@
+import React from 'react';
+
+const ShareAltIcon = () => <i className="icon-fw icon-share-alt"></i>;
+
+export default ShareAltIcon;

+ 5 - 0
src/client/js/components/Icons/UserIcon.jsx

@@ -0,0 +1,5 @@
+import React from 'react';
+
+const UserIcon = () => <i className="icon-fw icon-user"></i>;
+
+export default UserIcon;

+ 1 - 1
src/client/js/components/LikeButton.jsx

@@ -39,7 +39,7 @@ class LikeButton extends React.Component {
       <button
         type="button"
         onClick={this.handleClick}
-        className={`btn btn-like border-0 d-edit-none
+        className={`btn btn-like border-0
         ${pageContainer.state.isLiked ? 'active' : ''}`}
       >
         <i className="icon-like mr-3"></i>

+ 36 - 42
src/client/js/components/Me/PersonalSettings.jsx

@@ -1,59 +1,53 @@
 
-import React, { Fragment } from 'react';
+import React from 'react';
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
-
+import CustomNavigation from '../CustomNavigation';
 import UserSettings from './UserSettings';
 import PasswordSettings from './PasswordSettings';
 import ExternalAccountLinkedMe from './ExternalAccountLinkedMe';
 import ApiSettings from './ApiSettings';
 
+import UserIcon from '../Icons/UserIcon';
+import ShareAltIcon from '../Icons/ShareAltIcon';
+import LockIcon from '../Icons/LooockIcon';
+import PaperPlaneIcon from '../Icons/PaperPlaneIcon';
+
 class PersonalSettings extends React.Component {
 
   render() {
     const { t } = this.props;
 
+    const navTabMapping = {
+      user_infomation: {
+        Icon: UserIcon,
+        Content: UserSettings,
+        i18n: t('User Information'),
+        index: 0,
+      },
+      external_accounts: {
+        Icon: ShareAltIcon,
+        Content: ExternalAccountLinkedMe,
+        i18n: t('admin:user_management.external_accounts'),
+        index: 1,
+      },
+      password_settings: {
+        Icon: LockIcon,
+        Content: PasswordSettings,
+        i18n: t('Password Settings'),
+        index: 2,
+      },
+      api_settings: {
+        Icon: PaperPlaneIcon,
+        Content: ApiSettings,
+        i18n: t('API Settings'),
+        index: 3,
+      },
+    };
+
+
     return (
-      <Fragment>
-        <div className="personal-settings">
-          <ul className="nav nav-tabs" role="tablist">
-            <li className="nav-item">
-              <a className="nav-link active" href="#user-settings" data-toggle="tab" role="tab">
-                <i className="icon-fw icon-user"></i>{ t('User Information') }
-              </a>
-            </li>
-            <li className="nav-item">
-              <a className="nav-link" href="#external-accounts" data-toggle="tab" role="tab">
-                <i className="icon-fw icon-share-alt"></i>{ t('admin:user_management.external_accounts') }
-              </a>
-            </li>
-            <li className="nav-item">
-              <a className="nav-link" href="#password-settings" data-toggle="tab" role="tab">
-                <i className="icon-fw icon-lock"></i>{ t('Password Settings') }
-              </a>
-            </li>
-            <li className="nav-item">
-              <a className="nav-link" href="#apiToken" data-toggle="tab" role="tab">
-                <i className="icon-fw icon-paper-plane"></i>{ t('API Settings') }
-              </a>
-            </li>
-          </ul>
-          <div className="tab-content p-t-10">
-            <div id="user-settings" className="tab-pane active" role="tabpanel">
-              <UserSettings />
-            </div>
-            <div id="external-accounts" className="tab-pane" role="tabpanel">
-              <ExternalAccountLinkedMe />
-            </div>
-            <div id="password-settings" className="tab-pane" role="tabpanel">
-              <PasswordSettings />
-            </div>
-            <div id="apiToken" className="tab-pane" role="tabpanel">
-              <ApiSettings />
-            </div>
-          </div>
-        </div>
-      </Fragment>
+      <CustomNavigation navTabMapping={navTabMapping} />
     );
   }
 

+ 4 - 4
src/client/js/components/MyDraftList/Draft.jsx

@@ -105,10 +105,9 @@ class Draft extends React.Component {
   }
 
   renderControls() {
-    const { t, path } = this.props;
+    const { t, path, index } = this.props;
 
-    const encodedPath = path.replace(/\//g, '-');
-    const tooltipTargetId = `draft-copied-tooltip_${encodedPath}`;
+    const tooltipTargetId = `draft-copied-tooltip_${index}`;
 
     return (
       <div className="icon-container">
@@ -116,7 +115,7 @@ class Draft extends React.Component {
           ? null
           : (
             <a
-              href={`${this.props.path}#edit`}
+              href={`${path}#edit`}
               target="_blank"
               rel="noopener noreferrer"
               data-toggle="tooltip"
@@ -203,6 +202,7 @@ Draft.propTypes = {
   t: PropTypes.func.isRequired,
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 
+  index: PropTypes.number.isRequired,
   path: PropTypes.string.isRequired,
   markdown: PropTypes.string.isRequired,
   isExist: PropTypes.bool.isRequired,

+ 2 - 3
src/client/js/components/MyDraftList/MyDraftList.jsx

@@ -91,9 +91,10 @@ class MyDraftList extends React.Component {
    *
    */
   generateDraftList(drafts) {
-    return drafts.map((draft) => {
+    return drafts.map((draft, index) => {
       return (
         <Draft
+          index={index}
           key={draft.path}
           path={draft.path}
           markdown={draft.markdown}
@@ -135,8 +136,6 @@ class MyDraftList extends React.Component {
 
     return (
       <div className="page-list-container-create ">
-        <h1>My Drafts</h1>
-        <hr />
         { totalCount === 0
           && <span className="mt-2">No drafts yet.</span>
         }

+ 3 - 3
src/client/js/components/Navbar/GrowiSubNavigation.jsx

@@ -71,7 +71,7 @@ const PagePathNav = ({ pageId, pagePath, isPageForbidden }) => {
 const PageReactionButtons = ({ appContainer, pageContainer }) => {
 
   const {
-    pageId, isLiked, pageUser, shareLinkId,
+    pageUser, shareLinkId,
   } = pageContainer.state;
 
   const isSharedPage = useMemo(() => {
@@ -82,11 +82,11 @@ const PageReactionButtons = ({ appContainer, pageContainer }) => {
     <>
       {pageUser == null && !isSharedPage && (
       <span className="mr-2">
-        <LikeButton pageId={pageId} isLiked={isLiked} />
+        <LikeButton />
       </span>
       )}
       <span>
-        <BookmarkButton pageId={pageId} crowi={appContainer} />
+        <BookmarkButton crowi={appContainer} />
       </span>
     </>
   );

+ 1 - 1
src/client/js/components/Page/ShareLinkAlert.jsx

@@ -41,7 +41,7 @@ const ShareLinkAlert = (props) => {
   }
 
   return (
-    <p className={`alert alert-${specifyColor()} py-3 px-4`}>
+    <p className={`alert alert-${specifyColor()} py-3 px-4 d-edit-none`}>
       <i className="icon-fw icon-link"></i>
       {(expiredAt === '' ? <span>{t('page_page.notice.no_deadline')}</span>
       // eslint-disable-next-line react/no-danger

+ 2 - 2
src/client/js/components/PageContentFooter.jsx

@@ -16,10 +16,10 @@ const PageContentFooter = (props) => {
   return (
     <div className="page-content-footer mt-5 py-4 d-edit-none d-print-none">
       <div className="container-lg">
-        <p className="page-meta">
+        <div className="page-meta">
           <AuthorInfo user={creator} date={createdAt} mode="create" locate="footer" />
           <AuthorInfo user={revisionAuthor} date={updatedAt} mode="update" locate="footer" />
-        </p>
+        </div>
       </div>
     </div>
   );

+ 1 - 1
src/client/js/components/User/UserInfo.jsx

@@ -12,7 +12,7 @@ const UserInfo = (props) => {
   }
 
   return (
-    <div className="grw-users-info d-flex align-items-center d-edit-none pb-2 border-bottom">
+    <div className="grw-users-info d-flex align-items-center d-edit-none mb-5 pb-3 border-bottom">
       <UserPicture user={pageUser} />
 
       <div className="users-meta">

+ 5 - 8
src/client/js/services/PageContainer.js

@@ -179,14 +179,11 @@ export default class PageContainer extends Container {
   }
 
   async retrieveBookmarkInfo() {
-    const response = await this.appContainer.apiv3Get('/bookmarks', { pageId: this.state.pageId });
-    if (response.data.bookmarks != null) {
-      this.setState({ isBookmarked: true });
-    }
-    else {
-      this.setState({ isBookmarked: false });
-    }
-    this.setState({ sumOfBookmarks: response.data.sumOfBookmarks });
+    const response = await this.appContainer.apiv3Get('/bookmarks/info', { pageId: this.state.pageId });
+    this.setState({
+      sumOfBookmarks: response.data.sumOfBookmarks,
+      isBookmarked: response.data.isBookmarked,
+    });
   }
 
   async toggleBookmark() {

+ 0 - 10
src/client/styles/scss/_page_list.scss

@@ -72,13 +72,3 @@ body .page-list {
     background-color: $gray-300;
   }
 }
-
-.grw-page-list-m {
-  .grw-page-list-title-m {
-    svg {
-      width: 35px;
-      height: 35px;
-      margin-bottom: 6px;
-    }
-  }
-}

+ 1 - 1
src/client/styles/scss/_subnav.scss

@@ -106,7 +106,7 @@ $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
   z-index: $zindex-sticky - 5;
 
   .grw-subnav {
-    box-shadow: 0px 6px 6px 3px rgba(black, 0.15);
+    box-shadow: 0px 0px 6px 3px rgba(black, 0.15);
   }
 }
 

+ 10 - 0
src/client/styles/scss/_user.scss

@@ -43,3 +43,13 @@ $easeInOutCubic: cubic-bezier(0.65, 0, 0.35, 1);
     }
   }
 }
+
+.user-page {
+  .grw-user-page-header {
+    svg {
+      width: 35px;
+      height: 35px;
+      margin-bottom: 6px;
+    }
+  }
+}

+ 1 - 1
src/client/styles/scss/atoms/_buttons.scss

@@ -1,5 +1,5 @@
 .btn.btn-like {
-  @include button-outline-variant($secondary, lighten($info, 15%), rgba(lighten($info, 10%), 0.5), rgba(lighten($info, 10%), 0.5));
+  @include button-outline-variant($secondary, lighten($info, 15%), rgba(lighten($info, 10%), 0.15), rgba(lighten($info, 10%), 0.5));
   &:not(:disabled):not(.disabled):active,
   &:not(:disabled):not(.disabled).active {
     color: lighten($info, 15%);

+ 0 - 11
src/client/styles/scss/theme/_apply-colors-dark.scss

@@ -364,14 +364,3 @@ body.on-edit {
     background-color: $bgcolor-tags;
   }
 }
-
-/*
- * GROWI user page
- */
-.grw-page-list-m {
-  .grw-page-list-title-m {
-    svg {
-      fill: $color-global;
-    }
-  }
-}

+ 0 - 11
src/client/styles/scss/theme/_apply-colors-light.scss

@@ -294,14 +294,3 @@ $table-hover-bg: $bgcolor-table-hover;
     background-color: $bgcolor-tags;
   }
 }
-
-/*
- * GROWI user page
- */
-.grw-page-list-m {
-  .grw-page-list-title-m {
-    svg {
-      fill: $color-global;
-    }
-  }
-}

+ 4 - 0
src/client/styles/scss/theme/spring.scss

@@ -146,6 +146,10 @@ html[dark] {
   h1,
   h2 {
     color: $subthemecolor;
+
+    svg {
+      fill: $subthemecolor;
+    }
   }
 
   .nav.nav-tabs {

+ 43 - 36
src/server/routes/apiv3/bookmarks.js

@@ -50,11 +50,23 @@ const router = express.Router();
  *          bool:
  *            type: boolean
  *            description: boolean for bookmark status
+ *
+ *      BookmarkInfo:
+ *        description: BookmarkInfo
+ *        type: object
+ *        properties:
+ *          sumOfBookmarks:
+ *            type: number
+ *            description: how many people bookmarked the page
+ *          isBookmarked:
+ *            type: boolean
+ *            description: Whether the request user bookmarked (will be returned if the user is included in the request)
  */
 
 module.exports = (crowi) => {
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
-  const loginRequired = require('../../middlewares/login-required')(crowi);
+  const loginRequiredStrictly = require('@server/middlewares/login-required')(crowi);
+  const loginRequired = require('../../middlewares/login-required')(crowi, true);
   const csrf = require('../../middlewares/csrf')(crowi);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
 
@@ -73,12 +85,12 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /bookmarks:
+   *    /bookmarks/info:
    *      get:
    *        tags: [Bookmarks]
-   *        summary: /bookmarks
-   *        description: Get bookmarked status
-   *        operationId: getBookmarkedStatus
+   *        summary: /bookmarks/info
+   *        description: Get bookmarked info
+   *        operationId: getBookmarkedInfo
    *        parameters:
    *          - name: pageId
    *            in: query
@@ -87,24 +99,41 @@ module.exports = (crowi) => {
    *              type: string
    *        responses:
    *          200:
-   *            description: Succeeded to get bookmarked status.
+   *            description: Succeeded to get bookmark info.
    *            content:
    *              application/json:
    *                schema:
-   *                  $ref: '#/components/schemas/Bookmark'
+   *                  $ref: '#/components/schemas/BookmarkInfo'
    */
-  router.get('/', accessTokenParser, loginRequired, validator.bookmarkInfo, async(req, res) => {
+  router.get('/info', accessTokenParser, loginRequired, validator.bookmarkInfo, apiV3FormValidator, async(req, res) => {
+    const { user } = req;
     const { pageId } = req.query;
 
+    const responsesParams = {};
+
     try {
-      const bookmarks = await Bookmark.findByPageIdAndUserId(pageId, req.user);
-      const sumOfBookmarks = await Bookmark.countByPageId(pageId);
-      return res.apiv3({ bookmarks, sumOfBookmarks });
+      responsesParams.sumOfBookmarks = await Bookmark.countByPageId(pageId);
     }
     catch (err) {
-      logger.error('get-bookmark-failed', err);
+      logger.error('get-bookmark-count-failed', err);
       return res.apiv3Err(err, 500);
     }
+
+    // guest user only get bookmark count
+    if (user == null) {
+      return res.apiv3(responsesParams);
+    }
+
+    try {
+      const bookmark = await Bookmark.findByPageIdAndUserId(pageId, user._id);
+      responsesParams.isBookmarked = (bookmark != null);
+      return res.apiv3(responsesParams);
+    }
+    catch (err) {
+      logger.error('get-bookmark-state-failed', err);
+      return res.apiv3Err(err, 500);
+    }
+
   });
 
   // select page from bookmark where userid = userid
@@ -152,7 +181,7 @@ module.exports = (crowi) => {
     query('limit').if(value => value != null).isInt({ max: 300 }).withMessage('You should set less than 300 or not to set limit.'),
   ];
 
-  router.get('/:userId', accessTokenParser, loginRequired, validator.myBookmarkList, apiV3FormValidator, async(req, res) => {
+  router.get('/:userId', accessTokenParser, loginRequiredStrictly, validator.myBookmarkList, apiV3FormValidator, async(req, res) => {
     const { userId } = req.params;
     const page = req.query.page;
     const limit = parseInt(req.query.limit) || await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM') || 30;
@@ -213,7 +242,7 @@ module.exports = (crowi) => {
    *                schema:
    *                  $ref: '#/components/schemas/Bookmark'
    */
-  router.put('/', accessTokenParser, loginRequired, csrf, validator.bookmarks, apiV3FormValidator, async(req, res) => {
+  router.put('/', accessTokenParser, loginRequiredStrictly, csrf, validator.bookmarks, apiV3FormValidator, async(req, res) => {
     const { pageId, bool } = req.body;
 
     let bookmark;
@@ -240,27 +269,5 @@ module.exports = (crowi) => {
     return res.apiv3({ bookmark });
   });
 
-  /**
-   * @swagger
-   *
-   *    /count-bookmarks:
-   *      get:
-   *        tags: [Bookmarks]
-   *        summary: /bookmarks
-   *        description: Count bookmsrks
-   *        requestBody:
-   *          content:
-   *            application/json:
-   *              schema:
-   *                $ref: '#/components/schemas/BookmarkParams'
-   *        responses:
-   *          200:
-   *            description: Succeeded to count bookmarks.
-   *            content:
-   *              application/json:
-   *                schema:
-   *                  $ref: '#/components/schemas/Bookmark'
-   */
-
   return router;
 };

+ 1 - 5
src/server/views/layout-growi/forbidden.html

@@ -9,11 +9,7 @@
 
 {% block content_main %}
   <div class="container-lg">
-    <div class="row">
-      <div class="col">
-        {% include '../widget/forbidden_content.html' %}
-      </div>
-    </div>
+    {% include '../widget/forbidden_content.html' %}
   </div>
 {% endblock %}
 

+ 1 - 5
src/server/views/layout-growi/not_creatable.html

@@ -10,11 +10,7 @@
 
 {% block content_main %}
   <div class="container-lg">
-    <div class="row">
-      <div class="col">
-        {% include '../widget/not_creatable_content.html' %}
-      </div>
-    </div>
+    {% include '../widget/not_creatable_content.html' %}
   </div>
 {% endblock %}
 

+ 1 - 5
src/server/views/layout-growi/not_found.html

@@ -10,11 +10,7 @@
 
 {% block content_main %}
   <div class="container-lg">
-    <div class="row">
-      <div class="col">
-        {% include '../widget/not_found_content.html' %}
-      </div>
-    </div>
+    {% include '../widget/not_found_content.html' %}
   </div>
 {% endblock %}
 

+ 28 - 0
src/server/views/layout-growi/user_page.html

@@ -17,6 +17,34 @@
 {% block content_main_after %}
   {% include 'widget/comments.html' %}
 
+  {% if page %}
+    <div class="container-lg">
+
+      <div class="grw-page-list-m mt-5 pb-5 d-edit-none">
+        <h2 class="grw-user-page-header border-bottom pb-2 mb-3" id="bookmarks-list">
+          <i id="user-bookmark-icon"></i>
+          Bookmarks
+        </h2>
+        <div class="page-list" id="user-bookmark-list">
+          <div class="page-list-container">
+          </div>
+        </div>
+      </div>
+
+      <div class="grw-page-list-m mt-5 pb-5 d-edit-none">
+        <h2 class="grw-user-page-header border-bottom pb-2 mb-3" id="recently-created-list">
+          <i id="recent-created-icon"></i>
+          Recently Created
+        </h2>
+        <div class="page-list" id="user-created-list">
+          <div class="page-list-container">
+          </div>
+        </div>
+      </div>
+
+    </div>
+  {% endif %}
+
   <div id="page-content-footer"></div>
 
 {% endblock %}

+ 15 - 12
src/server/views/me/drafts.html

@@ -1,18 +1,21 @@
-{% extends '../layout-growi/base/layout.html' %}
+{% extends '../layout/layout.html' %}
 
 {% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('My Drafts')) }}{% endblock %}
 
-{% block content_header %}
+{% block layout_main %}
+
+{% block content_header_wrapper %}
+<header class="py-3">
+  <div class="container-fluid">
+    <h1 class="title">{{ t('My Drafts') }}</h1>
+  </div>
+</header>
+<div id="grw-fav-sticky-trigger" class="sticky-top"></div>
 {% endblock %}
 
-{% block content_main %}
-<div id="content-main" class="content-main container">
-  <div id="my-drafts"></div>
+<div id="main" class="main">
+  <div id="content-main" class="content-main container-lg">
+    <div id="my-drafts"></div>
+  </div>
 </div>
-{% endblock content_main %}
-
-{% block content_footer %}
-{% endblock content_footer %}
-
-{% block layout_footer %}
-{% endblock layout_footer %}
+{% endblock %}

+ 15 - 13
src/server/views/me/index.html

@@ -1,19 +1,21 @@
-{% extends '../layout-growi/base/layout.html' %}
+{% extends '../layout/layout.html' %}
 
 {% block html_title %}{{ customizeService.generateCustomTitleForFixedPageName(t('User Settings')) }}{% endblock %}
 
-{% block html_base_css %}user-settings-page{% endblock %}
+{% block layout_main %}
 
-{% block content_header %}
-<h1 class="title">{{ t('User Settings') }}</h1>
+{% block content_header_wrapper %}
+<header class="py-3">
+  <div class="container-fluid">
+    <h1 class="title">{{ t('User Settings') }}</h1>
+  </div>
+</header>
+<div id="grw-fav-sticky-trigger" class="sticky-top"></div>
 {% endblock %}
 
-{% block content_main %}
-<div class="content-main" id="personal-setting"></div>
-{% endblock content_main %}
-
-{% block content_footer %}
-{% endblock content_footer %}
-
-{% block layout_footer %}
-{% endblock layout_footer %}
+<div id="main" class="main">
+  <div id="content-main" class="content-main container-lg">
+    <div class="content-main" id="personal-setting"></div>
+  </div>
+</div>
+{% endblock %}

+ 2 - 2
src/server/views/widget/page_content.html

@@ -55,11 +55,11 @@
 </div>
 
 {% if revision %}
-<div class="d-none d-lg-block d-editor-none grw-side-contents-container">
+<div class="d-none d-lg-block d-edit-none grw-side-contents-container">
   <div class="grw-side-contents-sticky-container">
     <div id="page-accessories" class="page-accessories"></div>
     <div id="revision-toc" class="revision-toc sps sps--abv" data-sps-offset="123">
-      <div id="revision-toc-content" class="revision-toc-content"></div>
+      <div id="revision-toc-content" class="revision-toc-content mb-3"></div>
     </div>
     {% if pageUser %}
       <div id="grw-user-contents-links"></div>