Browse Source

SONAR-10274 Better page title handling for SonarCloud

tags/7.5
Grégoire Aubert 6 years ago
parent
commit
8fdadf9937

+ 8
- 2
server/sonar-web/src/main/js/app/components/App.tsx View File

@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { connect } from 'react-redux';
import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import GlobalLoading from './GlobalLoading';
import { CurrentUser } from '../types';
import { fetchCurrentUser } from '../../store/users/actions';
@@ -117,7 +118,12 @@ class App extends React.PureComponent<Props, State> {
if (this.state.loading) {
return <GlobalLoading />;
}
return this.props.children;
return (
<>
<Helmet defaultTitle={this.state.onSonarCloud ? 'SonarCloud' : 'SonarQube'} />
{this.props.children}
</>
);
}
}


+ 76
- 0
server/sonar-web/src/main/js/app/components/AppContextContainer.tsx View File

@@ -0,0 +1,76 @@
/*
* 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 * as PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import GlobalLoading from './GlobalLoading';
import { tryGetGlobalNavigation } from '../../api/nav';

interface Props {
children?: React.ReactNode;
}

interface State {
loading: boolean;
onSonarCloud: boolean;
}

export default class AppContextContainer extends React.PureComponent<Props, State> {
mounted = false;
static childContextTypes = { onSonarCloud: PropTypes.bool };
state: State = { loading: true, onSonarCloud: false };

getChildContext() {
return { onSonarCloud: this.state.onSonarCloud };
}

componentDidMount() {
this.mounted = true;
tryGetGlobalNavigation().then(
appState => {
if (this.mounted) {
this.setState({
loading: false,
onSonarCloud: Boolean(
appState.settings && appState.settings['sonar.sonarcloud.enabled'] === 'true'
)
});
}
},
() => {}
);
}

componentWillUnmount() {
this.mounted = false;
}

render() {
if (this.state.loading) {
return <GlobalLoading />;
}
return (
<>
<Helmet defaultTitle={this.state.onSonarCloud ? 'SonarCloud' : 'SonarQube'} />
{this.props.children}
</>
);
}
}

+ 0
- 40
server/sonar-web/src/main/js/app/components/DefaultHelmetContainer.js View File

@@ -1,40 +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 React from 'react';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
import { getGlobalSettingValue } from '../../store/rootReducer';

function DefaultHelmetContainer({ children, onSonarCloud }) {
return (
<div>
<Helmet
defaultTitle={onSonarCloud && onSonarCloud.value === 'true' ? 'SonarCloud' : 'SonarQube'}
/>
{children}
</div>
);
}

const mapStateToProps = state => ({
onSonarCloud: getGlobalSettingValue(state, 'sonar.sonarcloud.enabled')
});

export default connect(mapStateToProps)(DefaultHelmetContainer);

+ 8
- 4
server/sonar-web/src/main/js/app/components/GlobalLoading.tsx View File

@@ -18,12 +18,16 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import Helmet from 'react-helmet';

export default function GlobalLoading() {
return (
<div className="global-loading">
<i className="spinner global-loading-spinner" />
<span className="global-loading-text">Loading...</span>
</div>
<>
<Helmet defaultTitle={'Loading...'} />
<div className="global-loading">
<i className="spinner global-loading-spinner" />
<span className="global-loading-text">Loading...</span>
</div>
</>
);
}

+ 2
- 2
server/sonar-web/src/main/js/app/components/LocalizationContainer.tsx View File

@@ -72,8 +72,8 @@ export default class LocalizationContainer extends React.PureComponent<Props, St
}
return (
<IntlProvider
locale={this.state.lang || DEFAULT_LANGUAGE}
defaultLocale={this.state.lang || DEFAULT_LANGUAGE}>
defaultLocale={this.state.lang || DEFAULT_LANGUAGE}
locale={this.state.lang || DEFAULT_LANGUAGE}>
{this.props.children}
</IntlProvider>
);

+ 9
- 55
server/sonar-web/src/main/js/app/components/SimpleContainer.tsx View File

@@ -18,11 +18,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import * as PropTypes from 'prop-types';
import GlobalLoading from './GlobalLoading';
import GlobalFooterContainer from './GlobalFooterContainer';
import * as theme from '../theme';
import { tryGetGlobalNavigation } from '../../api/nav';
import NavBar from '../../components/nav/NavBar';

interface Props {
@@ -30,57 +27,14 @@ interface Props {
hideLoggedInInfo?: boolean;
}

interface State {
loading: boolean;
onSonarCloud: boolean;
}

export default class SimpleContainer extends React.PureComponent<Props, State> {
mounted = false;

static childContextTypes = {
onSonarCloud: PropTypes.bool
};

state: State = { loading: true, onSonarCloud: false };

getChildContext() {
return { onSonarCloud: this.state.onSonarCloud };
}

componentDidMount() {
this.mounted = true;
tryGetGlobalNavigation().then(
appState => {
if (this.mounted) {
this.setState({
loading: false,
onSonarCloud: Boolean(
appState.settings && appState.settings['sonar.sonarcloud.enabled'] === 'true'
)
});
}
},
() => {}
);
}

componentWillUnmount() {
this.mounted = false;
}

render() {
if (this.state.loading) {
return <GlobalLoading />;
}
return (
<div className="global-container">
<div className="page-wrapper" id="container">
<NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
{this.props.children}
</div>
<GlobalFooterContainer hideLoggedInInfo={this.props.hideLoggedInInfo} />
export default function SimpleContainer({ children, hideLoggedInInfo }: Props) {
return (
<div className="global-container">
<div className="page-wrapper" id="container">
<NavBar className="navbar-global" height={theme.globalNavHeightRaw} />
{children}
</div>
);
}
<GlobalFooterContainer hideLoggedInInfo={hideLoggedInInfo} />
</div>
);
}

+ 77
- 80
server/sonar-web/src/main/js/app/utils/startReactApp.js View File

@@ -17,13 +17,14 @@
* 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 react/jsx-sort-props */
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, Redirect } from 'react-router';
import { Provider } from 'react-redux';
import getStore from './getStore';
import getHistory from './getHistory';
import DefaultHelmetContainer from '../components/DefaultHelmetContainer';
import AppContextContainer from '../components/AppContextContainer';
import LocalizationContainer from '../components/LocalizationContainer';
import MigrationContainer from '../components/MigrationContainer';
import App from '../components/App';
@@ -148,99 +149,95 @@ const startReactApp = () => {

<Route path="markdown/help" component={MarkdownHelp} />

<Route component={DefaultHelmetContainer}>
<Route component={LocalizationContainer}>
<Route component={SimpleContainer}>
<Route path="maintenance">{maintenanceRoutes}</Route>
<Route path="setup">{setupRoutes}</Route>
</Route>
<Route component={LocalizationContainer}>
<Route component={SimpleContainer}>
<Route path="maintenance">{maintenanceRoutes}</Route>
<Route path="setup">{setupRoutes}</Route>
</Route>

<Route component={MigrationContainer}>
<Route component={MigrationContainer}>
<Route component={AppContextContainer}>
<Route component={SimpleSessionsContainer}>
<Route path="/sessions" childRoutes={sessionsRoutes} />
</Route>
</Route>

<Route path="/" component={App}>
<IndexRoute component={Landing} />

<Route component={GlobalContainer}>
<Route path="about" childRoutes={aboutRoutes} />
<Route path="account" childRoutes={accountRoutes} />
<Route path="coding_rules" childRoutes={codingRulesRoutes} />
<Route path="component" childRoutes={componentRoutes} />
<Route path="explore" component={Explore}>
<Route path="issues" component={ExploreIssues} />
<Route path="projects" component={ExploreProjects} />
</Route>
<Route path="/" component={App}>
<IndexRoute component={Landing} />

<Route component={GlobalContainer}>
<Route path="about" childRoutes={aboutRoutes} />
<Route path="account" childRoutes={accountRoutes} />
<Route path="coding_rules" childRoutes={codingRulesRoutes} />
<Route path="component" childRoutes={componentRoutes} />
<Route path="explore" component={Explore}>
<Route path="issues" component={ExploreIssues} />
<Route path="projects" component={ExploreProjects} />
</Route>
<Route path="extension/:pluginKey/:extensionKey" component={GlobalPageExtension} />
<Route path="issues" component={IssuesPageSelector} />
<Route path="organizations" childRoutes={organizationsRoutes} />
<Route path="projects" childRoutes={projectsRoutes} />
<Route path="quality_gates" childRoutes={qualityGatesRoutes} />
<Route path="portfolios" component={PortfoliosPage} />
<Route path="profiles" childRoutes={qualityProfilesRoutes} />
<Route path="web_api" childRoutes={webAPIRoutes} />

<Route
getComponent={() =>
import('../components/ComponentContainer').then(i => i.default)
}>
<Route path="code" childRoutes={codeRoutes} />
<Route path="component_measures" childRoutes={componentMeasuresRoutes} />
<Route path="dashboard" childRoutes={overviewRoutes} />
<Route path="portfolio" childRoutes={portfolioRoutes} />
<Route path="project/activity" childRoutes={projectActivityRoutes} />
<Route
path="extension/:pluginKey/:extensionKey"
component={GlobalPageExtension}
path="project/extension/:pluginKey/:extensionKey"
component={ProjectPageExtension}
/>
<Route path="issues" component={IssuesPageSelector} />
<Route path="organizations" childRoutes={organizationsRoutes} />
<Route path="projects" childRoutes={projectsRoutes} />
<Route path="quality_gates" childRoutes={qualityGatesRoutes} />
<Route path="portfolios" component={PortfoliosPage} />
<Route path="profiles" childRoutes={qualityProfilesRoutes} />
<Route path="web_api" childRoutes={webAPIRoutes} />

<Route path="project/issues" component={Issues} />
<Route path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
<Route
getComponent={() =>
import('../components/ComponentContainer').then(i => i.default)
}>
<Route path="code" childRoutes={codeRoutes} />
<Route path="component_measures" childRoutes={componentMeasuresRoutes} />
<Route path="dashboard" childRoutes={overviewRoutes} />
<Route path="portfolio" childRoutes={portfolioRoutes} />
<Route path="project/activity" childRoutes={projectActivityRoutes} />
<Route
path="project/extension/:pluginKey/:extensionKey"
component={ProjectPageExtension}
/>
<Route path="project/issues" component={Issues} />
<Route path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
<Route
path="project/quality_profiles"
childRoutes={projectQualityProfilesRoutes}
/>
<Route component={ProjectAdminContainer}>
<Route path="custom_measures" childRoutes={customMeasuresRoutes} />
<Route
path="project/admin/extension/:pluginKey/:extensionKey"
component={ProjectAdminPageExtension}
/>
<Route path="project/background_tasks" childRoutes={backgroundTasksRoutes} />
<Route path="project/branches" childRoutes={projectBranchesRoutes} />
<Route path="project/settings" childRoutes={settingsRoutes} />
<Route path="project_roles" childRoutes={projectPermissionsRoutes} />
<Route path="project/webhooks" childRoutes={webhooksRoutes} />
</Route>
{projectAdminRoutes}
</Route>

<Route component={AdminContainer} path="admin">
path="project/quality_profiles"
childRoutes={projectQualityProfilesRoutes}
/>
<Route component={ProjectAdminContainer}>
<Route path="custom_measures" childRoutes={customMeasuresRoutes} />
<Route
path="extension/:pluginKey/:extensionKey"
component={GlobalAdminPageExtension}
path="project/admin/extension/:pluginKey/:extensionKey"
component={ProjectAdminPageExtension}
/>
<Route path="background_tasks" childRoutes={backgroundTasksRoutes} />
<Route path="custom_metrics" childRoutes={customMetricsRoutes} />
<Route path="groups" childRoutes={groupsRoutes} />
<Route path="permission_templates" childRoutes={permissionTemplatesRoutes} />
<Route path="roles/global" childRoutes={globalPermissionsRoutes} />
<Route path="permissions" childRoutes={globalPermissionsRoutes} />
<Route path="projects_management" childRoutes={projectsManagementRoutes} />
<Route path="settings" childRoutes={settingsRoutes} />
<Route path="system" childRoutes={systemRoutes} />
<Route path="marketplace" childRoutes={marketplaceRoutes} />
<Route path="users" childRoutes={usersRoutes} />
<Route path="webhooks" childRoutes={webhooksRoutes} />
<Route path="project/background_tasks" childRoutes={backgroundTasksRoutes} />
<Route path="project/branches" childRoutes={projectBranchesRoutes} />
<Route path="project/settings" childRoutes={settingsRoutes} />
<Route path="project_roles" childRoutes={projectPermissionsRoutes} />
<Route path="project/webhooks" childRoutes={webhooksRoutes} />
</Route>
{projectAdminRoutes}
</Route>

<Route path="not_found" component={NotFound} />
<Route path="*" component={NotFound} />
<Route component={AdminContainer} path="admin">
<Route
path="extension/:pluginKey/:extensionKey"
component={GlobalAdminPageExtension}
/>
<Route path="background_tasks" childRoutes={backgroundTasksRoutes} />
<Route path="custom_metrics" childRoutes={customMetricsRoutes} />
<Route path="groups" childRoutes={groupsRoutes} />
<Route path="permission_templates" childRoutes={permissionTemplatesRoutes} />
<Route path="roles/global" childRoutes={globalPermissionsRoutes} />
<Route path="permissions" childRoutes={globalPermissionsRoutes} />
<Route path="projects_management" childRoutes={projectsManagementRoutes} />
<Route path="settings" childRoutes={settingsRoutes} />
<Route path="system" childRoutes={systemRoutes} />
<Route path="marketplace" childRoutes={marketplaceRoutes} />
<Route path="users" childRoutes={usersRoutes} />
<Route path="webhooks" childRoutes={webhooksRoutes} />
</Route>
</Route>
<Route path="not_found" component={NotFound} />
<Route path="*" component={NotFound} />
</Route>
</Route>
</Route>

+ 146
- 138
server/sonar-web/src/main/js/apps/maintenance/components/App.tsx View File

@@ -19,6 +19,7 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
import Helmet from 'react-helmet';
import { getMigrationStatus, getSystemStatus, migrateDatabase } from '../../../api/system';
import DateFromNow from '../../../components/intl/DateFromNow';
import TimeFormatter from '../../../components/intl/TimeFormatter';
@@ -129,156 +130,163 @@ export default class App extends React.PureComponent<Props, State> {
const { state, status } = this.state;

return (
<div className="page-wrapper-simple" id="bd">
<div
className={classNames('page-simple', { 'panel-warning': state === 'MIGRATION_REQUIRED' })}
id="nonav">
{status === 'OFFLINE' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.sonarqube_is_offline')}
</h1>
<p className="maintenance-text">
{translate('maintenance.sonarqube_is_offline.text')}
</p>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('maintenance.try_again')}</a>
</p>
</>
)}
<>
<Helmet defaultTitle={translate('maintenance.page')} />
<div className="page-wrapper-simple" id="bd">
<div
className={classNames('page-simple', {
'panel-warning': state === 'MIGRATION_REQUIRED'
})}
id="nonav">
{status === 'OFFLINE' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.sonarqube_is_offline')}
</h1>
<p className="maintenance-text">
{translate('maintenance.sonarqube_is_offline.text')}
</p>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('maintenance.try_again')}</a>
</p>
</>
)}

