Merge "Improve error dialog for 404 errors"
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html
index 09b928e..ffd7f896 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.html
@@ -34,6 +34,9 @@
           max-width: 50em;
         }
       }
+      .signInLink {
+        text-decoration: none;
+      }
     </style>
     <gr-dialog
         id="dialog"
@@ -43,6 +46,14 @@
         confirm-on-enter>
       <div class="header" slot="header">An error occurred</div>
       <div class="main" slot="main">[[text]]</div>
+      <gr-button
+          id="signIn"
+          class$="signInLink"
+          hidden$="[[!showSignInButton]]"
+          link
+          slot="footer">
+        <a href$="[[loginUrl]]" class="signInLink">Sign in</a>
+      </gr-button>
     </gr-dialog>
   </template>
   <script src="gr-error-dialog.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
index db70d57..63339c9 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
@@ -31,6 +31,20 @@
     static get properties() {
       return {
         text: String,
+        /**
+         * loginUrl to open on "sign in" button click
+         */
+        loginUrl: {
+          type: String,
+          value: '/login',
+        },
+        /**
+         * Show/hide "Sign In" button in dialog
+         */
+        showSignInButton: {
+          type: Boolean,
+          value: false,
+        },
       };
     }
 
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
index 03d8d6a..104d5b0 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.html
@@ -34,7 +34,9 @@
           id="errorDialog"
           on-dismiss="_handleDismissErrorDialog"
           confirm-label="Dismiss"
-          confirm-on-enter></gr-error-dialog>
+          confirm-on-enter
+          login-url="[[loginUrl]]"
+      ></gr-error-dialog>
     </gr-overlay>
     <gr-overlay
       id="noInteractionOverlay"
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index b6e8fc9..b828774 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -62,6 +62,11 @@
           type: Number,
           value() { return Date.now(); },
         },
+
+        loginUrl: {
+          type: String,
+          value: '/login',
+        },
       };
     }
 
@@ -139,20 +144,55 @@
           this._authService.clearCache();
           this.$.restAPI.getLoggedIn();
         } else if (!this._shouldSuppressError(errorText)) {
-          this._showErrorDialog(this._constructServerErrorMsg({
-            status,
-            statusText,
-            errorText,
-            url,
-            trace: response.headers && response.headers.get('X-Gerrit-Trace'),
-          }));
+          const trace =
+              response.headers && response.headers.get('X-Gerrit-Trace');
+          if (response.status === 404) {
+            this._showNotFoundMessageWithTip({
+              status,
+              statusText,
+              errorText,
+              url,
+              trace,
+            });
+          } else {
+            this._showErrorDialog(this._constructServerErrorMsg({
+              status,
+              statusText,
+              errorText,
+              url,
+              trace,
+            }));
+          }
         }
         console.log(`server error: ${errorText}`);
       });
     }
 
-    _constructServerErrorMsg({errorText, status, statusText, url, trace}) {
-      let err = `Error ${status}`;
+    _showNotFoundMessageWithTip({status, statusText, errorText, url, trace}) {
+      this.$.restAPI.getLoggedIn().then(isLoggedIn => {
+        const tip = isLoggedIn ?
+          'You might have not enough privileges.' :
+          'You might have not enough privileges. Sign in and try again.';
+        this._showErrorDialog(this._constructServerErrorMsg({
+          status,
+          statusText,
+          errorText,
+          url,
+          trace,
+          tip,
+        }), {
+          showSignInButton: !isLoggedIn,
+        });
+      });
+      return;
+    }
+
+    _constructServerErrorMsg({errorText, status, statusText, url, trace, tip}) {
+      let err = '';
+      if (tip) {
+        err += `${tip}\n\n`;
+      }
+      err += `Error ${status}`;
       if (statusText) { err += ` (${statusText})`; }
       if (errorText || url) { err += ': '; }
       if (errorText) { err += errorText; }
@@ -344,12 +384,14 @@
       this.$.errorOverlay.close();
     }
 
-    _showErrorDialog(message) {
+    _showErrorDialog(message, opt_options) {
       this.$.reporting.reportErrorDialog(message);
       this.$.errorDialog.text = message;
+      this.$.errorDialog.showSignInButton =
+          opt_options && opt_options.showSignInButton;
       this.$.errorOverlay.open();
     }
   }
 
   customElements.define(GrErrorManager.is, GrErrorManager);
-})();
\ No newline at end of file
+})();
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index e208148..c438047 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -220,7 +220,7 @@
               [[_registerText]]
             </a>
           </div>
