]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10289 Update login page UI and allow to display identity providers help
authorStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 6 Feb 2018 15:41:32 +0000 (16:41 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 7 Feb 2018 15:43:01 +0000 (16:43 +0100)
36 files changed:
server/sonar-web/src/main/js/api/users.ts
server/sonar-web/src/main/js/app/components/NotFound.js
server/sonar-web/src/main/js/app/components/SimpleContainer.tsx
server/sonar-web/src/main/js/app/styles/components/ui.css
server/sonar-web/src/main/js/app/types.ts
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.js
server/sonar-web/src/main/js/apps/maintenance/main-view.js
server/sonar-web/src/main/js/apps/maintenance/templates/maintenance-main.hbs
server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx
server/sonar-web/src/main/js/apps/sessions/components/LoginForm.css
server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx
server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx
server/sonar-web/src/main/js/apps/sessions/components/Logout.tsx
server/sonar-web/src/main/js/apps/sessions/components/OAuthProviders.css
server/sonar-web/src/main/js/apps/sessions/components/OAuthProviders.tsx
server/sonar-web/src/main/js/apps/sessions/components/Unauthorized.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap
server/sonar-web/src/main/js/apps/users/UsersApp.tsx
server/sonar-web/src/main/js/apps/users/UsersList.tsx
server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/users/__tests__/UsersList.tsx [deleted file]
server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx
server/sonar-web/src/main/js/apps/users/components/PasswordForm.tsx
server/sonar-web/src/main/js/apps/users/components/TokensFormModal.tsx
server/sonar-web/src/main/js/apps/users/components/UserActions.tsx
server/sonar-web/src/main/js/apps/users/components/UserForm.tsx
server/sonar-web/src/main/js/apps/users/components/UserGroups.tsx
server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx
server/sonar-web/src/main/js/apps/users/components/UserListItemIdentity.tsx
server/sonar-web/src/main/js/components/ui/GenericAvatar.tsx
server/sonar-web/src/main/js/helpers/colors.ts [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 26e1470a477e1fa7aea8003d8a947d896c5df7a6..b8667f54e3884f995187baa22d885859cf1c9f83 100644 (file)
  */
 import { getJSON, post, postJSON, RequestData } from '../helpers/request';
 import throwGlobalError from '../app/utils/throwGlobalError';
-import { Paging, HomePage, CurrentUser } from '../app/types';
-
-export interface IdentityProvider {
-  backgroundColor: string;
-  iconPath: string;
-  key: string;
-  name: string;
-}
-
-export interface User {
-  login: string;
-  name: string;
-  active: boolean;
-  email?: string;
-  scmAccounts?: string[];
-  groups?: string[];
-  tokensCount?: number;
-  local: boolean;
-  externalIdentity?: string;
-  externalProvider?: string;
-  avatar?: string;
-}
+import { Paging, HomePage, CurrentUser, IdentityProvider, User } from '../app/types';
 
 export function getCurrentUser(): Promise<CurrentUser> {
   return getJSON('/api/users/current');
index 1b466451aea405798ea0afafa3cb7bfde9567cbd..3f1d81ac0be6611073d8140b9e274ca94719e0bb 100644 (file)
@@ -24,11 +24,17 @@ import SimpleContainer from './SimpleContainer';
 export default function NotFound() {
   return (
     <SimpleContainer>
-      <h2 className="big-spacer-bottom">The page you were looking for does not exist.</h2>
-      <p className="spacer-bottom">You may have mistyped the address or the page may have moved.</p>
-      <p>
-        <Link to="/">Go back to the homepage</Link>
-      </p>
+      <div id="bd" className="page-wrapper-simple">
+        <div id="nonav" className="page-simple">
+          <h2 className="big-spacer-bottom">The page you were looking for does not exist.</h2>
+          <p className="spacer-bottom">
+            You may have mistyped the address or the page may have moved.
+          </p>
+          <p>
+            <Link to="/">Go back to the homepage</Link>
+          </p>
+        </div>
+      </div>
     </SimpleContainer>
   );
 }
index 8db3e7299c38557f80995dd10680889266d192e6..b14e0475b70515a8287e2eff2b67456cca0a8009 100644 (file)
@@ -77,12 +77,7 @@ export default class SimpleContainer extends React.PureComponent<Props, State> {
       <div className="global-container">
         <div className="page-wrapper" id="container">
           <NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
-
-          <div id="bd" className="page-wrapper-simple">
-            <div id="nonav" className="page-simple">
-              {this.props.children}
-            </div>
-          </div>
+          {this.props.children}
         </div>
         <GlobalFooterContainer hideLoggedInInfo={this.props.hideLoggedInInfo} />
       </div>
index a88b0441216e7762d03506158166d5ad8cb30e33..e6bc2245ac792602d2158133aa4b3ebd48f3e942 100644 (file)
 .identity-provider {
   display: inline-block;
   line-height: 14px;
-  padding: 3px 5px;
+  padding: 2px 5px;
+  border: 1px solid rgba(0, 0, 0, 0.15);
   border-radius: 3px;
+  box-sizing: border-box;
+  background-color: var(--darkBlue);
   font-size: var(--smallFontSize);
   color: #fff;
 }
index 0d7fb8b6fec7f82dbbaa58d7f209e054e26c4798..9d380964a467c0ca31dbfaf25f016394524fca15 100644 (file)
@@ -269,3 +269,25 @@ export enum RuleScope {
   Test = 'TEST',
   All = 'ALL'
 }
+
+export interface IdentityProvider {
+  backgroundColor: string;
+  helpMessage?: string;
+  iconPath: string;
+  key: string;
+  name: string;
+}
+
+export interface User {
+  active: boolean;
+  avatar?: string;
+  email?: string;
+  externalIdentity?: string;
+  externalProvider?: string;
+  groups?: string[];
+  local: boolean;
+  login: string;
+  name: string;
+  scmAccounts?: string[];
+  tokensCount?: number;
+}
index 88b2589e0a655ab901fdc416d1fa29ac6484d234..89e595feed43cb0f6a569014b77aef4b9077d6d3 100644 (file)
@@ -19,6 +19,8 @@
  */
 import React from 'react';
 import { getIdentityProviders } from '../../../api/users';
+import * as theme from '../../../app/theme';
+import { getTextColor } from '../../../helpers/colors';
 
 export default class UserExternalIdentity extends React.PureComponent {
   state = {
@@ -80,8 +82,12 @@ export default class UserExternalIdentity extends React.PureComponent {
     return (
       <div
         className="identity-provider"
-        style={{ backgroundColor: identityProvider.backgroundColor }}>
+        style={{
+          backgroundColor: identityProvider.backgroundColor,
+          color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
+        }}>
         <img
+          className="little-spacer-right"
           src={window.baseUrl + identityProvider.iconPath}
           width="14"
           height="14"
index 080c221ab0fdacc0b1b838dc9ec5d63cac677b62..da558206ba25fbb2249ccd93477f281646a724b8 100644 (file)
@@ -80,12 +80,6 @@ export default Marionette.ItemView.extend({
     );
   },
 
-  onRender() {
-    document
-      .querySelector('.page-simple')
-      .classList.toggle('panel-warning', this.model.get('state') === 'MIGRATION_REQUIRED');
-  },
-
   loadPreviousPage() {
     setInterval(() => {
       window.location = this.options.returnTo || getBaseUrl();
index 3a7f14e72a352eafa6b10ac0afaf0ce8a510249a..44a4bc8798d9d3d720d57ef676e9d21033388360 100644 (file)
@@ -1,26 +1,32 @@
-{{#eq status 'OFFLINE'}}
+<div id="bd" class="page-wrapper-simple">
+  <div id="nonav" class="page-simple {{#eq state 'MIGRATION_REQUIRED'}}panel-warning{{/eq}}">
 
-  {{> '_maintenance-status-offline'}}
+    {{#eq status 'OFFLINE'}}
 
-{{else}}
+      {{> '_maintenance-status-offline'}}
 
-  {{#unless setup}}
+    {{else}}
 
-    {{#eq status 'UP'}}{{> '_maintenance-status-up'}}{{/eq}}
-    {{#eq status 'STARTING'}}{{> '_maintenance-status-starting'}}{{/eq}}
-    {{#eq status 'DOWN'}}{{> '_maintenance-status-down'}}{{/eq}}
-    {{#eq status 'DB_MIGRATION_NEEDED'}}{{> '_maintenance-status-migration'}}{{/eq}}
-    {{#eq status 'DB_MIGRATION_RUNNING'}}{{> '_maintenance-status-migration'}}{{/eq}}
+      {{#unless setup}}
 
-  {{else}}
+        {{#eq status 'UP'}}{{> '_maintenance-status-up'}}{{/eq}}
+        {{#eq status 'STARTING'}}{{> '_maintenance-status-starting'}}{{/eq}}
+        {{#eq status 'DOWN'}}{{> '_maintenance-status-down'}}{{/eq}}
+        {{#eq status 'DB_MIGRATION_NEEDED'}}{{> '_maintenance-status-migration'}}{{/eq}}
+        {{#eq status 'DB_MIGRATION_RUNNING'}}{{> '_maintenance-status-migration'}}{{/eq}}
 
-    {{#eq state 'NO_MIGRATION'}}{{> '_maintenance-state-no-migration'}}{{/eq}}
-    {{#eq state 'MIGRATION_REQUIRED'}}{{> '_maintenance-state-migration-required'}}{{/eq}}
-    {{#eq state 'NOT_SUPPORTED'}}{{> '_maintenance-state-migration-not-supported'}}{{/eq}}
-    {{#eq state 'MIGRATION_RUNNING'}}{{> '_maintenance-state-migration-running'}}{{/eq}}
-    {{#eq state 'MIGRATION_SUCCEEDED'}}{{> '_maintenance-state-migration-succeeded'}}{{/eq}}
-    {{#eq state 'MIGRATION_FAILED'}}{{> '_maintenance-state-migration-failed'}}{{/eq}}
+      {{else}}
 
-  {{/unless}}
+        {{#eq state 'NO_MIGRATION'}}{{> '_maintenance-state-no-migration'}}{{/eq}}
+        {{#eq state 'MIGRATION_REQUIRED'}}{{> '_maintenance-state-migration-required'}}{{/eq}}
+        {{#eq state 'NOT_SUPPORTED'}}{{> '_maintenance-state-migration-not-supported'}}{{/eq}}
+        {{#eq state 'MIGRATION_RUNNING'}}{{> '_maintenance-state-migration-running'}}{{/eq}}
+        {{#eq state 'MIGRATION_SUCCEEDED'}}{{> '_maintenance-state-migration-succeeded'}}{{/eq}}
+        {{#eq state 'MIGRATION_FAILED'}}{{> '_maintenance-state-migration-failed'}}{{/eq}}
 
-{{/eq}}
+      {{/unless}}
+
+    {{/eq}}
+
+  </div>
+</div>
index 2e2c92a8a614a56bbdec2b1d4847c82388270a95..fd9ece71a5760ec88cd311df504f2fd1c9f057c2 100644 (file)
  */
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
-import { getIdentityProviders, IdentityProvider } from '../../../api/users';
+import { getIdentityProviders } from '../../../api/users';
+import * as theme from '../../../app/theme';
+import { IdentityProvider } from '../../../app/types';
+import { getTextColor } from '../../../helpers/colors';
 import { translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/urls';
 
@@ -75,7 +78,10 @@ export default class EmailAlreadyExists extends React.PureComponent<Props, State
     return identityProvider ? (
       <div
         className="identity-provider"
-        style={{ backgroundColor: identityProvider.backgroundColor }}>
+        style={{
+          backgroundColor: identityProvider.backgroundColor,
+          color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
+        }}>
         <img
           alt={identityProvider.name}
           className="little-spacer-right"
@@ -96,41 +102,43 @@ export default class EmailAlreadyExists extends React.PureComponent<Props, State
     const { query } = this.props.location;
 
     return (
-      <div>
-        <div className="big-spacer-bottom js-existing-account">
-          <p className="little-spacer-bottom">
-            <FormattedMessage
-              defaultMessage={translate('sessions.email_already_exists.1')}
-              id="sessions.email_already_exists.1"
-              values={{ email: <strong>{query.email}</strong> }}
-            />
-          </p>
-          {this.renderIdentityProvier(query.existingProvider, query.existingLogin)}
-        </div>
+      <div id="bd" className="page-wrapper-simple">
+        <div id="nonav" className="page-simple">
+          <div className="big-spacer-bottom js-existing-account">
+            <p className="little-spacer-bottom">
+              <FormattedMessage
+                defaultMessage={translate('sessions.email_already_exists.1')}
+                id="sessions.email_already_exists.1"
+                values={{ email: <strong>{query.email}</strong> }}
+              />
+            </p>
+            {this.renderIdentityProvier(query.existingProvider, query.existingLogin)}
+          </div>
 
-        <div className="big-spacer-bottom js-new-account">
-          <p className="little-spacer-bottom">{translate('sessions.email_already_exists.2')}</p>
-          {this.renderIdentityProvier(query.provider, query.login)}
-        </div>
+          <div className="big-spacer-bottom js-new-account">
+            <p className="little-spacer-bottom">{translate('sessions.email_already_exists.2')}</p>
+            {this.renderIdentityProvier(query.provider, query.login)}
+          </div>
 
-        <div className="alert alert-warning">
-          {translate('sessions.email_already_exists.3')}
-          <ul className="list-styled">
-            <li className="spacer-top">{translate('sessions.email_already_exists.4')}</li>
-            <li className="spacer-top">{translate('sessions.email_already_exists.5')}</li>
-            <li className="spacer-top">{translate('sessions.email_already_exists.6')}</li>
-          </ul>
-        </div>
+          <div className="alert alert-warning">
+            {translate('sessions.email_already_exists.3')}
+            <ul className="list-styled">
+              <li className="spacer-top">{translate('sessions.email_already_exists.4')}</li>
+              <li className="spacer-top">{translate('sessions.email_already_exists.5')}</li>
+              <li className="spacer-top">{translate('sessions.email_already_exists.6')}</li>
+            </ul>
+          </div>
 
-        <div className="big-spacer-top text-right">
-          <a
-            className="button js-continue"
-            href={`${getBaseUrl()}/sessions/init/${query.provider}?allowEmailShift=true`}>
-            {translate('continue')}
-          </a>
-          <a className="big-spacer-left js-cancel" href={getBaseUrl() + '/'}>
-            {translate('cancel')}
-          </a>
+          <div className="big-spacer-top text-right">
+            <a
+              className="button js-continue"
+              href={`${getBaseUrl()}/sessions/init/${query.provider}?allowEmailShift=true`}>
+              {translate('continue')}
+            </a>
+            <a className="big-spacer-left js-cancel" href={getBaseUrl() + '/'}>
+              {translate('cancel')}
+            </a>
+          </div>
         </div>
       </div>
     );
index cd52e1e08513b388fe1c017f37c1c3889bac2bc7..fc74e4bba23c4ffec0f5ceb48ebafa67eee16efe 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+.login-page {
+  padding-top: 10vh;
+}
+
+.login-form {
+  width: 300px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
 .login-title {
   margin-bottom: 40px;
   line-height: 1.5;
index c581bfe000aa1b0d4a07c204a9d2707bafe4c5c0..136f0b0052281aa129f69bf89b7a4e61c25309f6 100644 (file)
@@ -21,8 +21,8 @@ import * as React from 'react';
 import { Link } from 'react-router';
 import OAuthProviders from './OAuthProviders';
 import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
+import { IdentityProvider } from '../../../app/types';
 import { translate } from '../../../helpers/l10n';
-import { IdentityProvider } from '../../../api/users';
 import './LoginForm.css';
 
 interface Props {
@@ -70,7 +70,7 @@ export default class LoginForm extends React.PureComponent<Props, State> {
       : translate('login.login_to_sonarqube');
 
     return (
-      <div id="login_form">
+      <div className="login-page" id="login_form">
         <h1 className="login-title text-center">{loginTitle}</h1>
 
         {this.props.identityProviders.length > 0 && (
@@ -90,7 +90,7 @@ export default class LoginForm extends React.PureComponent<Props, State> {
             </a>
           </div>
         ) : (
-          <form onSubmit={this.handleSubmit}>
+          <form className="login-form" onSubmit={this.handleSubmit}>
             <GlobalMessagesContainer />
 
             <div className="big-spacer-bottom">
index bedb56c1fb69475beefa3de52ddf52678d4b2239..c1b706544b0f5e3cf65491e39f0e505ef8e55d80 100644 (file)
@@ -22,7 +22,8 @@ import * as PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import LoginForm from './LoginForm';
 import { doLogin } from '../../../store/rootActions';
-import { IdentityProvider, getIdentityProviders } from '../../../api/users';
+import { getIdentityProviders } from '../../../api/users';
+import { IdentityProvider } from '../../../app/types';
 import { getBaseUrl } from '../../../helpers/urls';
 
 interface Props {
index 59f975dc9d79f21a30a04926eacd9ae53cb44c87..8a07c646df846c1b222a5b4d5cd08db14f60c877 100644 (file)
@@ -42,9 +42,9 @@ class Logout extends React.PureComponent<Props> {
 
   render() {
     return (
-      <div>
+      <div className="page page-limited">
         <GlobalMessagesContainer />
-        {translate('logging_out')}
+        <div className="text-center">{translate('logging_out')}</div>
       </div>
     );
   }
index 99a8d520790386b4889920eae2ac023f54fdfd41..01d8ef46d8d3f11a668b236772fa52b4ebb73fc5 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 .oauth-providers > ul {
-  display: flex;
-  justify-content: space-around;
-  flex-wrap: wrap;
+  width: 180px;
+  margin-left: auto;
+  margin-right: auto;
 }
 
 .oauth-providers > ul > li {
+  position: relative;
   margin-bottom: 30px;
 }
 
   width: 180px;
   line-height: 22px;
   padding: 8px 12px;
-  border: none;
+  border: 1px solid rgba(0, 0, 0, 0.15);
   border-radius: 2px;
   box-sizing: border-box;
   background-color: var(--darkBlue);
   color: #fff;
   white-space: nowrap;
   overflow: hidden;
-  text-align: center;
   text-overflow: ellipsis;
 }
 
 .oauth-providers > ul > li > a:hover,
 .oauth-providers > ul > li > a:focus {
-  box-shadow: inset 0 0 16px rgba(0, 0, 0, 0.3);
+  box-shadow: 0 0 16px rgba(0, 0, 0, 0.2);
 }
 
 .oauth-providers > ul > li > a > span {
   padding-left: 6px;
 }
 
-.oauth-providers + form {
+.oauth-providers-help {
+  position: absolute;
+  top: 12px;
+  right: -32px;
+}
+
+.oauth-providers + .login-form {
   padding-top: 30px;
   border-top: 1px solid var(--barBorderColor);
 }
index f008e9f984a31d34d53b59e517b1c9b5ceb45108..5664080f8288aa8a6806de8c9f97fb77de45fb22 100644 (file)
  */
 import * as React from 'react';
 import { translateWithParameters } from '../../../helpers/l10n';
-import { IdentityProvider } from '../../../api/users';
+import * as theme from '../../../app/theme';
+import { IdentityProvider } from '../../../app/types';
+import Tooltip from '../../../components/controls/Tooltip';
+import HelpIcon from '../../../components/icons-components/HelpIcon';
+import { getTextColor } from '../../../helpers/colors';
 import { getBaseUrl } from '../../../helpers/urls';
 import './OAuthProviders.css';
 
 interface Props {
-  formatLabel?: (name: string) => string;
   identityProviders: IdentityProvider[];
   returnTo: string;
 }
 
 export default function OAuthProviders(props: Props) {
-  const formatLabel = props.formatLabel || defaultFormatLabel;
   return (
     <section className="oauth-providers">
       <ul>
@@ -41,16 +43,25 @@ export default function OAuthProviders(props: Props) {
                 `${getBaseUrl()}/sessions/init/${identityProvider.key}` +
                 `?return_to=${encodeURIComponent(props.returnTo)}`
               }
-              style={{ backgroundColor: identityProvider.backgroundColor }}
-              title={formatLabel(identityProvider.name)}>
+              style={{
+                backgroundColor: identityProvider.backgroundColor,
+                color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
+              }}>
               <img
                 alt={identityProvider.name}
                 width="20"
                 height="20"
                 src={getBaseUrl() + identityProvider.iconPath}
               />
-              <span>{formatLabel(identityProvider.name)}</span>
+              <span>{defaultFormatLabel(identityProvider.name)}</span>
             </a>
+            {identityProvider.helpMessage && (
+              <Tooltip overlay={identityProvider.helpMessage}>
+                <div className="oauth-providers-help">
+                  <HelpIcon fill={theme.blue} />
+                </div>
+              </Tooltip>
+            )}
           </li>
         ))}
       </ul>
index 712fe6065a7ae5dc3edc3e0cc9c81c643ca4922c..c765e11ca1534593fc45c0b844c9615bc3cf0aa6 100644 (file)
@@ -33,17 +33,21 @@ export default function Unauthorized(props: Props) {
   const { message } = props.location.query;
 
   return (
-    <div className="text-center">
-      <p id="unauthorized">{translate('unauthorized.message')}</p>
+    <div id="bd" className="page-wrapper-simple">
+      <div id="nonav" className="page-simple">
+        <div className="text-center">
+          <p id="unauthorized">{translate('unauthorized.message')}</p>
 
-      {!!message && (
-        <p className="spacer-top">
-          {translate('unauthorized.reason')} {message}
-        </p>
-      )}
+          {!!message && (
+            <p className="spacer-top">
+              {translate('unauthorized.reason')} {message}
+            </p>
+          )}
 
-      <div className="big-spacer-top">
-        <Link to="/">{translate('layout.home')}</Link>
+          <div className="big-spacer-top">
+            <Link to="/">{translate('layout.home')}</Link>
+          </div>
+        </div>
       </div>
     </div>
   );
index 95ec95ce038da8f6d797d8b161395662228dd92e..096b7135b1c670a9dee14ce576fd87e2ffbfffa9 100644 (file)
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`render 1`] = `
-<div>
+<div
+  className="page-wrapper-simple"
+  id="bd"
+>
   <div
-    className="big-spacer-bottom js-existing-account"
+    className="page-simple"
+    id="nonav"
   >
-    <p
-      className="little-spacer-bottom"
+    <div
+      className="big-spacer-bottom js-existing-account"
     >
-      <FormattedMessage
-        defaultMessage="sessions.email_already_exists.1"
-        id="sessions.email_already_exists.1"
-        values={
+      <p
+        className="little-spacer-bottom"
+      >
+        <FormattedMessage
+          defaultMessage="sessions.email_already_exists.1"
+          id="sessions.email_already_exists.1"
+          values={
+            Object {
+              "email": <strong>
+                mail@example.com
+              </strong>,
+            }
+          }
+        />
+      </p>
+      <div
+        className="identity-provider"
+        style={
           Object {
-            "email": <strong>
-              mail@example.com
-            </strong>,
+            "backgroundColor": "#205081",
+            "color": "#fff",
           }
         }
-      />
-    </p>
+      >
+        <img
+          alt="Bitbucket"
+          className="little-spacer-right"
+          height="14"
+          src="/static/authbitbucket/bitbucket.svg"
+          width="14"
+        />
+        bar
+      </div>
+    </div>
     <div
-      className="identity-provider"
-      style={
-        Object {
-          "backgroundColor": "#205081",
-        }
-      }
+      className="big-spacer-bottom js-new-account"
     >
-      <img
-        alt="Bitbucket"
-        className="little-spacer-right"
-        height="14"
-        src="/static/authbitbucket/bitbucket.svg"
-        width="14"
-      />
-      bar
+      <p
+        className="little-spacer-bottom"
+      >
+        sessions.email_already_exists.2
+      </p>
+      <div
+        className="identity-provider"
+        style={
+          Object {
+            "backgroundColor": "#444444",
+            "color": "#fff",
+          }
+        }
+      >
+        <img
+          alt="GitHub"
+          className="little-spacer-right"
+          height="14"
+          src="/static/authgithub/github.svg"
+          width="14"
+        />
+        foo
+      </div>
     </div>
-  </div>
-  <div
-    className="big-spacer-bottom js-new-account"
-  >
-    <p
-      className="little-spacer-bottom"
-    >
-      sessions.email_already_exists.2
-    </p>
     <div
-      className="identity-provider"
-      style={
-        Object {
-          "backgroundColor": "#444444",
-        }
-      }
+      className="alert alert-warning"
     >
-      <img
-        alt="GitHub"
-        className="little-spacer-right"
-        height="14"
-        src="/static/authgithub/github.svg"
-        width="14"
-      />
-      foo
+      sessions.email_already_exists.3
+      <ul
+        className="list-styled"
+      >
+        <li
+          className="spacer-top"
+        >
+          sessions.email_already_exists.4
+        </li>
+        <li
+          className="spacer-top"
+        >
+          sessions.email_already_exists.5
+        </li>
+        <li
+          className="spacer-top"
+        >
+          sessions.email_already_exists.6
+        </li>
+      </ul>
     </div>
-  </div>
-  <div
-    className="alert alert-warning"
-  >
-    sessions.email_already_exists.3
-    <ul
-      className="list-styled"
+    <div
+      className="big-spacer-top text-right"
     >
-      <li
-        className="spacer-top"
+      <a
+        className="button js-continue"
+        href="/sessions/init/github?allowEmailShift=true"
       >
-        sessions.email_already_exists.4
-      </li>
-      <li
-        className="spacer-top"
+        continue
+      </a>
+      <a
+        className="big-spacer-left js-cancel"
+        href="/"
       >
-        sessions.email_already_exists.5
-      </li>
-      <li
-        className="spacer-top"
-      >
-        sessions.email_already_exists.6
-      </li>
-    </ul>
-  </div>
-  <div
-    className="big-spacer-top text-right"
-  >
-    <a
-      className="button js-continue"
-      href="/sessions/init/github?allowEmailShift=true"
-    >
-      continue
-    </a>
-    <a
-      className="big-spacer-left js-cancel"
-      href="/"
-    >
-      cancel
-    </a>
+        cancel
+      </a>
+    </div>
   </div>
 </div>
 `;
index 8c6b63082385301014f84e8bc07b44e20509b098..fddd44f6bcd7f7a5073f5c778bf8b08b802143ea 100644 (file)
@@ -2,6 +2,7 @@
 
 exports[`expands more options 1`] = `
 <div
+  className="login-page"
   id="login_form"
 >
   <h1
@@ -38,6 +39,7 @@ exports[`expands more options 1`] = `
 
 exports[`expands more options 2`] = `
 <div
+  className="login-page"
   id="login_form"
 >
   <h1
@@ -59,6 +61,7 @@ exports[`expands more options 2`] = `
     returnTo=""
   />
   <form
+    className="login-form"
     onSubmit={[Function]}
   >
     <Connect(GlobalMessages) />
@@ -130,6 +133,7 @@ exports[`expands more options 2`] = `
 
 exports[`logs in with identity provider 1`] = `
 <div
+  className="login-page"
   id="login_form"
 >
   <h1
@@ -166,6 +170,7 @@ exports[`logs in with identity provider 1`] = `
 
 exports[`logs in with simple credentials 1`] = `
 <div
+  className="login-page"
   id="login_form"
 >
   <h1
@@ -174,6 +179,7 @@ exports[`logs in with simple credentials 1`] = `
     login.login_to_sonarqube
   </h1>
   <form
+    className="login-form"
     onSubmit={[Function]}
   >
     <Connect(GlobalMessages) />
index 411ddc18db860134016ec79ab8de88b6ff45981a..f66cabe81ac057426013172dc62226d80d3a88ae 100644 (file)
@@ -26,8 +26,8 @@ import Search from './Search';
 import UsersList from './UsersList';
 import { parseQuery, Query, serializeQuery } from './utils';
 import ListFooter from '../../components/controls/ListFooter';
-import { getIdentityProviders, IdentityProvider, searchUsers, User } from '../../api/users';
-import { Paging } from '../../app/types';
+import { getIdentityProviders, searchUsers } from '../../api/users';
+import { Paging, IdentityProvider, User } from '../../app/types';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
index 556bf7828023f8f878b2568017bb4b9ba38eeeb7..de981b356f09deec78fd46c13878fe440afd7f97 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import UserListItem from './components/UserListItem';
-import { IdentityProvider, User } from '../../api/users';
+import { IdentityProvider, User } from '../../app/types';
 import { translate } from '../../helpers/l10n';
 
 interface Props {
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersList-test.tsx
new file mode 100644 (file)
index 0000000..ff95230
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import UsersList from '../UsersList';
+
+const users = [
+  {
+    login: 'luke',
+    name: 'Luke',
+    active: true,
+    scmAccounts: [],
+    local: false
+  },
+  {
+    login: 'obi',
+    name: 'One',
+    active: true,
+    scmAccounts: [],
+    local: false
+  }
+];
+
+it('should render correctly', () => {
+  expect(getWrapper()).toMatchSnapshot();
+});
+
+it('should show a group column', () => {
+  const wrapper = getWrapper({ organizationsEnabled: false });
+  expect(wrapper.find('th').filterWhere(elem => elem.text() === 'my_profile.groups')).toHaveLength(
+    1
+  );
+});
+
+function getWrapper(props = {}) {
+  return shallow(
+    <UsersList
+      currentUser={{ isLoggedIn: true, login: 'luke' }}
+      identityProviders={[
+        {
+          backgroundColor: 'blue',
+          iconPath: 'icon/path',
+          key: 'foo',
+          name: 'Foo Provider'
+        }
+      ]}
+      onUpdateUsers={jest.fn()}
+      organizationsEnabled={true}
+      updateTokensCount={jest.fn()}
+      users={users}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/UsersList.tsx b/server/sonar-web/src/main/js/apps/users/__tests__/UsersList.tsx
deleted file mode 100644 (file)
index ff95230..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import UsersList from '../UsersList';
-
-const users = [
-  {
-    login: 'luke',
-    name: 'Luke',
-    active: true,
-    scmAccounts: [],
-    local: false
-  },
-  {
-    login: 'obi',
-    name: 'One',
-    active: true,
-    scmAccounts: [],
-    local: false
-  }
-];
-
-it('should render correctly', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should show a group column', () => {
-  const wrapper = getWrapper({ organizationsEnabled: false });
-  expect(wrapper.find('th').filterWhere(elem => elem.text() === 'my_profile.groups')).toHaveLength(
-    1
-  );
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <UsersList
-      currentUser={{ isLoggedIn: true, login: 'luke' }}
-      identityProviders={[
-        {
-          backgroundColor: 'blue',
-          iconPath: 'icon/path',
-          key: 'foo',
-          name: 'Foo Provider'
-        }
-      ]}
-      onUpdateUsers={jest.fn()}
-      organizationsEnabled={true}
-      updateTokensCount={jest.fn()}
-      users={users}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList-test.tsx.snap
new file mode 100644 (file)
index 0000000..b3a9562
--- /dev/null
@@ -0,0 +1,70 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+  className="boxed-group boxed-group-inner"
+>
+  <table
+    className="data zebra"
+    id="users-list"
+  >
+    <thead>
+      <tr>
+        <th />
+        <th
+          className="nowrap"
+        />
+        <th
+          className="nowrap"
+        >
+          my_profile.scm_accounts
+        </th>
+        <th
+          className="nowrap"
+        >
+          users.tokens
+        </th>
+        <th
+          className="nowrap"
+        >
+           
+        </th>
+      </tr>
+    </thead>
+    <tbody>
+      <UserListItem
+        isCurrentUser={true}
+        key="luke"
+        onUpdateUsers={[MockFunction]}
+        organizationsEnabled={true}
+        updateTokensCount={[MockFunction]}
+        user={
+          Object {
+            "active": true,
+            "local": false,
+            "login": "luke",
+            "name": "Luke",
+            "scmAccounts": Array [],
+          }
+        }
+      />
+      <UserListItem
+        isCurrentUser={false}
+        key="obi"
+        onUpdateUsers={[MockFunction]}
+        organizationsEnabled={true}
+        updateTokensCount={[MockFunction]}
+        user={
+          Object {
+            "active": true,
+            "local": false,
+            "login": "obi",
+            "name": "One",
+            "scmAccounts": Array [],
+          }
+        }
+      />
+    </tbody>
+  </table>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap b/server/sonar-web/src/main/js/apps/users/__tests__/__snapshots__/UsersList.tsx.snap
deleted file mode 100644 (file)
index b3a9562..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
-  className="boxed-group boxed-group-inner"
->
-  <table
-    className="data zebra"
-    id="users-list"
-  >
-    <thead>
-      <tr>
-        <th />
-        <th
-          className="nowrap"
-        />
-        <th
-          className="nowrap"
-        >
-          my_profile.scm_accounts
-        </th>
-        <th
-          className="nowrap"
-        >
-          users.tokens
-        </th>
-        <th
-          className="nowrap"
-        >
-           
-        </th>
-      </tr>
-    </thead>
-    <tbody>
-      <UserListItem
-        isCurrentUser={true}
-        key="luke"
-        onUpdateUsers={[MockFunction]}
-        organizationsEnabled={true}
-        updateTokensCount={[MockFunction]}
-        user={
-          Object {
-            "active": true,
-            "local": false,
-            "login": "luke",
-            "name": "Luke",
-            "scmAccounts": Array [],
-          }
-        }
-      />
-      <UserListItem
-        isCurrentUser={false}
-        key="obi"
-        onUpdateUsers={[MockFunction]}
-        organizationsEnabled={true}
-        updateTokensCount={[MockFunction]}
-        user={
-          Object {
-            "active": true,
-            "local": false,
-            "login": "obi",
-            "name": "One",
-            "scmAccounts": Array [],
-          }
-        }
-      />
-    </tbody>
-  </table>
-</div>
-`;
index a88cab7893029eb36867c39f1ce19ee37836d41c..a6ad6f8372d5f6614a27c7105d20c8212f01d47f 100644 (file)
@@ -19,7 +19,8 @@
  */
 import * as React from 'react';
 import Modal from '../../../components/controls/Modal';
-import { deactivateUser, User } from '../../../api/users';
+import { deactivateUser } from '../../../api/users';
+import { User } from '../../../app/types';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export interface Props {
index 5a1bd050e79b1e88743226aa2bbbe48baa180672..fd6cc251dba3856c6b3490f743734a159c123433 100644 (file)
@@ -19,9 +19,9 @@
  */
 import * as React from 'react';
 import * as escapeHtml from 'escape-html';
+import { User } from '../../../app/types';
 import Modal from '../../../components/controls/Modal';
 import SelectList from '../../../components/SelectList';
-import { User } from '../../../api/users';
 import { translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/urls';
 
index 6e8b686899d0eb30e09f6f6c047db46c8b4bdfdf..053ece8b03de2b3df2843e7aae66bf1d4cfa1322 100644 (file)
@@ -21,8 +21,9 @@ import * as React from 'react';
 import Modal from '../../../components/controls/Modal';
 import addGlobalSuccessMessage from '../../../app/utils/addGlobalSuccessMessage';
 import throwGlobalError from '../../../app/utils/throwGlobalError';
+import { User } from '../../../app/types';
 import { parseError } from '../../../helpers/request';
-import { changePassword, User } from '../../../api/users';
+import { changePassword } from '../../../api/users';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
index 79cf17f38cca35d5532f84a7cd6c991031b21ab1..79676aeb23e63f6594c44bccf5dd08838ab39933 100644 (file)
@@ -38,8 +38,8 @@
  */
 import * as React from 'react';
 import TokensForm from './TokensForm';
+import { User } from '../../../app/types';
 import Modal from '../../../components/controls/Modal';
-import { User } from '../../../api/users';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
index 63b30598e4b82b98802f6d5b620e02b3eaf917d7..ccd1471a245a536c182323673ce9b2f6451e9477 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import DeactivateForm from './DeactivateForm';
 import PasswordForm from './PasswordForm';
 import UserForm from './UserForm';
-import { User } from '../../../api/users';
+import { User } from '../../../app/types';
 import ActionsDropdown, {
   ActionsDropdownItem,
   ActionsDropdownDivider
index ab3a5f75deb3183afc6102db1109727b9c848484..867d503b933e1219fb9dceacd8b506c9f4c5eb98 100644 (file)
@@ -23,7 +23,8 @@ import UserScmAccountInput from './UserScmAccountInput';
 import Modal from '../../../components/controls/Modal';
 import throwGlobalError from '../../../app/utils/throwGlobalError';
 import { parseError } from '../../../helpers/request';
-import { createUser, updateUser, User } from '../../../api/users';
+import { createUser, updateUser } from '../../../api/users';
+import { User } from '../../../app/types';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 export interface Props {
index f47c43058fa929e4f88b5defebb870e0137ba664..dceecaeafd19b0ad04a26483cd674b1460e9bc30 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import GroupsForm from './GroupsForm';
 import BulletListIcon from '../../../components/icons-components/BulletListIcon';
-import { User } from '../../../api/users';
+import { User } from '../../../app/types';
 import { ButtonIcon } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
index 8dc4a86944517901fa39a8fea66ddd5c5eb04687..4848a05c853c788fe3cfdc6a0debd64fa3caf8a3 100644 (file)
@@ -23,7 +23,7 @@ import UserActions from './UserActions';
 import UserGroups from './UserGroups';
 import UserListItemIdentity from './UserListItemIdentity';
 import UserScmAccounts from './UserScmAccounts';
-import { IdentityProvider, User } from '../../../api/users';
+import { IdentityProvider, User } from '../../../app/types';
 import BulletListIcon from '../../../components/icons-components/BulletListIcon';
 import Avatar from '../../../components/ui/Avatar';
 import { ButtonIcon } from '../../../components/ui/buttons';
index 13a82987ef9cb30f45f1ab250c090c9de2fcccac..a609e1c91452d26e91ca0b76aeb53437fa30cd32 100644 (file)
@@ -18,7 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { IdentityProvider, User } from '../../../api/users';
+import * as theme from '../../../app/theme';
+import { IdentityProvider, User } from '../../../app/types';
+import { getTextColor } from '../../../helpers/colors';
 import { getBaseUrl } from '../../../helpers/urls';
 
 interface Props {
@@ -57,9 +59,13 @@ export function ExternalProvider({ identityProvider, user }: Props) {
     <div className="js-user-identity-provider little-spacer-top">
       <div
         className="identity-provider"
-        style={{ 'background-color': identityProvider.backgroundColor }}>
+        style={{
+          'background-color': identityProvider.backgroundColor,
+          color: getTextColor(identityProvider.backgroundColor, theme.secondFontColor)
+        }}>
         <img
           alt={identityProvider.name}
+          className="little-spacer-right"
           src={getBaseUrl() + identityProvider.iconPath}
           width="14"
           height="14"
index 0eeee6e4cd5f3d0307f8081d52f036bb9379cc15..7c52a4cf0e1b525f5607e9b7f4d0144701c48486 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import * as classNames from 'classnames';
+import { stringToColor, getTextColor } from '../../helpers/colors';
 
 interface Props {
   className?: string;
@@ -56,26 +57,3 @@ export default function GenericAvatar({ className, name, size }: Props) {
     </div>
   );
 }
-
-/* eslint-disable no-bitwise, no-mixed-operators */
-function stringToColor(str: string) {
-  let hash = 0;
-  for (let i = 0; i < str.length; i++) {
-    hash = str.charCodeAt(i) + ((hash << 5) - hash);
-  }
-  let color = '#';
-  for (let i = 0; i < 3; i++) {
-    const value = (hash >> (i * 8)) & 0xff;
-    color += ('00' + value.toString(16)).substr(-2);
-  }
-  return color;
-}
-
-function getTextColor(background: string) {
-  const rgb = parseInt(background.substr(1), 16);
-  const r = (rgb >> 16) & 0xff;
-  const g = (rgb >> 8) & 0xff;
-  const b = (rgb >> 0) & 0xff;
-  const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-  return luma > 140 ? '#222' : '#fff';
-}
diff --git a/server/sonar-web/src/main/js/helpers/colors.ts b/server/sonar-web/src/main/js/helpers/colors.ts
new file mode 100644 (file)
index 0000000..3d26b37
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/* eslint-disable no-bitwise, no-mixed-operators */
+export function stringToColor(str: string) {
+  let hash = 0;
+  for (let i = 0; i < str.length; i++) {
+    hash = str.charCodeAt(i) + ((hash << 5) - hash);
+  }
+  let color = '#';
+  for (let i = 0; i < 3; i++) {
+    const value = (hash >> (i * 8)) & 0xff;
+    color += ('00' + value.toString(16)).substr(-2);
+  }
+  return color;
+}
+
+export function getTextColor(background: string, dark = '#222', light = '#fff') {
+  background = background.substr(1);
+  if (background.length === 3) {
+    // shortcut notation: #f90
+    background =
+      background[0] + background[0] + background[1] + background[1] + background[2] + background[2];
+  }
+  const rgb = parseInt(background.substr(1), 16);
+  const r = (rgb >> 16) & 0xff;
+  const g = (rgb >> 8) & 0xff;
+  const b = (rgb >> 0) & 0xff;
+  const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
+  return luma > 140 ? dark : light;
+}
index e9f5f8a0ed249a80bd25b0fa3664220b29b6919d..b64d7d91bc41a3e51cc3f0df4d306528bb8cf6c5 100644 (file)
@@ -528,11 +528,11 @@ process.fail=Failed
 
 sessions.log_in=Log in
 sessions.email_already_exists.1=The email address {email} is already associated to this user account:
-sessions.email_already_exists.2=By clicking on "Continue" you will associate this email address to a new user account:
+sessions.email_already_exists.2=By clicking on "Continue" you will associate this email address to another user account:
 sessions.email_already_exists.3=This means the following:
-sessions.email_already_exists.4=Your email address will be erased from this account.
+sessions.email_already_exists.4=Your email address will be erased from the first account.
 sessions.email_already_exists.5=You will no longer receive email notifications from this account.
-sessions.email_already_exists.6=Issues won't be automatically assigned on the first account anymore.
+sessions.email_already_exists.6=Issues won't be automatically assigned to this account anymore.
 
 
 #------------------------------------------------------------------------------