{status === 'UP' && (
<>
<h1 className="maintenance-title">{translate('maintenance.sonarqube_is_up')}</h1>
<p className="maintenance-text text-center">
{translate('maintenance.all_systems_opetational')}
</p>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
</p>
</>
)}

{status === 'UP' && (
<>
<h1 className="maintenance-title">{translate('maintenance.sonarqube_is_up')}</h1>
<p className="maintenance-text text-center">
{translate('maintenance.all_systems_opetational')}
</p>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
</p>
</>
)}
{status === 'STARTING' && (
<>
<h1 className="maintenance-title">
{translate('maintenance.sonarqube_is_starting')}
</h1>
<p className="maintenance-spinner">
<i className="spinner" />
</p>
</>
)}

{status === 'STARTING' && (
<>
<h1 className="maintenance-title">
{translate('maintenance.sonarqube_is_starting')}
</h1>
<p className="maintenance-spinner">
<i className="spinner" />
</p>
</>
)}
{status === 'DOWN' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.sonarqube_is_down')}
</h1>
<p className="maintenance-text">
{translate('maintenance.sonarqube_is_down.text')}
</p>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('maintenance.try_again')}</a>
</p>
</>
)}

{status === 'DOWN' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.sonarqube_is_down')}
</h1>
<p className="maintenance-text">{translate('maintenance.sonarqube_is_down.text')}</p>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('maintenance.try_again')}</a>
</p>
</>
)}
{(status === 'DB_MIGRATION_NEEDED' || status === 'DB_MIGRATION_RUNNING') && (
<>
<h1 className="maintenance-title">
{translate('maintenance.sonarqube_is_under_maintenance')}
</h1>
<p
className="maintenance-text"
dangerouslySetInnerHTML={{
__html: translate('maintenance.sonarqube_is_under_maintenance.1')
}}
/>
<p
className="maintenance-text"
dangerouslySetInnerHTML={{
__html: translate('maintenance.sonarqube_is_under_maintenance.2')
}}
/>
</>
)}

