aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2017-05-01 15:06:25 +0200
committerStas Vilchik <stas-vilchik@users.noreply.github.com>2017-05-02 14:45:47 +0200
commit19f9ad34bdca23ae4cfcc0bd601581f7c9c5442e (patch)
treefb67bd5531c4c1f921149e937195a7bcd8d62e06
parentcfa382b33eb58413abcf84635d5e45d1f21c4f21 (diff)
downloadsonarqube-19f9ad34bdca23ae4cfcc0bd601581f7c9c5442e.tar.gz
sonarqube-19f9ad34bdca23ae4cfcc0bd601581f7c9c5442e.zip
SONAR-9164 Display project visibility badge
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java12
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/ProvisionedAction.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java5
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-ghosts.json6
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-provisioned.json6
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java16
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/ProvisionedActionTest.java9
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js7
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.js32
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/main.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/projects.js4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js2
-rw-r--r--server/sonar-web/src/main/js/components/common/PrivateBadge.css12
-rw-r--r--server/sonar-web/src/main/js/components/common/PrivateBadge.js39
-rw-r--r--sonar-ws/src/main/protobuf/ws-components.proto1
-rw-r--r--sonar-ws/src/main/protobuf/ws-projects.proto1
18 files changed, 138 insertions, 29 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java
index 7d6bda694e1..35a3ded8af7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/GhostsAction.java
@@ -39,14 +39,16 @@ import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession;
import static com.google.common.collect.Sets.newHashSet;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
+import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.project.Visibility.PRIVATE;
+import static org.sonar.server.project.Visibility.PUBLIC;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
public class GhostsAction implements ProjectsWsAction {
private static final String PARAM_ORGANIZATION = "organization";
private static final String ACTION = "ghosts";
- private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");
+ private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate", "visibility");
private final DbClient dbClient;
private final UserSession userSession;
@@ -65,7 +67,8 @@ public class GhostsAction implements ProjectsWsAction {
action.setChangelog(new Change("6.4", "The 'uuid' field is deprecated in the response"));
action
- .setDescription("List ghost projects.<br /> Requires 'Administer System' permission.")
+ .setDescription("List ghost projects.<br /> " +
+ "Requires 'Administer System' permission.")
.setResponseExample(Resources.getResource(getClass(), "projects-example-ghosts.json"))
.setSince("5.2")
.addPagingParams(100, MAX_LIMIT)
@@ -74,7 +77,7 @@ public class GhostsAction implements ProjectsWsAction {
.setHandler(this);
action.createParam(PARAM_ORGANIZATION)
- .setDescription("the organization key")
+ .setDescription("Organization key")
.setRequired(false)
.setInternal(true)
.setSince("6.3");
@@ -121,6 +124,7 @@ public class GhostsAction implements ProjectsWsAction {
writeIfWished(json, "key", project.key(), fieldsToReturn);
writeIfWished(json, "name", project.name(), fieldsToReturn);
writeIfWished(json, "creationDate", project.getCreatedAt(), fieldsToReturn);
+ writeIfWished(json, "visibility", project.isPrivate() ? PRIVATE.getLabel() : PUBLIC.getLabel(), fieldsToReturn);
json.endObject();
}
json.endArray();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProvisionedAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProvisionedAction.java
index 5c6675783ef..3bcda13deb1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProvisionedAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/ProvisionedAction.java
@@ -49,13 +49,15 @@ import static java.util.Optional.ofNullable;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.project.Visibility.PRIVATE;
+import static org.sonar.server.project.Visibility.PUBLIC;
import static org.sonar.server.project.ws.ProjectsWsSupport.PARAM_ORGANIZATION;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class ProvisionedAction implements ProjectsWsAction {
private static final Set<String> QUALIFIERS_FILTER = newHashSet(Qualifiers.PROJECT);
- private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");
+ private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate", "visibility");
private final ProjectsWsSupport support;
private final DbClient dbClient;
@@ -125,6 +127,7 @@ public class ProvisionedAction implements ProjectsWsAction {
writeIfNeeded("key", project.key(), compBuilder::setKey, desiredFields);
writeIfNeeded("name", project.name(), compBuilder::setName, desiredFields);
writeIfNeeded("creationDate", project.getCreatedAt(), compBuilder::setCreationDate, desiredFields);
+ writeIfNeeded("visibility", project.isPrivate() ? PRIVATE.getLabel() : PUBLIC.getLabel(), compBuilder::setVisibility, desiredFields);
return compBuilder.build();
}).collect(toList());
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
index 77c59e029af..47b94ec9082 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
@@ -41,6 +41,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Optional.ofNullable;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.VIEW;
+import static org.sonar.server.project.Visibility.PRIVATE;
+import static org.sonar.server.project.Visibility.PUBLIC;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
import static org.sonarqube.ws.WsProjects.SearchWsResponse.newBuilder;
@@ -151,7 +153,8 @@ public class SearchAction implements ProjectsWsAction {
.setId(dto.uuid())
.setKey(dto.key())
.setName(dto.name())
- .setQualifier(dto.qualifier());
+ .setQualifier(dto.qualifier())
+ .setVisibility(dto.isPrivate() ? PRIVATE.getLabel() : PUBLIC.getLabel());
return builder.build();
}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-ghosts.json b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-ghosts.json
index 4bb9f843654..87444dadeaa 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-ghosts.json
+++ b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-ghosts.json
@@ -4,13 +4,15 @@
"uuid": "ce4c03d6-430f-40a9-b777-ad877c00aa4d",
"key": "org.apache.hbas:hbase",
"name": "HBase",
- "creationDate": "2015-03-04T23:03:44+0100"
+ "creationDate": "2015-03-04T23:03:44+0100",
+ "visibility": "public"
},
{
"uuid": "c526ef20-131b-4486-9357-063fa64b5079",
"key": "com.microsoft.roslyn:roslyn",
"name": "Roslyn",
- "creationDate": "2013-03-04T23:03:44+0100"
+ "creationDate": "2013-03-04T23:03:44+0100",
+ "visibility": "private"
}
],
"total": 2,
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-provisioned.json b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-provisioned.json
index a0e8c528b7c..6361d3cd0ec 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-provisioned.json
+++ b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/projects-example-provisioned.json
@@ -9,13 +9,15 @@
"uuid": "ce4c03d6-430f-40a9-b777-ad877c00aa4d",
"key": "org.apache.hbas:hbase",
"name": "HBase",
- "creationDate": "2015-03-04T23:03:44+0100"
+ "creationDate": "2015-03-04T23:03:44+0100",
+ "visibility": "public"
},
{
"uuid": "c526ef20-131b-4486-9357-063fa64b5079",
"key": "com.microsoft.roslyn:roslyn",
"name": "Roslyn",
- "creationDate": "2013-03-04T23:03:44+0100"
+ "creationDate": "2013-03-04T23:03:44+0100",
+ "visibility": "private"
}
]
}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json
index fca854523c0..dd9c8a510c2 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json
+++ b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/search-example.json
@@ -10,14 +10,16 @@
"id": "project-uuid-1",
"key": "project-key-1",
"name": "Project Name 1",
- "qualifier": "TRK"
+ "qualifier": "TRK",
+ "visibility": "public"
},
{
"organization": "my-org-1",
"id": "project-uuid-2",
"key": "project-key-2",
"name": "Project Name 1",
- "qualifier": "TRK"
+ "qualifier": "TRK",
+ "visibility": "private"
}
]
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java
index e73a3405a6a..1e517907272 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/GhostsActionTest.java
@@ -40,7 +40,6 @@ import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
@@ -73,7 +72,7 @@ public class GhostsActionTest {
assertThat(action.params()).hasSize(5);
Param organization = action.param("organization");
- assertThat(organization.description()).isEqualTo("the organization key");
+ assertThat(organization.description()).isEqualTo("Organization key");
assertThat(organization.since()).isEqualTo("6.3");
assertThat(organization.isRequired()).isFalse();
assertThat(organization.isInternal()).isTrue();
@@ -97,12 +96,14 @@ public class GhostsActionTest {
" {" +
" \"uuid\": \"" + ghost1.uuid() + "\"," +
" \"key\": \"" + ghost1.key() + "\"," +
- " \"name\": \"" + ghost1.name() + "\"" +
+ " \"name\": \"" + ghost1.name() + "\"," +
+ " \"visibility\": \"private\"" +
" }," +
" {" +
" \"uuid\": \"" + ghost2.uuid() + "\"," +
" \"key\": \"" + ghost2.key() + "\"," +
- " \"name\": \"" + ghost2.name() + "\"" +
+ " \"name\": \"" + ghost2.name() + "\"," +
+ " \"visibility\": \"private\"" +
" }" +
" ]" +
"}");
@@ -191,7 +192,8 @@ public class GhostsActionTest {
ComponentDto hBaseProject = ComponentTesting.newPrivateProjectDto(organization, "ce4c03d6-430f-40a9-b777-ad877c00aa4d")
.setKey("org.apache.hbas:hbase")
.setName("HBase")
- .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"));
+ .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"))
+ .setPrivate(false);
dbClient.componentDao().insert(db.getSession(), hBaseProject);
dbClient.snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(hBaseProject)
.setStatus(STATUS_UNPROCESSED));
@@ -225,14 +227,12 @@ public class GhostsActionTest {
@Test
public void fail_with_NotFoundException_when_organization_with_specified_key_does_not_exist() {
- TestRequest request = underTest.newRequest()
- .setParam("organization", "foo");
userSessionRule.logIn();
expectedException.expect(NotFoundException.class);
expectedException.expectMessage("No organization for key 'foo'");
- request.execute();
+ underTest.newRequest().setParam("organization", "foo").execute();
}
private ComponentDto insertGhostProject(OrganizationDto organization) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProvisionedActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProvisionedActionTest.java
index 1326c44a467..a1e95d7ffe7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProvisionedActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProvisionedActionTest.java
@@ -106,12 +106,14 @@ public class ProvisionedActionTest {
" {" +
" \"uuid\":\"provisioned-uuid-1\"," +
" \"key\":\"provisioned-key-1\"," +
- " \"name\":\"provisioned-name-1\"" +
+ " \"name\":\"provisioned-name-1\"," +
+ " \"visibility\":\"private\"" +
" }," +
" {" +
" \"uuid\":\"provisioned-uuid-2\"," +
" \"key\":\"provisioned-key-2\"," +
- " \"name\":\"provisioned-name-2\"" +
+ " \"name\":\"provisioned-name-2\"," +
+ " \"visibility\":\"private\"" +
" }" +
" ]" +
"}");
@@ -174,7 +176,8 @@ public class ProvisionedActionTest {
ComponentDto hBaseProject = ComponentTesting.newPrivateProjectDto(org, "ce4c03d6-430f-40a9-b777-ad877c00aa4d")
.setKey("org.apache.hbas:hbase")
.setName("HBase")
- .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"));
+ .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"))
+ .setPrivate(false);
ComponentDto roslynProject = ComponentTesting.newPrivateProjectDto(org, "c526ef20-131b-4486-9357-063fa64b5079")
.setKey("com.microsoft.roslyn:roslyn")
.setName("Roslyn")
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
index f46707cb609..90ce3fd69ed 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
@@ -261,7 +261,7 @@ public class SearchActionTest {
OrganizationDto organization = db.organizations().insertForKey("my-org-1");
userSession.addPermission(ADMINISTER, organization);
db.components().insertComponents(
- newPrivateProjectDto(organization, "project-uuid-1").setName("Project Name 1").setKey("project-key-1"),
+ newPrivateProjectDto(organization, "project-uuid-1").setName("Project Name 1").setKey("project-key-1").setPrivate(false),
newPrivateProjectDto(organization, "project-uuid-2").setName("Project Name 1").setKey("project-key-2"));
String response = ws.newRequest()
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
index 653ad06f901..6a8b16d5e36 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
@@ -23,10 +23,14 @@ import { Link } from 'react-router';
import QualifierIcon from '../../../../components/shared/QualifierIcon';
import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
import OrganizationLink from '../../../../components/ui/OrganizationLink';
+import PrivateBadge from '../../../../components/common/PrivateBadge';
class ComponentNavBreadcrumbs extends React.PureComponent {
static propTypes = {
- breadcrumbs: React.PropTypes.array
+ breadcrumbs: React.PropTypes.array,
+ component: React.PropTypes.shape({
+ visibility: React.PropTypes.string
+ }).isRequired
};
render() {
@@ -73,6 +77,7 @@ class ComponentNavBreadcrumbs extends React.PureComponent {
<span className="slash-separator" />
</span>}
{items}
+ {this.props.component.visibility === 'private' && <PrivateBadge className="spacer-left" />}
</h2>
);
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.js b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.js
index 6fce85f2d97..3a524a9ee61 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.js
+++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBreadcrumbs-test.js
@@ -22,20 +22,46 @@ import { shallow } from 'enzyme';
import { Unconnected } from '../ComponentNavBreadcrumbs';
it('should not render breadcrumbs with one element', () => {
- const breadcrumbs = [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }];
- const result = shallow(<Unconnected breadcrumbs={breadcrumbs} />);
+ const component = {
+ key: 'my-project',
+ name: 'My Project',
+ qualifier: 'TRK',
+ visibility: 'public'
+ };
+ const breadcrumbs = [component];
+ const result = shallow(<Unconnected breadcrumbs={breadcrumbs} component={component} />);
expect(result).toMatchSnapshot();
});
it('should render organization', () => {
- const breadcrumbs = [{ key: 'my-project', name: 'My Project', qualifier: 'TRK' }];
+ const component = {
+ key: 'my-project',
+ name: 'My Project',
+ organization: 'foo',
+ qualifier: 'TRK',
+ visibility: 'public'
+ };
+ const breadcrumbs = [component];
const organization = { key: 'foo', name: 'The Foo Organization' };
const result = shallow(
<Unconnected
breadcrumbs={breadcrumbs}
+ component={component}
organization={organization}
shouldOrganizationBeDisplayed={true}
/>
);
expect(result).toMatchSnapshot();
});
+
+it('renders private badge', () => {
+ const component = {
+ key: 'my-project',
+ name: 'My Project',
+ qualifier: 'TRK',
+ visibility: 'private'
+ };
+ const breadcrumbs = [component];
+ const result = shallow(<Unconnected breadcrumbs={breadcrumbs} component={component} />);
+ expect(result.find('PrivateBadge')).toHaveLength(1);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/main.js b/server/sonar-web/src/main/js/apps/projects-admin/main.js
index c7b6340673e..3079aae3c63 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/main.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/main.js
@@ -108,7 +108,7 @@ export default class Main extends React.PureComponent {
if (this.state.page > 1) {
projects = [].concat(this.state.projects, projects);
}
- this.setState({ ready: true, projects, total: r.total });
+ this.setState({ ready: true, projects, total: r.paging.total });
});
};
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/projects.js b/server/sonar-web/src/main/js/apps/projects-admin/projects.js
index a6def849018..bcb3b1c82e0 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/projects.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/projects.js
@@ -24,6 +24,7 @@ import { getComponentPermissionsUrl } from '../../helpers/urls';
import ApplyTemplateView from '../permissions/project/views/ApplyTemplateView';
import Checkbox from '../../components/controls/Checkbox';
import QualifierIcon from '../../components/shared/QualifierIcon';
+import PrivateBadge from '../../components/common/PrivateBadge';
import { translate } from '../../helpers/l10n';
export default class Projects extends React.PureComponent {
@@ -81,6 +82,9 @@ export default class Projects extends React.PureComponent {
<td className="nowrap">
<span className="note">{project.key}</span>
</td>
+ <td className="width-20">
+ {project.visibility === 'private' && <PrivateBadge />}
+ </td>
<td className="thin nowrap">
<div className="dropdown">
<button className="dropdown-toggle" data-toggle="dropdown">
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js
index 018b753affc..40a74de7747 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js
@@ -27,6 +27,7 @@ import ProjectCardMeasures from './ProjectCardMeasures';
import FavoriteContainer from '../../../components/controls/FavoriteContainer';
import Organization from '../../../components/shared/Organization';
import TagsList from '../../../components/tags/TagsList';
+import PrivateBadge from '../../../components/common/PrivateBadge';
import { translate, translateWithParameters } from '../../../helpers/l10n';
export default class ProjectCard extends React.PureComponent {
@@ -84,6 +85,7 @@ export default class ProjectCard extends React.PureComponent {
{project.name}
</Link>
</h2>
+ {project.visibility === 'private' && <PrivateBadge className="spacer-left" />}
{project.tags.length > 0 && <TagsList tags={project.tags} customClass="spacer-left" />}
</div>
diff --git a/server/sonar-web/src/main/js/components/common/PrivateBadge.css b/server/sonar-web/src/main/js/components/common/PrivateBadge.css
new file mode 100644
index 00000000000..6de55f9937f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/PrivateBadge.css
@@ -0,0 +1,12 @@
+.private-badge {
+ display: inline-block;
+ vertical-align: middle;
+ height: 20px;
+ line-height: 19px;
+ padding: 0 8px;
+ border: 1px solid #cdcdcd;
+ border-radius: 2px;
+ box-sizing: border-box;
+ color: #777;
+ font-size: 12px;
+} \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/components/common/PrivateBadge.js b/server/sonar-web/src/main/js/components/common/PrivateBadge.js
new file mode 100644
index 00000000000..775c0727911
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/common/PrivateBadge.js
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import classNames from 'classnames';
+import Tooltip from '../controls/Tooltip';
+import { translate } from '../../helpers/l10n';
+import './PrivateBadge.css';
+
+type Props = {
+ className?: string
+};
+
+export default function PrivateBadge(props: Props) {
+ return (
+ <Tooltip overlay={translate('visibility.private.description')}>
+ <div className={classNames('private-badge', props.className)}>
+ {translate('visibility.private')}
+ </div>
+ </Tooltip>
+ );
+}
diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto
index e115c235fa7..3159ed3ba95 100644
--- a/sonar-ws/src/main/protobuf/ws-components.proto
+++ b/sonar-ws/src/main/protobuf/ws-components.proto
@@ -97,6 +97,7 @@ message ProvisionedWsResponse {
optional string key = 2;
optional string name = 3;
optional string creationDate = 4;
+ optional string visibility = 5;
}
}
diff --git a/sonar-ws/src/main/protobuf/ws-projects.proto b/sonar-ws/src/main/protobuf/ws-projects.proto
index 2836db8c48c..0ec1cbad85c 100644
--- a/sonar-ws/src/main/protobuf/ws-projects.proto
+++ b/sonar-ws/src/main/protobuf/ws-projects.proto
@@ -69,6 +69,7 @@ message SearchWsResponse {
optional string key = 3;
optional string name = 4;
optional string qualifier = 5;
+ optional string visibility = 6;
}
}