Add a project admin screen to show the projects you own
Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/appjar/src/main/java/com/google/gerrit/Gerrit.gwt.xml b/appjar/src/main/java/com/google/gerrit/Gerrit.gwt.xml
index 77e4058..71b0040 100644
--- a/appjar/src/main/java/com/google/gerrit/Gerrit.gwt.xml
+++ b/appjar/src/main/java/com/google/gerrit/Gerrit.gwt.xml
@@ -30,6 +30,8 @@
class='com.google.gerrit.server.GroupAdminServiceSrv'/>
<servlet path='/rpc/PatchDetailService'
class='com.google.gerrit.server.PatchDetailServiceSrv'/>
+ <servlet path='/rpc/ProjectAdminService'
+ class='com.google.gerrit.server.ProjectAdminServiceSrv'/>
<servlet path='/rpc/SuggestService'
class='com.google.gerrit.server.SuggestServiceSrv'/>
<servlet path='/rpc/SystemInfoService'
diff --git a/appjar/src/main/java/com/google/gerrit/client/Link.java b/appjar/src/main/java/com/google/gerrit/client/Link.java
index 857c5bd..6a21b5d 100644
--- a/appjar/src/main/java/com/google/gerrit/client/Link.java
+++ b/appjar/src/main/java/com/google/gerrit/client/Link.java
@@ -18,6 +18,7 @@
import com.google.gerrit.client.account.NewAgreementScreen;
import com.google.gerrit.client.admin.AccountGroupScreen;
import com.google.gerrit.client.admin.GroupListScreen;
+import com.google.gerrit.client.admin.ProjectListScreen;
import com.google.gerrit.client.changes.AccountDashboardScreen;
import com.google.gerrit.client.changes.ChangeScreen;
import com.google.gerrit.client.changes.MineStarredScreen;
@@ -29,6 +30,7 @@
import com.google.gerrit.client.reviewdb.AccountGroup;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.Patch;
+import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.rpc.RpcUtil;
import com.google.gerrit.client.ui.Screen;
import com.google.gwt.core.client.GWT;
@@ -86,6 +88,10 @@
return "admin,group," + id.toString();
}
+ public static String toProjectAdmin(final Project.Id id) {
+ return "admin,project," + id.toString();
+ }
+
public void onHistoryChanged(final String token) {
Screen s;
try {
@@ -150,6 +156,10 @@
if (ADMIN_GROUPS.equals(token)) {
return new GroupListScreen();
}
+
+ if (ADMIN_PROJECTS.equals(token)) {
+ return new ProjectListScreen();
+ }
}
return null;
diff --git a/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index 3cbf826..41df89f 100644
--- a/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -37,4 +37,5 @@
String columnGroupDescription();
String groupListTitle();
+ String projectListTitle();
}
diff --git a/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index cab3b58..625c506 100644
--- a/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/appjar/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -18,3 +18,4 @@
columnGroupDescription = Description
groupListTitle = Groups
+projectListTitle = Projects
diff --git a/appjar/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java b/appjar/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java
new file mode 100644
index 0000000..3eb5cee
--- /dev/null
+++ b/appjar/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java
@@ -0,0 +1,39 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.reviewdb.Project;
+import com.google.gerrit.client.rpc.SignInRequired;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.List;
+
+public interface ProjectAdminService extends RemoteJsonService {
+ @SignInRequired
+ void ownedProjects(AsyncCallback<List<Project>> callback);
+
+ @SignInRequired
+ void projectDetail(Project.Id projectId, AsyncCallback<ProjectDetail> callback);
+
+ @SignInRequired
+ void changeProjectDescription(Project.Id projectId, String description,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void changeProjectOwner(Project.Id projectId, String newOwnerName,
+ AsyncCallback<VoidResult> callback);
+}
diff --git a/appjar/src/main/java/com/google/gerrit/client/admin/ProjectDetail.java b/appjar/src/main/java/com/google/gerrit/client/admin/ProjectDetail.java
new file mode 100644
index 0000000..cbf95dc
--- /dev/null
+++ b/appjar/src/main/java/com/google/gerrit/client/admin/ProjectDetail.java
@@ -0,0 +1,33 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.reviewdb.AccountGroup;
+import com.google.gerrit.client.reviewdb.Project;
+import com.google.gerrit.client.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+
+public class ProjectDetail {
+ protected Project project;
+ protected AccountGroup ownerGroup;
+
+ public ProjectDetail() {
+ }
+
+ public void load(final ReviewDb db, final Project g) throws OrmException {
+ project = g;
+ ownerGroup = db.accountGroups().get(project.getOwnerGroupId());
+ }
+}
diff --git a/appjar/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/appjar/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
new file mode 100644
index 0000000..2a21eba
--- /dev/null
+++ b/appjar/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -0,0 +1,122 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.Link;
+import com.google.gerrit.client.reviewdb.Project;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.ui.Hyperlink;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.SourcesTableEvents;
+import com.google.gwt.user.client.ui.TableListener;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+
+import java.util.List;
+
+public class ProjectListScreen extends AccountScreen {
+ private ProjectTable projects;
+
+ public ProjectListScreen() {
+ super(Util.C.projectListTitle());
+ }
+
+ @Override
+ public Object getScreenCacheToken() {
+ return getClass();
+ }
+
+ @Override
+ public void onLoad() {
+ if (projects == null) {
+ initUI();
+ }
+
+ Util.PROJECT_SVC.ownedProjects(new GerritCallback<List<Project>>() {
+ public void onSuccess(final List<Project> result) {
+ if (isAttached()) {
+ projects.display(result);
+ projects.finishDisplay(true);
+ }
+ }
+ });
+ }
+
+ private void initUI() {
+ projects = new ProjectTable();
+ projects.setSavePointerId(Link.ADMIN_PROJECTS);
+ add(projects);
+
+ final VerticalPanel fp = new VerticalPanel();
+ fp.setStyleName("gerrit-AddSshKeyPanel");
+ final Label hdr = new Label(Util.C.headingCreateGroup());
+ hdr.setStyleName("gerrit-SmallHeading");
+ fp.add(hdr);
+ }
+
+ private class ProjectTable extends FancyFlexTable<Project> {
+ ProjectTable() {
+ table.setText(0, 1, Util.C.columnGroupName());
+ table.setText(0, 2, Util.C.columnGroupDescription());
+ table.addTableListener(new TableListener() {
+ public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
+ if (cell != 1 && getRowItem(row) != null) {
+ movePointerTo(row);
+ }
+ }
+ });
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_DATA_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ }
+
+ @Override
+ protected Object getRowItemKey(final Project item) {
+ return item.getId();
+ }
+
+ @Override
+ protected void onOpenItem(final Project item) {
+ History.newItem(Link.toProjectAdmin(item.getId()));
+ }
+
+ void display(final List<Project> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final Project k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ populate(row, k);
+ }
+ }
+
+ void populate(final int row, final Project k) {
+ table.setWidget(row, 1, new Hyperlink(k.getName(), Link.toProjectAdmin(k
+ .getId())));
+ table.setText(row, 2, k.getDescription());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_DATA_CELL);
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+ }
+}
diff --git a/appjar/src/main/java/com/google/gerrit/client/admin/Util.java b/appjar/src/main/java/com/google/gerrit/client/admin/Util.java
index b03e528..5de4377 100644
--- a/appjar/src/main/java/com/google/gerrit/client/admin/Util.java
+++ b/appjar/src/main/java/com/google/gerrit/client/admin/Util.java
@@ -21,9 +21,13 @@
public static final AdminConstants C = GWT.create(AdminConstants.class);
public static final AdminMessages M = GWT.create(AdminMessages.class);
public static final GroupAdminService GROUP_SVC;
+ public static final ProjectAdminService PROJECT_SVC;
static {
GROUP_SVC = GWT.create(GroupAdminService.class);
JsonUtil.bind(GROUP_SVC, "rpc/GroupAdminService");
+
+ PROJECT_SVC = GWT.create(ProjectAdminService.class);
+ JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
}
}
diff --git a/appjar/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java b/appjar/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java
index e419498..566c178 100644
--- a/appjar/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java
+++ b/appjar/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java
@@ -31,6 +31,9 @@
@Query("ORDER BY name")
ResultSet<Project> all() throws OrmException;
+ @Query("WHERE ownerGroupId = ?")
+ ResultSet<Project> ownedByGroup(AccountGroup.Id groupId) throws OrmException;
+
@Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
ResultSet<Project> suggestByName(String nameA, String nameB, int limit)
throws OrmException;
diff --git a/appjar/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java b/appjar/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java
new file mode 100644
index 0000000..03bfbf1
--- /dev/null
+++ b/appjar/src/main/java/com/google/gerrit/server/ProjectAdminServiceImpl.java
@@ -0,0 +1,151 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import com.google.gerrit.client.admin.ProjectAdminService;
+import com.google.gerrit.client.admin.ProjectDetail;
+import com.google.gerrit.client.reviewdb.AccountGroup;
+import com.google.gerrit.client.reviewdb.AccountGroupMember;
+import com.google.gerrit.client.reviewdb.Project;
+import com.google.gerrit.client.reviewdb.ReviewDb;
+import com.google.gerrit.client.rpc.BaseServiceImplementation;
+import com.google.gerrit.client.rpc.NoSuchEntityException;
+import com.google.gerrit.client.rpc.RpcUtil;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class ProjectAdminServiceImpl extends BaseServiceImplementation
+ implements ProjectAdminService {
+ private final AccountGroup.Id adminId;
+
+ public ProjectAdminServiceImpl(final GerritServer server) {
+ super(server.getDatabase());
+ adminId = server.getAdminGroupId();
+ }
+
+ public void ownedProjects(final AsyncCallback<List<Project>> callback) {
+ run(callback, new Action<List<Project>>() {
+ public List<Project> run(ReviewDb db) throws OrmException {
+ final List<Project> result;
+ if (amAdmin(db)) {
+ result = db.projects().all().toList();
+ } else {
+ result = myOwnedProjects(db);
+ Collections.sort(result, new Comparator<Project>() {
+ public int compare(final Project a, final Project b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+ }
+ return result;
+ }
+ });
+ }
+
+ public void projectDetail(final Project.Id projectId,
+ final AsyncCallback<ProjectDetail> callback) {
+ run(callback, new Action<ProjectDetail>() {
+ public ProjectDetail run(ReviewDb db) throws OrmException, Failure {
+ assertAmProjectOwner(db, projectId);
+ final Project proj = db.projects().get(projectId);
+ if (proj == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final ProjectDetail d = new ProjectDetail();
+ d.load(db, proj);
+ return d;
+ }
+ });
+ }
+
+ public void changeProjectDescription(final Project.Id projectId,
+ final String description, final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ assertAmProjectOwner(db, projectId);
+ final Project proj = db.projects().get(projectId);
+ if (proj == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ proj.setDescription(description);
+ db.projects().update(Collections.singleton(proj));
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void changeProjectOwner(final Project.Id projectId,
+ final String newOwnerName, final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ assertAmProjectOwner(db, projectId);
+ final Project project = db.projects().get(projectId);
+ if (project == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final AccountGroup owner =
+ db.accountGroups().get(new AccountGroup.NameKey(newOwnerName));
+ if (owner == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ project.setOwnerGroupId(owner.getId());
+ db.projects().update(Collections.singleton(project));
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ private static boolean amInGroup(final ReviewDb db,
+ final AccountGroup.Id groupId) throws OrmException {
+ return db.accountGroupMembers().get(
+ new AccountGroupMember.Key(RpcUtil.getAccountId(), groupId)) != null;
+ }
+
+ private boolean amAdmin(final ReviewDb db) throws OrmException {
+ return adminId != null && amInGroup(db, adminId);
+ }
+
+ private void assertAmProjectOwner(final ReviewDb db,
+ final Project.Id projectId) throws OrmException, Failure {
+ final Project project = db.projects().get(projectId);
+ if (project == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ if (!amInGroup(db, project.getOwnerGroupId()) && !amAdmin(db)) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ }
+
+ private static List<Project> myOwnedProjects(final ReviewDb db)
+ throws OrmException {
+ final List<Project> own = new ArrayList<Project>();
+ for (final AccountGroupMember m : db.accountGroupMembers().byAccount(
+ RpcUtil.getAccountId()).toList()) {
+ for (final Project g : db.projects().ownedByGroup(m.getAccountGroupId())) {
+ own.add(g);
+ }
+ }
+ return own;
+ }
+}
diff --git a/appjar/src/main/java/com/google/gerrit/server/ProjectAdminServiceSrv.java b/appjar/src/main/java/com/google/gerrit/server/ProjectAdminServiceSrv.java
new file mode 100644
index 0000000..dc00465
--- /dev/null
+++ b/appjar/src/main/java/com/google/gerrit/server/ProjectAdminServiceSrv.java
@@ -0,0 +1,24 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+
+/** Publishes {@link ProjectAdminServiceImpl} over JSON. */
+public class ProjectAdminServiceSrv extends GerritJsonServlet {
+ @Override
+ protected Object createServiceHandle() throws Exception {
+ return new ProjectAdminServiceImpl(GerritServer.getInstance());
+ }
+}
diff --git a/appwar/src/main/webapp/WEB-INF/web.xml b/appwar/src/main/webapp/WEB-INF/web.xml
index 85b4828..e9eea29 100644
--- a/appwar/src/main/webapp/WEB-INF/web.xml
+++ b/appwar/src/main/webapp/WEB-INF/web.xml
@@ -122,6 +122,16 @@
<servlet-mapping>
<servlet-name>PatchDetailService</servlet-name>
<url-pattern>/rpc/PatchDetailService</url-pattern>
+
+ <servlet>
+ <servlet-name>ProjectAdminService</servlet-name>
+ <servlet-class>com.google.gerrit.server.ProjectAdminServiceSrv</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>ProjectAdminService</servlet-name>
+ <url-pattern>/rpc/ProjectAdminService</url-pattern>
+ </servlet-mapping>
</servlet-mapping>
<servlet>