From 710344da59f61f395593782deb1c342d473da5bf Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 6 Sep 2016 16:02:40 +0200 Subject: [PATCH] SONAR-7980 Rewrite the "Licenses" page (#1207) --- .../java/it/settings/LicensesPageTest.java | 84 ++++++++++++++ .../SettingsTestRestartingOrchestrator.java | 6 - .../src/test/java/pageobjects/Navigation.java | 5 + .../pageobjects/licenses/LicenseItem.java | 46 ++++++++ .../pageobjects/licenses/LicensesPage.java | 52 +++++++++ .../SettingsTest/display-license.html | 69 ------------ .../SettingsTest/display-untyped-license.html | 69 ------------ .../ignore-corrupted-license.html | 59 ---------- server/sonar-web/src/main/js/api/licenses.js | 35 ++++++ .../src/main/js/apps/settings/app.js | 4 + .../settings/licenses/LicenseChangeForm.js | 48 ++++++++ .../js/apps/settings/licenses/LicenseRow.js | 69 ++++++++++++ .../settings/licenses/LicenseRowContainer.js | 32 ++++++ .../apps/settings/licenses/LicenseStatus.js | 41 +++++++ .../settings/licenses/LicenseValueView.hbs | 16 +++ .../settings/licenses/LicenseValueView.js | 41 +++++++ .../js/apps/settings/licenses/LicensesApp.js | 35 ++++++ .../settings/licenses/LicensesAppHeader.js | 33 ++++++ .../js/apps/settings/licenses/LicensesList.js | 61 +++++++++++ .../licenses/LicensesListContainer.js | 32 ++++++ .../__tests__/LicenseChangeForm-test.js | 27 +++++ .../licenses/__tests__/LicenseRow-test.js | 103 ++++++++++++++++++ .../licenses/__tests__/LicenseStatus-test.js | 50 +++++++++ .../licenses/__tests__/LicensesApp-test.js | 32 ++++++ .../__tests__/LicensesAppHeader-test.js | 28 +++++ .../licenses/__tests__/LicensesList-test.js | 41 +++++++ .../apps/settings/store/licenses/actions.js | 54 +++++++++ .../apps/settings/store/licenses/reducer.js | 36 ++++++ .../js/apps/settings/store/rootReducer.js | 6 + .../src/main/js/main/nav/links-mixin.js | 2 +- .../main/js/main/nav/settings/settings-nav.js | 6 +- .../sonar-web/src/main/less/init/forms.less | 4 + .../app/controllers/settings_controller.rb | 4 + .../resources/org/sonar/l10n/core.properties | 9 ++ 34 files changed, 1032 insertions(+), 207 deletions(-) create mode 100644 it/it-tests/src/test/java/it/settings/LicensesPageTest.java create mode 100644 it/it-tests/src/test/java/pageobjects/licenses/LicenseItem.java create mode 100644 it/it-tests/src/test/java/pageobjects/licenses/LicensesPage.java delete mode 100644 it/it-tests/src/test/resources/settings/SettingsTest/display-license.html delete mode 100644 it/it-tests/src/test/resources/settings/SettingsTest/display-untyped-license.html delete mode 100644 it/it-tests/src/test/resources/settings/SettingsTest/ignore-corrupted-license.html create mode 100644 server/sonar-web/src/main/js/api/licenses.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicenseStatus.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicensesAppHeader.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicensesList.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseChangeForm-test.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseRow-test.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseStatus-test.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesApp-test.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesAppHeader-test.js create mode 100644 server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesList-test.js create mode 100644 server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js create mode 100644 server/sonar-web/src/main/js/apps/settings/store/licenses/reducer.js diff --git a/it/it-tests/src/test/java/it/settings/LicensesPageTest.java b/it/it-tests/src/test/java/it/settings/LicensesPageTest.java new file mode 100644 index 00000000000..4fe48857884 --- /dev/null +++ b/it/it-tests/src/test/java/it/settings/LicensesPageTest.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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. + */ +package it.settings; + +import com.sonar.orchestrator.Orchestrator; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.sonarqube.ws.Settings.ValuesWsResponse; +import org.sonarqube.ws.client.WsClient; +import org.sonarqube.ws.client.setting.ValuesRequest; +import pageobjects.Navigation; +import pageobjects.licenses.LicenseItem; +import pageobjects.licenses.LicensesPage; + +import static com.codeborne.selenide.Condition.text; +import static org.assertj.core.api.Assertions.assertThat; +import static util.ItUtils.newAdminWsClient; +import static util.ItUtils.pluginArtifact; + +public class LicensesPageTest { + private static Orchestrator orchestrator; + private static WsClient wsClient; + + @Rule + public Navigation nav = Navigation.get(orchestrator); + + @BeforeClass + public static void start() { + orchestrator = Orchestrator.builderEnv() + .addPlugin(pluginArtifact("license-plugin")) + .build(); + orchestrator.start(); + + wsClient = newAdminWsClient(orchestrator); + } + + @AfterClass + public static void stop() { + if (orchestrator != null) { + orchestrator.stop(); + } + } + + @Test + public void display_licenses() { + LicensesPage page = nav.logIn().asAdmin().openLicenses(); + + page.getLicenses().shouldHaveSize(2); + page.getLicensesAsItems().get(0).getName().shouldHave(text("typed.license.secured")); + page.getLicensesAsItems().get(1).getName().shouldHave(text("untyped.license.secured")); + } + + @Test + public void change_licenses() { + String EXAMPLE_LICENSE = "TmFtZTogRGV2ZWxvcHBlcnMKUGx1Z2luOiBhdXRvY29udHJvbApFeHBpcmVzOiAyMDEyLTA0LTAxCktleTogNjI5N2MxMzEwYzg2NDZiZTE5MDU1MWE4ZmZmYzk1OTBmYzEyYTIyMgo="; + + LicensesPage page = nav.logIn().asAdmin().openLicenses(); + LicenseItem licenseItem = page.getLicenseByKey("typed.license.secured"); + licenseItem.setLicense(EXAMPLE_LICENSE); + + ValuesWsResponse response = wsClient.settingsService() + .values(ValuesRequest.builder().setKeys("typed.license.secured").build()); + assertThat(response.getSettings(0).getValue()).isEqualTo(EXAMPLE_LICENSE); + } +} diff --git a/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java b/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java index 67b2eefa62f..7280a23305d 100644 --- a/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java +++ b/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java @@ -72,12 +72,6 @@ public class SettingsTestRestartingOrchestrator { // test encryption "/settings/SettingsTest/generate-secret-key.html", "/settings/SettingsTest/encrypt-text.html" - - // test licenses - // TODO enable when license page will be rewritten - // "/settings/SettingsTest/ignore-corrupted-license.html", - // "/settings/SettingsTest/display-license.html", - // "/settings/SettingsTest/display-untyped-license.html" ).build(); new SeleneseTest(selenese).runOn(orchestrator); } diff --git a/it/it-tests/src/test/java/pageobjects/Navigation.java b/it/it-tests/src/test/java/pageobjects/Navigation.java index 28a99e4b3f8..018fdbaaa1a 100644 --- a/it/it-tests/src/test/java/pageobjects/Navigation.java +++ b/it/it-tests/src/test/java/pageobjects/Navigation.java @@ -29,6 +29,7 @@ import java.net.URLEncoder; import javax.annotation.Nullable; import org.junit.rules.ExternalResource; import org.openqa.selenium.By; +import pageobjects.licenses.LicensesPage; import pageobjects.settings.SettingsPage; import static com.codeborne.selenide.Selenide.$; @@ -95,6 +96,10 @@ public class Navigation extends ExternalResource { return open(url, SettingsPage.class); } + public LicensesPage openLicenses() { + return open("/settings/licenses", LicensesPage.class); + } + public void open(String relativeUrl) { Selenide.open(relativeUrl); } diff --git a/it/it-tests/src/test/java/pageobjects/licenses/LicenseItem.java b/it/it-tests/src/test/java/pageobjects/licenses/LicenseItem.java new file mode 100644 index 00000000000..2df9869b75a --- /dev/null +++ b/it/it-tests/src/test/java/pageobjects/licenses/LicenseItem.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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. + */ +package pageobjects.licenses; + +import com.codeborne.selenide.SelenideElement; + +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + +public class LicenseItem { + + private final SelenideElement elt; + + public LicenseItem(SelenideElement elt) { + this.elt = elt; + } + + public SelenideElement getName() { + return elt.find(".js-product"); + } + + public LicenseItem setLicense(String value) { + elt.find(".js-change").click(); + $("#license-input").shouldBe(visible).val(value); + $(".js-modal-submit").click(); + $("#license-input").shouldNotBe(visible); + return this; + } +} diff --git a/it/it-tests/src/test/java/pageobjects/licenses/LicensesPage.java b/it/it-tests/src/test/java/pageobjects/licenses/LicensesPage.java new file mode 100644 index 00000000000..129d6c6844b --- /dev/null +++ b/it/it-tests/src/test/java/pageobjects/licenses/LicensesPage.java @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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. + */ +package pageobjects.licenses; + +import com.codeborne.selenide.ElementsCollection; +import com.codeborne.selenide.SelenideElement; +import java.util.List; +import java.util.stream.Collectors; + +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; +import static com.codeborne.selenide.Selenide.$$; + +public class LicensesPage { + + public LicensesPage() { + $("#licenses-page").shouldBe(visible); + } + + public ElementsCollection getLicenses() { + return $$(".js-license"); + } + + public List getLicensesAsItems() { + return getLicenses() + .stream() + .map(LicenseItem::new) + .collect(Collectors.toList()); + } + + public LicenseItem getLicenseByKey(String key) { + SelenideElement element = $(".js-license[data-license-key=\"" + key + "\"]"); + return new LicenseItem(element); + } +} diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/display-license.html b/it/it-tests/src/test/resources/settings/SettingsTest/display-license.html deleted file mode 100644 index f2f1d31bb87..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/display-license.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - display-license - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/logout
open/settings?category=general
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
typeid=input_typed.license.securedTmFtZTogRGV2ZWxvcHBlcnMKUGx1Z2luOiBhdXRvY29udHJvbApFeHBpcmVzOiAyMDEyLTA0LTAxCktleTogNjI5N2MxMzEwYzg2NDZiZTE5MDU1MWE4ZmZmYzk1OTBmYzEyYTIyMgo=
clickid=submit_settings
waitForTextblock_typed.license.secured*autocontrol*
waitForTextblock_typed.license.secured*Developpers*
waitForTextblock_typed.license.secured*2012*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/display-untyped-license.html b/it/it-tests/src/test/resources/settings/SettingsTest/display-untyped-license.html deleted file mode 100644 index ae353cf7618..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/display-untyped-license.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - display-untyped-license - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/logout
open/settings?category=general
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
typeid=input_untyped.license.securedTmFtZTogRGV2ZWxvcHBlcnMKUGx1Z2luOiBhdXRvY29udHJvbApFeHBpcmVzOiAyMDEyLTA0LTAxCktleTogNjI5N2MxMzEwYzg2NDZiZTE5MDU1MWE4ZmZmYzk1OTBmYzEyYTIyMgo=
clicksubmit_settings
waitForTextblock_untyped.license.secured*autocontrol*
waitForTextblock_untyped.license.secured*Developpers*
waitForTextblock_untyped.license.secured*2012*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/ignore-corrupted-license.html b/it/it-tests/src/test/resources/settings/SettingsTest/ignore-corrupted-license.html deleted file mode 100644 index e05b5727102..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/ignore-corrupted-license.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - ignore-corrupted-license - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/logout
open/settings?category=general
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
typeid=input_typed.license.securedABCDE
clickid=submit_settings
waitForTextblock_typed.license.secured*Product*-*Organization*-*Expiration*-*
- - diff --git a/server/sonar-web/src/main/js/api/licenses.js b/server/sonar-web/src/main/js/api/licenses.js new file mode 100644 index 00000000000..466c67dcbc3 --- /dev/null +++ b/server/sonar-web/src/main/js/api/licenses.js @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { getJSON, post } from '../helpers/request'; + +export const getLicenses = () => + getJSON('/api/licenses/list').then(r => r.licenses); + +export const setLicense = (key, value) => { + const url = '/api/settings/set'; + const data = { key, value }; + return post(url, data); +}; + +export const resetLicense = key => { + const url = '/api/settings/reset'; + const data = { keys: key }; + return post(url, data); +}; diff --git a/server/sonar-web/src/main/js/apps/settings/app.js b/server/sonar-web/src/main/js/apps/settings/app.js index cf9dfcf07ad..51c70703f9d 100644 --- a/server/sonar-web/src/main/js/apps/settings/app.js +++ b/server/sonar-web/src/main/js/apps/settings/app.js @@ -23,6 +23,7 @@ import { Provider } from 'react-redux'; import { Router, Route, Redirect, useRouterHistory } from 'react-router'; import { createHistory } from 'history'; import App from './components/App'; +import LicensesApp from './licenses/LicensesApp'; import rootReducer from './store/rootReducer'; import configureStore from '../../components/store/configureStore'; @@ -44,6 +45,9 @@ window.sonarqube.appStarted.then(options => { + {options.component == null && ( + + )} ), el); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js new file mode 100644 index 00000000000..8976c35593f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseChangeForm.js @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 LicenseValueView from './LicenseValueView'; +import { translate } from '../../../helpers/l10n'; + +export default class LicenseChangeForm extends React.Component { + static propTypes = { + license: React.PropTypes.object.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + onClick (e) { + e.preventDefault(); + e.target.blur(); + + const { license, onChange } = this.props; + + new LicenseValueView({ + productName: license.name || license.key, + value: license.value, + onChange + }).render(); + } + + render () { + return ( + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js new file mode 100644 index 00000000000..2b095f0bec7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRow.js @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 shallowCompare from 'react-addons-shallow-compare'; +import moment from 'moment'; +import LicenseStatus from './LicenseStatus'; +import LicenseChangeForm from './LicenseChangeForm'; + +export default class LicenseRow extends React.Component { + static propTypes = { + license: React.PropTypes.object.isRequired, + setLicense: React.PropTypes.func.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + handleSet (value) { + return this.props.setLicense(this.props.license.key, value).catch(() => { /* do nothing */ }); + } + + render () { + const { license } = this.props; + + return ( + + + +
+ {license.name || license.key} +
+ + {license.organization} + +
+ {moment(license.expiration).format('LL')} +
+ + {license.type} + +
+ {license.serverId} +
+ + + this.handleSet(value)}/> + + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js new file mode 100644 index 00000000000..af004df569f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseRowContainer.js @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; +import LicenseRow from './LicenseRow'; +import { getLicenseByKey } from '../store/rootReducer'; +import { setLicense } from '../store/licenses/actions'; + +const mapStateToProps = (state, ownProps) => ({ + license: getLicenseByKey(state, ownProps.licenseKey) +}); + +export default connect( + mapStateToProps, + { setLicense } +)(LicenseRow); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseStatus.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseStatus.js new file mode 100644 index 00000000000..657435686d7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseStatus.js @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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'; + +export default class LicenseStatus extends React.Component { + static propTypes = { + license: React.PropTypes.object.isRequired + }; + + render () { + const { license } = this.props; + + if (license.value == null) { + return null; + } + + const isInvalid = !!license.invalidProduct || !!license.invalidExpiration || !!license.invalidServerId; + if (isInvalid) { + return ; + } + + return ; + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs new file mode 100644 index 00000000000..c472b282dda --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.hbs @@ -0,0 +1,16 @@ +
+ + + +
diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js new file mode 100644 index 00000000000..4e80de4bb9a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicenseValueView.js @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 ModalForm from '../../../components/common/modal-form'; +import Template from './LicenseValueView.hbs'; + +export default ModalForm.extend({ + template: Template, + + onFormSubmit () { + ModalForm.prototype.onFormSubmit.apply(this, arguments); + this.disableForm(); + this.showSpinner(); + + const value = this.$('textarea').val(); + this.options.onChange(value).then(() => this.destroy()); + }, + + serializeData () { + return { + productName: this.options.productName, + value: this.options.value + }; + } +}); 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 new file mode 100644 index 00000000000..0568461d769 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesApp.js @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 GlobalMessagesContainer from '../components/GlobalMessagesContainer'; +import LicensesAppHeader from './LicensesAppHeader'; +import LicensesListContainer from './LicensesListContainer'; + +export default class LicensesApp extends React.Component { + render () { + return ( +
+ + + +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesAppHeader.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesAppHeader.js new file mode 100644 index 00000000000..a504570db5c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesAppHeader.js @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { translate } from '../../../helpers/l10n'; + +export default class LicensesAppHeader extends React.Component { + render () { + return ( +
+

{translate('property.category.licenses')}

+
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesList.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesList.js new file mode 100644 index 00000000000..84bfb603c3b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesList.js @@ -0,0 +1,61 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 shallowCompare from 'react-addons-shallow-compare'; +import LicenseRowContainer from './LicenseRowContainer'; +import { translate } from '../../../helpers/l10n'; + +export default class LicensesList extends React.Component { + static propTypes = { + licenses: React.PropTypes.array.isRequired, + fetchLicenses: React.PropTypes.func.isRequired + }; + + componentDidMount () { + this.props.fetchLicenses().catch(() => { /* do nothing */ }); + } + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + render () { + return ( + + + + + + + + + + + + + + {this.props.licenses.map(licenseKey => ( + + ))} + +
 {translate('licenses.list.product')}{translate('licenses.list.organization')}{translate('licenses.list.expiration')}{translate('licenses.list.type')}{translate('licenses.list.server')} 
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js new file mode 100644 index 00000000000..bd692b7e390 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/LicensesListContainer.js @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; +import LicensesList from './LicensesList'; +import { fetchLicenses } from '../store/licenses/actions'; +import { getAllLicenseKeys } from '../store/rootReducer'; + +const mapStateToProps = state => ({ + licenses: getAllLicenseKeys(state) +}); + +export default connect( + mapStateToProps, + { fetchLicenses } +)(LicensesList); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseChangeForm-test.js b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseChangeForm-test.js new file mode 100644 index 00000000000..7d52015161f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseChangeForm-test.js @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { shallow } from 'enzyme'; +import LicenseChangeForm from '../LicenseChangeForm'; + +it('should render button', () => { + const form = shallow(); + expect(form.is('button')).toBe(true); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseRow-test.js b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseRow-test.js new file mode 100644 index 00000000000..87a75c90292 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseRow-test.js @@ -0,0 +1,103 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { shallow } from 'enzyme'; +import LicenseRow from '../LicenseRow'; +import LicenseStatus from '../LicenseStatus'; +import LicenseChangeForm from '../LicenseChangeForm'; + +it('should render status', () => { + const license = {}; + const licenseStatus = shallow().find(LicenseStatus); + expect(licenseStatus.length).toBe(1); + expect(licenseStatus.prop('license')).toBe(license); +}); + +it('should render product', () => { + const license = { name: 'foo' }; + const licenseProduct = shallow().find('.js-product'); + expect(licenseProduct.length).toBe(1); + expect(licenseProduct.text()).toContain('foo'); +}); + +it('should render invalid product', () => { + const license = { product: 'foo', invalidProduct: true }; + const licenseProduct = shallow().find('.js-product'); + expect(licenseProduct.find('.text-danger').length).toBe(1); +}); + +it('should render key when no name', () => { + const license = { key: 'foo.secured' }; + const licenseProduct = shallow().find('.js-product'); + expect(licenseProduct.length).toBe(1); + expect(licenseProduct.text()).toContain('foo.secured'); +}); + +it('should render organization', () => { + const license = { organization: 'org' }; + const licenseOrg = shallow().find('.js-organization'); + expect(licenseOrg.length).toBe(1); + expect(licenseOrg.text()).toContain('org'); +}); + +it('should render expiration', () => { + const license = { expiration: '2015-01-01' }; + const licenseExpiration = shallow().find('.js-expiration'); + expect(licenseExpiration.length).toBe(1); + expect(licenseExpiration.text()).toContain('2015'); +}); + +it('should render invalid expiration', () => { + const license = { expiration: '2015-01-01', invalidExpiration: true }; + const licenseExpiration = shallow().find('.js-expiration'); + expect(licenseExpiration.find('.text-danger').length).toBe(1); +}); + +it('should render type', () => { + const license = { type: 'PRODUCTION' }; + const licenseType = shallow().find('.js-type'); + expect(licenseType.length).toBe(1); + expect(licenseType.text()).toContain('PRODUCTION'); +}); + +it('should render server id', () => { + const license = { serverId: 'bar' }; + const licenseServerId = shallow().find('.js-server-id'); + expect(licenseServerId.length).toBe(1); + expect(licenseServerId.text()).toContain('bar'); +}); + +it('should render invalid server id', () => { + const license = { serverId: 'bar', invalidServerId: true }; + const licenseServerId = shallow().find('.js-server-id'); + expect(licenseServerId.find('.text-danger').length).toBe(1); +}); + +it('should render change form', () => { + const license = { key: 'foo' }; + const setLicense = jest.fn(() => Promise.resolve()); + const licenseChangeForm = shallow().find(LicenseChangeForm); + expect(licenseChangeForm.length).toBe(1); + expect(licenseChangeForm.prop('license')).toBe(license); + expect(typeof licenseChangeForm.prop('onChange')).toBe('function'); + + licenseChangeForm.prop('onChange')('license-hash'); + expect(setLicense).toBeCalledWith('foo', 'license-hash'); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseStatus-test.js b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseStatus-test.js new file mode 100644 index 00000000000..106f1d614bb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicenseStatus-test.js @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { shallow } from 'enzyme'; +import LicenseStatus from '../LicenseStatus'; + +it('should render nothing when no value', () => { + const status = shallow(); + expect(status.node).toBeNull(); +}); + +it('should render ok', () => { + const status = shallow(); + expect(status.is('.icon-check')).toBe(true); +}); + +it('should render error when invalid product', () => { + const status = shallow(); + expect(status.is('.icon-check')).toBe(false); + expect(status.is('.icon-alert-error')).toBe(true); +}); + +it('should render error when invalid expiration', () => { + const status = shallow(); + expect(status.is('.icon-check')).toBe(false); + expect(status.is('.icon-alert-error')).toBe(true); +}); + +it('should render error when invalid server id', () => { + const status = shallow(); + expect(status.is('.icon-check')).toBe(false); + expect(status.is('.icon-alert-error')).toBe(true); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesApp-test.js b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesApp-test.js new file mode 100644 index 00000000000..ab23f0f8ed0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesApp-test.js @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { shallow } from 'enzyme'; +import LicensesApp from '../LicensesApp'; +import GlobalMessagesContainer from '../../components/GlobalMessagesContainer'; +import LicensesAppHeader from '../LicensesAppHeader'; +import LicensesListContainer from '../LicensesListContainer'; + +it('should render', () => { + const app = shallow(); + expect(app.find(GlobalMessagesContainer).length).toBe(1); + expect(app.find(LicensesAppHeader).length).toBe(1); + expect(app.find(LicensesListContainer).length).toBe(1); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesAppHeader-test.js b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesAppHeader-test.js new file mode 100644 index 00000000000..a5b04e03129 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesAppHeader-test.js @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { shallow } from 'enzyme'; +import LicensesAppHeader from '../LicensesAppHeader'; + +it('should render', () => { + const header = shallow(); + expect(header.find('.page-title').length).toBe(1); + expect(header.find('.page-description').length).toBe(1); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesList-test.js b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesList-test.js new file mode 100644 index 00000000000..2b3fb17e89e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/licenses/__tests__/LicensesList-test.js @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { shallow, mount } from 'enzyme'; +import LicensesList from '../LicensesList'; +import LicenseRowContainer from '../LicenseRowContainer'; + +it('should render', () => { + const list = shallow(); + expect(list.is('table')).toBe(true); +}); + +it('should fetch licenses', () => { + const fetchLicenses = jest.fn(() => Promise.resolve()); + mount(); + expect(fetchLicenses).toBeCalled(); +}); + +it('should render rows', () => { + const list = shallow(); + expect(list.find(LicenseRowContainer).length).toBe(2); + expect(list.find(LicenseRowContainer).at(0).prop('licenseKey')).toBe('foo'); + expect(list.find(LicenseRowContainer).at(1).prop('licenseKey')).toBe('bar'); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js b/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js new file mode 100644 index 00000000000..d94b51a5902 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/store/licenses/actions.js @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 licenses from '../../../../api/licenses'; +import { parseError } from '../../../code/utils'; +import { addGlobalSuccessMessage, addGlobalErrorMessage } from '../../../../components/store/globalMessages'; +import { translate } from '../../../../helpers/l10n'; + +export const RECEIVE_LICENSES = 'RECEIVE_LICENSES'; + +const receiveLicenses = licenses => ({ + type: RECEIVE_LICENSES, + licenses +}); + +export const fetchLicenses = () => dispatch => + licenses.getLicenses() + .then(licenses => { + dispatch(receiveLicenses(licenses)); + }) + .catch(e => { + parseError(e).then(message => dispatch(addGlobalErrorMessage(key, message))); + return Promise.reject(); + }); + +export const setLicense = (key, value) => dispatch => { + const request = value ? licenses.setLicense(key, value) : licenses.resetLicense(key); + + return request + .then(() => { + dispatch(fetchLicenses()); + dispatch(addGlobalSuccessMessage(translate('licenses.success_message'))); + }) + .catch(e => { + parseError(e).then(message => dispatch(addGlobalErrorMessage(message))); + return Promise.reject(); + }); +}; diff --git a/server/sonar-web/src/main/js/apps/settings/store/licenses/reducer.js b/server/sonar-web/src/main/js/apps/settings/store/licenses/reducer.js new file mode 100644 index 00000000000..e51c5438254 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/store/licenses/reducer.js @@ -0,0 +1,36 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 keyBy from 'lodash/keyBy'; +import { RECEIVE_LICENSES } from './actions'; + +const reducer = (state = {}, action = {}) => { + if (action.type === RECEIVE_LICENSES) { + const licensesByKey = keyBy(action.licenses, 'key'); + return { ...state, ...licensesByKey }; + } + + return state; +}; + +export default reducer; + +export const getLicenseByKey = (state, key) => state[key]; + +export const getAllLicenseKeys = state => Object.keys(state); diff --git a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js b/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js index 603cb3b753a..e886b4616c0 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js +++ b/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js @@ -21,12 +21,14 @@ import { combineReducers } from 'redux'; import definitions, * as fromDefinitions from './definitions/reducer'; import values, * as fromValues from './values/reducer'; import settingsPage, * as fromSettingsPage from './settingsPage/reducer'; +import licenses, * as fromLicenses from './licenses/reducer'; import globalMessages, * as fromGlobalMessages from '../../../components/store/globalMessages'; const rootReducer = combineReducers({ definitions, values, settingsPage, + licenses, globalMessages }); @@ -50,6 +52,10 @@ export const getChangedValue = (state, key) => fromSettingsPage.getChangedValue( export const isLoading = (state, key) => fromSettingsPage.isLoading(state.settingsPage, key); +export const getLicenseByKey = (state, key) => fromLicenses.getLicenseByKey(state.licenses, key); + +export const getAllLicenseKeys = state => fromLicenses.getAllLicenseKeys(state.licenses); + export const getValidationMessage = (state, key) => fromSettingsPage.getValidationMessage(state.settingsPage, key); export const getGlobalMessages = state => fromGlobalMessages.getGlobalMessages(state.globalMessages); diff --git a/server/sonar-web/src/main/js/main/nav/links-mixin.js b/server/sonar-web/src/main/js/main/nav/links-mixin.js index 4b39a6738e2..56f465f973d 100644 --- a/server/sonar-web/src/main/js/main/nav/links-mixin.js +++ b/server/sonar-web/src/main/js/main/nav/links-mixin.js @@ -30,7 +30,7 @@ export default { const fullUrl = window.baseUrl + url; const check = _.isFunction(highlightUrl) ? highlightUrl : this.activeLink; return ( -
  • +
  • {title}
  • ); diff --git a/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js b/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js index fe673cbadb3..d9125a3644e 100644 --- a/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js +++ b/server/sonar-web/src/main/js/main/nav/settings/settings-nav.js @@ -76,7 +76,8 @@ export default React.createClass({
      - {this.renderLink('/settings', translate('settings.page'))} + {this.renderLink('/settings', translate('settings.page'), url => window.location.pathname === url)} + {this.renderLink('/settings/licenses', translate('property.category.licenses'))} {this.renderLink('/metrics', 'Custom Metrics')} {this.renderLink('/admin_dashboards', translate('default_dashboards.page'))} @@ -120,8 +121,7 @@ export default React.createClass({
        - {this.renderLink('/updatecenter', - translate('update_center.page'))} + {this.renderLink('/updatecenter', translate('update_center.page'))} {this.renderLink('/system', translate('system_info.page'))}
      diff --git a/server/sonar-web/src/main/less/init/forms.less b/server/sonar-web/src/main/less/init/forms.less index bdc1c17ced3..384455b0b98 100644 --- a/server/sonar-web/src/main/less/init/forms.less +++ b/server/sonar-web/src/main/less/init/forms.less @@ -70,6 +70,10 @@ input[type="search"]::-webkit-search-decoration { textarea { padding: 3px; + + &.width-100 { + max-width: 100%; + } } select { diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb index ee4e72f070e..80946705813 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/settings_controller.rb @@ -24,4 +24,8 @@ class SettingsController < ApplicationController def index end + + def licenses + render :action => 'index' + end end 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 8b42dba8ba5..dc1353f58f3 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2074,6 +2074,15 @@ server_id_configuration.ip.desc=A server ID is linked to the IP address of the h server_id_configuration.generation_error=Organisation and/or IP address are not valid. server_id_configuration.fields_cannot_be_blank=Organisation and IP address cannot be blank. server_id_configuration.does_not_match_organisation_pattern=Organisation does not match the required pattern. +licenses.list.product=Product +licenses.list.organization=Organization +licenses.list.expiration=Expiration +licenses.list.type=Type +licenses.list.server=Server +licenses.change_license_for_x=Change License for {0} +licenses.license_input_label=Insert the license text below: +licenses.license_input_note=Keep empty if you want to unset this license. +licenses.success_message=New license has been set. #------------------------------------------------------------------------------ -- 2.39.5