Bläddra i källkod

Merge branch 'feat/tag-list-page-for-master-merge' into feat/brush-up-tag-feature

yusuketk 7 år sedan
förälder
incheckning
869ef02e71
40 ändrade filer med 159 tillägg och 158 borttagningar
  1. 1 0
      .vscode/extensions.json
  2. 6 1
      CHANGES.md
  3. 1 1
      package.json
  4. 9 4
      resource/locales/en-US/translation.json
  5. 9 4
      resource/locales/ja/translation.json
  6. 1 1
      src/client/js/app.js
  7. 4 2
      src/client/js/components/PageAttachment/Attachment.js
  8. 3 2
      src/client/js/components/PageAttachment/DeleteAttachmentModal.js
  9. 3 5
      src/client/js/components/PageComment/Comment.jsx
  10. 1 4
      src/client/js/components/PageComment/CommentForm.jsx
  11. 0 0
      src/client/js/components/PageComment/CommentPreview.jsx
  12. 2 1
      src/client/js/components/PageComment/DeleteCommentModal.jsx
  13. 0 0
      src/client/js/components/PageHistory.jsx
  14. 3 2
      src/client/js/components/PageHistory/Revision.jsx
  15. 0 0
      src/client/js/components/PageHistory/RevisionDiff.jsx
  16. 1 1
      src/client/js/components/SearchTypeahead.js
  17. 0 40
      src/client/js/components/User/User.js
  18. 0 0
      src/client/js/components/User/UserDate.jsx
  19. 9 1
      src/client/js/components/User/UserPicture.jsx
  20. 7 9
      src/client/js/components/User/UserPictureList.jsx
  21. 22 0
      src/client/js/components/User/Username.jsx
  22. 13 12
      src/client/styles/agile-admin/inverse/colors/future.scss
  23. 5 0
      src/client/styles/scss/_attachments.scss
  24. 0 2
      src/client/styles/scss/_editor-attachment.scss
  25. 4 6
      src/client/styles/scss/_layout_kibela.scss
  26. 9 11
      src/client/styles/scss/_on-edit.scss
  27. 2 5
      src/client/styles/scss/_page.scss
  28. 0 9
      src/client/styles/scss/_user.scss
  29. 10 7
      src/server/crowi/express-init.js
  30. 12 6
      src/server/routes/admin.js
  31. 1 1
      src/server/views/admin/app.html
  32. 2 2
      src/server/views/admin/security.html
  33. 0 4
      src/server/views/layout-crowi/base/layout.html
  34. 0 4
      src/server/views/layout-growi/base/layout.html
  35. 0 4
      src/server/views/layout-kibela/base/layout.html
  36. 2 1
      src/server/views/layout/layout.html
  37. 1 1
      src/server/views/login.html
  38. 0 4
      src/server/views/search.html
  39. 15 0
      src/server/views/widget/alert_breaking_changes.html
  40. 1 1
      src/server/views/widget/alert_siteurl_undefined.html

+ 1 - 0
.vscode/extensions.json

@@ -11,6 +11,7 @@
     "eg2.vscode-npm-script",
     "christian-kohler.npm-intellisense",
     "esbenp.prettier-vscode",
+    "shinnn.stylelint",
 	],
 	// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
 	"unwantedRecommendations": [

+ 6 - 1
CHANGES.md

@@ -1,6 +1,11 @@
 # CHANGES
 
-## 3.4.5-RC
+## 3.4.6-RC
+
+* Improvement: Show display name when mouse hover to user image
+* Fix: URL in slack message is broken on Safari
+
+## 3.4.5
 
 * Improvement: Pass autolink through the XSS filter according to CommonMark Spec
 * Fix: Update ElasticSearch index when deleting/duplicating pages

+ 1 - 1
package.json

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

+ 9 - 4
resource/locales/en-US/translation.json

@@ -115,6 +115,11 @@
     "unavaliable_user_id": "This 'User ID' is unavailable."
   },
 