{(status === 'DB_MIGRATION_NEEDED' || status === 'DB_MIGRATION_RUNNING') && (
<>
<h1 className="maintenance-title">
{translate('maintenance.sonarqube_is_under_maintenance')}
</h1>
<p
className="maintenance-text"
dangerouslySetInnerHTML={{
__html: translate('maintenance.sonarqube_is_under_maintenance.1')
}}
/>
<p
className="maintenance-text"
dangerouslySetInnerHTML={{
__html: translate('maintenance.sonarqube_is_under_maintenance.2')
}}
/>
</>
)}
{state === 'NO_MIGRATION' && (
<>
<h1 className="maintenance-title">
{translate('maintenance.database_is_up_to_date')}
</h1>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
</p>
</>
)}

{state === 'NO_MIGRATION' && (
<>
<h1 className="maintenance-title">
{translate('maintenance.database_is_up_to_date')}
</h1>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
</p>
</>
)}
{state === 'MIGRATION_REQUIRED' && (
<>
<h1 className="maintenance-title">{translate('maintenance.upgrade_database')}</h1>
<p className="maintenance-text">{translate('maintenance.upgrade_database.1')}</p>
<p className="maintenance-text">{translate('maintenance.upgrade_database.2')}</p>
<p className="maintenance-text">{translate('maintenance.upgrade_database.3')}</p>
<div className="maintenance-spinner">
<Button id="start-migration" onClick={this.handleMigrateClick}>
{translate('maintenance.upgrade')}
</Button>
</div>
</>
)}

