aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-05-09 15:43:19 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-05-10 11:56:20 +0200
commit9821488bd9e3f764dc4f3a7fdd4beda93e870254 (patch)
tree493e62164676d84fc491b1c9421d1ae0d20100c9
parenta6f37fa019f167563152670e3812efb383c4eeb2 (diff)
downloadsonarqube-9821488bd9e3f764dc4f3a7fdd4beda93e870254.tar.gz
sonarqube-9821488bd9e3f764dc4f3a7fdd4beda93e870254.zip
SONAR-8369 SONAR-8722 Sanitize page titles
-rw-r--r--server/sonar-web/src/main/js/app/components/AdminContainer.js3
-rw-r--r--server/sonar-web/src/main/js/app/components/App.js1
-rw-r--r--server/sonar-web/src/main/js/app/components/DefaultHelmetContainer.js30
-rw-r--r--server/sonar-web/src/main/js/app/components/ProjectContainer.js19
-rw-r--r--server/sonar-web/src/main/js/app/components/extensions/PortfoliosPage.js13
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js9
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap13
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.js130
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Account.js4
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Security.js6
-rw-r--r--server/sonar-web/src/main/js/apps/account/notifications/Notifications.js4
-rw-r--r--server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap3
-rw-r--r--server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.js4
-rw-r--r--server/sonar-web/src/main/js/apps/account/projects/ProjectsContainer.js8
-rw-r--r--server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js3
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/App.js4
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/app/App.js3
-rw-r--r--server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js9
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js9
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.js5
-rw-r--r--server/sonar-web/src/main/js/apps/issues/utils.js1
-rw-r--r--server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js9
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationDelete.js7
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js3
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js3
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationGroups.js9
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationMembers.js3
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js2
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js3
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationDelete-test.js.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationMembers-test.js.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/App.js25
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/Home.js2
-rw-r--r--server/sonar-web/src/main/js/apps/permission-templates/components/Template.js4
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/global/components/App.js5
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/project/components/App.js6
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js3
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/key/Key.js2
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/links/Links.js3
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js3
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js4
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/main.js6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.js3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/App.js3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Details.js2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/App.js17
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/App.js4
-rw-r--r--server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js3
-rw-r--r--server/sonar-web/src/main/js/apps/settings/serverId/ServerIdApp.js2
-rw-r--r--server/sonar-web/src/main/js/apps/system/main.js2
-rw-r--r--server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js3
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js9
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js5
-rw-r--r--server/sonar-web/src/main/js/components/common/OrganizationHelmet.js32
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
63 files changed, 365 insertions, 157 deletions
diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.js b/server/sonar-web/src/main/js/app/components/AdminContainer.js
index 05d64849395..da8c9376dbc 100644
--- a/server/sonar-web/src/main/js/app/components/AdminContainer.js
+++ b/server/sonar-web/src/main/js/app/components/AdminContainer.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import SettingsNav from './nav/settings/SettingsNav';
import { getCurrentUser, getAppState } from '../../store/rootReducer';
@@ -25,6 +26,7 @@ import { isUserAdmin } from '../../helpers/users';
import { onFail } from '../../store/rootActions';
import { getSettingsNavigation } from '../../api/nav';
import { setAdminPages } from '../../store/appState/duck';
+import { translate } from '../../helpers/l10n';
class AdminContainer extends React.PureComponent {
componentDidMount() {
@@ -50,6 +52,7 @@ class AdminContainer extends React.PureComponent {
return (
<div>
+ <Helmet title={translate('layout.settings')} />
<SettingsNav location={this.props.location} extensions={this.props.adminPages} />
{this.props.children}
</div>
diff --git a/server/sonar-web/src/main/js/app/components/App.js b/server/sonar-web/src/main/js/app/components/App.js
index 8f4d06a1078..ea68bd92461 100644
--- a/server/sonar-web/src/main/js/app/components/App.js
+++ b/server/sonar-web/src/main/js/app/components/App.js
@@ -61,7 +61,6 @@ class App extends React.PureComponent {
if (this.state.loading) {
return <GlobalLoading />;
}
-
return this.props.children;
}
}
diff --git a/server/sonar-web/src/main/js/app/components/DefaultHelmetContainer.js b/server/sonar-web/src/main/js/app/components/DefaultHelmetContainer.js
new file mode 100644
index 00000000000..6006da488b1
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/DefaultHelmetContainer.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Helmet from 'react-helmet';
+
+export default function DefaultHelmetContainer({ children }) {
+ return (
+ <div>
+ <Helmet defaultTitle="SonarQube" />
+ {children}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/app/components/ProjectContainer.js b/server/sonar-web/src/main/js/app/components/ProjectContainer.js
index d1cf872b14f..8df53c24ff3 100644
--- a/server/sonar-web/src/main/js/app/components/ProjectContainer.js
+++ b/server/sonar-web/src/main/js/app/components/ProjectContainer.js
@@ -37,6 +37,7 @@ class ProjectContainer extends React.PureComponent {
},
project?: {
configuration: {},
+ name: string,
qualifier: string
},
fetchProject: string => Promise<*>,
@@ -68,27 +69,23 @@ class ProjectContainer extends React.PureComponent {
};
render() {
+ const { project } = this.props;
+
// check `breadcrumbs` to be sure that /api/navigation/component has been already called
- if (!this.props.project || this.props.project.breadcrumbs == null) {
+ if (!project || project.breadcrumbs == null) {
return null;
}
- const isFile = ['FIL', 'UTS'].includes(this.props.project.qualifier);
-
- // $FlowFixMe `this.props.project` is always defined at this point
- const configuration = this.props.project.configuration || {};
+ const isFile = ['FIL', 'UTS'].includes(project.qualifier);
+ const configuration = project.configuration || {};
return (
<div>
{!isFile &&
- <ComponentNav
- component={this.props.project}
- conf={configuration}
- location={this.props.location}
- />}
+ <ComponentNav component={project} conf={configuration} location={this.props.location} />}
{/* $FlowFixMe */}
{React.cloneElement(this.props.children, {
- component: this.props.project,
+ component: project,
onComponentChange: this.handleProjectChange
})}
</div>
diff --git a/server/sonar-web/src/main/js/app/components/extensions/PortfoliosPage.js b/server/sonar-web/src/main/js/app/components/extensions/PortfoliosPage.js
index a7f47a2fa3a..4e6da93deea 100644
--- a/server/sonar-web/src/main/js/app/components/extensions/PortfoliosPage.js
+++ b/server/sonar-web/src/main/js/app/components/extensions/PortfoliosPage.js
@@ -19,13 +19,18 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import GlobalPageExtension from './GlobalPageExtension';
+import { translate } from '../../../helpers/l10n';
export default function PortfoliosPage(props: Object) {
return (
- <GlobalPageExtension
- location={props.location}
- params={{ pluginKey: 'governance', extensionKey: 'portfolios' }}
- />
+ <div>
+ <Helmet title={translate('portfolios.page')} />
+ <GlobalPageExtension
+ location={props.location}
+ params={{ pluginKey: 'governance', extensionKey: 'portfolios' }}
+ />
+ </div>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
index 9c02a29013d..42c3efaa232 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
@@ -22,6 +22,7 @@ import { connect } from 'react-redux';
import { Link } from 'react-router';
import QualifierIcon from '../../../../components/shared/QualifierIcon';
import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
+import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
import OrganizationLink from '../../../../components/ui/OrganizationLink';
import PrivateBadge from '../../../../components/common/PrivateBadge';
import { collapsePath, limitComponentName } from '../../../../helpers/path';
@@ -35,7 +36,7 @@ class ComponentNavBreadcrumbs extends React.PureComponent {
};
render() {
- const { breadcrumbs, organization, shouldOrganizationBeDisplayed } = this.props;
+ const { breadcrumbs, component, organization, shouldOrganizationBeDisplayed } = this.props;
if (!breadcrumbs) {
return null;
@@ -70,6 +71,10 @@ class ComponentNavBreadcrumbs extends React.PureComponent {
return (
<h2 className="navbar-context-title">
+ <OrganizationHelmet
+ title={component.name}
+ organization={displayOrganization ? organization : null}
+ />
{displayOrganization &&
<span>
<span className="navbar-context-title-qualifier little-spacer-right">
@@ -81,7 +86,7 @@ class ComponentNavBreadcrumbs extends React.PureComponent {
<span className="slash-separator" />
</span>}
{items}
- {this.props.component.visibility === 'private' && <PrivateBadge className="spacer-left" />}
+ {component.visibility === 'private' && <PrivateBadge className="spacer-left" />}
</h2>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap
index 02834a1f242..385012e7de0 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap
@@ -4,6 +4,10 @@ exports[`should not render breadcrumbs with one element 1`] = `
<h2
className="navbar-context-title"
>
+ <OrganizationHelmet
+ organization={null}
+ title="My Project"
+ />
<span>
<span
className="navbar-context-title-qualifier little-spacer-right"
@@ -38,6 +42,15 @@ exports[`should render organization 1`] = `
<h2
className="navbar-context-title"
>
+ <OrganizationHelmet
+ organization={
+ Object {
+ "key": "foo",
+ "name": "The Foo Organization",
+ }
+ }
+ title="My Project"
+ />
<span>
<span
className="navbar-context-title-qualifier little-spacer-right"
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js
index fa8714220bf..5b4a0d37e15 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.js
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js
@@ -21,6 +21,7 @@ import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, Redirect } from 'react-router';
import { Provider } from 'react-redux';
+import DefaultHelmetContainer from '../components/DefaultHelmetContainer';
import LocalizationContainer from '../components/LocalizationContainer';
import MigrationContainer from '../components/MigrationContainer';
import App from '../components/App';
@@ -127,82 +128,87 @@ const startReactApp = () => {
<Route path="markdown/help" component={MarkdownHelp} />
- <Route component={LocalizationContainer}>
- <Route component={SimpleContainer}>
- <Route path="maintenance">{maintenanceRoutes}</Route>
- <Route path="setup">{setupRoutes}</Route>
- </Route>
-
- <Route component={MigrationContainer}>
- <Route component={SimpleSessionsContainer}>
- <Route path="/sessions">{sessionsRoutes}</Route>
+ <Route component={DefaultHelmetContainer}>
+ <Route component={LocalizationContainer}>
+ <Route component={SimpleContainer}>
+ <Route path="maintenance">{maintenanceRoutes}</Route>
+ <Route path="setup">{setupRoutes}</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="extension/:pluginKey/:extensionKey" component={GlobalPageExtension} />
- <Route path="issues" childRoutes={issuesRoutes} />
- <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 component={ProjectContainer}>
- <Route path="code" childRoutes={codeRoutes} />
- <Route path="component_measures" childRoutes={componentMeasuresRoutes} />
- <Route path="custom_measures" childRoutes={customMeasuresRoutes} />
- <Route path="dashboard" childRoutes={overviewRoutes} />
- <Route path="project">
- <Route path="activity" childRoutes={projectActivityRoutes} />
- <Route path="admin" component={ProjectAdminContainer}>
+ <Route component={MigrationContainer}>
+ <Route component={SimpleSessionsContainer}>
+ <Route path="/sessions">{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="extension/:pluginKey/:extensionKey"
+ component={GlobalPageExtension}
+ />
+ <Route path="issues" childRoutes={issuesRoutes} />
+ <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 component={ProjectContainer}>
+ <Route path="code" childRoutes={codeRoutes} />
+ <Route path="component_measures" childRoutes={componentMeasuresRoutes} />
+ <Route path="custom_measures" childRoutes={customMeasuresRoutes} />
+ <Route path="dashboard" childRoutes={overviewRoutes} />
+ <Route path="project">
+ <Route path="activity" childRoutes={projectActivityRoutes} />
+ <Route path="admin" component={ProjectAdminContainer}>
+ <Route
+ path="extension/:pluginKey/:extensionKey"
+ component={ProjectAdminPageExtension}
+ />
+ </Route>
+ <Redirect from="extension/governance/governance" to="/view" />
<Route
path="extension/:pluginKey/:extensionKey"
- component={ProjectAdminPageExtension}
+ component={ProjectPageExtension}
/>
+ <Route path="background_tasks" childRoutes={backgroundTasksRoutes} />
+ <Route path="issues" childRoutes={issuesRoutes} />
+ <Route path="settings" childRoutes={settingsRoutes} />
+ {projectAdminRoutes}
</Route>
- <Redirect from="extension/governance/governance" to="/view" />
+ <Route path="project_roles" childRoutes={projectPermissionsRoutes} />
+ <Route path="view" component={ViewDashboard} />
+ </Route>
+
+ <Route component={AdminContainer}>
<Route
- path="extension/:pluginKey/:extensionKey"
- component={ProjectPageExtension}
+ path="admin/extension/:pluginKey/:extensionKey"
+ component={GlobalAdminPageExtension}
/>
<Route path="background_tasks" childRoutes={backgroundTasksRoutes} />
- <Route path="issues" childRoutes={issuesRoutes} />
+ <Route path="groups" childRoutes={groupsRoutes} />
+ <Route path="metrics" childRoutes={metricsRoutes} />
+ <Route path="permission_templates" childRoutes={permissionTemplatesRoutes} />
+ <Route path="projects_admin" childRoutes={projectsAdminRoutes} />
+ <Route path="roles/global" childRoutes={globalPermissionsRoutes} />
<Route path="settings" childRoutes={settingsRoutes} />
- {projectAdminRoutes}
+ <Route path="system" childRoutes={systemRoutes} />
+ <Route path="updatecenter" childRoutes={updateCenterRoutes} />
+ <Route path="users" childRoutes={usersRoutes} />
</Route>
- <Route path="project_roles" childRoutes={projectPermissionsRoutes} />
- <Route path="view" component={ViewDashboard} />
</Route>
- <Route component={AdminContainer}>
- <Route
- path="admin/extension/:pluginKey/:extensionKey"
- component={GlobalAdminPageExtension}
- />
- <Route path="background_tasks" childRoutes={backgroundTasksRoutes} />
- <Route path="groups" childRoutes={groupsRoutes} />
- <Route path="metrics" childRoutes={metricsRoutes} />
- <Route path="permission_templates" childRoutes={permissionTemplatesRoutes} />
- <Route path="projects_admin" childRoutes={projectsAdminRoutes} />
- <Route path="roles/global" childRoutes={globalPermissionsRoutes} />
- <Route path="settings" childRoutes={settingsRoutes} />
- <Route path="system" childRoutes={systemRoutes} />
- <Route path="updatecenter" childRoutes={updateCenterRoutes} />
- <Route path="users" childRoutes={usersRoutes} />
- </Route>
+ <Route path="not_found" component={NotFound} />
+ <Route path="*" component={NotFound} />
</Route>
-
- <Route path="not_found" component={NotFound} />
- <Route path="*" component={NotFound} />
</Route>
</Route>
</Route>
diff --git a/server/sonar-web/src/main/js/apps/account/components/Account.js b/server/sonar-web/src/main/js/apps/account/components/Account.js
index aed774b7bac..f9918546f6e 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Account.js
+++ b/server/sonar-web/src/main/js/apps/account/components/Account.js
@@ -19,9 +19,11 @@
*/
import React from 'react';
import { connect } from 'react-redux';
+import Helmet from 'react-helmet';
import Nav from './Nav';
import UserCard from './UserCard';
import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
import '../account.css';
@@ -39,8 +41,10 @@ class Account extends React.PureComponent {
return null;
}
+ const title = translate('my_account.page');
return (
<div id="account-page">
+ <Helmet defaultTitle={title} titleTemplate={'%s - ' + title} />
<header className="account-header">
<div className="account-container clearfix">
<UserCard user={currentUser} />
diff --git a/server/sonar-web/src/main/js/apps/account/components/Security.js b/server/sonar-web/src/main/js/apps/account/components/Security.js
index f444538f1ee..d52a3b6e1ea 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Security.js
+++ b/server/sonar-web/src/main/js/apps/account/components/Security.js
@@ -18,8 +18,8 @@
* 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 { connect } from 'react-redux';
import Password from './Password';
import Tokens from './Tokens';
import { translate } from '../../../helpers/l10n';
@@ -28,11 +28,9 @@ import { getCurrentUser } from '../../../store/rootReducer';
function Security(props) {
const { user } = props;
- const title = translate('my_account.page') + ' - ' + translate('my_account.security');
-
return (
<div className="account-body account-container">
- <Helmet title={title} titleTemplate="SonarQube - %s" />
+ <Helmet title={translate('my_account.security')} />
<Tokens user={user} />
diff --git a/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js b/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js
index 6f27b3141dc..b8c596cba1d 100644
--- a/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js
+++ b/server/sonar-web/src/main/js/apps/account/notifications/Notifications.js
@@ -36,11 +36,9 @@ class Notifications extends React.PureComponent {
}
render() {
- const title = translate('my_account.page') + ' - ' + translate('my_account.notifications');
-
return (
<div className="account-body account-container">
- <Helmet title={title} titleTemplate="SonarQube - %s" />
+ <Helmet title={translate('my_account.notifications')} />
<p className="big-spacer-bottom">
{translate('notification.dispatcher.information')}
diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap
index 51c289290b8..66eea69153b 100644
--- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/Notifications-test.js.snap
@@ -5,8 +5,7 @@ exports[`should match snapshot 1`] = `
className="account-body account-container"
>
<HelmetWrapper
- title="my_account.page - my_account.notifications"
- titleTemplate="SonarQube - %s"
+ title="my_account.notifications"
/>
<p
className="big-spacer-bottom"
diff --git a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.js b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.js
index 34fad4cf4c3..82e5fb84797 100644
--- a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.js
+++ b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.js
@@ -62,8 +62,6 @@ class UserOrganizations extends React.PureComponent {
}
render() {
- const title = translate('my_account.organizations') + ' - ' + translate('my_account.page');
-
const anyoneCanCreate =
this.props.anyoneCanCreate != null && this.props.anyoneCanCreate.value === 'true';
@@ -72,7 +70,7 @@ class UserOrganizations extends React.PureComponent {
return (
<div className="account-body account-container">
- <Helmet title={title} titleTemplate="%s - SonarQube" />
+ <Helmet title={translate('my_account.organizations')} />
<header className="page-header">
<h2 className="page-title">{translate('my_account.organizations')}</h2>
diff --git a/server/sonar-web/src/main/js/apps/account/projects/ProjectsContainer.js b/server/sonar-web/src/main/js/apps/account/projects/ProjectsContainer.js
index b944998be0f..69aea617c93 100644
--- a/server/sonar-web/src/main/js/apps/account/projects/ProjectsContainer.js
+++ b/server/sonar-web/src/main/js/apps/account/projects/ProjectsContainer.js
@@ -74,20 +74,20 @@ export default class ProjectsContainer extends React.PureComponent {
}
render() {
+ const helmet = <Helmet title={translate('my_account.projects')} />;
+
if (this.state.projects == null) {
return (
<div className="text-center">
+ {helmet}
<i className="spinner spinner-margin" />
</div>
);
}
- const title = translate('my_account.page') + ' - ' + translate('my_account.projects');
-
return (
<div className="account-body account-container">
- <Helmet title={title} titleTemplate="SonarQube - %s" />
-
+ {helmet}
<Projects
projects={this.state.projects}
total={this.state.total}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js
index 6591fc3c4c5..c27316d41bd 100644
--- a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js
+++ b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js
@@ -19,6 +19,7 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { debounce, uniq } from 'lodash';
import { connect } from 'react-redux';
import { DEFAULT_FILTERS, DEBOUNCE_DELAY, STATUSES, CURRENTS } from './../constants';
@@ -39,6 +40,7 @@ import { Task } from '../types';
import { getComponent } from '../../../store/rootReducer';
import '../background-tasks.css';
import { fetchOrganizations } from '../../../store/rootActions';
+import { translate } from '../../../helpers/l10n';
type Props = {
component: Object,
@@ -211,6 +213,7 @@ class BackgroundTasksApp extends React.PureComponent {
return (
<div className="page page-limited">
+ <Helmet title={translate('background_tasks.page')} />
<Header />
<Stats
diff --git a/server/sonar-web/src/main/js/apps/code/components/App.js b/server/sonar-web/src/main/js/apps/code/components/App.js
index 33c71efd189..1918cac2abf 100644
--- a/server/sonar-web/src/main/js/apps/code/components/App.js
+++ b/server/sonar-web/src/main/js/apps/code/components/App.js
@@ -19,6 +19,7 @@
*/
import classNames from 'classnames';
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import Components from './Components';
import Breadcrumbs from './Breadcrumbs';
@@ -33,6 +34,7 @@ import {
} from '../utils';
import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket';
import { getComponent } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
import '../code.css';
class App extends React.PureComponent {
@@ -179,6 +181,8 @@ class App extends React.PureComponent {
return (
<div className="page page-limited">
+ <Helmet title={translate('code')} />
+
{error &&
<div className="alert alert-danger">
{error}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js
index cc7a145f49c..52b1b29d6a1 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesAppContainer.js
@@ -19,9 +19,11 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { getAppState } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
import init from '../init';
class CodingRulesAppContainer extends React.PureComponent {
@@ -70,6 +72,7 @@ class CodingRulesAppContainer extends React.PureComponent {
// but react wants it to be there to unmount it
return (
<div>
+ <Helmet title={translate('rules')} />
<div ref="container" />
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/component-measures/app/App.js b/server/sonar-web/src/main/js/apps/component-measures/app/App.js
index 1e005ee4c70..fd7caef81a8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/app/App.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/app/App.js
@@ -18,7 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import Spinner from './../components/Spinner';
+import { translate } from '../../../helpers/l10n';
import '../styles.css';
export default class App extends React.PureComponent {
@@ -37,6 +39,7 @@ export default class App extends React.PureComponent {
return (
<main id="component-measures">
+ <Helmet title={translate('layout.measures')} />
{this.props.children}
</main>
);
diff --git a/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js b/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js
index 3ce24f3779d..1f40c41775f 100644
--- a/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/custom-measures/components/CustomMeasuresAppContainer.js
@@ -18,9 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import init from '../init';
import { getComponent } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
class CustomMeasuresAppContainer extends React.PureComponent {
componentDidMount() {
@@ -28,7 +30,12 @@ class CustomMeasuresAppContainer extends React.PureComponent {
}
render() {
- return <div ref="container" />;
+ return (
+ <div>
+ <Helmet title={translate('custom_measures.page')} />
+ <div ref="container" />
+ </div>
+ );
}
}
diff --git a/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js b/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js
index acd77e833cb..16525d55ee5 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/groups/components/GroupsAppContainer.js
@@ -18,7 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import init from '../init';
+import { translate } from '../../../helpers/l10n';
export default class GroupsAppContainer extends React.PureComponent {
componentDidMount() {
@@ -26,6 +28,11 @@ export default class GroupsAppContainer extends React.PureComponent {
}
render() {
- return <div ref="container" />;
+ return (
+ <div>
+ <Helmet title={translate('user_groups.page')} />
+ <div ref="container" />
+ </div>
+ );
}
}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js
index a91ef8ea68f..4a9dca21d04 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/App.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.js
@@ -793,16 +793,13 @@ export default class App extends React.PureComponent {
</div>
);
}
-
render() {
const { component } = this.props;
const { openIssue, paging } = this.state;
-
const selectedIndex = this.getSelectedIndex();
-
return (
<div className="layout-page issues" id="issues-page">
- <Helmet title={translate('issues.page')} titleTemplate="%s - SonarQube" />
+ <Helmet title={translate('issues.page')} />
{this.renderSide(openIssue)}
diff --git a/server/sonar-web/src/main/js/apps/issues/utils.js b/server/sonar-web/src/main/js/apps/issues/utils.js
index 140aea14e25..d33defd8135 100644
--- a/server/sonar-web/src/main/js/apps/issues/utils.js
+++ b/server/sonar-web/src/main/js/apps/issues/utils.js
@@ -217,6 +217,7 @@ export type ReferencedLanguage = {
export type Component = {
key: string,
+ name: string,
organization: string,
qualifier: string
};
diff --git a/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js b/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js
index 6193721adef..f5f031e9839 100644
--- a/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/metrics/components/MetricsAppContainer.js
@@ -18,7 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import init from '../init';
+import { translate } from '../../../helpers/l10n';
export default class MetricsAppContainer extends React.PureComponent {
componentDidMount() {
@@ -26,6 +28,11 @@ export default class MetricsAppContainer extends React.PureComponent {
}
render() {
- return <div ref="container" />;
+ return (
+ <div>
+ <Helmet title={translate('custom_metrics.page')} />
+ <div ref="container" />
+ </div>
+ );
}
}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationDelete.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationDelete.js
index be7b726a7f4..1cd73c0be0f 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationDelete.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationDelete.js
@@ -19,8 +19,8 @@
*/
// @flow
import React from 'react';
-import Modal from 'react-modal';
import Helmet from 'react-helmet';
+import Modal from 'react-modal';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { translate } from '../../../helpers/l10n';
@@ -97,10 +97,7 @@ class OrganizationDelete extends React.PureComponent {
render() {
return (
<div className="page page-limited">
- <Helmet
- title={`${translate('organization.delete')} - ${this.props.organization.name}`}
- titleTemplate="%s - SonarQube"
- />
+ <Helmet title={translate('delete')} />
<header className="page-header">
<h1 className="page-title">{translate('organization.delete')}</h1>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
index d5d9870c116..e108da282a5 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.js
@@ -19,6 +19,7 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { debounce } from 'lodash';
import { translate } from '../../../helpers/l10n';
@@ -96,6 +97,8 @@ class OrganizationEdit extends React.PureComponent {
render() {
return (
<div className="page page-limited">
+ <Helmet title={translate('edit')} />
+
<header className="page-header">
<h1 className="page-title">{translate('organization.edit')}</h1>
</header>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js
index 53654f58b01..be73dafc0af 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js
@@ -20,11 +20,9 @@
// @flow
import React from 'react';
import { connect } from 'react-redux';
-import Helmet from 'react-helmet';
import FavoriteProjectsContainer from '../../projects/components/FavoriteProjectsContainer';
import { getOrganizationByKey } from '../../../store/rootReducer';
import { updateOrganization } from '../actions';
-import { translate } from '../../../helpers/l10n';
class OrganizationFavoriteProjects extends React.PureComponent {
props: {
@@ -52,7 +50,6 @@ class OrganizationFavoriteProjects extends React.PureComponent {
render() {
return (
<div id="projects-page">
- <Helmet title={translate('projects.page')} titleTemplate="%s - SonarQube" />
<FavoriteProjectsContainer
location={this.props.location}
organization={this.props.organization}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationGroups.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationGroups.js
index 521d40d9184..4f751110345 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationGroups.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationGroups.js
@@ -19,9 +19,11 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import init from '../../groups/init';
import { getOrganizationByKey } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
import type { Organization } from '../../../store/organizations/duck';
class OrganizationGroups extends React.PureComponent {
@@ -34,7 +36,12 @@ class OrganizationGroups extends React.PureComponent {
}
render() {
- return <div ref="container" />;
+ return (
+ <div>
+ <Helmet title={translate('global_permissions.groups')} />
+ <div ref="container" />
+ </div>
+ );
}
}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationMembers.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationMembers.js
index c4e3f6bcd25..71f7d3f9551 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationMembers.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationMembers.js
@@ -19,11 +19,13 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import MembersPageHeader from './MembersPageHeader';
import MembersListHeader from './MembersListHeader';
import MembersList from './MembersList';
import AddMemberForm from './forms/AddMemberForm';
import ListFooter from '../../../components/controls/ListFooter';
+import { translate } from '../../../helpers/l10n';
import type { Organization, OrgGroup } from '../../../store/organizations/duck';
import type { Member } from '../../../store/organizationsMembers/actions';
@@ -80,6 +82,7 @@ export default class OrganizationMembers extends React.PureComponent {
const { organization, status, members } = this.props;
return (
<div className="page page-limited">
+ <Helmet title={translate('organization.members.page')} />
<MembersPageHeader loading={status.loading} total={status.total}>
{organization.canAdmin &&
<div className="page-actions">
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
index 7955b3c82fd..739948592e3 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
@@ -19,6 +19,7 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import OrganizationNavigation from '../navigation/OrganizationNavigation';
import { fetchOrganization } from '../actions';
@@ -71,6 +72,7 @@ class OrganizationPage extends React.PureComponent {
return (
<div>
+ <Helmet defaultTitle={organization.name} titleTemplate={'%s - ' + organization.name} />
<OrganizationNavigation organization={organization} location={this.props.location} />
{this.props.children}
</div>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js
index 99b789f1af4..c342564368a 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js
@@ -20,11 +20,9 @@
// @flow
import React from 'react';
import { connect } from 'react-redux';
-import Helmet from 'react-helmet';
import AllProjectsContainer from '../../projects/components/AllProjectsContainer';
import { getOrganizationByKey } from '../../../store/rootReducer';
import { updateOrganization } from '../actions';
-import { translate } from '../../../helpers/l10n';
class OrganizationProjects extends React.PureComponent {
props: {
@@ -52,7 +50,6 @@ class OrganizationProjects extends React.PureComponent {
render() {
return (
<div id="projects-page">
- <Helmet title={translate('projects.page')} titleTemplate="%s - SonarQube" />
<AllProjectsContainer
isFavorite={false}
location={this.props.location}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationDelete-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationDelete-test.js.snap
index 365adc0701d..fa13f06a074 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationDelete-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationDelete-test.js.snap
@@ -5,8 +5,7 @@ exports[`smoke test 1`] = `
className="page page-limited"
>
<HelmetWrapper
- title="organization.delete - Foo"
- titleTemplate="%s - SonarQube"
+ title="delete"
/>
<header
className="page-header"
@@ -39,8 +38,7 @@ exports[`smoke test 2`] = `
className="page page-limited"
>
<HelmetWrapper
- title="organization.delete - Foo"
- titleTemplate="%s - SonarQube"
+ title="delete"
/>
<header
className="page-header"
@@ -121,8 +119,7 @@ exports[`smoke test 3`] = `
className="page page-limited"
>
<HelmetWrapper
- title="organization.delete - Foo"
- titleTemplate="%s - SonarQube"
+ title="delete"
/>
<header
className="page-header"
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
index 43c8be0ddb6..9fe30235a62 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEdit-test.js.snap
@@ -4,6 +4,9 @@ exports[`smoke test 1`] = `
<div
className="page page-limited"
>
+ <HelmetWrapper
+ title="edit"
+ />
<header
className="page-header"
>
@@ -132,6 +135,9 @@ exports[`smoke test 2`] = `
<div
className="page page-limited"
>
+ <HelmetWrapper
+ title="edit"
+ />
<header
className="page-header"
>
@@ -275,6 +281,9 @@ exports[`smoke test 3`] = `
<div
className="page page-limited"
>
+ <HelmetWrapper
+ title="edit"
+ />
<header
className="page-header"
>
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationMembers-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationMembers-test.js.snap
index f6da626a33f..20de4c58030 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationMembers-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationMembers-test.js.snap
@@ -4,6 +4,9 @@ exports[`should not render actions for non admin 1`] = `
<div
className="page page-limited"
>
+ <HelmetWrapper
+ title="organization.members.page"
+ />
<MembersPageHeader
total={2}
/>
@@ -50,6 +53,9 @@ exports[`should render actions for admin 1`] = `
<div
className="page page-limited"
>
+ <HelmetWrapper
+ title="organization.members.page"
+ />
<MembersPageHeader
loading={true}
total={2}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
index a30c2f0a74b..6c8f73738d4 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
@@ -6,6 +6,10 @@ exports[`smoke test 1`] = `null`;
exports[`smoke test 2`] = `
<div>
+ <HelmetWrapper
+ defaultTitle="Foo"
+ titleTemplate="%s - Foo"
+ />
<OrganizationNavigation
organization={
Object {
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/App.js b/server/sonar-web/src/main/js/apps/permission-templates/components/App.js
index 6d4d00d4c6f..b28c3564d9e 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/App.js
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/App.js
@@ -20,8 +20,10 @@
import React from 'react';
import Home from './Home';
import Template from './Template';
+import OrganizationHelmet from '../../../components/common/OrganizationHelmet';
import { getPermissionTemplates } from '../../../api/permissions';
import { sortPermissions, mergePermissionsToTemplates, mergeDefaultsToTemplates } from '../utils';
+import { translate } from '../../../helpers/l10n';
import '../../permissions/styles.css';
export default class App extends React.PureComponent {
@@ -83,13 +85,7 @@ export default class App extends React.PureComponent {
);
}
- render() {
- const { id } = this.props.location.query;
-
- if (id) {
- return this.renderTemplate(id);
- }
-
+ renderHome() {
return (
<Home
organization={this.props.organization}
@@ -101,4 +97,19 @@ export default class App extends React.PureComponent {
/>
);
}
+
+ render() {
+ const { id } = this.props.location.query;
+ return (
+ <div>
+ <OrganizationHelmet
+ title={translate('permission_templates.page')}
+ organization={this.props.organization}
+ />
+
+ {id && this.renderTemplate(id)}
+ {!id && this.renderHome()}
+ </div>
+ );
+ }
}
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js
index 0f70164617e..7b2135fc335 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.js
@@ -36,7 +36,7 @@ export default class Home extends React.PureComponent {
render() {
return (
<div className="page page-limited">
- <Helmet title={translate('permission_templates.page')} titleTemplate="SonarQube - %s" />
+ <Helmet title={translate('permission_templates.page')} />
<Header
organization={this.props.organization}
diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.js
index 8ee95b10525..f36ded96c5b 100644
--- a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.js
+++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.js
@@ -171,8 +171,6 @@ export default class Template extends React.PureComponent {
};
render() {
- const title = translate('permission_templates.page') + ' - ' + this.props.template.name;
-
const permissions = PERMISSIONS_ORDER_FOR_PROJECT.map(p => ({
key: p,
name: translate('projects_role', p),
@@ -197,7 +195,7 @@ export default class Template extends React.PureComponent {
return (
<div className="page page-limited">
- <Helmet title={title} titleTemplate="SonarQube - %s" />
+ <Helmet title={this.props.template.name} />
<TemplateHeader
organization={this.props.organization}
diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/App.js b/server/sonar-web/src/main/js/apps/permissions/global/components/App.js
index d7a02f5c987..da906b66606 100644
--- a/server/sonar-web/src/main/js/apps/permissions/global/components/App.js
+++ b/server/sonar-web/src/main/js/apps/permissions/global/components/App.js
@@ -19,16 +19,17 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import PageHeader from './PageHeader';
import AllHoldersList from './AllHoldersList';
import PageError from '../../shared/components/PageError';
+import { translate } from '../../../../helpers/l10n';
import '../../styles.css';
-// TODO helmet
-
export default function App(props: { organization?: {} }) {
return (
<div className="page page-limited">
+ <Helmet title={translate('global_permissions.permission')} />
<PageHeader organization={props.organization} />
<PageError />
<AllHoldersList organization={props.organization} />
diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/App.js b/server/sonar-web/src/main/js/apps/permissions/project/components/App.js
index 7590041e302..1d7a82ff6f5 100644
--- a/server/sonar-web/src/main/js/apps/permissions/project/components/App.js
+++ b/server/sonar-web/src/main/js/apps/permissions/project/components/App.js
@@ -19,6 +19,7 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { without } from 'lodash';
import PageHeader from './PageHeader';
import UpgradeOrganizationBox from '../../../../components/common/UpgradeOrganizationBox';
@@ -27,10 +28,9 @@ import AllHoldersList from './AllHoldersList';
import PublicProjectDisclaimer from './PublicProjectDisclaimer';
import PageError from '../../shared/components/PageError';
import * as api from '../../../../api/permissions';
+import { translate } from '../../../../helpers/l10n';
import '../../styles.css';
-// TODO helmet
-
export type Props = {|
component: {
configuration?: {
@@ -339,6 +339,8 @@ export default class App extends React.PureComponent {
return (
<div className="page page-limited" id="project-permissions-page">
+ <Helmet title={translate('permissions.page')} />
+
<PageHeader
component={this.props.component}
loading={this.state.loading}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js b/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js
index 6ef724f1b77..e94863a7ac1 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/deletion/Deletion.js
@@ -18,10 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import Header from './Header';
import Form from './Form';
import { getComponent } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
class Deletion extends React.PureComponent {
static propTypes = {
@@ -35,6 +37,7 @@ class Deletion extends React.PureComponent {
return (
<div className="page page-limited">
+ <Helmet title={translate('deletion.page')} />
<Header component={this.props.component} />
<Form component={this.props.component} />
</div>
diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js
index b3f47fc3785..97cd01c3f1c 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import Header from './Header';
import UpdateForm from './UpdateForm';
@@ -87,6 +88,7 @@ class Key extends React.PureComponent {
return (
<div id="project-key" className="page page-limited">
+ <Helmet title={translate('update_key.page')} />
<Header />
{modules == null && <i className="spinner" />}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js
index b8ac2300dc9..e06a045a9aa 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js
@@ -18,12 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import Header from './Header';
import Table from './Table';
import DeletionModal from './views/DeletionModal';
import { fetchProjectLinks, deleteProjectLink, createProjectLink } from '../store/actions';
import { getProjectAdminProjectLinks, getComponent } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
class Links extends React.PureComponent {
static propTypes = {
@@ -55,6 +57,7 @@ class Links extends React.PureComponent {
render() {
return (
<div className="page page-limited">
+ <Helmet title={translate('project_links.page')} />
<Header onCreate={this.handleCreateLink} />
<Table links={this.props.links} onDelete={this.handleDeleteLink} />
</div>
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js b/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js
index 944be04082b..fd5759a40b9 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-gate/QualityGate.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import Header from './Header';
import Form from './Form';
@@ -27,6 +28,7 @@ import {
getProjectAdminProjectGate,
getComponent
} from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
class QualityGate extends React.PureComponent {
static propTypes = {
@@ -46,6 +48,7 @@ class QualityGate extends React.PureComponent {
render() {
return (
<div id="project-quality-gate" className="page page-limited">
+ <Helmet title={translate('project_quality_gate.page')} />
<Header />
<Form
allGates={this.props.allGates}
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
index ee3d6a0e774..00e93a54471 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
@@ -19,6 +19,7 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import Header from './Header';
import Table from './Table';
@@ -29,6 +30,7 @@ import {
getProjectAdminProjectProfiles,
getComponent
} from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
type Props = {
allProfiles: Array<{}>,
@@ -59,6 +61,8 @@ class QualityProfiles extends React.PureComponent {
return (
<div className="page page-limited">
+ <Helmet title={translate('project_quality_profiles.page')} />
+
<Header />
{profiles.length > 0
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
index 8cf6bcf2522..f1972c57bc3 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
@@ -19,12 +19,14 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import ProjectActivityPageHeader from './ProjectActivityPageHeader';
import ProjectActivityAnalysesList from './ProjectActivityAnalysesList';
import ProjectActivityPageFooter from './ProjectActivityPageFooter';
import { fetchProjectActivity } from '../actions';
import { getComponent } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
import './projectActivity.css';
type Props = {
@@ -71,6 +73,8 @@ class ProjectActivityApp extends React.PureComponent {
return (
<div id="project-activity" className="page page-limited">
+ <Helmet title={translate('project_activity.page')} />
+
<ProjectActivityPageHeader
project={project}
filter={this.state.filter}
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/main.js b/server/sonar-web/src/main/js/apps/projects-admin/main.js
index cb1c99b30f3..01b9b56fb11 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/main.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/main.js
@@ -19,14 +19,16 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { debounce, uniq, without } from 'lodash';
import Header from './header';
import Search from './search';
import Projects from './projects';
import CreateProjectForm from './CreateProjectForm';
+import ListFooter from '../../components/controls/ListFooter';
import { PAGE_SIZE, TYPE } from './constants';
import { getComponents, getProvisioned, getGhosts, deleteComponents } from '../../api/components';
-import ListFooter from '../../components/controls/ListFooter';
+import { translate } from '../../helpers/l10n';
import type { Organization } from '../../store/organizations/duck';
type Props = {|
@@ -231,6 +233,8 @@ export default class Main extends React.PureComponent {
render() {
return (
<div className="page page-limited" id="projects-management-page">
+ <Helmet title={translate('projects_management')} />
+
<Header
hasProvisionPermission={this.props.hasProvisionPermission}
onProjectCreate={this.openCreateProjectForm}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
index add7d2bd892..e848e7657de 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
@@ -18,12 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import PageHeaderContainer from './PageHeaderContainer';
import ProjectsListContainer from './ProjectsListContainer';
import ProjectsListFooterContainer from './ProjectsListFooterContainer';
import PageSidebar from './PageSidebar';
import VisualizationsContainer from '../visualizations/VisualizationsContainer';
import { parseUrlQuery } from '../store/utils';
+import { translate } from '../../../helpers/l10n';
import '../styles.css';
export default class AllProjects extends React.PureComponent {
@@ -96,6 +98,7 @@ export default class AllProjects extends React.PureComponent {
return (
<div className="layout-page projects-page">
+ <Helmet title={translate('projects.page')} />
<div className="layout-page-side-outer">
<div className="layout-page-side" style={{ top }}>
<div className="layout-page-side-inner">
diff --git a/server/sonar-web/src/main/js/apps/projects/components/App.js b/server/sonar-web/src/main/js/apps/projects/components/App.js
index 8897f1e6f36..2bd70ec15bd 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/App.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/App.js
@@ -18,8 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
-import Helmet from 'react-helmet';
-import { translate } from '../../../helpers/l10n';
export default class App extends React.PureComponent {
componentDidMount() {
@@ -33,7 +31,6 @@ export default class App extends React.PureComponent {
render() {
return (
<div id="projects-page">
- <Helmet title={translate('projects.page')} titleTemplate="%s - SonarQube" />
{this.props.children}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
index 7d603a8baa1..60957d01b68 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React, { Component } from 'react';
+import Helmet from 'react-helmet';
import {
fetchQualityGate,
setQualityGateAsDefault,
@@ -113,6 +114,7 @@ export default class Details extends Component {
return (
<div className="search-navigator-workspace">
+ <Helmet title={qualityGate.name} />
<DetailsHeader
qualityGate={qualityGate}
edit={edit}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
index 6bfddd60fe8..575a9f212e7 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
@@ -18,12 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React, { Component } from 'react';
+import Helmet from 'react-helmet';
import ListHeader from './ListHeader';
import List from './List';
import {
fetchQualityGatesAppDetails,
fetchQualityGates as fetchQualityGatesAPI
} from '../../../api/quality-gates';
+import { translate } from '../../../helpers/l10n';
import '../styles.css';
export default class QualityGatesApp extends Component {
@@ -52,9 +54,11 @@ export default class QualityGatesApp extends Component {
render() {
const { children, qualityGates, edit } = this.props;
-
+ const defaultTitle = translate('quality_gates.page');
return (
<div className="search-navigator sticky search-navigator-extended-view">
+ <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
+
<div className="search-navigator-side search-navigator-side-light" style={{ top: 30 }}>
<div className="search-navigator-filters">
<ListHeader canEdit={edit} onAdd={this.handleAdd.bind(this)} />
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
index 982553d7993..564996b4c5c 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
@@ -21,6 +21,8 @@
import React from 'react';
import { getQualityProfiles, getExporters } from '../../../api/quality-profiles';
import { sortProfiles } from '../utils';
+import { translate } from '../../../helpers/l10n';
+import OrganizationHelmet from '../../../components/common/OrganizationHelmet';
import type { Exporter } from '../propTypes';
import '../styles.css';
@@ -28,7 +30,7 @@ type Props = {
children: React.Element<*>,
currentUser: { permissions: { global: Array<string> } },
languages: Array<*>,
- organization: { canAdmin?: boolean, key: string } | null
+ organization: { name: string, canAdmin?: boolean, key: string } | null
};
type State = {
@@ -94,11 +96,11 @@ export default class App extends React.PureComponent {
if (this.state.loading) {
return <i className="spinner" />;
}
-
+ const { organization } = this.props;
const finalLanguages = Object.values(this.props.languages);
- const canAdmin = this.props.organization
- ? this.props.organization.canAdmin
+ const canAdmin = organization
+ ? organization.canAdmin
: this.props.currentUser.permissions.global.includes('profileadmin');
return React.cloneElement(this.props.children, {
@@ -106,7 +108,7 @@ export default class App extends React.PureComponent {
languages: finalLanguages,
exporters: this.state.exporters,
updateProfiles: this.updateProfiles,
- organization: this.props.organization ? this.props.organization.key : null,
+ organization: organization ? organization.key : null,
canAdmin
});
}
@@ -114,6 +116,11 @@ export default class App extends React.PureComponent {
render() {
return (
<div className="page page-limited">
+ <OrganizationHelmet
+ title={translate('quality_profiles.page')}
+ organization={this.props.organization}
+ />
+
{this.renderChild()}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
index 0ee269b6a25..ed1d683ed1c 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
@@ -22,7 +22,6 @@ import React from 'react';
import Helmet from 'react-helmet';
import ProfileNotFound from './ProfileNotFound';
import ProfileHeader from '../details/ProfileHeader';
-import { translate } from '../../../helpers/l10n';
import type { Profile } from '../propTypes';
type Props = {
@@ -85,12 +84,9 @@ export default class ProfileContainer extends React.PureComponent {
...other
});
- const title = translate('quality_profiles.page') + ' - ' + profile.name;
-
return (
<div>
- <Helmet title={title} titleTemplate="SonarQube - %s" />
-
+ <Helmet title={profile.name} />
<ProfileHeader
canAdmin={this.props.canAdmin}
organization={organization}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
index e675d6245f9..df44389a9c6 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
@@ -19,11 +19,9 @@
*/
// @flow
import React from 'react';
-import Helmet from 'react-helmet';
import PageHeader from './PageHeader';
import Evolution from './Evolution';
import ProfilesList from './ProfilesList';
-import { translate } from '../../../helpers/l10n';
import type { Profile } from '../propTypes';
type Props = {
@@ -41,8 +39,6 @@ export default class HomeContainer extends React.PureComponent {
render() {
return (
<div>
- <Helmet title={translate('quality_profiles.page')} titleTemplate="SonarQube - %s" />
-
<PageHeader {...this.props} />
<div className="page-with-sidebar">
diff --git a/server/sonar-web/src/main/js/apps/settings/components/App.js b/server/sonar-web/src/main/js/apps/settings/components/App.js
index c86abf3956a..d357cff1fbc 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/App.js
+++ b/server/sonar-web/src/main/js/apps/settings/components/App.js
@@ -19,6 +19,7 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import PageHeader from './PageHeader';
import CategoryDefinitionsList from './CategoryDefinitionsList';
@@ -26,6 +27,7 @@ import AllCategoriesList from './AllCategoriesList';
import WildcardsHelp from './WildcardsHelp';
import { fetchSettings } from '../store/actions';
import { getSettingsAppDefaultCategory } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
import '../styles.css';
type Props = {
@@ -78,6 +80,8 @@ class App extends React.PureComponent {
return (
<div id="settings-page" className="page page-limited">
+ <Helmet title={translate('settings.page')} />
+
<PageHeader component={this.props.component} />
<div className="settings-layout">
<div className="settings-side">
diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js
index 05b0b9ca524..72927a664e3 100644
--- a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js
+++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import GenerateSecretKeyForm from './GenerateSecretKeyForm';
import EncryptionForm from './EncryptionForm';
import { translate } from '../../../helpers/l10n';
@@ -42,6 +43,7 @@ export default class EncryptionApp extends React.PureComponent {
render() {
return (
<div id="encryption-page" className="page page-limited">
+ <Helmet title={translate('property.category.security.encryption')} />
<header className="page-header">
<h1 className="page-title">{translate('property.category.security.encryption')}</h1>
{this.props.loading && <i className="spinner" />}
diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js
index 5a2a95b6544..8958da9608a 100644
--- a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js
+++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js
@@ -18,12 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import LicensesAppHeader from './LicensesAppHeader';
import LicensesListContainer from './LicensesListContainer';
+import { translate } from '../../../helpers/l10n';
export default function LicensesApp() {
return (
<div id="licenses-page" className="page page-limited">
+ <Helmet title={translate('property.category.licenses')} />
<LicensesAppHeader />
<LicensesListContainer />
</div>
diff --git a/server/sonar-web/src/main/js/apps/settings/serverId/ServerIdApp.js b/server/sonar-web/src/main/js/apps/settings/serverId/ServerIdApp.js
index 40c7197b3dd..ebe44676016 100644
--- a/server/sonar-web/src/main/js/apps/settings/serverId/ServerIdApp.js
+++ b/server/sonar-web/src/main/js/apps/settings/serverId/ServerIdApp.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { translate } from '../../../helpers/l10n';
import { getServerId, generateServerId } from '../../../api/settings';
import { parseError } from '../../code/utils';
@@ -76,6 +77,7 @@ export default class ServerIdApp extends React.PureComponent {
render() {
return (
<div id="server-id-page" className="page page-limited">
+ <Helmet title={translate('property.category.server_id')} />
<header className="page-header">
<h1 className="page-title">{translate('property.category.server_id')}</h1>
{this.state.loading && <i className="spinner" />}
diff --git a/server/sonar-web/src/main/js/apps/system/main.js b/server/sonar-web/src/main/js/apps/system/main.js
index 5707ee5604d..3bb364b83c0 100644
--- a/server/sonar-web/src/main/js/apps/system/main.js
+++ b/server/sonar-web/src/main/js/apps/system/main.js
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { sortBy } from 'lodash';
import { getSystemInfo } from '../../api/system';
import Section from './section';
@@ -75,6 +76,7 @@ export default class Main extends React.PureComponent {
return (
<div className="page">
+ <Helmet title={translate('system_info.page')} />
<header className="page-header">
<h1 className="page-title">{translate('system_info.page')}</h1>
<div className="page-actions">
diff --git a/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js b/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js
index 4745b919ec2..cd5a9f8f105 100644
--- a/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/update-center/components/UpdateCenterAppContainer.js
@@ -18,9 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import init from '../init';
import { getSettingValue } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
class UpdateCenterAppContainer extends React.PureComponent {
componentDidMount() {
@@ -38,6 +40,7 @@ class UpdateCenterAppContainer extends React.PureComponent {
// but react wants it to be there to unmount it
return (
<div>
+ <Helmet title={translate('update_center.page')} />
<div ref="container" />
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js b/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js
index 99bdd44584a..d70383fd891 100644
--- a/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/users/components/UsersAppContainer.js
@@ -18,9 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
+import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import init from '../init';
import { getCurrentUser } from '../../../store/rootReducer';
+import { translate } from '../../../helpers/l10n';
class UsersAppContainer extends React.PureComponent {
static propTypes = {
@@ -32,7 +34,12 @@ class UsersAppContainer extends React.PureComponent {
}
render() {
- return <div ref="container" />;
+ return (
+ <div>
+ <Helmet title={translate('users.page')} />
+ <div ref="container" />
+ </div>
+ );
}
}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js
index a97c27b9d7a..166babf4c99 100644
--- a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js
+++ b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js
@@ -19,12 +19,14 @@
*/
// @flow
import React from 'react';
+import Helmet from 'react-helmet';
import { Link } from 'react-router';
import { fetchWebApi } from '../../../api/web-api';
import Menu from './Menu';
import Search from './Search';
import Domain from './Domain';
import { getActionKey, isDomainPathActive } from '../utils';
+import { translate } from '../../../helpers/l10n';
import type { Domain as DomainType } from '../../../api/web-api';
import '../styles/web-api.css';
@@ -142,10 +144,11 @@ export default class WebApiApp extends React.PureComponent {
return (
<div className="search-navigator sticky">
+ <Helmet title={translate('api_documentation.page')} />
<div className="search-navigator-side search-navigator-side-light" style={{ top: 30 }}>
<div className="web-api-page-header">
<Link to="/web_api/">
- <h1>Web API</h1>
+ <h1>{translate('api_documentation.page')}</h1>
</Link>
</div>
diff --git a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js b/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js
new file mode 100644
index 00000000000..ffffec47d4b
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import React from 'react';
+import Helmet from 'react-helmet';
+
+type Props = {
+ title: string,
+ organization?: ?{ name: string }
+};
+
+export default function OrganizationHelmet({ title, organization }: Props) {
+ const defaultTitle = title + (organization ? ' - ' + organization.name : '');
+ return <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />;
+}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 42cdecd025a..654c487f390 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -510,6 +510,7 @@ analysis_reports.page=Project Computation
coding_rules.page=Rules
global_permissions.page=Global Permissions
global_permissions.page.description=Grant and revoke permissions to make changes at the global level. These permissions include editing quality profiles, sharing dashboards, and performing global system administration.
+custom_metrics.page=Custom Metrics
manual_metrics.page=Manual Metrics
manual_metrics.page.description=These metrics are available for all projects. Manual measures can be set at project level via the configuration interface.
manual_metrics.add_manual_metric=Add New Manual Metric