-          <a class="loginButton" href$="[[_loginURL]]">Sign in</a>
+          <a class="loginButton" href$="[[loginUrl]]">Sign in</a>
           <a
               class="settingsButton"
               href$="[[_generateSettingsLink()]]"
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index 53c5ef5..05765fb 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -122,7 +122,7 @@
           computed: '_computeLinks(_defaultLinks, _userLinks, _adminLinks, ' +
             '_topMenus, _docBaseUrl)',
         },
-        _loginURL: {
+        loginUrl: {
           type: String,
           value: '/login',
         },
@@ -162,36 +162,17 @@
       super.attached();
       this._loadAccount();
       this._loadConfig();
-      this.listen(window, 'location-change', '_handleLocationChange');
     }
 
     /** @override */
     detached() {
       super.detached();
-      this.unlisten(window, 'location-change', '_handleLocationChange');
     }
 
     reload() {
       this._loadAccount();
     }
 
-    _handleLocationChange(e) {
-      const baseUrl = this.getBaseUrl();
-      if (baseUrl) {
-        // Strip the canonical path from the path since needing canonical in
-        // the path is uneeded and breaks the url.
-        this._loginURL = baseUrl + '/login/' + encodeURIComponent(
-            '/' + window.location.pathname.substring(baseUrl.length) +
-            window.location.search +
-            window.location.hash);
-      } else {
-        this._loginURL = '/login/' + encodeURIComponent(
-            window.location.pathname +
-            window.location.search +
-            window.location.hash);
-      }
-    }
-
     _computeRelativeURL(path) {
       return '//' + window.location.host + this.getBaseUrl() + path;
     }
diff --git a/polygerrit-ui/app/elements/gr-app-element.html b/polygerrit-ui/app/elements/gr-app-element.html
index f758280..6c334b5 100644
--- a/polygerrit-ui/app/elements/gr-app-element.html
+++ b/polygerrit-ui/app/elements/gr-app-element.html
@@ -126,7 +126,9 @@
       <gr-main-header
           id="mainHeader"
           search-query="{{params.query}}"
-          on-mobile-search="_mobileSearchToggle">
+          on-mobile-search="_mobileSearchToggle"
+          login-url="[[_loginUrl]]"
+      >
       </gr-main-header>
     </gr-fixed-panel>
     <main>
@@ -222,7 +224,7 @@
       </gr-registration-dialog>
     </gr-overlay>
     <gr-endpoint-decorator name="plugin-overlay"></gr-endpoint-decorator>
-    <gr-error-manager id="errorManager"></gr-error-manager>
+    <gr-error-manager id="errorManager" login-url="[[_loginUrl]]"></gr-error-manager>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
     <gr-reporting id="reporting"></gr-reporting>
     <gr-router id="router"></gr-router>
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index c1e317a..e9b994d 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -94,6 +94,15 @@
           type: Boolean,
           value: false,
         },
+
+        /**
+         * Other elements in app must open this URL when
+         * user login is required.
+         */
+        _loginUrl: {
+          type: String,
+          value: '/login',
+        },
       };
     }
 
@@ -132,6 +141,7 @@
     /** @override */
     ready() {
       super.ready();
+      this._updateLoginUrl();
       this.$.reporting.appStarted();
       this.$.router.start();
 
@@ -385,6 +395,8 @@
     }
 
     _handleLocationChange(e) {
+      this._updateLoginUrl();
+
       const hash = e.detail.hash.substring(1);
       let pathname = e.detail.pathname;
       if (pathname.startsWith('/c/') && parseInt(hash, 10) > 0) {
@@ -393,6 +405,23 @@
       this.set('_path', pathname);
     }
 
+    _updateLoginUrl() {
+      const baseUrl = this.getBaseUrl();
+      if (baseUrl) {
+        // Strip the canonical path from the path since needing canonical in
+        // the path is uneeded and breaks the url.
+        this._loginUrl = baseUrl + '/login/' + encodeURIComponent(
+            '/' + window.location.pathname.substring(baseUrl.length) +
+            window.location.search +
+            window.location.hash);
+      } else {
+        this._loginUrl = '/login/' + encodeURIComponent(
+            window.location.pathname +
+            window.location.search +
+            window.location.hash);
+      }
+    }
+
     _paramsChanged(paramsRecord) {
       const params = paramsRecord.base;
       const viewsToCheck = [Gerrit.Nav.View.SEARCH, Gerrit.Nav.View.DASHBOARD];