{state === 'MIGRATION_REQUIRED' && (
<>
<h1 className="maintenance-title">{translate('maintenance.upgrade_database')}</h1>
<p className="maintenance-text">{translate('maintenance.upgrade_database.1')}</p>
<p className="maintenance-text">{translate('maintenance.upgrade_database.2')}</p>
<p className="maintenance-text">{translate('maintenance.upgrade_database.3')}</p>
<div className="maintenance-spinner">
<Button id="start-migration" onClick={this.handleMigrateClick}>
{translate('maintenance.upgrade')}
</Button>
</div>
</>
)}
{state === 'NOT_SUPPORTED' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.migration_not_supported')}
</h1>
<p>{translate('maintenance.migration_not_supported.text')}</p>
</>
)}

{state === 'NOT_SUPPORTED' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.migration_not_supported')}
</h1>
<p>{translate('maintenance.migration_not_supported.text')}</p>
</>
)}
{state === 'MIGRATION_RUNNING' && (
<>
<h1 className="maintenance-title">{translate('maintenance.database_migration')}</h1>
{this.state.message && (
<p className="maintenance-text text-center">{this.state.message}</p>
)}
{this.state.startedAt && (
<p className="maintenance-text text-center">
{translate('background_tasks.table.started')}{' '}
<DateFromNow date={this.state.startedAt} />
<br />
<small className="text-muted">
<TimeFormatter date={this.state.startedAt} long={true} />
</small>
</p>
)}
<p className="maintenance-spinner">
<i className="spinner" />
</p>
</>
)}

