aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2017-03-27 14:00:14 +0200
committerStas Vilchik <stas-vilchik@users.noreply.github.com>2017-04-03 10:38:52 +0200
commit32a73efa05cb12056a93f08b9124e647213f1f02 (patch)
tree89c545631a613d2041383b5143afb0e040c327e0
parentbe8738ea1e0322d81e238c4462c7ec6f22d2177c (diff)
downloadsonarqube-32a73efa05cb12056a93f08b9124e647213f1f02.tar.gz
sonarqube-32a73efa05cb12056a93f08b9124e647213f1f02.zip
SONAR-9008 support quality profiles for organizations
-rw-r--r--it/it-tests/src/test/java/it/Category5Suite.java4
-rw-r--r--it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java206
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html60
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html110
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html110
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html115
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html55
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html74
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html50
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html60
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html50
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html45
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html89
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html54
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html110
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html80
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html95
-rw-r--r--it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html100
-rw-r--r--server/sonar-web/src/main/js/api/quality-profiles.js79
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js3
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js7
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap27
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/routes.js5
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/Meta.js7
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js16
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js43
-rw-r--r--server/sonar-web/src/main/js/apps/project-admin/store/actions.js9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js18
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js81
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js21
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js11
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js11
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js55
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js29
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/App.js51
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js73
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js35
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js15
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js19
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js12
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js45
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js106
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js30
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js43
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js68
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js15
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js21
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js19
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js15
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js54
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js46
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js22
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js29
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js25
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/routes.js10
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs24
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/utils.js61
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js6
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js2
-rw-r--r--server/sonar-web/src/main/js/helpers/urls.js9
76 files changed, 2383 insertions, 513 deletions
diff --git a/it/it-tests/src/test/java/it/Category5Suite.java b/it/it-tests/src/test/java/it/Category5Suite.java
index 9050e43e2a2..5bd6f18cc49 100644
--- a/it/it-tests/src/test/java/it/Category5Suite.java
+++ b/it/it-tests/src/test/java/it/Category5Suite.java
@@ -20,6 +20,7 @@
package it;
import it.organization.OrganizationMembershipTest;
+import it.organization.OrganizationQualityProfilesPageTest;
import it.serverSystem.ClusterTest;
import it.serverSystem.RestartTest;
import it.serverSystem.ServerSystemRestartingOrchestrator;
@@ -47,7 +48,8 @@ import org.junit.runners.Suite;
UpdateCenterTest.class,
RealmAuthenticationTest.class,
SsoAuthenticationTest.class,
- OrganizationMembershipTest.class
+ OrganizationMembershipTest.class,
+ OrganizationQualityProfilesPageTest.class
})
public class Category5Suite {
diff --git a/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java b/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java
new file mode 100644
index 00000000000..3cabbe545d0
--- /dev/null
+++ b/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java
@@ -0,0 +1,206 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package it.organization;
+
+import com.codeborne.selenide.Condition;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.organization.CreateWsRequest;
+import pageobjects.Navigation;
+
+import static com.codeborne.selenide.Selenide.$;
+import static java.util.Collections.emptyMap;
+import static util.ItUtils.newAdminWsClient;
+import static util.ItUtils.projectDir;
+import static util.ItUtils.xooPlugin;
+import static util.selenium.Selenese.runSelenese;
+
+public class OrganizationQualityProfilesPageTest {
+
+ @ClassRule
+ public static final Orchestrator orchestrator = Orchestrator.builderEnv()
+ .addPlugin(xooPlugin())
+ .build();
+
+ private static WsClient adminWsClient;
+ private static final String ORGANIZATION = "test-org";
+
+ @BeforeClass
+ public static void setUp() {
+ adminWsClient = newAdminWsClient(orchestrator);
+ orchestrator.resetData();
+ orchestrator.getServer().post("api/organizations/enable_support", emptyMap());
+ createOrganization();
+ }
+
+ @Before
+ public void createSampleProfile() {
+ createProfile("xoo", "sample");
+ inheritProfile("xoo", "sample", "Basic");
+ analyzeProject("shared/xoo-sample");
+ addProfileToProject("xoo", "sample", "sample");
+ }
+
+ @After
+ public void deleteSampleProfile() {
+ setDefault("xoo", "Basic");
+ deleteProfile("xoo", "sample");
+ deleteProfile("xoo", "new name");
+ }
+
+ @Test
+ public void testNoGlobalPage(){
+ Navigation nav = Navigation.get(orchestrator);
+ nav.open("/profiles");
+ $(".page-wrapper-simple").should(Condition.visible);
+ }
+
+ @Test
+ public void testHomePage() throws Exception {
+ runSelenese(orchestrator,
+ "/organization/OrganizationQualityProfilesPageTest/should_display_list.html",
+ "/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html",
+ "/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html");
+ }
+
+ @Test
+ public void testProfilePage() throws Exception {
+ runSelenese(orchestrator,
+ "/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html",
+ "/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html",
+ "/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html",
+ "/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html");
+ }
+
+ @Test
+ public void testNotFound() {
+ Navigation nav = Navigation.get(orchestrator);
+ nav.open("/organizations/test-org/quality_profiles/show?key=unknown");
+ $(".quality-profile-not-found").should(Condition.visible);
+ }
+
+ @Test
+ public void testProfileChangelog() throws Exception {
+ runSelenese(orchestrator,
+ "/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html");
+ }
+
+ @Ignore("find a way to know profile key inside selenium tests")
+ @Test
+ public void testComparison() throws Exception {
+ runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_compare.html");
+ }
+
+ @Test
+ public void testCreation() throws Exception {
+ runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_create.html");
+ }
+
+ @Test
+ public void testDeletion() throws Exception {
+ runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_delete.html");
+ }
+
+ @Test
+ public void testCopying() throws Exception {
+ runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_copy.html");
+ }
+
+ @Test
+ public void testRenaming() throws Exception {
+ runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_rename.html");
+ }
+
+ @Test
+ public void testSettingDefault() throws Exception {
+ runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_set_default.html");
+ }
+
+ @Test
+ public void testRestoration() throws Exception {
+ deleteProfile("xoo", "empty");
+
+ runSelenese(orchestrator,
+ "/organization/OrganizationQualityProfilesPageTest/should_restore.html",
+ "/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html");
+ }
+
+ private static void createProfile(String language, String name) {
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/qualityprofiles/create")
+ .setParam("language", language)
+ .setParam("name", name)
+ .setParam("organization", ORGANIZATION));
+ }
+
+ private static void inheritProfile(String language, String name, String parentName) {
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/qualityprofiles/change_parent")
+ .setParam("language", language)
+ .setParam("profileName", name)
+ .setParam("parentName", parentName)
+ .setParam("organization", ORGANIZATION));
+ }
+
+ private static void analyzeProject(String path) {
+ orchestrator.executeBuild(SonarScanner.create(projectDir(path)).setProperties(
+ "sonar.organization", ORGANIZATION,
+ "sonar.login", "admin",
+ "sonar.password", "admin"
+ ));
+ }
+
+ private static void addProfileToProject(String language, String profileName, String projectKey) {
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/qualityprofiles/add_project")
+ .setParam("language", language)
+ .setParam("profileName", profileName)
+ .setParam("organization", ORGANIZATION)
+ .setParam("projectKey", projectKey));
+ }
+
+ private static void deleteProfile(String language, String name) {
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/qualityprofiles/delete")
+ .setParam("language", language)
+ .setParam("profileName", name)
+ .setParam("organization", ORGANIZATION));
+ }
+
+ private static void setDefault(String language, String name) {
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/qualityprofiles/set_default")
+ .setParam("language", language)
+ .setParam("profileName", name)
+ .setParam("organization", ORGANIZATION));
+ }
+
+ private static void createOrganization() {
+ adminWsClient.organizations().create(new CreateWsRequest.Builder().setKey(ORGANIZATION).setName(ORGANIZATION).build());
+ }
+}
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html
new file mode 100644
index 00000000000..b8ea98d1690
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html
@@ -0,0 +1,60 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_display_changelog</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_changelog</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table-name a[href^=&quot;/organizations/test-org/quality_profiles/show?key=xoo-sample&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.quality-profiles-table-name a[href^=&quot;/organizations/test-org/quality_profiles/show?key=xoo-sample&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profile-compare</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.js-profile-comparison .Select</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.js-profile-comparison .Select-control</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html
new file mode 100644
index 00000000000..e233ad8f005
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html
@@ -0,0 +1,110 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profile-copy</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>css=#copy-profile-name</td>
+ <td>copied</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#copy-profile-submit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-header</td>
+ <td>*copied*</td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-rules</td>
+ <td>*1*Bugs*0*Vulnerabilities*0*Code Smells*1*</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;copied&quot;]</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html
new file mode 100644
index 00000000000..253880e05b4
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html
@@ -0,0 +1,110 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#quality-profiles-create</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profiles-create</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#create-profile-name</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=#create-profile-form-backup-XooProfileImporter</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>css=#create-profile-name</td>
+ <td>test</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#create-profile-submit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-header</td>
+ <td>*test*</td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-rules</td>
+ <td>*Bugs*0*Vulnerabilities*0*Code Smells*0*</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table-row[data-name=&quot;test&quot;]</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html
new file mode 100644
index 00000000000..bdaab3efc06
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html
@@ -0,0 +1,115 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profile-delete</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#delete-profile-submit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#delete-profile-submit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementNotPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;]</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html
new file mode 100644
index 00000000000..417700e47db
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html
@@ -0,0 +1,55 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_display_changelog</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_changelog</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=a[href^=&quot;/organizations/test-org/quality_profiles/changelog&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=a[href^=&quot;/organizations/test-org/quality_profiles/changelog&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.js-profile-changelog-event</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-profile-changelog-event</td>
+ <td>*Administrator*Activated*Has Tag*Major*tag*xoo*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html
new file mode 100644
index 00000000000..b3341945d1c
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html
@@ -0,0 +1,74 @@
+<?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>should_display_list</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_list</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=.quality-profiles-table .data[data-language=&quot;xoo&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=.quality-profiles-table .data[data-language=&quot;xoo2&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name</td>
+ <td>*Basic*</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-projects</td>
+ <td>*Default*</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-rules</td>
+ <td>*1*</td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-rules a[href^=&quot;/organizations/test-org/rules#qprofile&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;empty&quot;] .quality-profiles-table-projects</td>
+ <td>*0*</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=table[data-language=&quot;xoo2&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name</td>
+ <td>*Basic*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html
new file mode 100644
index 00000000000..b26d162f2e7
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html
@@ -0,0 +1,50 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_display_profile_projects.html</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_profile_projects.html</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-exporters</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-exporters [data-key=&quot;XooFakeExporter&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-exporters a[href^=&quot;/api/qualityprofiles/export&quot;]</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html
new file mode 100644
index 00000000000..c403fbf5196
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html
@@ -0,0 +1,60 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_display_profile_inheritance.html</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_profile_inheritance.html</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-inheritance</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.js-inheritance-ancestor</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-inheritance-ancestor</td>
+ <td>*Basic*1*</td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=.js-inheritance-current</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-inheritance-current</td>
+ <td>*sample*1*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html
new file mode 100644
index 00000000000..062014eb239
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html
@@ -0,0 +1,50 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_display_profile_projects.html</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_profile_projects.html</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-projects</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.js-profile-project</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-profile-project</td>
+ <td>*Sample*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html
new file mode 100644
index 00000000000..6b497fc7b58
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html
@@ -0,0 +1,45 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_display_profile_rules.html</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_display_profile_rules.html</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-rules</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-rules</td>
+ <td>*Active*1*Bugs*0*Vulnerabilities*0*Code Smells*1*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html
new file mode 100644
index 00000000000..a913a70a34f
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html
@@ -0,0 +1,89 @@
+<?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>should_filter_by_language</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_filter_by_language</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table .data[data-language=&quot;xoo&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=.quality-profiles-table .data[data-language=&quot;xoo2&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-language-filter</td>
+ <td>*All*</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.js-language-filter</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.js-language-filter-option[data-language=&quot;xoo2&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.js-language-filter-option[data-language=&quot;xoo2&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementNotPresent</td>
+ <td>css=.quality-profiles-table[data-language=&quot;xoo&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementPresent</td>
+ <td>css=.quality-profiles-table .data[data-language=&quot;xoo2&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-language-filter</td>
+ <td>*Xoo2*</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles?language=xoo2</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table .data[data-language=&quot;xoo2&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>css=.quality-profiles-table[data-language=&quot;xoo&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.js-language-filter</td>
+ <td>*Xoo2*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html
new file mode 100644
index 00000000000..2e753ba1cd7
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html
@@ -0,0 +1,54 @@
+<?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>should_open_from_list</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_open_from_list</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;Basic&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-rules</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-inheritance</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-projects</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html
new file mode 100644
index 00000000000..c0c437e809f
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html
@@ -0,0 +1,110 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profile-rename</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#rename-profile-name</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>css=#rename-profile-name</td>
+ <td>new name</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#rename-profile-submit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-header</td>
+ <td>*new name*</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;new name&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html
new file mode 100644
index 00000000000..1debd02fcbb
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html
@@ -0,0 +1,80 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.js-more-admin-actions</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.js-more-admin-actions</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profiles-restore</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#restore-profile-backup</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#restore-profile-submit</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html
new file mode 100644
index 00000000000..82005c7102e
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html
@@ -0,0 +1,95 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table-row[data-name=&quot;sample&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertElementNotPresent</td>
+ <td>css=.quality-profiles-table-row[data-name=&quot;empty&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.js-more-admin-actions</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profiles-restore-built-in</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#restore-built-in-profiles-submit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=#restore-built-in-profiles-form .alert-success</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.js-modal-close</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table-row[data-name=&quot;empty&quot;]</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html
new file mode 100644
index 00000000000..bb996462b3e
--- /dev/null
+++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html
@@ -0,0 +1,100 @@
+<?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"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_create</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_create</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>admin</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=login</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>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=table[data-language=&quot;xoo&quot;] tr[data-name=&quot;sample&quot;] .quality-profiles-table-name a</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=.quality-profile-header .dropdown-toggle</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>css=#quality-profile-set-as-default</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>css=.quality-profile-projects</td>
+ <td>*Default*</td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/organizations/test-org/quality_profiles</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>css=.quality-profiles-table-row[data-name=&quot;sample&quot;]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>css=.quality-profiles-table-row[data-name=&quot;sample&quot;] .quality-profiles-table-projects</td>
+ <td>*Default*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/server/sonar-web/src/main/js/api/quality-profiles.js b/server/sonar-web/src/main/js/api/quality-profiles.js
index 6f482bd3a9a..2f178014559 100644
--- a/server/sonar-web/src/main/js/api/quality-profiles.js
+++ b/server/sonar-web/src/main/js/api/quality-profiles.js
@@ -17,14 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import { request, checkStatus, parseJSON, getJSON, post, postJSON } from '../helpers/request';
-export function getQualityProfiles(data) {
+export function getQualityProfiles(data: { organization?: string, projectKey?: string }) {
const url = '/api/qualityprofiles/search';
return getJSON(url, data).then(r => r.profiles);
}
-export function createQualityProfile(data) {
+export function createQualityProfile(data: Object) {
return request('/api/qualityprofiles/create')
.setMethod('post')
.setData(data)
@@ -33,7 +34,7 @@ export function createQualityProfile(data) {
.then(parseJSON);
}
-export function restoreQualityProfile(data) {
+export function restoreQualityProfile(data: Object) {
return request('/api/qualityprofiles/restore')
.setMethod('post')
.setData(data)
@@ -42,128 +43,80 @@ export function restoreQualityProfile(data) {
.then(parseJSON);
}
-export function getProfileProjects(data) {
+export function getProfileProjects(data: Object) {
const url = '/api/qualityprofiles/projects';
return getJSON(url, data);
}
-export function getProfileInheritance(profileKey) {
+export function getProfileInheritance(profileKey: string) {
const url = '/api/qualityprofiles/inheritance';
const data = { profileKey };
return getJSON(url, data);
}
-export function setDefaultProfile(profileKey) {
+export function setDefaultProfile(profileKey: string) {
const url = '/api/qualityprofiles/set_default';
const data = { profileKey };
return post(url, data);
}
-/**
- * Rename profile
- * @param {string} key
- * @param {string} name
- * @returns {Promise}
- */
-export function renameProfile(key, name) {
+export function renameProfile(key: string, name: string) {
const url = '/api/qualityprofiles/rename';
const data = { key, name };
return post(url, data);
}
-/**
- * Copy profile
- * @param {string} fromKey
- * @param {string} toName
- * @returns {Promise}
- */
-export function copyProfile(fromKey, toName) {
+export function copyProfile(fromKey: string, toName: string) {
const url = '/api/qualityprofiles/copy';
const data = { fromKey, toName };
return postJSON(url, data);
}
-/**
- * Delete profile
- * @param {string} profileKey
- * @returns {Promise}
- */
-export function deleteProfile(profileKey) {
+export function deleteProfile(profileKey: string) {
const url = '/api/qualityprofiles/delete';
const data = { profileKey };
return post(url, data);
}
-/**
- * Change profile parent
- * @param {string} profileKey
- * @param {string} parentKey
- * @returns {Promise}
- */
-export function changeProfileParent(profileKey, parentKey) {
+export function changeProfileParent(profileKey: string, parentKey: string) {
const url = '/api/qualityprofiles/change_parent';
const data = { profileKey, parentKey };
return post(url, data);
}
-/**
- * Get list of available importers
- * @returns {Promise}
- */
export function getImporters() {
const url = '/api/qualityprofiles/importers';
return getJSON(url).then(r => r.importers);
}
-/**
- * Get list of available exporters
- * @returns {Promise}
- */
export function getExporters() {
const url = '/api/qualityprofiles/exporters';
return getJSON(url).then(r => r.exporters);
}
-/**
- * Restore built-in profiles
- * @param {string} languageKey
- * @returns {Promise}
- */
-export function restoreBuiltInProfiles(languageKey) {
+export function restoreBuiltInProfiles(data: Object) {
const url = '/api/qualityprofiles/restore_built_in';
- const data = { language: languageKey };
return post(url, data);
}
-/**
- * Get changelog of a quality profile
- * @param {Object} data API parameters
- * @returns {Promise}
- */
-export function getProfileChangelog(data) {
+export function getProfileChangelog(data: Object) {
const url = '/api/qualityprofiles/changelog';
return getJSON(url, data);
}
-/**
- * Compare two profiles
- * @param {string} leftKey
- * @param {string} rightKey
- * @returns {Promise}
- */
-export function compareProfiles(leftKey, rightKey) {
+export function compareProfiles(leftKey: string, rightKey: string) {
const url = '/api/qualityprofiles/compare';
const data = { leftKey, rightKey };
return getJSON(url, data);
}
-export function associateProject(profileKey, projectKey) {
+export function associateProject(profileKey: string, projectKey: string) {
const url = '/api/qualityprofiles/add_project';
const data = { profileKey, projectKey };
return post(url, data);
}
-export function dissociateProject(profileKey, projectKey) {
+export function dissociateProject(profileKey: string, projectKey: string) {
const url = '/api/qualityprofiles/remove_project';
const data = { profileKey, projectKey };
return post(url, data);
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
index 7eebc3d0393..90249a0e7e2 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
@@ -143,6 +143,7 @@ export default class GlobalNavMenu extends React.Component {
render() {
const governanceInstalled = this.props.appState.qualifiers.includes('VW');
+ const { organizationsEnabled } = this.props.appState;
return (
<ul className="nav navbar-nav">
@@ -150,7 +151,7 @@ export default class GlobalNavMenu extends React.Component {
{governanceInstalled && this.renderPortfolios()}
{this.renderIssuesLink()}
{this.renderRulesLink()}
- {this.renderProfilesLink()}
+ {!organizationsEnabled && this.renderProfilesLink()}
{this.renderQualityGatesLink()}
{this.renderAdministrationLink()}
{this.renderMore()}
diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js
index a38d9829abd..fdf72a5009e 100644
--- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js
+++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js
@@ -154,6 +154,13 @@ export default class OrganizationNavigation extends React.Component {
{translate('organization.members.page')}
</Link>
</li>
+ <li>
+ <Link
+ to={`/organizations/${organization.key}/quality_profiles`}
+ activeClassName="active">
+ {translate('quality_profiles.page')}
+ </Link>
+ </li>
{organization.canAdmin && this.renderAdministration()}
</ul>
</div>
diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap
index 8fbbcd9b6a3..46358abc847 100644
--- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap
@@ -44,6 +44,15 @@ exports[`test admin 1`] = `
organization.members.page
</Link>
</li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/organizations/foo/quality_profiles">
+ quality_profiles.page
+ </Link>
+ </li>
<li
className="">
<a
@@ -165,6 +174,15 @@ exports[`test regular user 1`] = `
organization.members.page
</Link>
</li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/organizations/foo/quality_profiles">
+ quality_profiles.page
+ </Link>
+ </li>
</ul>
</div>
</div>
@@ -217,6 +235,15 @@ exports[`test undeletable org 1`] = `
organization.members.page
</Link>
</li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/organizations/foo/quality_profiles">
+ quality_profiles.page
+ </Link>
+ </li>
<li
className="">
<a
diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.js b/server/sonar-web/src/main/js/apps/organizations/routes.js
index eae57052d3e..1b7f81eda3d 100644
--- a/server/sonar-web/src/main/js/apps/organizations/routes.js
+++ b/server/sonar-web/src/main/js/apps/organizations/routes.js
@@ -28,6 +28,7 @@ import OrganizationPermissions from './components/OrganizationPermissions';
import OrganizationPermissionTemplates from './components/OrganizationPermissionTemplates';
import OrganizationProjectsManagement from './components/OrganizationProjectsManagement';
import OrganizationDelete from './components/OrganizationDelete';
+import qualityProfilesRoutes from '../quality-profiles/routes';
const routes = [
{
@@ -55,6 +56,10 @@ const routes = [
component: OrganizationMembersContainer
},
{
+ path: 'quality_profiles',
+ childRoutes: qualityProfilesRoutes
+ },
+ {
component: OrganizationAdmin,
childRoutes: [
{ path: 'delete', component: OrganizationDelete },
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/Meta.js b/server/sonar-web/src/main/js/apps/overview/meta/Meta.js
index 9f67196b368..a18e1385f1b 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/Meta.js
+++ b/server/sonar-web/src/main/js/apps/overview/meta/Meta.js
@@ -57,7 +57,12 @@ const Meta = ({ component, measures, areThereCustomOrganizations }) => {
{shouldShowQualityGate && <MetaQualityGate gate={qualityGate} />}
- {shouldShowQualityProfiles && <MetaQualityProfiles profiles={qualityProfiles} />}
+ {shouldShowQualityProfiles &&
+ <MetaQualityProfiles
+ component={component}
+ customOrganizations={areThereCustomOrganizations}
+ profiles={qualityProfiles}
+ />}
<MetaLinks component={component} />
diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js
index ff4c39b3db0..fb5c176df85 100644
--- a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js
+++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
@@ -27,6 +28,15 @@ import { searchRules } from '../../../api/rules';
import { getLanguages } from '../../../store/rootReducer';
class MetaQualityProfiles extends React.Component {
+ mounted: boolean;
+
+ props: {
+ component: { organization: string },
+ customOrganizations: boolean,
+ languages: { [string]: { name: string } },
+ profiles: Array<{ key: string, language: string, name: string }>
+ };
+
state = {
deprecatedByKey: {}
};
@@ -74,12 +84,16 @@ class MetaQualityProfiles extends React.Component {
const languageFromStore = this.props.languages[profile.language];
const languageName = languageFromStore ? languageFromStore.name : profile.language;
+ const path = this.props.customOrganizations
+ ? getQualityProfileUrl(profile.key, this.props.component.organization)
+ : getQualityProfileUrl(profile.key);
+
const inner = (
<div className="text-ellipsis">
<span className="note spacer-right">
{'(' + languageName + ')'}
</span>
- <Link to={getQualityProfileUrl(profile.key)}>
+ <Link to={path}>
{profile.name}
</Link>
</div>
diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
index 88d4c79c5c7..ee3d6a0e774 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js
@@ -17,36 +17,42 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { connect } from 'react-redux';
-import shallowCompare from 'react-addons-shallow-compare';
import Header from './Header';
import Table from './Table';
import { fetchProjectProfiles, setProjectProfile } from '../store/actions';
import {
+ areThereCustomOrganizations,
getProjectAdminAllProfiles,
getProjectAdminProjectProfiles,
getComponent
} from '../../../store/rootReducer';
-class QualityProfiles extends React.Component {
- static propTypes = {
- component: React.PropTypes.object.isRequired,
- allProfiles: React.PropTypes.array,
- profiles: React.PropTypes.array
- };
+type Props = {
+ allProfiles: Array<{}>,
+ component: { key: string, organization: string },
+ customOrganizations: boolean,
+ fetchProjectProfiles: (componentKey: string, organization?: string) => void,
+ profiles: Array<{}>,
+ setProjectProfile: (string, string, string) => void
+};
- componentDidMount() {
- this.props.fetchProjectProfiles(this.props.component.key);
- }
+class QualityProfiles extends React.PureComponent {
+ props: Props;
- shouldComponentUpdate(nextProps, nextState) {
- return shallowCompare(this, nextProps, nextState);
+ componentDidMount() {
+ if (this.props.customOrganizations) {
+ this.props.fetchProjectProfiles(this.props.component.key, this.props.component.organization);
+ } else {
+ this.props.fetchProjectProfiles(this.props.component.key);
+ }
}
- handleChangeProfile(oldKey, newKey) {
+ handleChangeProfile = (oldKey, newKey) => {
this.props.setProjectProfile(this.props.component.key, oldKey, newKey);
- }
+ };
render() {
const { allProfiles, profiles } = this.props;
@@ -59,7 +65,7 @@ class QualityProfiles extends React.Component {
? <Table
allProfiles={allProfiles}
profiles={profiles}
- onChangeProfile={this.handleChangeProfile.bind(this)}
+ onChangeProfile={this.handleChangeProfile}
/>
: <i className="spinner" />}
</div>
@@ -69,10 +75,11 @@ class QualityProfiles extends React.Component {
const mapStateToProps = (state, ownProps) => ({
component: getComponent(state, ownProps.location.query.id),
+ customOrganizations: areThereCustomOrganizations(state),
allProfiles: getProjectAdminAllProfiles(state),
profiles: getProjectAdminProjectProfiles(state, ownProps.location.query.id)
});
-export default connect(mapStateToProps, { fetchProjectProfiles, setProjectProfile })(
- QualityProfiles
-);
+const mapDispatchToProps = { fetchProjectProfiles, setProjectProfile };
+
+export default connect(mapStateToProps, mapDispatchToProps)(QualityProfiles);
diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js
index 2f96a8bf723..d3f66e9af6c 100644
--- a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js
+++ b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js
@@ -47,9 +47,14 @@ export const receiveProjectProfiles = (projectKey, profiles) => ({
profiles
});
-export const fetchProjectProfiles = projectKey =>
+export const fetchProjectProfiles = (projectKey, organization) =>
dispatch => {
- Promise.all([getQualityProfiles(), getQualityProfiles({ projectKey })]).then(responses => {
+ Promise.all([
+ organization ? getQualityProfiles({ organization }) : getQualityProfiles(),
+ organization
+ ? getQualityProfiles({ organization, projectKey })
+ : getQualityProfiles({ projectKey })
+ ]).then(responses => {
const [allProfiles, projectProfiles] = responses;
dispatch(receiveProfiles(allProfiles));
dispatch(receiveProjectProfiles(projectKey, projectProfiles));
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js
index cd70278214d..2e84e64d2a3 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import moment from 'moment';
@@ -24,10 +25,19 @@ import ChangesList from './ChangesList';
import { translate } from '../../../helpers/l10n';
import { getRulesUrl } from '../../../helpers/urls';
-export default class Changelog extends React.Component {
- static propTypes = {
- events: React.PropTypes.array.isRequired
- };
+type Props = {
+ events: Array<{
+ action: string,
+ authorName: string,
+ date: string,
+ params?: {},
+ ruleKey: string,
+ ruleName: string
+ }>
+};
+
+export default class Changelog extends React.PureComponent {
+ props: Props;
render() {
let isEvenRow = false;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js
index 8de4383ca42..c7ea2d89572 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js
@@ -17,40 +17,52 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import Changelog from './Changelog';
import ChangelogSearch from './ChangelogSearch';
import ChangelogEmpty from './ChangelogEmpty';
import { getProfileChangelog } from '../../../api/quality-profiles';
-import { ProfileType } from '../propTypes';
import { translate } from '../../../helpers/l10n';
-
-export default class ChangelogContainer extends React.Component {
- static propTypes = {
- location: React.PropTypes.object.isRequired,
- profile: ProfileType
- };
+import { getProfileChangelogPath } from '../utils';
+import type { Profile } from '../propTypes';
+
+type Props = {
+ location: {
+ query: {
+ since?: string,
+ to?: string
+ }
+ },
+ organization: ?string,
+ profile: Profile
+};
+
+type State = {
+ events?: Array<*>,
+ loading: boolean,
+ page?: number,
+ total?: number
+};
+
+export default class ChangelogContainer extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
static contextTypes = {
router: React.PropTypes.object
};
- state = {
+ state: State = {
loading: true
};
- componentWillMount() {
- this.handleFromDateChange = this.handleFromDateChange.bind(this);
- this.handleToDateChange = this.handleToDateChange.bind(this);
- this.handleReset = this.handleReset.bind(this);
- }
-
componentDidMount() {
this.mounted = true;
this.loadChangelog();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.location !== this.props.location) {
this.loadChangelog();
}
@@ -63,7 +75,7 @@ export default class ChangelogContainer extends React.Component {
loadChangelog() {
this.setState({ loading: true });
const { query } = this.props.location;
- const data = { profileKey: this.props.profile.key };
+ const data: Object = { profileKey: this.props.profile.key };
if (query.since) {
data.since = query.since;
}
@@ -83,13 +95,13 @@ export default class ChangelogContainer extends React.Component {
});
}
- loadMore(e) {
+ loadMore(e: SyntheticInputEvent) {
e.preventDefault();
e.target.blur();
this.setState({ loading: true });
const { query } = this.props.location;
- const data = {
+ const data: Object = {
profileKey: this.props.profile.key,
p: this.state.page + 1
};
@@ -101,7 +113,7 @@ export default class ChangelogContainer extends React.Component {
}
getProfileChangelog(data).then(r => {
- if (this.mounted) {
+ if (this.mounted && this.state.events) {
this.setState({
events: [...this.state.events, ...r.events],
total: r.total,
@@ -112,25 +124,32 @@ export default class ChangelogContainer extends React.Component {
});
}
- handleFromDateChange(fromDate) {
- const query = { ...this.props.location.query, since: fromDate };
- this.context.router.push({ pathname: '/profiles/changelog', query });
- }
+ handleFromDateChange = (fromDate?: string) => {
+ const path = getProfileChangelogPath(this.props.profile.key, this.props.organization, {
+ since: fromDate,
+ to: this.props.location.query.to
+ });
+ this.context.router.push(path);
+ };
- handleToDateChange(toDate) {
- const query = { ...this.props.location.query, to: toDate };
- this.context.router.push({ pathname: '/profiles/changelog', query });
- }
+ handleToDateChange = (toDate?: string) => {
+ const path = getProfileChangelogPath(this.props.profile.key, this.props.organization, {
+ since: this.props.location.query.since,
+ to: toDate
+ });
+ this.context.router.push(path);
+ };
- handleReset() {
- const query = { key: this.props.profile.key };
- this.context.router.push({ pathname: '/profiles/changelog', query });
- }
+ handleReset = () => {
+ const path = getProfileChangelogPath(this.props.profile.key, this.props.organization);
+ this.context.router.push(path);
+ };
render() {
const { query } = this.props.location;
const shouldDisplayFooter = this.state.events != null &&
+ this.state.total != null &&
this.state.events.length < this.state.total;
return (
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js
index 1baf1d73e4f..96db0d8c8eb 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js
@@ -17,10 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { translate } from '../../../helpers/l10n';
-export default class ChangelogEmpty extends React.Component {
+export default class ChangelogEmpty extends React.PureComponent {
render() {
return (
<div className="big-spacer-top">
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js
index 1106ba94b34..89d2099a229 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js
@@ -17,20 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import DateInput from '../../../components/controls/DateInput';
import { translate } from '../../../helpers/l10n';
-export default class ChangelogSearch extends React.Component {
- static propTypes = {
- fromDate: React.PropTypes.string,
- toDate: React.PropTypes.string,
- onFromDateChange: React.PropTypes.func.isRequired,
- onToDateChange: React.PropTypes.func.isRequired,
- onReset: React.PropTypes.func.isRequired
- };
+type Props = {
+ fromDate?: string,
+ toDate?: string,
+ onFromDateChange: () => void,
+ onReset: () => void,
+ onToDateChange: () => void
+};
- handleResetClick(e) {
+export default class ChangelogSearch extends React.PureComponent {
+ props: Props;
+
+ handleResetClick(e: SyntheticInputEvent) {
e.preventDefault();
e.target.blur();
this.props.onReset();
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js
index 9ec1807227a..6377b4f3799 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js
@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import SeverityChange from './SeverityChange';
import ParameterChange from './ParameterChange';
-export default class ChangesList extends React.Component {
- static propTypes = {
- changes: React.PropTypes.object.isRequired
- };
+type Props = {
+ changes: { [string]: ?string }
+};
+
+export default class ChangesList extends React.PureComponent {
+ props: Props;
render() {
const { changes } = this.props;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js
index 7b4e0168979..d4e01d55b72 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js
@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { translateWithParameters } from '../../../helpers/l10n';
-export default class ParameterChange extends React.Component {
- static propTypes = {
- name: React.PropTypes.string.isRequired,
- value: React.PropTypes.any
- };
+type Props = {
+ name: string,
+ value: ?string
+};
+
+export default class ParameterChange extends React.PureComponent {
+ props: Props;
render() {
const { name, value } = this.props;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js
index c6e20846d83..5497d9ad0fb 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js
@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import SeverityHelper from '../../../components/shared/severity-helper';
import { translate } from '../../../helpers/l10n';
-export default class SeverityChange extends React.Component {
- static propTypes = {
- severity: React.PropTypes.string.isRequired
- };
+type Props = {
+ severity: ?string
+};
+
+export default class SeverityChange extends React.PureComponent {
+ props: Props;
render() {
return (
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js
index a41bc66c8ad..cdc70a050ed 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js
@@ -17,28 +17,42 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import ComparisonForm from './ComparisonForm';
import ComparisonResults from './ComparisonResults';
-import { ProfileType, ProfilesListType } from '../propTypes';
import { compareProfiles } from '../../../api/quality-profiles';
+import { getProfileComparePath } from '../utils';
+import type { Profile } from '../propTypes';
-export default class ComparisonContainer extends React.Component {
- static propTypes = {
- profile: ProfileType,
- profiles: ProfilesListType
- };
+type Props = {
+ location: { query: { withKey?: string } },
+ organization: ?string,
+ profile: Profile,
+ profiles: Array<Profile>
+};
+
+type State = {
+ loading: boolean,
+ left?: { name: string },
+ right?: { name: string },
+ inLeft?: Array<*>,
+ inRight?: Array<*>,
+ modified?: Array<*>
+};
+
+export default class ComparisonContainer extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State;
static contextTypes = {
router: React.PropTypes.object
};
- state = {
- loading: false
- };
-
- componentWillMount() {
- this.handleCompare = this.handleCompare.bind(this);
+ constructor(props: Props) {
+ super(props);
+ this.state = { loading: false };
}
componentDidMount() {
@@ -46,7 +60,7 @@ export default class ComparisonContainer extends React.Component {
this.loadResults();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.profile !== this.props.profile || prevProps.location !== this.props.location) {
this.loadResults();
}
@@ -59,7 +73,7 @@ export default class ComparisonContainer extends React.Component {
loadResults() {
const { withKey } = this.props.location.query;
if (!withKey) {
- this.setState({ left: null, loading: false });
+ this.setState({ left: undefined, loading: false });
return;
}
@@ -78,15 +92,10 @@ export default class ComparisonContainer extends React.Component {
});
}
- handleCompare(withKey) {
- this.context.router.push({
- pathname: '/profiles/compare',
- query: {
- key: this.props.profile.key,
- withKey
- }
- });
- }
+ handleCompare = (withKey: string) => {
+ const path = getProfileComparePath(this.props.profile.key, this.props.organization, withKey);
+ this.context.router.push(path);
+ };
render() {
const { profile, profiles, location } = this.props;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js
index 7d7298dc761..b3a19a37f5c 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js
@@ -17,10 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { translate } from '../../../helpers/l10n';
-export default class ComparisonEmpty extends React.Component {
+export default class ComparisonEmpty extends React.PureComponent {
render() {
return (
<div className="big-spacer-top">
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js
index f686b415b5a..a99f64b2186 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js
@@ -17,19 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import Select from 'react-select';
-import { ProfileType, ProfilesListType } from '../propTypes';
import { translate } from '../../../helpers/l10n';
+import type { Profile } from '../propTypes';
-export default class ComparisonForm extends React.Component {
- static propTypes = {
- profile: ProfileType.isRequired,
- profiles: ProfilesListType.isRequired,
- onCompare: React.PropTypes.func.isRequired
- };
+type Props = {
+ profile: Profile,
+ profiles: Array<Profile>,
+ onCompare: (string) => void,
+ withKey: string
+};
- handleChange(option) {
+export default class ComparisonForm extends React.PureComponent {
+ props: Props;
+
+ handleChange(option: { value: string }) {
this.props.onCompare(option.value);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js
index bd8793490d3..c6acf738546 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import ComparisonEmpty from './ComparisonEmpty';
@@ -24,20 +25,20 @@ import SeverityIcon from '../../../components/shared/severity-icon';
import { translateWithParameters } from '../../../helpers/l10n';
import { getRulesUrl } from '../../../helpers/urls';
-export default class ComparisonResults extends React.Component {
- static propTypes = {
- left: React.PropTypes.shape({
- name: React.PropTypes.string.isRequired
- }).isRequired,
- right: React.PropTypes.shape({
- name: React.PropTypes.string.isRequired
- }).isRequired,
- inLeft: React.PropTypes.array.isRequired,
- inRight: React.PropTypes.array.isRequired,
- modified: React.PropTypes.array.isRequired
- };
+type Params = { [string]: string };
- renderRule(rule, severity) {
+type Props = {
+ left: { name: string },
+ right: { name: string },
+ inLeft: Array<*>,
+ inRight: Array<*>,
+ modified: Array<*>
+};
+
+export default class ComparisonResults extends React.PureComponent {
+ props: Props;
+
+ renderRule(rule: { key: string, name: string }, severity: string) {
return (
<div>
<SeverityIcon severity={severity} />
@@ -49,7 +50,7 @@ export default class ComparisonResults extends React.Component {
);
}
- renderParameters(params) {
+ renderParameters(params: Params) {
if (!params) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
index f8fd41c66c5..982553d7993 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js
@@ -17,17 +17,36 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { getQualityProfiles, getExporters } from '../../../api/quality-profiles';
import { sortProfiles } from '../utils';
+import type { Exporter } from '../propTypes';
import '../styles.css';
-export default class App extends React.Component {
- state = { loading: true };
+type Props = {
+ children: React.Element<*>,
+ currentUser: { permissions: { global: Array<string> } },
+ languages: Array<*>,
+ organization: { canAdmin?: boolean, key: string } | null
+};
+
+type State = {
+ loading: boolean,
+ exporters?: Array<Exporter>,
+ profiles?: Array<*>
+};
+
+export default class App extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = { loading: true };
componentWillMount() {
- document.querySelector('html').classList.add('dashboard-page');
- this.updateProfiles = this.updateProfiles.bind(this);
+ const html = document.querySelector('html');
+ if (html) {
+ html.classList.add('dashboard-page');
+ }
}
componentDidMount() {
@@ -37,12 +56,21 @@ export default class App extends React.Component {
componentWillUnmount() {
this.mounted = false;
- document.querySelector('html').classList.remove('dashboard-page');
+ const html = document.querySelector('html');
+ if (html) {
+ html.classList.remove('dashboard-page');
+ }
+ }
+
+ fetchProfiles() {
+ const { organization } = this.props;
+ const data = organization ? { organization: organization.key } : {};
+ return getQualityProfiles(data);
}
loadData() {
this.setState({ loading: true });
- Promise.all([getExporters(), getQualityProfiles()]).then(responses => {
+ Promise.all([getExporters(), this.fetchProfiles()]).then(responses => {
if (this.mounted) {
const [exporters, profiles] = responses;
this.setState({
@@ -54,13 +82,13 @@ export default class App extends React.Component {
});
}
- updateProfiles() {
- return getQualityProfiles().then(profiles => {
+ updateProfiles = () => {
+ return this.fetchProfiles().then(profiles => {
if (this.mounted) {
this.setState({ profiles: sortProfiles(profiles) });
}
});
- }
+ };
renderChild() {
if (this.state.loading) {
@@ -69,13 +97,16 @@ export default class App extends React.Component {
const finalLanguages = Object.values(this.props.languages);
- const canAdmin = this.props.currentUser.permissions.global.includes('profileadmin');
+ const canAdmin = this.props.organization
+ ? this.props.organization.canAdmin
+ : this.props.currentUser.permissions.global.includes('profileadmin');
return React.cloneElement(this.props.children, {
profiles: this.state.profiles,
languages: finalLanguages,
exporters: this.state.exporters,
updateProfiles: this.updateProfiles,
+ organization: this.props.organization ? this.props.organization.key : null,
canAdmin
});
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
index a19ddff98b6..b5ff40a9ace 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js
@@ -19,9 +19,14 @@
*/
import { connect } from 'react-redux';
import App from './App';
-import { getLanguages, getCurrentUser } from '../../../store/rootReducer';
+import { getLanguages, getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer';
-export default connect(state => ({
+const mapStateToProps = (state, ownProps) => ({
currentUser: getCurrentUser(state),
- languages: getLanguages(state)
-}))(App);
+ languages: getLanguages(state),
+ organization: ownProps.params.organizationKey
+ ? getOrganizationByKey(state, ownProps.params.organizationKey)
+ : null
+});
+
+export default connect(mapStateToProps)(App);
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
index 1616c0936c8..1101c1560ce 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js
@@ -17,78 +17,74 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import RenameProfileView from '../views/RenameProfileView';
import CopyProfileView from '../views/CopyProfileView';
import DeleteProfileView from '../views/DeleteProfileView';
import { translate } from '../../../helpers/l10n';
-import { ProfileType } from '../propTypes';
import { getRulesUrl } from '../../../helpers/urls';
import { setDefaultProfile } from '../../../api/quality-profiles';
+import { getProfilePath, getProfileComparePath, getProfilesPath } from '../utils';
+import type { Profile } from '../propTypes';
-export default class ProfileActions extends React.Component {
- static propTypes = {
- profile: ProfileType.isRequired,
- canAdmin: React.PropTypes.bool.isRequired,
- updateProfiles: React.PropTypes.func.isRequired
- };
+type Props = {
+ canAdmin: boolean,
+ organization: ?string,
+ profile: Profile,
+ updateProfiles: () => Promise<*>
+};
+
+export default class ProfileActions extends React.PureComponent {
+ props: Props;
static contextTypes = {
router: React.PropTypes.object
};
- handleRenameClick(e) {
+ handleRenameClick = (e: SyntheticInputEvent) => {
e.preventDefault();
- new RenameProfileView({
- profile: this.props.profile
- })
- .on('done', () => {
- this.props.updateProfiles();
- })
+ new RenameProfileView({ profile: this.props.profile })
+ .on('done', () => this.props.updateProfiles())
.render();
- }
+ };
- handleCopyClick(e) {
+ handleCopyClick = (e: SyntheticInputEvent) => {
e.preventDefault();
- new CopyProfileView({
- profile: this.props.profile
- })
+ new CopyProfileView({ profile: this.props.profile })
.on('done', profile => {
this.props.updateProfiles().then(() => {
- this.context.router.push({
- pathname: '/profiles/show',
- query: { key: profile.key }
- });
+ this.context.router.push(getProfilePath(profile.key, this.props.organization));
});
})
.render();
- }
+ };
- handleSetDefaultClick(e) {
+ handleSetDefaultClick = (e: SyntheticInputEvent) => {
e.preventDefault();
setDefaultProfile(this.props.profile.key).then(this.props.updateProfiles);
- }
+ };
- handleDeleteClick(e) {
+ handleDeleteClick = (e: SyntheticInputEvent) => {
e.preventDefault();
- new DeleteProfileView({
- profile: this.props.profile
- })
+ new DeleteProfileView({ profile: this.props.profile })
.on('done', () => {
- this.context.router.replace('/profiles');
+ this.context.router.replace(getProfilesPath(this.props.organization));
this.props.updateProfiles();
})
.render();
- }
+ };
render() {
const { profile, canAdmin } = this.props;
+ // FIXME use org, name and lang
const backupUrl = window.baseUrl +
'/api/qualityprofiles/backup?profileKey=' +
encodeURIComponent(profile.key);
+ // FIXME getRulesUrl
const activateMoreUrl = getRulesUrl({
qprofile: profile.key,
activation: 'false'
@@ -109,37 +105,34 @@ export default class ProfileActions extends React.Component {
</li>
<li>
<Link
- to={{ pathname: '/profiles/compare', query: { key: profile.key } }}
+ to={getProfileComparePath(profile.key, this.props.organization)}
id="quality-profile-compare">
{translate('compare')}
</Link>
</li>
{canAdmin &&
<li>
- <a id="quality-profile-copy" href="#" onClick={this.handleCopyClick.bind(this)}>
+ <a id="quality-profile-copy" href="#" onClick={this.handleCopyClick}>
{translate('copy')}
</a>
</li>}
{canAdmin &&
<li>
- <a id="quality-profile-rename" href="#" onClick={this.handleRenameClick.bind(this)}>
+ <a id="quality-profile-rename" href="#" onClick={this.handleRenameClick}>
{translate('rename')}
</a>
</li>}
{canAdmin &&
!profile.isDefault &&
<li>
- <a
- id="quality-profile-set-as-default"
- href="#"
- onClick={this.handleSetDefaultClick.bind(this)}>
+ <a id="quality-profile-set-as-default" href="#" onClick={this.handleSetDefaultClick}>
{translate('set_as_default')}
</a>
</li>}
{canAdmin &&
!profile.isDefault &&
<li>
- <a id="quality-profile-delete" href="#" onClick={this.handleDeleteClick.bind(this)}>
+ <a id="quality-profile-delete" href="#" onClick={this.handleDeleteClick}>
{translate('delete')}
</a>
</li>}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
index e11cbe0968c..d55f57dd8fe 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js
@@ -17,31 +17,41 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import Helmet from 'react-helmet';
import ProfileNotFound from './ProfileNotFound';
import ProfileHeader from '../details/ProfileHeader';
import { translate } from '../../../helpers/l10n';
-import { ProfilesListType } from '../propTypes';
+import type { Profile } from '../propTypes';
-export default class ProfileContainer extends React.Component {
- static propTypes = {
- location: React.PropTypes.object,
- profiles: ProfilesListType,
- canAdmin: React.PropTypes.bool,
- updateProfiles: React.PropTypes.func
- };
+type Props = {
+ canAdmin: boolean,
+ children: React.Element<*>,
+ location: { query: { key: string } },
+ organization: ?string,
+ profiles: Array<Profile>,
+ updateProfiles: () => Promise<*>
+};
+
+export default class ProfileContainer extends React.PureComponent {
+ props: Props;
render() {
- const { profiles, location, ...other } = this.props;
+ const { organization, profiles, location, ...other } = this.props;
const { key } = location.query;
const profile = profiles.find(profile => profile.key === key);
if (!profile) {
- return <ProfileNotFound />;
+ return <ProfileNotFound organization={organization} />;
}
- const child = React.cloneElement(this.props.children, { profile, profiles, ...other });
+ const child = React.cloneElement(this.props.children, {
+ organization,
+ profile,
+ profiles,
+ ...other
+ });
const title = translate('quality_profiles.page') + ' - ' + profile.name;
@@ -50,8 +60,9 @@ export default class ProfileContainer extends React.Component {
<Helmet title={title} titleTemplate="SonarQube - %s" />
<ProfileHeader
- profile={profile}
canAdmin={this.props.canAdmin}
+ organization={organization}
+ profile={profile}
updateProfiles={this.props.updateProfiles}
/>
{child}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js
index 369a6a067fa..158e6b9955b 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js
@@ -17,18 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import moment from 'moment';
import shallowCompare from 'react-addons-shallow-compare';
import { translate } from '../../../helpers/l10n';
-export default class ProfileDate extends React.Component {
- static propTypes = {
- date: React.PropTypes.string
- };
+type Props = {
+ date?: string
+};
- shouldComponentUpdate(nextProps, nextState) {
- return shallowCompare(this, nextProps, nextState);
+export default class ProfileDate extends React.PureComponent {
+ props: Props;
+
+ shouldComponentUpdate(nextProps: Props) {
+ return shallowCompare(this, nextProps);
}
render() {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js
index e6d332735b7..a60c37ab9b1 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js
@@ -17,20 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
+import { getProfilePath } from '../utils';
-export default class ProfileLink extends React.Component {
- static propTypes = {
- profileKey: React.PropTypes.string.isRequired
- };
+type Props = {
+ children?: React.Element<*>,
+ organization: ?string,
+ profileKey: string
+};
+
+export default class ProfileLink extends React.PureComponent {
+ props: Props;
render() {
- const { profileKey, children, ...other } = this.props;
- const query = { key: profileKey };
+ const { profileKey, organization, children, ...other } = this.props;
return (
<Link
- to={{ pathname: '/profiles/show', query }}
+ to={getProfilePath(profileKey, organization)}
activeClassName="link-no-underline"
{...other}>
{children}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js
index 2dff3d655e0..7401007841d 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js
@@ -17,16 +17,24 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { IndexLink } from 'react-router';
import { translate } from '../../../helpers/l10n';
+import { getProfilesPath } from '../utils';
+
+type Props = {
+ organization: ?string
+};
+
+export default class ProfileNotFound extends React.PureComponent {
+ props: Props;
-export default class ProfileNotFound extends React.Component {
render() {
return (
<div className="quality-profile-not-found">
<div className="note spacer-bottom">
- <IndexLink to="/profiles/" className="text-muted">
+ <IndexLink to={getProfilesPath(this.props.organization)} className="text-muted">
{translate('quality_profiles.page')}
</IndexLink>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js
index b6deaa8ddc7..7b7a296536c 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js
@@ -17,19 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import ProfileRules from './ProfileRules';
import ProfileProjects from './ProfileProjects';
import ProfileInheritance from './ProfileInheritance';
import ProfileExporters from './ProfileExporters';
-import { ProfileType } from '../propTypes';
+import type { Profile, Exporter } from '../propTypes';
-export default class ProfileDetails extends React.Component {
- static propTypes = {
- profile: ProfileType,
- canAdmin: React.PropTypes.bool,
- updateProfiles: React.PropTypes.func
- };
+type Props = {
+ canAdmin: boolean,
+ exporters: Array<Exporter>,
+ organization: ?string,
+ profile: Profile,
+ profiles: Array<Profile>,
+ updateProfiles: () => Promise<*>
+};
+
+export default class ProfileDetails extends React.PureComponent {
+ props: Props;
render() {
return (
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js
index 1a7e9df653f..be6702dd7ed 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js
@@ -17,23 +17,34 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
+import { stringify } from 'querystring';
import React from 'react';
import { translate } from '../../../helpers/l10n';
+import type { Profile, Exporter } from '../propTypes';
-export default class ProfileExporters extends React.Component {
- static propTypes = {
- exporters: React.PropTypes.array.isRequired
- };
+type Props = {
+ exporters: Array<Exporter>,
+ organization: ?string,
+ profile: Profile
+};
- getExportUrl(exporter) {
- return window.baseUrl +
- '/api/qualityprofiles/export' +
- '?exporterKey=' +
- encodeURIComponent(exporter.key) +
- '&language=' +
- encodeURIComponent(this.props.profile.language) +
- '&name=' +
- encodeURIComponent(this.props.profile.name);
+export default class ProfileExporters extends React.PureComponent {
+ props: Props;
+
+ getExportUrl(exporter: Exporter) {
+ const { organization, profile } = this.props;
+
+ const path = '/api/qualityprofiles/export';
+ const parameters: { [string]: string } = {
+ exporterKey: exporter.key,
+ language: profile.language,
+ name: profile.name
+ };
+ if (organization) {
+ Object.assign(parameters, { organization });
+ }
+ return window.baseUrl + path + '?' + stringify(parameters);
}
render() {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
index a581dad1983..3a06c63e472 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js
@@ -17,21 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link, IndexLink } from 'react-router';
import ProfileLink from '../components/ProfileLink';
import ProfileActions from '../components/ProfileActions';
import ProfileDate from '../components/ProfileDate';
-import { ProfileType } from '../propTypes';
import { translate } from '../../../helpers/l10n';
-import { isStagnant } from '../utils';
+import {
+ isStagnant,
+ getProfilesPath,
+ getProfilesForLanguagePath,
+ getProfileChangelogPath
+} from '../utils';
+import type { Profile } from '../propTypes';
-export default class ProfileHeader extends React.Component {
- static propTypes = {
- profile: ProfileType.isRequired,
- canAdmin: React.PropTypes.bool.isRequired,
- updateProfiles: React.PropTypes.func.isRequired
- };
+type Props = {
+ canAdmin: boolean,
+ organization: ?string,
+ profile: Profile,
+ updateProfiles: () => Promise<*>
+};
+
+export default class ProfileHeader extends React.PureComponent {
+ props: Props;
renderUpdateDate() {
const { profile } = this.props;
@@ -81,25 +90,28 @@ export default class ProfileHeader extends React.Component {
}
render() {
- const { profile } = this.props;
+ const { organization, profile } = this.props;
return (
<header className="page-header quality-profile-header">
<div className="note spacer-bottom">
- <IndexLink to="/profiles/" className="text-muted">
+ <IndexLink to={getProfilesPath(organization)} className="text-muted">
{translate('quality_profiles.page')}
</IndexLink>
{' / '}
<Link
- to={{ pathname: '/profiles/', query: { language: profile.language } }}
+ to={getProfilesForLanguagePath(profile.language, organization)}
className="text-muted">
{profile.languageName}
</Link>
</div>
<h1 className="page-title">
- <ProfileLink profileKey={profile.key} className="link-base-color">
- {profile.name}
+ <ProfileLink
+ organization={organization}
+ profileKey={profile.key}
+ className="link-base-color">
+ <span>{profile.name}</span>
</ProfileLink>
</h1>
@@ -108,9 +120,7 @@ export default class ProfileHeader extends React.Component {
{this.renderUpdateDate()}
{this.renderUsageDate()}
<li>
- <Link
- to={{ pathname: '/profiles/changelog', query: { key: profile.key } }}
- className="button">
+ <Link to={getProfileChangelogPath(profile.key, organization)} className="button">
{translate('changelog')}
</Link>
</li>
@@ -122,8 +132,9 @@ export default class ProfileHeader extends React.Component {
<i className="icon-dropdown" />
</button>
<ProfileActions
- profile={profile}
canAdmin={this.props.canAdmin}
+ organization={organization}
+ profile={profile}
updateProfiles={this.props.updateProfiles}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js
index d17198f89d5..f6760f674f5 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js
@@ -17,34 +17,58 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import classNames from 'classnames';
import ProfileInheritanceBox from './ProfileInheritanceBox';
import ChangeParentView from '../views/ChangeParentView';
-import { ProfileType } from '../propTypes';
import { translate } from '../../../helpers/l10n';
import { getProfileInheritance } from '../../../api/quality-profiles';
+import type { Profile } from '../propTypes';
-export default class ProfileInheritance extends React.Component {
- static propTypes = {
- profile: ProfileType.isRequired,
- canAdmin: React.PropTypes.bool.isRequired
- };
+type Props = {
+ canAdmin: boolean,
+ organization: ?string,
+ profile: Profile,
+ profiles: Array<Profile>,
+ updateProfiles: () => Promise<*>
+};
+
+type State = {
+ ancestors?: Array<{
+ activeRuleCount: number,
+ key: string,
+ name: string,
+ overridingRuleCount?: number
+ }>,
+ children?: Array<{
+ activeRuleCount: number,
+ key: string,
+ name: string,
+ overridingRuleCount?: number
+ }>,
+ loading: boolean,
+ profile?: {
+ activeRuleCount: number,
+ key: string,
+ name: string,
+ overridingRuleCount?: number
+ }
+};
- state = {
+export default class ProfileInheritance extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = {
loading: true
};
- componentWillMount() {
- this.handleChangeParent = this.handleChangeParent.bind(this);
- }
-
componentDidMount() {
this.mounted = true;
this.loadData();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.profile !== this.props.profile) {
this.loadData();
}
@@ -68,20 +92,17 @@ export default class ProfileInheritance extends React.Component {
});
}
- handleChangeParent(e) {
+ handleChangeParent = (e: SyntheticInputEvent) => {
e.preventDefault();
- new ChangeParentView({
- profile: this.props.profile,
- profiles: this.props.profiles
- })
- .on('done', () => {
- this.props.updateProfiles();
- })
+ new ChangeParentView({ profile: this.props.profile, profiles: this.props.profiles })
+ .on('done', () => this.props.updateProfiles())
.render();
- }
+ };
render() {
const highlightCurrent = !this.state.loading &&
+ this.state.ancestors != null &&
+ this.state.children != null &&
(this.state.ancestors.length > 0 || this.state.children.length > 0);
const currentClassName = classNames('js-inheritance-current', {
selected: highlightCurrent
@@ -102,30 +123,35 @@ export default class ProfileInheritance extends React.Component {
{!this.state.loading &&
<table className="data zebra">
<tbody>
- {this.state.ancestors.map((ancestor, index) => (
- <ProfileInheritanceBox
- key={ancestor.key}
- profile={ancestor}
- depth={index}
- className="js-inheritance-ancestor"
- />
- ))}
+ {this.state.ancestors != null &&
+ this.state.ancestors.map((ancestor, index) => (
+ <ProfileInheritanceBox
+ className="js-inheritance-ancestor"
+ depth={index}
+ key={ancestor.key}
+ organization={this.props.organization}
+ profile={ancestor}
+ />
+ ))}
<ProfileInheritanceBox
- profile={this.state.profile}
- depth={this.state.ancestors.length}
- displayLink={false}
className={currentClassName}
+ depth={this.state.ancestors ? this.state.ancestors.length : 0}
+ displayLink={false}
+ organization={this.props.organization}
+ profile={this.state.profile}
/>
- {this.state.children.map(child => (
- <ProfileInheritanceBox
- key={child.key}
- profile={child}
- depth={this.state.ancestors.length + 1}
- className="js-inheritance-child"
- />
- ))}
+ {this.state.children != null &&
+ this.state.children.map(child => (
+ <ProfileInheritanceBox
+ className="js-inheritance-child"
+ depth={this.state.ancestors ? this.state.ancestors.length + 1 : 0}
+ key={child.key}
+ organization={this.props.organization}
+ profile={child}
+ />
+ ))}
</tbody>
</table>}
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js
index 7e8b464ad16..5af9d91b207 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js
@@ -17,22 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import ProfileLink from '../components/ProfileLink';
import { translateWithParameters } from '../../../helpers/l10n';
-export default class ProfileInheritanceBox extends React.Component {
- static propTypes = {
- profile: React.PropTypes.shape({
- key: React.PropTypes.string.isRequired,
- name: React.PropTypes.string.isRequired,
- activeRuleCount: React.PropTypes.number.isRequired,
- overridingRuleCount: React.PropTypes.number
- }).isRequired,
- depth: React.PropTypes.number.isRequired,
- displayLink: React.PropTypes.bool,
- className: React.PropTypes.string
- };
+type Props = {
+ className?: string,
+ depth: number,
+ displayLink?: boolean,
+ organization: ?string,
+ profile: {
+ activeRuleCount: number,
+ key: string,
+ name: string,
+ overridingRuleCount?: number
+ }
+};
+
+export default class ProfileInheritanceBox extends React.PureComponent {
+ props: Props;
static defaultProps = {
displayLink: true
@@ -47,7 +51,7 @@ export default class ProfileInheritanceBox extends React.Component {
<td>
<div style={{ paddingLeft: offset }}>
{this.props.displayLink
- ? <ProfileLink profileKey={profile.key}>
+ ? <ProfileLink organization={this.props.organization} profileKey={profile.key}>
{profile.name}
</ProfileLink>
: profile.name}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js
index 650eae3746e..6d877d785fa 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js
@@ -17,34 +17,42 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import ChangeProjectsView from '../views/ChangeProjectsView';
import QualifierIcon from '../../../components/shared/qualifier-icon';
-import { ProfileType } from '../propTypes';
import { getProfileProjects } from '../../../api/quality-profiles';
import { translate } from '../../../helpers/l10n';
-
-export default class ProfileProjects extends React.Component {
- static propTypes = {
- profile: ProfileType,
- canAdmin: React.PropTypes.bool.isRequired
- };
-
- state = {
+import type { Profile } from '../propTypes';
+
+type Props = {
+ canAdmin: boolean,
+ organization: ?string,
+ profile: Profile,
+ updateProfiles: () => Promise<*>
+};
+
+type State = {
+ loading: boolean,
+ more?: boolean,
+ projects: ?Array<*>
+};
+
+export default class ProfileProjects extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = {
+ loading: true,
projects: null
};
- componentWillMount() {
- this.loadProjects = this.loadProjects.bind(this);
- }
-
componentDidMount() {
this.mounted = true;
this.loadProjects();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.profile !== this.props.profile) {
this.loadProjects();
}
@@ -71,12 +79,13 @@ export default class ProfileProjects extends React.Component {
});
}
- handleChange(e) {
+ handleChange(e: SyntheticInputEvent) {
e.preventDefault();
e.target.blur();
new ChangeProjectsView({
- profile: this.props.profile,
- loadProjects: this.props.updateProfiles
+ loadProjects: this.props.updateProfiles,
+ organization: this.props.organization,
+ profile: this.props.profile
}).render();
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js
index 2dee1b5680e..7a68617ab25 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js
@@ -17,26 +17,36 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import { keyBy } from 'lodash';
import ProfileRulesRow from './ProfileRulesRow';
-import { ProfileType } from '../propTypes';
import { searchRules, takeFacet } from '../../../api/rules';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
import { getRulesUrl, getDeprecatedActiveRulesUrl } from '../../../helpers/urls';
import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
+import type { Profile } from '../propTypes';
const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
-export default class ProfileRules extends React.Component {
- static propTypes = {
- profile: ProfileType.isRequired,
- canAdmin: React.PropTypes.bool.isRequired
- };
-
- state = {
+type Props = {
+ canAdmin: boolean,
+ profile: Profile
+};
+
+type State = {
+ total: ?number,
+ activatedTotal: ?number,
+ allByType?: { [string]: ?{ val: string, count: ?number } },
+ activatedByType?: { [string]: ?{ val: string, count: ?number } }
+};
+
+export default class ProfileRules extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = {
total: null,
activatedTotal: null,
allByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val'),
@@ -48,7 +58,7 @@ export default class ProfileRules extends React.Component {
this.loadRules();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.profile !== this.props.profile) {
this.loadRules();
}
@@ -89,7 +99,7 @@ export default class ProfileRules extends React.Component {
});
}
- getTooltip(count, total) {
+ getTooltip(count: ?number, total: ?number) {
if (count == null || total == null) {
return '';
}
@@ -101,10 +111,17 @@ export default class ProfileRules extends React.Component {
);
}
- getTooltipForType(type) {
- const { count } = this.state.activatedByType[type];
- const total = this.state.allByType[type].count;
- return this.getTooltip(count, total);
+ getTooltipForType(type: string) {
+ if (
+ this.state.activatedByType &&
+ this.state.activatedByType[type] &&
+ this.state.allByType &&
+ this.state.allByType[type]
+ ) {
+ const { count } = this.state.activatedByType[type];
+ const total = this.state.allByType[type].count;
+ return this.getTooltip(count, total);
+ }
}
renderActiveTitle() {
@@ -140,7 +157,7 @@ export default class ProfileRules extends React.Component {
activation: 'false'
});
- if (this.state.total == null) {
+ if (this.state.total == null || this.state.activatedTotal == null) {
return null;
}
@@ -157,7 +174,7 @@ export default class ProfileRules extends React.Component {
);
}
- renderTitleForType(type) {
+ renderTitleForType(type: string) {
return (
<span>
<IssueTypeIcon query={type} className="little-spacer-right" />
@@ -166,14 +183,16 @@ export default class ProfileRules extends React.Component {
);
}
- renderCountForType(type) {
+ renderCountForType(type: string) {
const rulesUrl = getRulesUrl({
qprofile: this.props.profile.key,
activation: 'true',
types: type
});
- const { count } = this.state.activatedByType[type];
+ const count = this.state.activatedByType && this.state.activatedByType[type]
+ ? this.state.activatedByType[type].count
+ : null;
if (count == null) {
return null;
@@ -186,17 +205,22 @@ export default class ProfileRules extends React.Component {
);
}
- renderTotalForType(type) {
+ renderTotalForType(type: string) {
const rulesUrl = getRulesUrl({
qprofile: this.props.profile.key,
activation: 'false',
types: type
});
- const { count } = this.state.activatedByType[type];
- const { count: total } = this.state.allByType[type];
+ const count = this.state.activatedByType && this.state.activatedByType[type]
+ ? this.state.activatedByType[type].count
+ : null;
- if (count == null) {
+ const total = this.state.allByType && this.state.allByType[type]
+ ? this.state.allByType[type].count
+ : null;
+
+ if (count == null || total == null) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js
index ae74c615802..9a7d140fa14 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js
@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
-export default class ProfileRulesRow extends React.Component {
- static propTypes = {
- renderTitle: React.PropTypes.func.isRequired,
- renderCount: React.PropTypes.func.isRequired,
- renderTotal: React.PropTypes.func.isRequired
- };
+type Props = {
+ renderCount: () => ?React.Element<*>,
+ renderTitle: () => React.Element<*>,
+ renderTotal: () => ?React.Element<*>
+};
+
+export default class ProfileRulesRow extends React.PureComponent {
+ props: Props;
render() {
const { renderTitle, renderCount, renderTotal } = this.props;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js
index b87f3ceb5df..23995d62e19 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js
@@ -17,22 +17,28 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import EvolutionDeprecated from './EvolutionDeprecated';
import EvolutionStagnant from './EvolutionStagnant';
import EvolutionRules from './EvolutionRules';
-import { ProfilesListType } from '../propTypes';
+import type { Profile } from '../propTypes';
-export default class Evolution extends React.Component {
- static propTypes = {
- profiles: ProfilesListType.isRequired
- };
+type Props = {
+ organization: ?string,
+ profiles: Array<Profile>
+};
+
+export default class Evolution extends React.PureComponent {
+ props: Props;
render() {
+ const { organization, profiles } = this.props;
+
return (
<div className="quality-profiles-evolution">
- <EvolutionDeprecated profiles={this.props.profiles} />
- <EvolutionStagnant profiles={this.props.profiles} />
+ <EvolutionDeprecated organization={organization} profiles={profiles} />
+ <EvolutionStagnant organization={organization} profiles={profiles} />
<EvolutionRules />
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js
index 8c2a79cc697..ce8f4ddafbc 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js
@@ -17,20 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import { sortBy } from 'lodash';
import ProfileLink from '../components/ProfileLink';
import { getDeprecatedActiveRulesUrl } from '../../../helpers/urls';
-import { ProfilesListType } from '../propTypes';
import { translateWithParameters, translate } from '../../../helpers/l10n';
+import type { Profile } from '../propTypes';
-export default class EvolutionDeprecated extends React.Component {
- static propTypes = {
- profiles: ProfilesListType.isRequired
- };
+type Props = {
+ organization: ?string,
+ profiles: Array<Profile>
+};
+
+export default class EvolutionDeprecated extends React.PureComponent {
+ props: Props;
render() {
+ // FIXME getDeprecatedActiveRulesUrl
+
const profilesWithDeprecations = this.props.profiles.filter(
profile => profile.activeDeprecatedRuleCount > 0
);
@@ -56,7 +62,10 @@ export default class EvolutionDeprecated extends React.Component {
{sortedProfiles.map(profile => (
<li key={profile.key} className="spacer-top">
<div className="text-ellipsis">
- <ProfileLink profileKey={profile.key} className="link-no-underline">
+ <ProfileLink
+ organization={this.props.organization}
+ profileKey={profile.key}
+ className="link-no-underline">
{profile.name}
</ProfileLink>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js
index 224be2a3b28..553e6bdf62c 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import moment from 'moment';
@@ -38,7 +39,11 @@ function parseRules(r) {
});
}
-export default class EvolutionRules extends React.Component {
+type Props = {};
+
+export default class EvolutionRules extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
state = {};
componentDidMount() {
@@ -70,6 +75,8 @@ export default class EvolutionRules extends React.Component {
}
render() {
+ // FIXME getRulesUrl
+
if (!this.state.latestRulesTotal) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js
index 833ec05fd33..29679aca1ae 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js
@@ -17,17 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import moment from 'moment';
import ProfileLink from '../components/ProfileLink';
-import { ProfilesListType } from '../propTypes';
import { translate } from '../../../helpers/l10n';
import { isStagnant } from '../utils';
+import type { Profile } from '../propTypes';
-export default class EvolutionStagnant extends React.Component {
- static propTypes = {
- profiles: ProfilesListType.isRequired
- };
+type Props = {
+ organization: ?string,
+ profiles: Array<Profile>
+};
+
+export default class EvolutionStagnant extends React.PureComponent {
+ props: Props;
render() {
// TODO filter built-in out
@@ -50,7 +54,10 @@ export default class EvolutionStagnant extends React.Component {
{outdated.map(profile => (
<li key={profile.key} className="spacer-top">
<div className="text-ellipsis">
- <ProfileLink profileKey={profile.key} className="link-no-underline">
+ <ProfileLink
+ organization={this.props.organization}
+ profileKey={profile.key}
+ className="link-no-underline">
{profile.name}
</ProfileLink>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
index e5979422006..e675d6245f9 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js
@@ -17,14 +17,27 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import Helmet from 'react-helmet';
import PageHeader from './PageHeader';
import Evolution from './Evolution';
import ProfilesList from './ProfilesList';
import { translate } from '../../../helpers/l10n';
+import type { Profile } from '../propTypes';
+
+type Props = {
+ canAdmin: boolean,
+ languages: Array<{ key: string, name: string }>,
+ location: { query: { [string]: string } },
+ organization?: string,
+ profiles: Array<Profile>,
+ updateProfiles: () => Promise<*>
+};
+
+export default class HomeContainer extends React.PureComponent {
+ props: Props;
-export default class HomeContainer extends React.Component {
render() {
return (
<div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
index 9d4ca3dbefb..48ff0383758 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js
@@ -17,17 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import CreateProfileView from '../views/CreateProfileView';
import RestoreProfileView from '../views/RestoreProfileView';
import RestoreBuiltInProfilesView from '../views/RestoreBuiltInProfilesView';
+import { getProfilePath } from '../utils';
import { translate } from '../../../helpers/l10n';
import { getImporters } from '../../../api/quality-profiles';
-export default class PageHeader extends React.Component {
- static propTypes = {
- canAdmin: React.PropTypes.bool.isRequired
- };
+type Props = {
+ canAdmin: boolean,
+ languages: Array<{ key: string, name: string }>,
+ organization: ?string,
+ updateProfiles: () => Promise<*>
+};
+
+export default class PageHeader extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
static contextTypes = {
router: React.PropTypes.object
@@ -54,37 +62,42 @@ export default class PageHeader extends React.Component {
}
}
- handleCreateClick(e) {
+ handleCreateClick = (e: SyntheticInputEvent) => {
e.preventDefault();
e.target.blur();
this.retrieveImporters().then(importers => {
new CreateProfileView({
languages: this.props.languages,
+ organization: this.props.organization,
importers
})
.on('done', profile => {
this.props.updateProfiles().then(() => {
- this.context.router.push({
- pathname: '/profiles/show',
- query: { key: profile.key }
- });
+ this.context.router.push(getProfilePath(profile.key, this.props.organization));
});
})
.render();
});
- }
+ };
- handleRestoreClick(e) {
+ handleRestoreClick = (e: SyntheticInputEvent) => {
e.preventDefault();
- new RestoreProfileView().on('done', this.props.updateProfiles).render();
- }
+ new RestoreProfileView({
+ organization: this.props.organization
+ })
+ .on('done', this.props.updateProfiles)
+ .render();
+ };
- handleRestoreBuiltIn(e) {
+ handleRestoreBuiltIn = (e: SyntheticInputEvent) => {
e.preventDefault();
- new RestoreBuiltInProfilesView({ languages: this.props.languages })
+ new RestoreBuiltInProfilesView({
+ languages: this.props.languages,
+ organization: this.props.organization
+ })
.on('done', this.props.updateProfiles)
.render();
- }
+ };
render() {
return (
@@ -95,7 +108,7 @@ export default class PageHeader extends React.Component {
{this.props.canAdmin &&
<div className="page-actions button-group dropdown">
- <button id="quality-profiles-create" onClick={this.handleCreateClick.bind(this)}>
+ <button id="quality-profiles-create" onClick={this.handleCreateClick}>
{translate('create')}
</button>
<button className="dropdown-toggle js-more-admin-actions" data-toggle="dropdown">
@@ -103,10 +116,7 @@ export default class PageHeader extends React.Component {
</button>
<ul className="dropdown-menu dropdown-menu-right">
<li>
- <a
- href="#"
- id="quality-profiles-restore"
- onClick={this.handleRestoreClick.bind(this)}>
+ <a href="#" id="quality-profiles-restore" onClick={this.handleRestoreClick}>
{translate('quality_profiles.restore_profile')}
</a>
</li>
@@ -115,7 +125,7 @@ export default class PageHeader extends React.Component {
<a
href="#"
id="quality-profiles-restore-built-in"
- onClick={this.handleRestoreBuiltIn.bind(this)}>
+ onClick={this.handleRestoreBuiltIn}>
{translate('quality_profiles.restore_built_in_profiles')}
</a>
</li>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
index 34d0317107f..c2b0681f686 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js
@@ -17,36 +17,46 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { groupBy, pick, sortBy } from 'lodash';
import ProfilesListRow from './ProfilesListRow';
import ProfilesListHeader from './ProfilesListHeader';
-import { ProfilesListType, LanguagesListType } from '../propTypes';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
+import type { Profile } from '../propTypes';
-export default class ProfilesList extends React.Component {
- static propTypes = {
- profiles: ProfilesListType,
- languages: LanguagesListType,
- location: React.PropTypes.object,
- canAdmin: React.PropTypes.bool.isRequired,
- updateProfiles: React.PropTypes.func.isRequired
- };
+type Props = {
+ canAdmin: boolean,
+ languages: Array<{ key: string, name: string }>,
+ location: { query: { [string]: string } },
+ organization: ?string,
+ profiles: Array<Profile>,
+ updateProfiles: () => Promise<*>
+};
- renderProfiles(profiles) {
+export default class ProfilesList extends React.PureComponent {
+ props: Props;
+
+ renderProfiles(profiles: Array<Profile>) {
return profiles.map(profile => (
<ProfilesListRow
+ canAdmin={this.props.canAdmin}
key={profile.key}
+ organization={this.props.organization}
profile={profile}
- canAdmin={this.props.canAdmin}
updateProfiles={this.props.updateProfiles}
/>
));
}
- renderHeader(languageKey, profilesCount) {
+ renderHeader(languageKey: string, profilesCount: number) {
const language = this.props.languages.find(l => l.key === languageKey);
+
+ if (!language) {
+ return null;
+ }
+
return (
<thead>
<tr>
@@ -84,7 +94,11 @@ export default class ProfilesList extends React.Component {
return (
<div>
- <ProfilesListHeader languages={languages} currentFilter={language} />
+ <ProfilesListHeader
+ currentFilter={language}
+ languages={languages}
+ organization={this.props.organization}
+ />
{Object.keys(profilesToShow).length === 0 &&
<div className="alert alert-warning spacer-top">
@@ -95,11 +109,13 @@ export default class ProfilesList extends React.Component {
<div key={languageKey} className="quality-profile-box quality-profiles-table">
<table data-language={languageKey} className="data zebra zebra-hover">
- {this.renderHeader(languageKey, profilesToShow[languageKey].length)}
+ {profilesToShow[languageKey] != null &&
+ this.renderHeader(languageKey, profilesToShow[languageKey].length)}
<TooltipsContainer>
<tbody>
- {this.renderProfiles(profilesToShow[languageKey])}
+ {profilesToShow[languageKey] != null &&
+ this.renderProfiles(profilesToShow[languageKey])}
</tbody>
</TooltipsContainer>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js
index 3fd7c18d5ff..1d0c931aa04 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js
@@ -17,22 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { IndexLink } from 'react-router';
-import { LanguagesListType } from '../propTypes';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { getProfilesPath, getProfilesForLanguagePath } from '../utils';
-export default class ProfilesListHeader extends React.Component {
- static propTypes = {
- languages: LanguagesListType.isRequired,
- currentFilter: React.PropTypes.string
- };
+type Props = {
+ currentFilter?: string,
+ languages: Array<{ key: string, name: string }>,
+ organization: ?string
+};
+
+export default class ProfilesListHeader extends React.PureComponent {
+ props: Props;
renderFilterToggle() {
const { languages, currentFilter } = this.props;
const currentLanguage = currentFilter && languages.find(l => l.key === currentFilter);
- const label = currentFilter
+ const label = currentLanguage
? translateWithParameters('quality_profiles.x_Profiles', currentLanguage.name)
: translate('quality_profiles.all_profiles');
@@ -50,14 +54,14 @@ export default class ProfilesListHeader extends React.Component {
return (
<ul className="dropdown-menu">
<li>
- <IndexLink to="/profiles/">
+ <IndexLink to={getProfilesPath(this.props.organization)}>
{translate('quality_profiles.all_profiles')}
</IndexLink>
</li>
{this.props.languages.map(language => (
<li key={language.key}>
<IndexLink
- to={{ pathname: '/profiles/', query: { language: language.key } }}
+ to={getProfilesForLanguagePath(language.key, this.props.organization)}
className="js-language-filter-option"
data-language={language.key}>
{language.name}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js
index eca33432e52..3a53ee66e19 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js
@@ -17,26 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import React from 'react';
import { Link } from 'react-router';
import shallowCompare from 'react-addons-shallow-compare';
import ProfileLink from '../components/ProfileLink';
import ProfileDate from '../components/ProfileDate';
import ProfileActions from '../components/ProfileActions';
-import { ProfileType } from '../propTypes';
import { translate } from '../../../helpers/l10n';
import { getRulesUrl } from '../../../helpers/urls';
import { isStagnant } from '../utils';
+import type { Profile } from '../propTypes';
-export default class ProfilesListRow extends React.Component {
- static propTypes = {
- profile: ProfileType.isRequired,
- canAdmin: React.PropTypes.bool.isRequired,
- updateProfiles: React.PropTypes.func.isRequired
- };
+type Props = {
+ canAdmin: boolean,
+ organization: ?string,
+ profile: Profile,
+ updateProfiles: () => Promise<*>
+};
- shouldComponentUpdate(nextProps, nextState) {
- return shallowCompare(this, nextProps, nextState);
+export default class ProfilesListRow extends React.PureComponent {
+ props: Props;
+
+ shouldComponentUpdate(nextProps: Props) {
+ return shallowCompare(this, nextProps);
}
renderName() {
@@ -44,7 +48,7 @@ export default class ProfilesListRow extends React.Component {
const offset = 25 * (profile.depth - 1);
return (
<div style={{ paddingLeft: offset }}>
- <ProfileLink profileKey={profile.key}>
+ <ProfileLink organization={this.props.organization} profileKey={profile.key}>
{profile.name}
</ProfileLink>
</div>
@@ -70,6 +74,8 @@ export default class ProfilesListRow extends React.Component {
}
renderRules() {
+ // FIXME getRulesUrl
+
const { profile } = this.props;
const activeRulesUrl = getRulesUrl({
@@ -150,8 +156,9 @@ export default class ProfilesListRow extends React.Component {
<i className="icon-dropdown" />
</button>
<ProfileActions
- profile={this.props.profile}
canAdmin={this.props.canAdmin}
+ organization={this.props.organization}
+ profile={this.props.profile}
updateProfiles={this.props.updateProfiles}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js b/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js
index 5ba29f3a37e..f8438011916 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js
@@ -17,10 +17,35 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import { PropTypes } from 'react';
const { shape, string, number, bool, arrayOf } = PropTypes;
+export type Profile = {
+ key: string,
+ name: string,
+ isDefault: boolean,
+ isInherited: boolean,
+ language: string,
+ languageName: string,
+ activeRuleCount: number,
+ activeDeprecatedRuleCount: number,
+ projectCount?: number,
+ parentKey?: string,
+ parentName?: string,
+ userUpdatedAt?: string,
+ lastUsed?: string,
+ rulesUpdatedAt: string,
+ depth: number
+};
+
+export type Exporter = {
+ key: string,
+ name: string,
+ languages: Array<string>
+};
+
export const ProfileType = shape({
key: string.isRequired,
name: string.isRequired,
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js
index ac8f4c22894..57c5ca1baad 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js
@@ -19,9 +19,15 @@
*/
const routes = [
{
- getComponent(_, callback) {
+ getComponent(state, callback) {
require.ensure([], require => {
- callback(null, require('./components/AppContainer').default);
+ const AppContainer = require('./components/AppContainer').default;
+ if (state.params.organizationKey) {
+ callback(null, AppContainer);
+ } else {
+ const forSingleOrganization = require('../organizations/forSingleOrganization').default;
+ callback(null, forSingleOrganization(AppContainer));
+ }
});
},
getIndexRoute(_, callback) {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
index 845e4843ef6..acdc7f93805 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs
@@ -1,14 +1,16 @@
-<form id="create-profile-form" action="{{link '/api/qualityprofiles/create'}}" enctype="multipart/form-data"
- method="POST">
+<form id="create-profile-form" action="{{link '/api/qualityprofiles/create'}}" enctype="multipart/form-data" method="POST">
<div class="modal-head">
<h2>{{t 'quality_profiles.new_profile'}}</h2>
</div>
<div class="modal-body">
<div class="js-modal-messages"></div>
+ {{#if organization}}
+ <input type="hidden" name="organization" value="{{organization}}">
+ {{/if}}
<div class="modal-field">
- <label for="create-profile-name">{{t 'name'}}<em class="mandatory">*</em></label>
- <input id="create-profile-name" name="name" type="text" size="50" maxlength="100" required>
- </div>
+ <label for="create-profile-name">{{t 'name'}}<em class="mandatory">*</em></label>
+ <input id="create-profile-name" name="name" type="text" size="50" maxlength="100" required>
+ </div>
<div class="modal-field spacer-bottom">
<label for="create-profile-language">{{t 'language'}}<em class="mandatory">*</em></label>
<select id="create-profile-language" name="language" required>
@@ -18,15 +20,15 @@
</select>
</div>
{{#each importers}}
- <div class="modal-field spacer-bottom js-importer" data-key="{{key}}">
- <label for="create-profile-form-backup-{{key}}">{{name}}</label>
- <input id="create-profile-form-backup-{{key}}" name="backup_{{key}}" type="file">
- <p class="note">{{t 'quality_profiles.optional_configuration_file'}}</p>
- </div>
+ <div class="modal-field spacer-bottom js-importer" data-key="{{key}}">
+ <label for="create-profile-form-backup-{{key}}">{{name}}</label>
+ <input id="create-profile-form-backup-{{key}}" name="backup_{{key}}" type="file">
+ <p class="note">{{t 'quality_profiles.optional_configuration_file'}}</p>
+ </div>
{{/each}}
</div>
<div class="modal-foot">
<button id="create-profile-submit">{{t 'create'}}</button>
<a href="#" class="js-modal-close" id="create-profile-cancel">{{t 'cancel'}}</a>
</div>
-</form>
+</form> \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
index 9a8b0182717..7c22c83aab5 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs
@@ -5,6 +5,9 @@
<div class="modal-body">
<div class="js-modal-messages"></div>
+ {{#if organization}}
+ <input type="hidden" name="organization" value="{{organization}}">
+ {{/if}}
{{#if profile}}
{{#if ruleFailures}}
<div class="alert alert-warning">
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/utils.js b/server/sonar-web/src/main/js/apps/quality-profiles/utils.js
index d5ea476bab8..64bb0ea07d6 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/utils.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/utils.js
@@ -20,14 +20,9 @@
// @flow
import { sortBy } from 'lodash';
import moment from 'moment';
+import type { Profile } from './propTypes';
-type Profiles = Array<{
- key: string,
- name: string,
- parentKey?: string
-}>;
-
-export function sortProfiles(profiles: Profiles) {
+export function sortProfiles(profiles: Array<Profile>) {
const result = [];
const sorted = sortBy(profiles, 'name');
@@ -67,6 +62,56 @@ export function createFakeProfile(overrides: {}) {
};
}
-export function isStagnant(profile: { userUpdatedAt: string }) {
+export function isStagnant(profile: Profile) {
return moment().diff(moment(profile.userUpdatedAt), 'years') >= 1;
}
+
+export const getProfilesPath = (organization: ?string) =>
+ organization ? `/organizations/${organization}/quality_profiles` : '/profiles';
+
+export const getProfilesForLanguagePath = (language: string, organization: ?string) => ({
+ pathname: organization ? `/organizations/${organization}/quality_profiles` : '/profiles',
+ query: { language }
+});
+
+export const getProfilePath = (profile: string, organization: ?string) => ({
+ pathname: organization
+ ? `/organizations/${organization}/quality_profiles/show`
+ : '/profiles/show',
+ query: { key: profile }
+});
+
+export const getProfileComparePath = (profile: string, organization: ?string, withKey?: string) => {
+ const query: Object = { key: profile };
+ if (withKey) {
+ Object.assign(query, { withKey });
+ }
+ return {
+ pathname: organization
+ ? `/organizations/${organization}/quality_profiles/compare`
+ : '/profiles/compare',
+ query
+ };
+};
+
+export const getProfileChangelogPath = (
+ profile: string,
+ organization: ?string,
+ filter?: { since?: string, to?: string }
+) => {
+ const query: Object = { key: profile };
+ if (filter) {
+ if (filter.since) {
+ Object.assign(query, { since: filter.since });
+ }
+ if (filter.to) {
+ Object.assign(query, { to: filter.to });
+ }
+ }
+ return {
+ pathname: organization
+ ? `/organizations/${organization}/quality_profiles/changelog`
+ : '/profiles/changelog',
+ query
+ };
+};
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js
index 01c83a69ee1..912aecb10cb 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-change-profile-parent.hbs';
import { changeProfileParent } from '../../../api/quality-profiles';
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js
index df5a7facd0c..c08cc80a8cf 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import escapeHtml from 'escape-html';
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-change-projects.hbs';
@@ -27,6 +28,8 @@ export default ModalFormView.extend({
template: Template,
onRender() {
+ // TODO remove uuid usage
+
ModalFormView.prototype.onRender.apply(this, arguments);
const { key } = this.options.profile;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js
index c16397a652f..711946b8bfa 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-copy-profile.hbs';
import { copyProfile } from '../../../api/quality-profiles';
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js
index 8e12b9668a1..fb74811ec69 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import $ from 'jquery';
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-create-profile.hbs';
@@ -87,7 +88,8 @@ export default ModalFormView.extend({
return {
...ModalFormView.prototype.serializeData.apply(this, arguments),
languages: this.options.languages,
- importers: this.options.importers
+ importers: this.options.importers,
+ organization: this.options.organization
};
}
});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js
index 0c5d8a61732..ec4c5f878a4 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-delete-profile.hbs';
import { deleteProfile } from '../../../api/quality-profiles';
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js
index daf8db1ff04..308efa7c143 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-rename-profile.hbs';
import { renameProfile } from '../../../api/quality-profiles';
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js
index 39d0c720887..2edf05044b4 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-restore-built-in-profiles.hbs';
import TemplateSuccess from '../templates/quality-profiles-restore-built-in-profiles-success.hbs';
@@ -47,7 +48,10 @@ export default ModalFormView.extend({
sendRequest() {
const language = this.$('#restore-built-in-profiles-language').val();
this.selectedLanguage = this.options.languages.find(l => l.key === language).name;
- restoreBuiltInProfiles(language)
+ const data = this.options.organization
+ ? { language, organization: this.options.organization }
+ : { language };
+ restoreBuiltInProfiles(data)
.then(() => {
this.done = true;
this.render();
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js
index ebd1fabe870..d2393e5053d 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// @flow
import ModalFormView from '../../../components/common/modal-form';
import Template from '../templates/quality-profiles-restore-profile.hbs';
import { restoreQualityProfile } from '../../../api/quality-profiles';
@@ -47,6 +48,7 @@ export default ModalFormView.extend({
serializeData() {
return {
...ModalFormView.prototype.serializeData.apply(this, arguments),
+ organization: this.options.organization,
profile: this.profile,
ruleSuccesses: this.ruleSuccesses,
ruleFailures: this.ruleFailures
diff --git a/server/sonar-web/src/main/js/helpers/urls.js b/server/sonar-web/src/main/js/helpers/urls.js
index 571d7a6069e..de25aa553ee 100644
--- a/server/sonar-web/src/main/js/helpers/urls.js
+++ b/server/sonar-web/src/main/js/helpers/urls.js
@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { getProfilePath } from '../apps/quality-profiles/utils';
+
/**
* Generate URL for a component's home page
* @param {string} componentKey
@@ -92,11 +94,8 @@ export function getComponentPermissionsUrl(componentKey) {
* @param {string} key
* @returns {Object}
*/
-export function getQualityProfileUrl(key) {
- return {
- pathname: '/profiles/show',
- query: { key }
- };
+export function getQualityProfileUrl(key, organization) {
+ return getProfilePath(key, organization);
}
/**