aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java4
-rw-r--r--server/sonar-web/package.json1
-rw-r--r--server/sonar-web/src/main/js/app/components/App.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/PageTracker.tsx65
-rw-r--r--server/sonar-web/yarn.lock16
7 files changed, 92 insertions, 6 deletions
diff --git a/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java b/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
index 6c5deb927c7..9cec2c0a2b5 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/ProcessProperties.java
@@ -108,8 +108,8 @@ public class ProcessProperties {
SONAR_UPDATECENTER_ACTIVATE("sonar.updatecenter.activate", "true"),
SONARCLOUD_ENABLED("sonar.sonarcloud.enabled", "false"),
-
SONAR_PRISMIC_ACCESS_TOKEN("sonar.prismic.accessToken", ""),
+ SONAR_ANALYTICS_TRACKING_ID("sonar.analytics.trackingId", ""),
BITBUCKETCLOUD_APP_KEY("sonar.bitbucketcloud.appKey", "sonarcloud"),
BITBUCKETCLOUD_ENDPOINT("sonar.bitbucketcloud.endpoint", "https://api.bitbucket.org"),
@@ -122,8 +122,6 @@ public class ProcessProperties {
// whether the blue/green deployment of server is enabled
BLUE_GREEN_ENABLED("sonar.blueGreenEnabled", "false");
-
-
private final String key;
private final String defaultValue;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java
index f69de5baed5..841840822d7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/GlobalAction.java
@@ -52,6 +52,7 @@ import static org.sonar.core.config.WebConstants.SONAR_LF_GRAVATAR_SERVER_URL;
import static org.sonar.core.config.WebConstants.SONAR_LF_LOGO_URL;
import static org.sonar.core.config.WebConstants.SONAR_LF_LOGO_WIDTH_PX;
import static org.sonar.process.ProcessProperties.Property.SONARCLOUD_ENABLED;
+import static org.sonar.process.ProcessProperties.Property.SONAR_ANALYTICS_TRACKING_ID;
import static org.sonar.process.ProcessProperties.Property.SONAR_PRISMIC_ACCESS_TOKEN;
import static org.sonar.process.ProcessProperties.Property.SONAR_UPDATECENTER_ACTIVATE;
@@ -101,6 +102,7 @@ public class GlobalAction implements NavigationWsAction, Startable {
boolean isOnSonarCloud = config.getBoolean(SONARCLOUD_ENABLED.getKey()).orElse(false);
if (isOnSonarCloud) {
this.systemSettingValuesByKey.put(SONAR_PRISMIC_ACCESS_TOKEN.getKey(), config.get(SONAR_PRISMIC_ACCESS_TOKEN.getKey()).orElse(null));
+ this.systemSettingValuesByKey.put(SONAR_ANALYTICS_TRACKING_ID.getKey(), config.get(SONAR_ANALYTICS_TRACKING_ID.getKey()).orElse(null));
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java
index b0cb60037e9..aed04367e5a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/GlobalActionTest.java
@@ -130,11 +130,13 @@ public class GlobalActionTest {
public void return_prismic_setting_on_sonarcloud_only() {
settings.setProperty("sonar.sonarcloud.enabled", true);
settings.setProperty("sonar.prismic.accessToken", "secret");
+ settings.setProperty("sonar.analytics.trackingId", "ga_id");
init();
assertJson(call()).isSimilarTo("{" +
" \"settings\": {" +
" \"sonar.prismic.accessToken\": \"secret\"" +
+ " \"sonar.analytics.trackingId\": \"ga_id\"" +
" }" +
"}");
}
@@ -248,7 +250,6 @@ public class GlobalActionTest {
userSession.logIn().setRoot();
when(webServer.isStandalone()).thenReturn(true);
-
assertJson(call()).isSimilarTo("{\"standalone\":true}");
}
@@ -258,7 +259,6 @@ public class GlobalActionTest {
userSession.logIn().setRoot();
when(webServer.isStandalone()).thenReturn(false);
-
assertJson(call()).isSimilarTo("{\"standalone\":false}");
}
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index 241641cf904..a8b603c90d8 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -26,6 +26,7 @@
"react-day-picker": "7.1.8",
"react-dom": "16.2.0",
"react-draggable": "3.0.5",
+ "react-ga": "2.5.3",
"react-helmet": "5.2.0",
"react-intl": "2.4.0",
"react-modal": "3.4.4",
diff --git a/server/sonar-web/src/main/js/app/components/App.tsx b/server/sonar-web/src/main/js/app/components/App.tsx
index 3930b084f79..91c65cc4e6c 100644
--- a/server/sonar-web/src/main/js/app/components/App.tsx
+++ b/server/sonar-web/src/main/js/app/components/App.tsx
@@ -26,7 +26,10 @@ import { CurrentUser } from '../types';
import { fetchCurrentUser } from '../../store/users/actions';
import { fetchLanguages, fetchAppState } from '../../store/rootActions';
import { fetchMyOrganizations } from '../../apps/account/organizations/actions';
-import { getInstance } from '../../helpers/system';
+import { getInstance, isSonarCloud } from '../../helpers/system';
+import { lazyLoad } from '../../components/lazyLoad';
+
+const PageTracker = lazyLoad(() => import('./PageTracker'));
interface Props {
children: JSX.Element;
@@ -115,6 +118,7 @@ class App extends React.PureComponent<Props, State> {
return (
<>
<Helmet defaultTitle={getInstance()} />
+ {isSonarCloud() && <PageTracker />}
{this.props.children}
</>
);
diff --git a/server/sonar-web/src/main/js/app/components/PageTracker.tsx b/server/sonar-web/src/main/js/app/components/PageTracker.tsx
new file mode 100644
index 00000000000..e2a4f0c04dd
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/PageTracker.tsx
@@ -0,0 +1,65 @@
+/*
+ * 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 GoogleAnalytics from 'react-ga';
+import { withRouter, WithRouterProps } from 'react-router';
+import { connect } from 'react-redux';
+import { getGlobalSettingValue } from '../../store/rootReducer';
+
+interface StateProps {
+ trackingId?: string;
+}
+
+type Props = WithRouterProps & StateProps;
+
+export class PageTracker extends React.PureComponent<Props> {
+ componentDidMount() {
+ if (this.props.trackingId) {
+ GoogleAnalytics.initialize(this.props.trackingId);
+ this.trackPage();
+ }
+ }
+
+ componentDidUpdate(prevProps: Props) {
+ const currentPage = this.props.location.pathname;
+ const prevPage = prevProps.location.pathname;
+
+ if (currentPage !== prevPage) {
+ this.trackPage();
+ }
+ }
+
+ trackPage = () => {
+ const { location, trackingId } = this.props;
+ if (trackingId) {
+ GoogleAnalytics.pageview(location.pathname);
+ }
+ };
+
+ render() {
+ return null;
+ }
+}
+
+const mapStateToProps = (state: any): StateProps => ({
+ trackingId: (getGlobalSettingValue(state, 'sonar.analytics.trackingId') || {}).value
+});
+
+export default withRouter<{}>(connect<StateProps>(mapStateToProps)(PageTracker));
diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock
index e99a7491fcf..33dd4edc00b 100644
--- a/server/sonar-web/yarn.lock
+++ b/server/sonar-web/yarn.lock
@@ -6971,6 +6971,13 @@ react-error-overlay@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
+react-ga@2.5.3:
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.3.tgz#0f447c73664c069a5fc341f6f431262e3d4c23c4"
+ optionalDependencies:
+ prop-types "^15.6.0"
+ react "^15.6.2 || ^16.0"
+
react-helmet@5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-5.2.0.tgz#a81811df21313a6d55c5f058c4aeba5d6f3d97a7"
@@ -7082,6 +7089,15 @@ react@16.2.0:
object-assign "^4.1.1"
prop-types "^15.6.0"
+"react@^15.6.2 || ^16.0":
+ version "16.4.1"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32"
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"