{state === 'MIGRATION_RUNNING' && (
<>
<h1 className="maintenance-title">{translate('maintenance.database_migration')}</h1>
{this.state.message && (
<p className="maintenance-text text-center">{this.state.message}</p>
)}
{this.state.startedAt && (
{state === 'MIGRATION_SUCCEEDED' && (
<>
<h1 className="maintenance-title text-success">
{translate('maintenance.database_is_up_to_date')}
</h1>
<p className="maintenance-text text-center">
{translate('background_tasks.table.started')}{' '}
<DateFromNow date={this.state.startedAt} />
<br />
<small className="text-muted">
<TimeFormatter date={this.state.startedAt} long={true} />
</small>
<a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
</p>
)}
<p className="maintenance-spinner">
<i className="spinner" />
</p>
</>
)}

{state === 'MIGRATION_SUCCEEDED' && (
<>
<h1 className="maintenance-title text-success">
{translate('maintenance.database_is_up_to_date')}
</h1>
<p className="maintenance-text text-center">
<a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
</p>
</>
)}
</>
)}

{state === 'MIGRATION_FAILED' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.upgrade_failed')}
</h1>
<p className="maintenance-text">{translate('maintenance.upgrade_failed.text')}</p>
</>
)}
{state === 'MIGRATION_FAILED' && (
<>
<h1 className="maintenance-title text-danger">
{translate('maintenance.upgrade_failed')}
</h1>
<p className="maintenance-text">{translate('maintenance.upgrade_failed.text')}</p>
</>
)}
</div>
</div>
</div>
</>
);
}
}

