*/
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');
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>
);
}
<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>
.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;
}
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;
+}
*/
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 = {
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"
);
},
- onRender() {
- document
- .querySelector('.page-simple')
- .classList.toggle('panel-warning', this.model.get('state') === 'MIGRATION_REQUIRED');
- },
-
loadPreviousPage() {
setInterval(() => {
window.location = this.options.returnTo || getBaseUrl();
-{{#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>
*/
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';
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"
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>
);
* 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;
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 {
: 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 && (
</a>
</div>
) : (
- <form onSubmit={this.handleSubmit}>
+ <form className="login-form" onSubmit={this.handleSubmit}>
<GlobalMessagesContainer />
<div className="big-spacer-bottom">
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 {
render() {
return (
- <div>
+ <div className="page page-limited">
<GlobalMessagesContainer />
- {translate('logging_out')}
+ <div className="text-center">{translate('logging_out')}</div>
</div>
);
}
* 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);
}
*/
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>
`${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>
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>
);
// 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>
`;
exports[`expands more options 1`] = `
<div
+ className="login-page"
id="login_form"
>
<h1
exports[`expands more options 2`] = `
<div
+ className="login-page"
id="login_form"
>
<h1
returnTo=""
/>
<form
+ className="login-form"
onSubmit={[Function]}
>
<Connect(GlobalMessages) />
exports[`logs in with identity provider 1`] = `
<div
+ className="login-page"
id="login_form"
>
<h1
exports[`logs in with simple credentials 1`] = `
<div
+ className="login-page"
id="login_form"
>
<h1
login.login_to_sonarqube
</h1>
<form
+ className="login-form"
onSubmit={[Function]}
>
<Connect(GlobalMessages) />
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 {
*/
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 {
--- /dev/null
+/*
+ * 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}
+ />
+ );
+}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
--- /dev/null
+// 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>
+`;
+++ /dev/null
-// 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>
-`;
*/
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 {
*/
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';
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 {
*/
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 {
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
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 {
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';
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';
* 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 {
<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"
*/
import * as React from 'react';
import * as classNames from 'classnames';
+import { stringToColor, getTextColor } from '../../helpers/colors';
interface Props {
className?: string;
</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';
-}
--- /dev/null
+/*
+ * 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;
+}
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.
#------------------------------------------------------------------------------