@@ -19,28 +19,7 @@ | |||
*/ | |||
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'); |
@@ -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> | |||
); | |||
} |
@@ -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> |
@@ -122,8 +122,11 @@ | |||
.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; | |||
} |
@@ -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; | |||
} |
@@ -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" |
@@ -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(); |
@@ -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> |
@@ -19,7 +19,10 @@ | |||
*/ | |||
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> | |||
); |
@@ -17,6 +17,16 @@ | |||
* 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; |
@@ -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"> |
@@ -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 { |
@@ -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> | |||
); | |||
} |
@@ -18,12 +18,13 @@ | |||
* 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; | |||
} | |||
@@ -32,27 +33,32 @@ | |||
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); | |||
} |
@@ -19,18 +19,20 @@ | |||
*/ | |||
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> |
@@ -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> | |||
); |
@@ -1,108 +1,118 @@ | |||
// 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> | |||
`; |
@@ -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) /> |
@@ -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 { |
@@ -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 { |
@@ -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 { |
@@ -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'; | |||
@@ -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 { |
@@ -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 { |
@@ -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 |
@@ -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 { |
@@ -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'; | |||
@@ -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'; |
@@ -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" |
@@ -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'; | |||
} |
@@ -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; | |||
} |
@@ -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. | |||
#------------------------------------------------------------------------------ |