--- /dev/null
+/*
+ * 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);
+ }
+}
// 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);
}
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.$;
return open(url, SettingsPage.class);
}
+ public LicensesPage openLicenses() {
+ return open("/settings/licenses", LicensesPage.class);
+ }
+
public void open(String relativeUrl) {
Selenide.open(relativeUrl);
}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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<LicenseItem> 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);
+ }
+}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head profile="http://selenium-ide.openqa.org/profiles/test-case">
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <title>display-license</title>
-</head>
-<body>
-<table cellpadding="1" cellspacing="1" border="1">
- <tbody>
- <tr>
- <td>open</td>
- <td>/sessions/logout</td>
- <td></td>
- </tr>
- <tr>
- <td>open</td>
- <td>/settings?category=general</td>
- <td></td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=login</td>
- <td>admin</td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=password</td>
- <td>admin</td>
- </tr>
- <tr>
- <td>clickAndWait</td>
- <td>name=commit</td>
- <td></td>
- </tr>
- <tr>
- <td>waitForElementPresent</td>
- <td>css=.js-user-authenticated</td>
- <td></td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=input_typed.license.secured</td>
- <td>TmFtZTogRGV2ZWxvcHBlcnMKUGx1Z2luOiBhdXRvY29udHJvbApFeHBpcmVzOiAyMDEyLTA0LTAxCktleTogNjI5N2MxMzEwYzg2NDZiZTE5MDU1MWE4ZmZmYzk1OTBmYzEyYTIyMgo=</td>
- </tr>
- <tr>
- <td>click</td>
- <td>id=submit_settings</td>
- <td></td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_typed.license.secured</td>
- <td>*autocontrol*</td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_typed.license.secured</td>
- <td>*Developpers*</td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_typed.license.secured</td>
- <td>*2012*</td>
- </tr>
- </tbody>
-</table>
-</body>
-</html>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head profile="http://selenium-ide.openqa.org/profiles/test-case">
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <title>display-untyped-license</title>
-</head>
-<body>
-<table cellpadding="1" cellspacing="1" border="1">
- <tbody>
- <tr>
- <td>open</td>
- <td>/sessions/logout</td>
- <td></td>
- </tr>
- <tr>
- <td>open</td>
- <td>/settings?category=general</td>
- <td></td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=login</td>
- <td>admin</td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=password</td>
- <td>admin</td>
- </tr>
- <tr>
- <td>clickAndWait</td>
- <td>name=commit</td>
- <td></td>
- </tr>
- <tr>
- <td>waitForElementPresent</td>
- <td>css=.js-user-authenticated</td>
- <td></td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=input_untyped.license.secured</td>
- <td>TmFtZTogRGV2ZWxvcHBlcnMKUGx1Z2luOiBhdXRvY29udHJvbApFeHBpcmVzOiAyMDEyLTA0LTAxCktleTogNjI5N2MxMzEwYzg2NDZiZTE5MDU1MWE4ZmZmYzk1OTBmYzEyYTIyMgo=</td>
- </tr>
- <tr>
- <td>click</td>
- <td>submit_settings</td>
- <td></td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_untyped.license.secured</td>
- <td>*autocontrol*</td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_untyped.license.secured</td>
- <td>*Developpers*</td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_untyped.license.secured</td>
- <td>*2012*</td>
- </tr>
- </tbody>
-</table>
-</body>
-</html>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head profile="http://selenium-ide.openqa.org/profiles/test-case">
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <title>ignore-corrupted-license</title>
-</head>
-<body>
-<table cellpadding="1" cellspacing="1" border="1">
- <tbody>
- <tr>
- <td>open</td>
- <td>/sessions/logout</td>
- <td></td>
- </tr>
- <tr>
- <td>open</td>
- <td>/settings?category=general</td>
- <td></td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=login</td>
- <td>admin</td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=password</td>
- <td>admin</td>
- </tr>
- <tr>
- <td>clickAndWait</td>
- <td>name=commit</td>
- <td></td>
- </tr>
- <tr>
- <td>waitForElementPresent</td>
- <td>css=.js-user-authenticated</td>
- <td></td>
- </tr>
- <tr>
- <td>type</td>
- <td>id=input_typed.license.secured</td>
- <td>ABCDE</td>
- </tr>
- <tr>
- <td>click</td>
- <td>id=submit_settings</td>
- <td></td>
- </tr>
- <tr>
- <td>waitForText</td>
- <td>block_typed.license.secured</td>
- <td>*Product*-*Organization*-*Expiration*-*</td>
- </tr>
- </tbody>
-</table>
-</body>
-</html>
--- /dev/null
+/*
+ * 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);
+};
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';
<Router history={history}>
<Redirect from="/index" to="/"/>
<Route path="/" component={withComponent(App)}/>
+ {options.component == null && (
+ <Route path="/licenses" component={LicensesApp}/>
+ )}
</Router>
</Provider>
), el);
--- /dev/null
+/*
+ * 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 (
+ <button className="js-change" onClick={e => this.onClick(e)}>{translate('change_verb')}</button>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 (
+ <tr className="js-license" data-license-key={license.key}>
+ <td className="text-middle"><LicenseStatus license={license}/></td>
+ <td className="js-product text-middle">
+ <div className={license.invalidProduct ? 'text-danger' : null}>
+ {license.name || license.key}
+ </div>
+ </td>
+ <td className="js-organization text-middle">{license.organization}</td>
+ <td className="js-expiration text-middle">
+ <div className={license.invalidExpiration ? 'text-danger' : null}>
+ {moment(license.expiration).format('LL')}
+ </div>
+ </td>
+ <td className="js-type text-middle">{license.type}</td>
+ <td className="js-server-id text-middle">
+ <div className={license.invalidServerId ? 'text-danger' : null}>
+ {license.serverId}
+ </div>
+ </td>
+ <td className="text-right">
+ <LicenseChangeForm license={license} onChange={value => this.handleSet(value)}/>
+ </td>
+ </tr>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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 <i className="icon-alert-error"/>;
+ }
+
+ return <i className="icon-check"/>;
+ }
+}
--- /dev/null
+<form>
+ <div class="modal-head">
+ <h2>{{tp 'licenses.change_license_for_x' productName}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <label for="license-input">{{t 'licenses.license_input_label'}}</label>
+ <textarea class="width-100 spacer-top" rows="7" id="license-input">{{value}}</textarea>
+ <div class="spacer-top note">{{t 'licenses.license_input_note'}}</div>
+ </div>
+ <div class="modal-foot">
+ <i class="js-modal-spinner spinner spacer-right hidden"></i>
+ <button class="js-modal-submit">{{t 'save'}}</button>
+ <a href="#" class="js-modal-close">{{t 'cancel'}}</a>
+ </div>
+</form>
--- /dev/null
+/*
+ * 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
+ };
+ }
+});
--- /dev/null
+/*
+ * 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 (
+ <div id="licenses-page" className="page page-limited">
+ <LicensesAppHeader/>
+ <GlobalMessagesContainer/>
+ <LicensesListContainer/>
+ </div>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 (
+ <header className="page-header">
+ <h1 className="page-title">{translate('property.category.licenses')}</h1>
+ <div className="page-description"
+ dangerouslySetInnerHTML={{ __html: translate('property.category.licenses.description') }}/>
+ </header>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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 (
+ <table className="data zebra zebra-hover" style={{ tableLayout: 'fixed' }}>
+ <thead>
+ <tr>
+ <th width={40}> </th>
+ <th>{translate('licenses.list.product')}</th>
+ <th width={150}>{translate('licenses.list.organization')}</th>
+ <th width={150}>{translate('licenses.list.expiration')}</th>
+ <th width={150}>{translate('licenses.list.type')}</th>
+ <th width={150}>{translate('licenses.list.server')}</th>
+ <th width={80}> </th>
+ </tr>
+ </thead>
+ <tbody>
+ {this.props.licenses.map(licenseKey => (
+ <LicenseRowContainer key={licenseKey} licenseKey={licenseKey}/>
+ ))}
+ </tbody>
+ </table>
+ );
+ }
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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(<LicenseChangeForm license={{}} onChange={jest.fn()}/>);
+ expect(form.is('button')).toBe(true);
+});
--- /dev/null
+/*
+ * 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(<LicenseRow license={license} setLicense={jest.fn()}/>).find(LicenseStatus);
+ expect(licenseStatus.length).toBe(1);
+ expect(licenseStatus.prop('license')).toBe(license);
+});
+
+it('should render product', () => {
+ const license = { name: 'foo' };
+ const licenseProduct = shallow(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).find('.js-expiration');
+ expect(licenseExpiration.find('.text-danger').length).toBe(1);
+});
+
+it('should render type', () => {
+ const license = { type: 'PRODUCTION' };
+ const licenseType = shallow(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={jest.fn()}/>).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(<LicenseRow license={license} setLicense={setLicense}/>).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');
+});
--- /dev/null
+/*
+ * 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(<LicenseStatus license={{}}/>);
+ expect(status.node).toBeNull();
+});
+
+it('should render ok', () => {
+ const status = shallow(<LicenseStatus license={{ value: 'foo' }}/>);
+ expect(status.is('.icon-check')).toBe(true);
+});
+
+it('should render error when invalid product', () => {
+ const status = shallow(<LicenseStatus license={{ value: 'foo', invalidProduct: true }}/>);
+ 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(<LicenseStatus license={{ value: 'foo', invalidExpiration: true }}/>);
+ 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(<LicenseStatus license={{ value: 'foo', invalidServerId: true }}/>);
+ expect(status.is('.icon-check')).toBe(false);
+ expect(status.is('.icon-alert-error')).toBe(true);
+});
--- /dev/null
+/*
+ * 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(<LicensesApp/>);
+ expect(app.find(GlobalMessagesContainer).length).toBe(1);
+ expect(app.find(LicensesAppHeader).length).toBe(1);
+ expect(app.find(LicensesListContainer).length).toBe(1);
+});
--- /dev/null
+/*
+ * 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(<LicensesAppHeader/>);
+ expect(header.find('.page-title').length).toBe(1);
+ expect(header.find('.page-description').length).toBe(1);
+});
--- /dev/null
+/*
+ * 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(<LicensesList licenses={[]} fetchLicenses={jest.fn()}/>);
+ expect(list.is('table')).toBe(true);
+});
+
+it('should fetch licenses', () => {
+ const fetchLicenses = jest.fn(() => Promise.resolve());
+ mount(<LicensesList licenses={[]} fetchLicenses={fetchLicenses}/>);
+ expect(fetchLicenses).toBeCalled();
+});
+
+it('should render rows', () => {
+ const list = shallow(<LicensesList licenses={['foo', 'bar']} fetchLicenses={jest.fn()}/>);
+ 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');
+});
--- /dev/null
+/*
+ * 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();
+ });
+};
--- /dev/null
+/*
+ * 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);
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
});
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);
const fullUrl = window.baseUrl + url;
const check = _.isFunction(highlightUrl) ? highlightUrl : this.activeLink;
return (
- <li key={url} className={check(highlightUrl)}>
+ <li key={url} className={check(fullUrl)}>
<a href={fullUrl}>{title}</a>
</li>
);
<i className="icon-dropdown"></i>
</a>
<ul className="dropdown-menu">
- {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'))}
<i className="icon-dropdown"></i>
</a>
<ul className="dropdown-menu">
- {this.renderLink('/updatecenter',
- translate('update_center.page'))}
+ {this.renderLink('/updatecenter', translate('update_center.page'))}
{this.renderLink('/system', translate('system_info.page'))}
</ul>
</li>
textarea {
padding: 3px;
+
+ &.width-100 {
+ max-width: 100%;
+ }
}
select {
def index
end
+
+ def licenses
+ render :action => 'index'
+ end
end
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.
#------------------------------------------------------------------------------