+  "breaking_changes": {
+    "v346_passport_is_not_enabled": "Crowi Classic Authentication mechanism currently in use will <strong>no longer be supported</strong> in the near future. Switch to Passport from %s",
+    "v346_using_basic_auth": "Basic Authentication currently in use will <strong>no longer be available</strong> in the near future. Remove settings from %s"
+  },
+
   "page_register": {
     "notice": {
       "restricted": "Admin approval required.",
@@ -402,10 +407,10 @@
 		"for_instance": " For instance, if you use growi within a company, you can write ",
 		"only_those": " Only those whose e-mail address including the company address can register.",
     "insert_single": "Please insert single e-mail address per line.",
-    "page_listing_1": "Page listing<br>restricted by 'Just Me'",
-    "page_listing_1_desc": "Show pages that are restricted by 'Just Me' option when listing",
-    "page_listing_2": "Page listing<br>restricted by User Group",
-    "page_listing_2_desc": "Show pages that are restricted by User Group when listing",
+    "page_listing_1": "Page listing/searching<br>restricted by 'Just Me'",
+    "page_listing_1_desc": "Show pages that are restricted by 'Just Me' option when listing/searching",
+    "page_listing_2": "Page listing/searching<br>restricted by User Group",
+    "page_listing_2_desc": "Show pages that are restricted by User Group when listing/searching",
 
 		"Authentication mechanism settings": "Authentication mechanism settings",
     "note": "Note",

+ 9 - 4
resource/locales/ja/translation.json

@@ -134,6 +134,11 @@
     "unavaliable_user_id": "このユーザーIDは利用できません。"
   },
 
+  "breaking_changes": {
+    "v346_passport_is_not_enabled": "現在利用中の Crowi Classic Authentication mechanism は、近い将来<strong>サポートされなくなります</strong>。%s から Passport に切り替えてください。",
+    "v346_using_basic_auth": "現在利用中の Basic 認証機能は、近い将来<strong>廃止されます</strong>。%s から設定を削除してください。"
+  },
+
   "page_register": {
     "notice": {
        "restricted": "この Wiki への新規登録は制限されています。",
@@ -425,10 +430,10 @@
     "for_instance":"例えば、",
     "only_those":"と記載することで、そのドメインのメールアドレスを持っている人のみ登録可能になります。",
     "insert_single":"1行に1メールアドレス入力してください。",
-    "page_listing_1": "ページのリスト表示<br>'自分のみ'に閲覧制限しているページ",
-    "page_listing_1_desc": "ページのリスト表示、'自分のみ'に閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
-    "page_listing_2": "ページのリスト表示<br>特定グループに閲覧制限しているページ",
-    "page_listing_2_desc": "ページのリスト表示、特定グループにのみ閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
+    "page_listing_1": "ページのリスト表示と検索<br>'自分のみ'に閲覧制限しているページ",
+    "page_listing_1_desc": "ページのリスト表示や検索結果において、'自分のみ'に閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
+    "page_listing_2": "ページのリスト表示と検索<br>特定グループに閲覧制限しているページ",
+    "page_listing_2_desc": "ページのリスト表示や検索結果において、特定グループにのみ閲覧制限をしているページをアクセス権のないユーザーにも表示します。",
 
     "Authentication mechanism settings":"認証機構設定",
     "note": "メモ",

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

@@ -38,7 +38,7 @@ import BookmarkButton from './components/BookmarkButton';
 import LikeButton from './components/LikeButton';
 import PagePathAutoComplete from './components/PagePathAutoComplete';
 import RecentCreated from './components/RecentCreated/RecentCreated';
-import UserPictureList from './components/Common/UserPictureList';
+import UserPictureList from './components/User/UserPictureList';
 
 import CustomCssEditor from './components/Admin/CustomCssEditor';
 import CustomScriptEditor from './components/Admin/CustomScriptEditor';

+ 4 - 2
src/client/js/components/PageAttachment/Attachment.js

@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import User from '../User/User';
+import UserPicture from '../User/UserPicture';
 
 export default class Attachment extends React.Component {
 
@@ -53,7 +53,9 @@ export default class Attachment extends React.Component {
 
     return (
       <li className="attachment">
-        <User user={attachment.creator} />
+        <span className="mr-1 attachment-userpicture">
+          <UserPicture user={attachment.creator} size="sm"></UserPicture>
+        </span>
 
         <a href={attachment.filePathProxied}><i className={formatIcon}></i> {attachment.originalName}</a>
 

+ 3 - 2
src/client/js/components/PageAttachment/DeleteAttachmentModal.js

@@ -3,7 +3,8 @@ import React from 'react';
 import Button from 'react-bootstrap/es/Button';
 import Modal from 'react-bootstrap/es/Modal';
 
-import User from '../User/User';
+import UserPicture from '../User/UserPicture';
+import Username from '../User/Username';
 
 export default class DeleteAttachmentModal extends React.Component {
 
@@ -37,7 +38,7 @@ export default class DeleteAttachmentModal extends React.Component {
           <i className={this.iconNameByFormat(attachment.fileFormat)}></i> {attachment.originalName}
         </p>
         <p>
-          uploaded by <User user={attachment.creator} username />
+          uploaded by <UserPicture user={attachment.creator} size="sm"></UserPicture> <Username user={attachment.creator}></Username>
         </p>
         {content}
       </div>

+ 3 - 5
src/client/js/components/PageComment/Comment.js → src/client/js/components/PageComment/Comment.jsx

@@ -7,6 +7,7 @@ import RevisionBody from '../Page/RevisionBody';
 
 import ReactUtils from '../ReactUtils';
 import UserPicture from '../User/UserPicture';
+import Username from '../User/Username';
 
 /**
  *
@@ -120,19 +121,16 @@ export default class Comment extends React.Component {
     const rootClassName = this.getRootClassName();
     const commentDate = dateFnsFormat(comment.createdAt, 'YYYY/MM/DD HH:mm');
     const commentBody = isMarkdown ? this.renderRevisionBody() : ReactUtils.nl2br(comment.comment);
-    const creatorsPage = `/user/${creator.username}`;
     const revHref = `?revision=${comment.revision}`;
     const revFirst8Letters = comment.revision.substr(-8);
     const revisionLavelClassName = this.getRevisionLabelClassName();
 
     return (
       <div className={rootClassName}>
-        <a href={creatorsPage}>
-          <UserPicture user={creator} />
-        </a>
+        <UserPicture user={creator} />
         <div className="page-comment-main">
           <div className="page-comment-creator">
-            <a href={creatorsPage}>{creator.username}</a>
+            <Username user={creator} />
           </div>
           <div className="page-comment-body">{commentBody}</div>
           <div className="page-comment-meta">

+ 1 - 4
src/client/js/components/PageComment/CommentForm.jsx

@@ -237,7 +237,6 @@ export default class CommentForm extends React.Component {
     const crowi = this.props.crowi;
     const username = crowi.me;
     const user = crowi.findUser(username);
-    const creatorsPage = `/user/${username}`;
     const comment = this.state.comment;
     const commentPreview = this.state.isMarkdown ? this.getCommentHtml() : ReactUtils.nl2br(comment);
     const emojiStrategy = this.props.crowi.getEmojiStrategy();
@@ -261,9 +260,7 @@ export default class CommentForm extends React.Component {
               { isLayoutTypeGrowi
                 && (
                 <div className="comment-form-user">
-                  <a href={creatorsPage}>
-                    <UserPicture user={user} />
-                  </a>
+                  <UserPicture user={user} />
                 </div>
                 )
               }

+ 0 - 0
src/client/js/components/PageComment/CommentPreview.js → src/client/js/components/PageComment/CommentPreview.jsx


+ 2 - 1
src/client/js/components/PageComment/DeleteCommentModal.js → src/client/js/components/PageComment/DeleteCommentModal.jsx

@@ -8,6 +8,7 @@ import dateFnsFormat from 'date-fns/format';
 
 import ReactUtils from '../ReactUtils';
 import UserPicture from '../User/UserPicture';
+import Username from '../User/Username';
 
 export default class DeleteCommentModal extends React.Component {
 
@@ -43,7 +44,7 @@ export default class DeleteCommentModal extends React.Component {
           </Modal.Title>
         </Modal.Header>
         <Modal.Body>
-          <UserPicture user={comment.creator} size="xs" /> <strong>{comment.creator.username}</strong> wrote on {commentDate}:
+          <UserPicture user={comment.creator} size="xs" /> <strong><Username user={comment.creator}></Username></strong> wrote on {commentDate}:
           <p className="well well-sm comment-body m-t-5">{commentBody}</p>
         </Modal.Body>
         <Modal.Footer>

+ 0 - 0
src/client/js/components/PageHistory.js → src/client/js/components/PageHistory.jsx


+ 3 - 2
src/client/js/components/PageHistory/Revision.jsx

@@ -1,7 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import UserDate from '../Common/UserDate';
+import UserDate from '../User/UserDate';
+import Username from '../User/Username';
 import UserPicture from '../User/UserPicture';
 
 export default class Revision extends React.Component {
@@ -63,7 +64,7 @@ export default class Revision extends React.Component {
         </div>
         <div className="m-l-10">
           <div className="revision-history-author">
-            <strong>{author.username}</strong>
+            <strong><Username user={author}></Username></strong>
           </div>
           <div className="revision-history-meta">
             <p>

+ 0 - 0
src/client/js/components/PageHistory/RevisionDiff.js → src/client/js/components/PageHistory/RevisionDiff.jsx


+ 1 - 1
src/client/js/components/SearchTypeahead.js

@@ -151,7 +151,7 @@ export default class SearchTypeahead extends React.Component {
     const page = option;
     return (
       <span>
-        <UserPicture user={page.lastUpdateUser} size="sm" />
+        <UserPicture user={page.lastUpdateUser} size="sm" withoutLink />
         <PagePath page={page} />
         <PageListMeta page={page} />
       </span>

+ 0 - 40
src/client/js/components/User/User.js

@@ -1,40 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import UserPicture from './UserPicture';
-
-export default class User extends React.Component {
-
-  render() {
-    const user = this.props.user;
-    const userLink = `/user/${user.username}`;
-
-    const username = this.props.username;
-    const name = this.props.name;
-
-    return (
-      <span className="user-component">
-        <a href={userLink}>
-          <UserPicture user={user} />
-
-          {username
-              && <span className="user-component-username">@{user.username}</span>
-          }
-          {name
-              && <span className="user-component-name">({user.name})</span>
-          }
-        </a>
-      </span>
-    );
-  }
-
-}
-
-User.propTypes = {
-  user: PropTypes.object.isRequired,
-  name: PropTypes.bool,
-  username: PropTypes.bool,
-};
-
-User.defaultProps = {
-};

+ 0 - 0
src/client/js/components/Common/UserDate.js → src/client/js/components/User/UserDate.jsx


+ 9 - 1
src/client/js/components/User/UserPicture.js → src/client/js/components/User/UserPicture.jsx

@@ -40,14 +40,21 @@ export default class UserPicture extends React.Component {
 
   render() {
     const user = this.props.user;
+    const href = `/user/${user.username}`;
 
-    return (
+    const imgElem = (
       <img
         src={this.getUserPicture(user)}
         alt={user.username}
         className={this.getClassName()}
       />
     );
+
+    return (
+      (this.props.withoutLink)
+        ? <span>{imgElem}</span>
+        : <a href={href}>{imgElem}</a>
+    );
   }
 
 }
@@ -55,6 +62,7 @@ export default class UserPicture extends React.Component {
 UserPicture.propTypes = {
   user: PropTypes.object.isRequired,
   size: PropTypes.string,
+  withoutLink: PropTypes.bool,
 };
 
 UserPicture.defaultProps = {

+ 7 - 9
src/client/js/components/Common/UserPictureList.jsx → src/client/js/components/User/UserPictureList.jsx

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
 import OverlayTrigger from 'react-bootstrap/es/OverlayTrigger';
 import Tooltip from 'react-bootstrap/es/Tooltip';
 
-import UserPicture from '../User/UserPicture';
+import UserPicture from './UserPicture';
 
 export default class UserPictureList extends React.Component {
 
@@ -27,16 +27,14 @@ export default class UserPictureList extends React.Component {
   render() {
     const users = this.state.users.map((user) => {
       // create Tooltip
-      const tooltip = <Tooltip id={`tooltip-${user._id}`}>{user.username}</Tooltip>;
+      const tooltip = <Tooltip id={`tooltip-${user._id}`}>@{user.username}<br />{user.name}</Tooltip>;
 
       return (
-        <a key={user._id} data-user-id={user._id} href={`/user/${user.username}`}>
-          <OverlayTrigger overlay={tooltip} placement="bottom">
-            <span key={`span-${user._id}`}>{/* workaround from https://github.com/react-bootstrap/react-bootstrap/issues/2208#issuecomment-301737531 */}
-              <UserPicture user={user} size="xs" ref={`userPicture-${user._id}`} />
-            </span>
-          </OverlayTrigger>
-        </a>
+        <OverlayTrigger key={user._id} overlay={tooltip} placement="bottom">
+          <span key={`span-${user._id}`}>{/* workaround from https://github.com/react-bootstrap/react-bootstrap/issues/2208#issuecomment-301737531 */}
+            <UserPicture user={user} size="xs" ref={`userPicture-${user._id}`} />
+          </span>
+        </OverlayTrigger>
       );
     });
 

+ 22 - 0
src/client/js/components/User/Username.jsx

@@ -0,0 +1,22 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+export default class Username extends React.Component {
+
+  render() {
+    const { user } = this.props;
+
+    const name = user.name || '(no name)';
+    const username = user.username;
+    const href = `/user/${user.username}`;
+
+    return (
+      <a href={href}>{name} (@{username})</a>
+    );
+  }
+
+}
+
+Username.propTypes = {
+  user: PropTypes.object.isRequired,
+};

+ 13 - 12
src/client/styles/agile-admin/inverse/colors/future.scss

@@ -1,21 +1,20 @@
 @import '../variables';
 
-$basecolor: #16282D;
-$themecolor:rgba(11, 79, 104, 0.616);
+$basecolor: #16282d;
+$themecolor: rgba(11, 79, 104, 0.616);
 
-$topbar:#011414;
-$sidebar:#fff;
-$bodycolor:$basecolor;
-$headingtext: #D9A364;
+$topbar: #011414;
+$sidebar: #fff;
+$bodycolor: $basecolor;
+$headingtext: #d9a364;
 $bodytext: lighten($basecolor, 35%);
 $linktext: lighten($basecolor, 45%);
 $linktext-hover: lighten($linktext, 80%);
-$sidebar-text:rgb(65, 133, 124);
-$dark-themecolor:#4F5467;
-
+$sidebar-text: rgb(65, 133, 124);
+$dark-themecolor: #4f5467;
 
 $primary: $themecolor;
-$info: lighten($themecolor,20%);
+$info: lighten($themecolor, 20%);
 
 $logo-mark-fill: rgb(170, 245, 237);
 $wikilinktext: saturate($bodytext, 20%);
@@ -31,6 +30,8 @@ $inline-code-bg: darken($bodycolor, 5%);
 @import 'apply-colors';
 @import 'apply-colors-dark';
 
-.bg-title{
-  border-bottom: 1px solid rgb(131, 228, 215);
+.main-container:not(.on-edit) {
+  .bg-title {
+    border-bottom: 1px solid rgb(131, 228, 215);
+  }
 }

+ 5 - 0
src/client/styles/scss/_attachments.scss

@@ -6,6 +6,11 @@
   li.attachment {
     list-style: none;
   }
+
+  .attachment-userpicture {
+    line-height: 1.7em;
+    vertical-align: bottom;
+  }
 }
 
 .page-attachments,

+ 0 - 2
src/client/styles/scss/_editor-attachment.scss

@@ -1,7 +1,6 @@
 @import 'editor-overlay';
 
 .editor-container {
-
   // for Dropzone
   .dropzone {
     @mixin insertSimpleLineIcons($code) {
@@ -49,7 +48,6 @@
 
     // uploadable
     &.dropzone-uploadable {
-
       // accepted
       &.dropzone-accepted:not(.dropzone-rejected) {
         .overlay.overlay-dropzone-active {

+ 4 - 6
src/client/styles/scss/_layout_kibela.scss

@@ -1,5 +1,4 @@
 body.kibela {
-
   .icon-link,
   .CodeMirror-hint-active,
   .nav-main-left-tab,
@@ -131,8 +130,7 @@ body.kibela {
   .nav.nav-tabs {
     border-bottom-color: #f4f5f6;
 
-    >li>a {
-
+    > li > a {
       &,
       &:hover,
       &:focus {
@@ -143,7 +141,7 @@ body.kibela {
       }
     }
 
-    >li.active>a {
+    > li.active > a {
       background: transparent !important;
       border-top: none;
       border-right: none;
@@ -185,8 +183,8 @@ body.kibela {
     @include expand-editor($header-plus-footer);
 
     .main {
-      >.row.page-content {
-        >.col-xs-12 {
+      > .row.page-content {
+        > .col-xs-12 {
           width: 100%;
           padding: 0;
         }

+ 9 - 11
src/client/styles/scss/_on-edit.scss

@@ -1,7 +1,6 @@
 @import 'editor-overlay';
 
 body:not(.on-edit) {
-
   // hide .page-editor-footer
   .page-editor-footer {
     display: none !important;
@@ -19,11 +18,11 @@ body.on-edit {
 
   // for growi layout
   .main {
-    >.row {
+    > .row {
       margin: 0;
 
-      >.col-lg-10,
-      >.col-md-9 {
+      > .col-lg-10,
+      > .col-md-9 {
         width: 100%;
         padding: 0;
       }
@@ -72,7 +71,6 @@ body.on-edit {
 
   // hide hackmd related alert
   &.hackmd #page-status-alert {
-
     .alert-hackmd-someone-editing,
     .alert-hackmd-draft-exists {
       display: none;
@@ -98,7 +96,7 @@ body.on-edit {
 
     background: none;
 
-    >.header-container {
+    > .header-container {
       width: 100%; //   for crowi layout
       padding: 0; //    for crowi layout
       pointer-events: initial; // enable pointer-events
@@ -169,7 +167,6 @@ body.on-edit {
   }
 
   &.builtin-editor {
-
     /*****************
     * Editor styles
     *****************/
@@ -211,7 +208,8 @@ body.on-edit {
       }
     }
 
-    .page-editor-preview-container {}
+    .page-editor-preview-container {
+    }
 
     .page-editor-preview-body {
       padding-top: 18px;
@@ -231,7 +229,7 @@ body.on-edit {
           width: 20px;
         }
 
-        .dropdown-menu>li>a {
+        .dropdown-menu > li > a {
           display: flex;
           align-items: center;
           justify-content: space-between;
@@ -270,7 +268,7 @@ body.on-edit {
     }
 
     .hackmd-preinit,
-    #iframe-hackmd-container>iframe {
+    #iframe-hackmd-container > iframe {
       border: none;
     }
 
@@ -348,8 +346,8 @@ body.on-edit {
 
 #tag-edit-button-tooltip {
   .tooltip-inner {
-    background-color: #fff;
     color: #000;
+    background-color: #fff;
     border: 1px solid #ccc;
   }
 

+ 2 - 5
src/client/styles/scss/_page.scss

@@ -8,15 +8,14 @@
    * header
    */
   header {
-
     // the container of h1
     div.title-container {
       margin-right: auto;
     }
 
     .btn-tag {
-      line-height: 20px;
       display: block;
+      line-height: 20px;
     }
 
     .btn-copy,
@@ -34,7 +33,6 @@
 
     // change button opacity
     &:hover {
-
       .btn.btn-copy,
       .btn-copy-link,
       .btn-tag,
@@ -107,7 +105,6 @@
 .main-container .main .content-main .revision-history {
   .revision-history-list {
     .revision-history-outer {
-
       // add border-top except of first element
       &:not(:first-of-type) {
         border-top: 1px solid $border;
@@ -213,7 +210,7 @@
   opacity: 0;
   transition: opacity 0.3s ease-out;
 
-  &>* {
+  & > * {
     box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
   }
 }

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

@@ -63,12 +63,3 @@
     }
   }
 }
-
-.user-component {
-  img.picture {
-    margin-right: 4px;
-  }
-  span {
-    margin-right: 4px;
-  }
-}

+ 10 - 7
src/server/crowi/express-init.js

@@ -21,10 +21,14 @@ module.exports = function(crowi, app) {
   const i18nMiddleware = require('i18next-express-middleware');
   const i18nUserSettingDetector = require('../util/i18nUserSettingDetector');
   const env = crowi.node_env;
-  const config = crowi.getConfig();
   const middleware = require('../util/middlewares');
 
+  // Old type config API
+  const config = crowi.getConfig();
   const Config = crowi.model('Config');
+  // New type config API
+  const configManager = crowi.configManager;
+
   const User = crowi.model('User');
   const lngDetector = new i18nMiddleware.LanguageDetector();
   lngDetector.addDetector(i18nUserSettingDetector);
@@ -65,7 +69,7 @@ module.exports = function(crowi, app) {
     req.csrfToken = null;
 
     res.locals.req = req;
-    res.locals.baseUrl = crowi.configManager.getSiteUrl();
+    res.locals.baseUrl = configManager.getSiteUrl();
     res.locals.config = config;
     res.locals.env = env;
     res.locals.now = now;
@@ -114,11 +118,10 @@ module.exports = function(crowi, app) {
       return next();
     }
 
-    if (config.crowi['security:basicName'] && config.crowi['security:basicSecret']) {
-      return basicAuth(
-        config.crowi['security:basicName'],
-        config.crowi['security:basicSecret'],
-      )(req, res, next);
+    const basicName = configManager.getConfig('crowi', 'security:basicName');
+    const basicSecret = configManager.getConfig('crowi', 'security:basicSecret');
+    if (basicName && basicSecret) {
+      return basicAuth(basicName, basicSecret)(req, res, next);
     }
 
     next();

+ 12 - 6
src/server/routes/admin.js

@@ -906,7 +906,11 @@ module.exports = function(crowi, app) {
     }
   };
 
-  actions.api.securitySetting = function(req, res) {
+  actions.api.securitySetting = async function(req, res) {
+    if (!req.form.isValid) {
+      return res.json({ status: false, message: req.form.errors.join('\n') });
+    }
+
     const form = req.form.settingForm;
     const config = crowi.getConfig();
     const isPublicWikiOnly = Config.isPublicWikiOnly(config);
@@ -924,12 +928,14 @@ module.exports = function(crowi, app) {
       }
     }
 
-    if (req.form.isValid) {
-      debug('form content', form);
-      return saveSetting(req, res, form);
+    try {
+      await crowi.configManager.updateConfigsInTheSameNamespace('crowi', form);
+      return res.json({ status: true });
+    }
+    catch (err) {
+      logger.error(err);
+      return res.json({ status: false });
     }
-
-    return res.json({ status: false, message: req.form.errors.join('\n') });
   };
 
   actions.api.securityPassportLdapSetting = function(req, res) {

+ 1 - 1
src/server/views/admin/app.html

@@ -2,7 +2,7 @@
 
 {% block html_title %}{{ customTitle(t('App settings')) }}{% endblock %}
 
-{% block head_warn %} {# remove including block for './widget/alert_siteurl_undefined.html' #}
+{% block head_warn_alert_siteurl_undefined %} {# remove including block for './widget/alert_siteurl_undefined.html' #}
 {% endblock %}
 
 {% block content_header %}

+ 2 - 2
src/server/views/admin/security.html

@@ -44,11 +44,11 @@
             <label for="settingForm[security:registrationMode]" class="col-xs-3 control-label">{{ t('Basic authentication') }}</label>
             <div class="col-xs-3">
               <label for="">ID</label>
-              <input class="form-control" type="text" name="settingForm[security:basicName]"   value="{{ settingForm['security:basicName']|default('') }}" {% if not isAclEnabled  %}readonly{% endif%}>
+              <input class="form-control" type="text" name="settingForm[security:basicName]" value="{{ settingForm['security:basicName']|default('') }}" autocomplete="nope" {% if not isAclEnabled  %}readonly{% endif%}>
             </div>
             <div class="col-xs-3">
               <label for="">{{ t('Password') }}</label>
-              <input class="form-control" type="text" name="settingForm[security:basicSecret]" value="{{ settingForm['security:basicSecret']|default('') }}" {% if not isAclEnabled  %}readonly{% endif%}>
+              <input class="form-control" type="text" name="settingForm[security:basicSecret]" value="{{ settingForm['security:basicSecret']|default('') }}" autocomplete="nope" {% if not isAclEnabled  %}readonly{% endif%}>
             </div>
             <div class="col-xs-offset-3 col-xs-9">
               <p class="help-block small">

+ 0 - 4
src/server/views/layout-crowi/base/layout.html

@@ -7,10 +7,6 @@
   {{ cdnScriptTag('highlight-addons') }}
 {% endblock %}
 
-{% block head_warn %}
-  {% include '../../widget/alert_siteurl_undefined.html' %}
-{% endblock %}
-
 {% block layout_main %}
 <div class="container-fluid">
 

+ 0 - 4
src/server/views/layout-growi/base/layout.html

@@ -5,10 +5,6 @@
   {{ cdnScriptTag('highlight-addons') }}
 {% endblock %}
 
-{% block head_warn %}
-  {% include '../../widget/alert_siteurl_undefined.html' %}
-{% endblock %}
-
 {% block layout_main %}
 <div class="container-fluid">
 

+ 0 - 4
src/server/views/layout-kibela/base/layout.html

@@ -5,10 +5,6 @@
   {{ cdnScriptTag('highlight-addons') }}
 {% endblock %}
 
-{% block head_warn %}
-  {% include '../../widget/alert_siteurl_undefined.html' %}
-{% endblock %}
-
 {% block layout_main %}
 <div class="container-fluid">
 

+ 2 - 1
src/server/views/layout/layout.html

@@ -207,7 +207,8 @@
   {% include '../modal/create_page.html' %}
   {% endblock  %} {# layout_head_nav #}
 
-  {% block head_warn %}{% endblock %}
+  {% block head_warn_alert_siteurl_undefined %}{% include '../widget/alert_siteurl_undefined.html' %}{% endblock %}
+  {% block head_warn_breaking_changes %}{% include '../widget/alert_breaking_changes.html' %}{% endblock %}
 
   {% block sidebar %}
   <!-- Left navbar-header -->

+ 1 - 1
src/server/views/login.html

@@ -204,7 +204,7 @@
         <hr class="mt-2 mb-0">
         <div class="text-center">
           <button class="collapse-anchor btn btn-xs btn-collapse-oauth mb-3" data-toggle="collapse" data-parent="#accordion" href="#collapse-oauth" aria-expanded="true" aria-controls="collapseOne">
-            OAuth
+            External Auth
           </button>
         </div>
         {% else %}

+ 0 - 4
src/server/views/search.html

@@ -10,10 +10,6 @@
   data-target="#search-result-list"
 {% endblock %}
 
-{% block head_warn %}
-  {% include './widget/alert_siteurl_undefined.html' %}
-{% endblock %}
-
 {% block layout_main %}
 <div class="container-fluid">
 

+ 15 - 0
src/server/views/widget/alert_breaking_changes.html

@@ -0,0 +1,15 @@
+{# Added in v3.4.6 }
+
+{% if getConfig('crowi', 'security:isEnabledPassport') !== true %}
+<div class="myadmin-alert alert alert-warning mb-0">
+  <i class="icon-exclamation"></i>
+  {{ t("breaking_changes.v346_passport_is_not_enabled", '<a href="/admin/security">' + t('Security settings') + '<i class="icon-login"></i></a>') }}
+</div>
+{% endif %}
+
+{% if getConfig('crowi', 'security:basicName') || getConfig('crowi', 'security:basicSecret') %}
+<div class="myadmin-alert alert alert-warning mb-0">
+  <i class="icon-exclamation"></i>
+  {{ t("breaking_changes.v346_using_basic_auth", '<a href="/admin/security">' + t('Security settings') + '<i class="icon-login"></i></a>') }}
+</div>
+{% endif %}

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

@@ -1,5 +1,5 @@
 {% if !getConfig('crowi', 'app:siteUrl') %}
-<div class="alert alert-danger mb-0">
+<div class="myadmin-alert alert alert-danger mb-0">
   <i class="icon-exclamation"></i>
   {{ t("security_setting.alert_siteUrl_is_not_set", '<a href="/admin/app">' + t('App settings') + '<i class="icon-login"></i></a>') }}
 </div>