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

Merge remote-tracking branch 'origin/dev/4.0.x' into support/apply-bootstrap4

itizawa 5 лет назад
Родитель
Сommit
7aa589f9b3

+ 1 - 1
resource/locales/en-US/translation.json

@@ -55,7 +55,6 @@
   "Share Link": "Share Link",
   "Markdown Link": "Markdown Link",
   "Create/Edit Template": "Create/Edit Template Page",
-  "Unportalize": "Unportalize",
   "Go to this version": "View this version",
   "View diff": "View diff",
   "No diff": "No diff",
@@ -200,6 +199,7 @@
   "copy_to_clipboard": {
     "Copy to clipboard": "Copy to clipboard",
     "Page path": "Page path",
+    "Page URL": "Page URL",
     "Parmanent link": "Parmanent link",
     "Page path and parmanent link": "Page path and parmanent link",
     "Markdown link": "Markdown link"

+ 1 - 1
resource/locales/ja/translation.json

@@ -55,7 +55,6 @@
   "Share Link": "共有用リンク",
   "Markdown Link": "Markdown形式のリンク",
   "Create/Edit Template": "テンプレートページの作成/編集",
-  "Unportalize": "ポータル解除",
   "Go to this version": "このバージョンを見る",
   "View diff": "差分を表示",
   "No diff": "差分なし",
@@ -199,6 +198,7 @@
   "copy_to_clipboard": {
     "Copy to clipboard": "クリップボードにコピー",
     "Page path": "ページ名",
+    "Page URL": "ページURL",
     "Parmanent link": "パーマリンク",
     "Page path and parmanent link": "ページ名とパーマリンク",
     "Markdown link": "マークダウン形式のリンク"

+ 89 - 43
src/client/js/components/Page/CopyDropdown.jsx

@@ -15,9 +15,6 @@ class CopyDropdown extends React.Component {
   constructor(props) {
     super(props);
 
-    // retrieve xss library from window
-    this.xss = window.xss;
-
     this.state = {
       dropdownOpen: false,
       tooltipOpen: false,
@@ -25,7 +22,10 @@ class CopyDropdown extends React.Component {
 
     this.toggle = this.toggle.bind(this);
     this.showToolTip = this.showToolTip.bind(this);
-    this.generatePageUrl = this.generatePageUrl.bind(this);
+    this.generatePagePathWithParams = this.generatePagePathWithParams.bind(this);
+    this.generatePagePathUrl = this.generatePagePathUrl.bind(this);
+    this.generatePermalink = this.generatePermalink.bind(this);
+    this.generateMarkdownLink = this.generateMarkdownLink.bind(this);
   }
 
   toggle() {
@@ -39,25 +39,65 @@ class CopyDropdown extends React.Component {
     }, 1000);
   }
 
-  generatePageUrl() {
-    return (this.props.pageId == null)
-      ? decodeURIComponent(window.location.pathname + window.location.search)
-      : `${window.location.origin}/${this.props.pageId}`;
+  generatePagePathWithParams() {
+    const { pagePath } = this.props;
+    const {
+      search, hash,
+    } = window.location;
+
+    return decodeURI(`${pagePath}${search}${hash}`);
+  }
+
+  generatePagePathUrl() {
+    const { origin } = window.location;
+    return `${origin}${this.generatePagePathWithParams()}`;
+  }
+
+  generatePermalink() {
+    const { pageId } = this.props;
+    const { location } = window;
+
+    if (pageId == null) {
+      return null;
+    }
+
+    const {
+      origin, search, hash,
+    } = location;
+    return decodeURI(`${origin}/${pageId}${search}${hash}`);
   }
 
   generateMarkdownLink() {
-    return;
+    const { pagePath } = this.props;
+    const {
+      search, hash,
+    } = window.location;
+
+    const label = `${pagePath}${search}${hash}`;
+    const permalink = this.generatePermalink();
+
+    return decodeURI(`[${label}](${permalink})`);
   }
 
+  DropdownItemContents = ({ title, contents }) => (
+    <>
+      <div className="h6 mt-1 mb-2"><strong>{title}</strong></div>
+      <div className="card well mb-1 p-2">{contents}</div>
+    </>
+  );
+
   render() {
-    const { t } = this.props;
+    const { t, pageId } = this.props;
+
+    const pagePathWithParams = this.generatePagePathWithParams();
+    const pagePathUrl = this.generatePagePathUrl();
+    const permalink = this.generatePermalink();
 
-    const safePagePath = this.xss.process(this.props.pagePath);
-    const url = this.generatePageUrl();
+    const { DropdownItemContents } = this;
 
     return (
       <>
-        <Dropdown id="copyPagePathDropdown" isOpen={this.state.dropdownOpen} toggle={this.toggle}>
+        <Dropdown id="copyPagePathDropdown" className="grw-copy-dropdown" isOpen={this.state.dropdownOpen} toggle={this.toggle}>
 
           <DropdownToggle
             caret
@@ -68,49 +108,55 @@ class CopyDropdown extends React.Component {
           </DropdownToggle>
 
           <DropdownMenu>
-            <h5 className="ml-3 my-0 text-muted">{ t('copy_to_clipboard.Copy to clipboard') }</h5>
-            <DropdownItem divider></DropdownItem>
+            <DropdownItem header className="px-3">{ t('copy_to_clipboard.Copy to clipboard') }</DropdownItem>
+
+            <DropdownItem divider className="my-0"></DropdownItem>
 
             {/* Page path */}
-            <CopyToClipboard text={this.props.pagePath} onCopy={this.showToolTip}>
-              <DropdownItem tag="a">
-                <div className="d-inline-flex flex-column">
-                  <h6 className="mt-1 mb-2"><strong>{ t('copy_to_clipboard.Page path') }</strong></h6>
-                  <span className="small">{safePagePath}</span>
-                </div>
+            <CopyToClipboard text={pagePathWithParams} onCopy={this.showToolTip}>
+              <DropdownItem className="px-3">
+                <DropdownItemContents title={t('copy_to_clipboard.Page path')} contents={pagePathWithParams} />
+              </DropdownItem>
+            </CopyToClipboard>
+
+            <DropdownItem divider className="my-0"></DropdownItem>
+
+            {/* Page path URL */}
+            <CopyToClipboard text={pagePathUrl} onCopy={this.showToolTip}>
+              <DropdownItem className="px-3">
+                <DropdownItemContents title={t('copy_to_clipboard.Page URL')} contents={pagePathUrl} />
               </DropdownItem>
             </CopyToClipboard>
+
+            <DropdownItem divider className="my-0"></DropdownItem>
+
             {/* Parmanent Link */}
-            { this.props.pageId && (
-              <CopyToClipboard text={url} onCopy={this.showToolTip}>
-                <DropdownItem tag="a">
-                  <div className="d-inline-flex flex-column">
-                    <h6 className="mt-1 mb-2"><strong>{ t('copy_to_clipboard.Parmanent link') }</strong></h6>
-                    <span className="small">{url}</span>
-                  </div>
+            { pageId && (
+              <CopyToClipboard text={permalink} onCopy={this.showToolTip}>
+                <DropdownItem className="px-3">
+                  <DropdownItemContents title={t('copy_to_clipboard.Parmanent link')} contents={permalink} />
                 </DropdownItem>
               </CopyToClipboard>
             )}
+
+            <DropdownItem divider className="my-0"></DropdownItem>
+
             {/* Page path + Parmanent Link */}
-            { this.props.pageId && (
-              <CopyToClipboard text={`${this.props.pagePath}\n${url}`} onCopy={this.showToolTip}>
-                <DropdownItem tag="a">
-                  <div className="d-inline-flex flex-column">
-                    <h6 className="mt-1 mb-2"><strong>{ t('copy_to_clipboard.Page path and parmanent link') }</strong></h6>
-                    <span className="small mb-3">{safePagePath}</span>
-                    <span className="small">{url}</span>
-                  </div>
+            { pageId && (
+              <CopyToClipboard text={`${pagePathWithParams}\n${permalink}`} onCopy={this.showToolTip}>
+                <DropdownItem className="px-3">
+                  <DropdownItemContents title={t('copy_to_clipboard.Page path and parmanent link')} contents={<>{pagePathWithParams}<br />{permalink}</>} />
                 </DropdownItem>
               </CopyToClipboard>
             )}
+
+            <DropdownItem divider className="my-0"></DropdownItem>
+
             {/* Markdown Link */}
-            { this.props.pageId && (
-              <CopyToClipboard text={`[${this.props.pagePath}](${url})`} onCopy={this.showToolTip}>
-                <DropdownItem tag="a">
-                  <div className="d-inline-flex flex-column">
-                    <h6 className="mt-1 mb-2"><strong>{ t('copy_to_clipboard.Markdown link') }</strong></h6>
-                    <span className="small">{`[${safePagePath}](${url})`}</span>
-                  </div>
+            { pageId && (
+              <CopyToClipboard text={this.generateMarkdownLink()} onCopy={this.showToolTip}>
+                <DropdownItem className="px-3 text-wrap">
+                  <DropdownItemContents title={t('copy_to_clipboard.Markdown link')} contents={this.generateMarkdownLink()} isContentsWrap />
                 </DropdownItem>
               </CopyToClipboard>
             )}

+ 8 - 8
src/client/js/legacy/crowi.js

@@ -259,12 +259,12 @@ $(() => {
     return false;
   });
 
-  // rename/unportalize
-  $('#renamePage, #unportalize').on('shown.bs.modal', (e) => {
+  // rename
+  $('#renamePage').on('shown.bs.modal', (e) => {
     $('#renamePage #newPageName').focus();
-    $('#renamePage .msg, #unportalize .msg').hide();
+    $('#renamePage .msg').hide();
   });
-  $('#renamePageForm, #unportalize-form').submit(function(e) {
+  $('#renamePageForm').submit(function(e) {
     // create name-value map
     const nameValueMap = {};
     $(this).serializeArray().forEach((obj) => {
@@ -282,9 +282,9 @@ $(() => {
       // error
         if (!res.ok) {
           const linkPath = pathUtils.normalizePath(nameValueMap.new_path);
-          $('#renamePage .msg, #unportalize .msg').hide();
-          $(`#renamePage .msg-${res.code}, #unportalize .msg-${res.code}`).show();
-          $('#renamePage #linkToNewPage, #unportalize #linkToNewPage').html(`
+          $('#renamePage .msg').hide();
+          $(`#renamePage .msg-${res.code}`).show();
+          $('#renamePage #linkToNewPage').html(`
           <a href="${linkPath}">${linkPath} <i class="icon-login"></i></a>
         `);
         }
@@ -302,7 +302,7 @@ $(() => {
     $('#duplicatePage #duplicatePageName').focus();
     $('#duplicatePage .msg').hide();
   });
-  $('#duplicatePageForm, #unportalize-form').submit(function(e) {
+  $('#duplicatePageForm').submit(function(e) {
     // create name-value map
     const nameValueMap = {};
     $(this).serializeArray().forEach((obj) => {

+ 19 - 0
src/client/styles/scss/molecules/copy-dropdown.scss

@@ -0,0 +1,19 @@
+.grw-copy-dropdown {
+  .dropdown-menu {
+    .dropdown-header {
+      margin-bottom: 0.5em;
+      font-size: 1.1em;
+    }
+
+    // unset active styles
+    .dropdown-item:active {
+      color: unset;
+      background-color: unset;
+    }
+
+    .well {
+      font-size: 0.6em;
+      word-break: break-all;
+    }
+  }
+}

+ 3 - 0
src/client/styles/scss/style-app.scss

@@ -20,6 +20,9 @@
 @import 'atoms/spinners';
 @import 'atoms/custom_control';
 
+// molecules
+@import 'molecules/copy-dropdown';
+
 // growi component
 @import 'admin';
 @import 'attachments';

+ 5 - 9
src/server/routes/page.js

@@ -334,8 +334,8 @@ module.exports = function(crowi, app) {
     return res.render('page_presentation', renderVars);
   }
 
-  async function showPageListForCrowiBehavior(req, res, next) {
-    const portalPath = pathUtils.addTrailingSlash(getPathFromRequest(req));
+  async function showTopPage(req, res, next) {
+    const portalPath = req.path;
     const revisionId = req.query.revision;
 
     const view = 'customlayout-selector/page_list';
@@ -414,19 +414,15 @@ module.exports = function(crowi, app) {
   };
 
   actions.showTopPage = function(req, res) {
-    return showPageListForCrowiBehavior(req, res);
+    return showTopPage(req, res);
   };
 
   /**
-   * switch action by behaviorType
+   * Redirect to the page without trailing slash
    */
-  /* eslint-disable no-else-return */
   actions.showPageWithEndOfSlash = function(req, res, next) {
-    const path = getPathFromRequest(req); // end of slash should be omitted
-    // redirect and showPage action will be triggered
-    return res.redirect(path);
+    return res.redirect(pathUtils.removeTrailingSlash(req.path));
   };
-  /* eslint-enable no-else-return */
 
   /**
    * switch action

+ 0 - 1
src/server/views/layout-growi/page_list.html

@@ -55,6 +55,5 @@
   </div>
   <div id="crowi-modals">
     {% include '../widget/page_modals.html' %}
-    {% include '../modal/unportalize.html' %}
   </div>
 {% endblock %}

+ 0 - 1
src/server/views/layout-kibela/page_list.html

@@ -53,6 +53,5 @@
 </div>
 <div id="crowi-modals">
   {% include '../widget/page_modals.html' %}
-  {% include '../modal/unportalize.html' %}
 </div>
 {% endblock %}

+ 0 - 50
src/server/views/modal/unportalize.html

@@ -1,50 +0,0 @@
-{% if isTopPage() %}
-  {% set unportalizedPath = '/top-' + Date.now() %}
-{% else %}
-  {% set unportalizedPath = page.path|replace('(\/)$', '') %}
-{% endif %}
-  <div class="modal" id="unportalize">
-    <div class="modal-dialog">
-      <div class="modal-content">
-
-      <form role="form" id="unportalize-form" onsubmit="return false;">
-
-        <div class="modal-header bg-warning">
-          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-          <div class="modal-title">ポータル化を解除する</div>
-        </div>
-        <div class="modal-body">
-          <ul>
-           <li>このポータル化を解除し、通常のページに戻します。</li>
-          </ul>
-            <div class="form-group">
-              <p>
-                <label for="">このページ</label><br><code>{{ page.path }}</code>
-              </p>
-              <p>
-                <label for="">解除後のページ</label><br><code>{{ unportalizedPath }}</code>
-              </p>
-              {% if isTopPage() %}
-              <p class="alert alert-info">
-              このポータルはトップページのポータルのため、特別なページに移動します。
-              </p>
-              {% endif %}
-            </div>
-        </div>
-        <div class="modal-footer">
-          <div class="d-flex justify-content-between">
-            {% include '../widget/modal/page-api-error-messages.html' %}
-            <div>
-              <input type="hidden" name="_csrf" value="{{ csrf() }}">
-              <input type="hidden" name="path" value="{{ page.path }}">
-              <input type="hidden" name="new_path" value="{{ unportalizedPath }}">
-              <input type="hidden" name="page_id" value="{{ page._id.toString() }}">
-              <input type="hidden" name="revision_id" value="{{ page.revision._id.toString() }}">
-              <button type="submit" class="btn btn-warning">Unportalize</button>
-            </div>
-        </div>
-
-      </form>
-      </div><!-- /.modal-content -->
-    </div><!-- /.modal-dialog -->
-  </div><!-- /.modal -->

+ 0 - 7
src/server/views/widget/page_tabs.html

@@ -78,13 +78,6 @@
       </a>
       <div class="dropdown-menu dropdown-menu-right">
         <a class="dropdown-item" href="#" data-target="#create-template" data-toggle="modal"><i class="icon-fw icon-magic-wand"></i> {{ t('template.option_label.create/edit') }}</a>
-        {% if ('/' !== path) %}
-          <div class="dropdown-divider"></div>
-          <a class="dropdown-item" href="#" data-target="#unportalize" data-toggle="modal"><i class="fa fa-share"></i> {{ t('Unportalize') }}</a>
-          {% if isDeletablePage() %}
-            <a class="dropdown-item" href="#" data-target="#deletePage" data-toggle="modal"><i class="icon-fw icon-fire text-danger"></i> {{ t('Delete') }}</a>
-          {% endif %}
-        {% endif %}
       </div>
     </li>
     {% else %}

+ 11 - 15
src/server/views/widget/page_tabs_kibela.html

@@ -73,11 +73,9 @@
         <i class="icon-options-vertical"></i>
       </a>
       <div class="dropdown-menu dropdown-menu-right">
-        <a class="dropdown-item" href="#" data-target="#create-template" data-toggle="modal"><i class="icon-fw icon-magic-wand"></i> {{ t('template.option_label.create/edit') }}</a>
-        {% if ('/' !== path) %}
-        <div class="dropdown-divider"></div>
-        <a class="dropdown-item" href="#" data-target="#unportalize" data-toggle="modal"><i class="fa fa-share"></i> {{ t('Unportalize') }}</a></li>
-        {% endif %}
+        <a class="dropdown-item" href="#" data-target="#create-template" data-toggle="modal">
+          <i class="icon-fw icon-magic-wand"></i> {{ t('template.option_label.create/edit') }}
+        </a>
       </div>
     </li>
     {% else %}
@@ -91,18 +89,16 @@
       >
         <i class="icon-options-vertical"></i>
       </a>
-      <ul class="dropdown-menu dropdown-menu-right">
-        <li class="dropdown-item"><a href="#" data-target="#renamePage" data-toggle="modal"><i class="icon-fw icon-action-redo"></i> {{ t('Move/Rename') }}</a></li>
-        <li class="dropdown-item"><a href="#" data-target="#duplicatePage" data-toggle="modal"><i class="icon-fw icon-docs"></i> {{ t('Duplicate') }}</a></li>
-        <li class="dropdown-divider"></li>
-        <li class="dropdown-item">
-          <a href="#" data-target="#create-template" data-toggle="modal"><i class="icon-fw icon-magic-wand"></i> {{ t('template.option_label.create/edit') }}</a>
-        </li>
+      <div class="dropdown-menu dropdown-menu-right">
+        <a class="dropdown-item" href="#" data-target="#renamePage" data-toggle="modal"><i class="icon-fw icon-action-redo"></i> {{ t('Move/Rename') }}</a>
+        <a class="dropdown-item" href="#" data-target="#duplicatePage" data-toggle="modal"><i class="icon-fw icon-docs"></i> {{ t('Duplicate') }}</a>
+        <div class="dropdown-divider"></div>
+        <a class="dropdown-item" href="#" data-target="#create-template" data-toggle="modal"><i class="icon-fw icon-magic-wand"></i> {{ t('template.option_label.create/edit') }}</a>
         {% if isDeletablePage() %}
-        <li class="dropdown-divider"></li>
-        <li class="dropdown-item"><a href="#" data-target="#deletePage" data-toggle="modal"><i class="icon-fw icon-fire text-danger"></i> {{ t('Delete') }}</a></li>
+        <div class="dropdown-divider"></div>
+        <a class="dropdown-item" href="#" data-target="#deletePage" data-toggle="modal"><i class="icon-fw icon-fire text-danger"></i> {{ t('Delete') }}</a>
         {% endif %}
-      </ul>
+      </div>
     </li>
     {% endif %}
   {% endif %}