+ 396
- 312
server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap View File

@@ -1,398 +1,482 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Maintenance Page should render DB_MIGRATION_NEEDED status 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_under_maintenance
</h1>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.1",
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_under_maintenance
</h1>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.1",
}
}
}
/>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.2",
/>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.2",
}
}
}
/>
</React.Fragment>
/>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Maintenance Page should render DB_MIGRATION_RUNNING status 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_under_maintenance
</h1>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.1",
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_under_maintenance
</h1>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.1",
}
}
}
/>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.2",
/>
<p
className="maintenance-text"
dangerouslySetInnerHTML={
Object {
"__html": "maintenance.sonarqube_is_under_maintenance.2",
}
}
}
/>
</React.Fragment>
/>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Maintenance Page should render DOWN status 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.sonarqube_is_down
</h1>
<p
className="maintenance-text"
>
maintenance.sonarqube_is_down.text
</p>
<p
className="maintenance-text text-center"
>
<a
href="/"
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.sonarqube_is_down
</h1>
<p
className="maintenance-text"
>
maintenance.sonarqube_is_down.text
</p>
<p
className="maintenance-text text-center"
>
maintenance.try_again
</a>
</p>
</React.Fragment>
<a
href="/"
>
maintenance.try_again
</a>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Maintenance Page should render OFFLINE status 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.sonarqube_is_offline
</h1>
<p
className="maintenance-text"
>
maintenance.sonarqube_is_offline.text
</p>
<p
className="maintenance-text text-center"
>
<a
href="/"
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.try_again
</a>
</p>
</React.Fragment>
maintenance.sonarqube_is_offline
</h1>
<p
className="maintenance-text"
>
maintenance.sonarqube_is_offline.text
</p>
<p
className="maintenance-text text-center"
>
<a
href="/"
>
maintenance.try_again
</a>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Maintenance Page should render STARTING status 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_starting
</h1>
<p
className="maintenance-spinner"
>
<i
className="spinner"
/>
</p>
</React.Fragment>
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_starting
</h1>
<p
className="maintenance-spinner"
>
<i
className="spinner"
/>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Maintenance Page should render UP status 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_up
</h1>
<p
className="maintenance-text text-center"
>
maintenance.all_systems_opetational
</p>
<p
className="maintenance-text text-center"
>
<a
href="/"
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.sonarqube_is_up
</h1>
<p
className="maintenance-text text-center"
>
layout.home
</a>
</p>
</React.Fragment>
maintenance.all_systems_opetational
</p>
<p
className="maintenance-text text-center"
>
<a
href="/"
>
layout.home
</a>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Setup Page should render MIGRATION_FAILED state 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.upgrade_failed
</h1>
<p
className="maintenance-text"
>
maintenance.upgrade_failed.text
</p>
</React.Fragment>
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.upgrade_failed
</h1>
<p
className="maintenance-text"
>
maintenance.upgrade_failed.text
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Setup Page should render MIGRATION_SUCCEEDED state 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title text-success"
>
maintenance.database_is_up_to_date
</h1>
<p
className="maintenance-text text-center"
>
<a
href="/"
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title text-success"
>
maintenance.database_is_up_to_date
</h1>
<p
className="maintenance-text text-center"
>
layout.home
</a>
</p>
</React.Fragment>
<a
href="/"
>
layout.home
</a>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Setup Page should render NO_MIGRATION state 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.database_is_up_to_date
</h1>
<p
className="maintenance-text text-center"
>
<a
href="/"
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
layout.home
</a>
</p>
</React.Fragment>
maintenance.database_is_up_to_date
</h1>
<p
className="maintenance-text text-center"
>
<a
href="/"
>
layout.home
</a>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Setup Page should render NOT_SUPPORTED state 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.migration_not_supported
</h1>
<p>
maintenance.migration_not_supported.text
</p>
</React.Fragment>
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title text-danger"
>
maintenance.migration_not_supported
</h1>
<p>
maintenance.migration_not_supported.text
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Setup Page should start migration 1`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple panel-warning"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.upgrade_database
</h1>
<p
className="maintenance-text"
>
maintenance.upgrade_database.1
</p>
<p
className="maintenance-text"
>
maintenance.upgrade_database.2
</p>
<p
className="maintenance-text"
>
maintenance.upgrade_database.3
</p>
<div
className="maintenance-spinner"
>
<Button
id="start-migration"
onClick={[Function]}
<div
className="page-simple panel-warning"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.upgrade_database
</h1>
<p
className="maintenance-text"
>
maintenance.upgrade_database.1
</p>
<p
className="maintenance-text"
>
maintenance.upgrade
</Button>
</div>
</React.Fragment>
maintenance.upgrade_database.2
</p>
<p
className="maintenance-text"
>
maintenance.upgrade_database.3
</p>
<div
className="maintenance-spinner"
>
<Button
id="start-migration"
onClick={[Function]}
>
maintenance.upgrade
</Button>
</div>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

exports[`Setup Page should start migration 2`] = `
<div
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<HelmetWrapper
defaultTitle="maintenance.page"
defer={true}
encodeSpecialCharacters={true}
/>
<div
className="page-simple"
id="nonav"
className="page-wrapper-simple"
id="bd"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.database_migration
</h1>
<p
className="maintenance-text text-center"
>
background_tasks.table.started
<DateFromNow
date="2017-01-02T00:00:00.000Z"
/>
<br />
<small
className="text-muted"
<div
className="page-simple"
id="nonav"
>
<React.Fragment>
<h1
className="maintenance-title"
>
maintenance.database_migration
</h1>
<p
className="maintenance-text text-center"
>
<TimeFormatter
background_tasks.table.started
<DateFromNow
date="2017-01-02T00:00:00.000Z"
long={true}
/>
</small>
</p>
<p
className="maintenance-spinner"
>
<i
className="spinner"
/>
</p>
</React.Fragment>
<br />
<small
className="text-muted"
>
<TimeFormatter
date="2017-01-02T00:00:00.000Z"
long={true}
/>
</small>
</p>
<p
className="maintenance-spinner"
>
<i
className="spinner"
/>
</p>
</React.Fragment>
</div>
</div>
</div>
</React.Fragment>
`;

+ 1
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -2786,6 +2786,7 @@ groups.anyone=Anyone
# MAINTENANCE
#
#------------------------------------------------------------------------------
maintenance.page=Maintenance
maintenance.upgrade_failed=Upgrade Failed
maintenance.upgrade_failed.text=Database connection cannot be established. Please check database status and JDBC settings.
maintenance.migration_not_supported=Migration not supported

Loading…
Cancel
Save