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 77e4058afa..71b0040f58 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'/> + > callback); + + @SignInRequired + void projectDetail(Project.Id projectId, AsyncCallback callback); + + @SignInRequired + void changeProjectDescription(Project.Id projectId, String description, + AsyncCallback callback); + + @SignInRequired + void changeProjectOwner(Project.Id projectId, String newOwnerName, + AsyncCallback 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 0000000000..cbf95dcbde --- /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 0000000000..2a21ebadba --- /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>() { + public void onSuccess(final List 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 { + 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 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 b03e52885c..5de4377c80 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 class Util { 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 e4194982a5..566c178fe0 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 @@ public interface ProjectAccess extends Access { @Query("ORDER BY name") ResultSet all() throws OrmException; + @Query("WHERE ownerGroupId = ?") + ResultSet ownedByGroup(AccountGroup.Id groupId) throws OrmException; + @Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?") ResultSet 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 0000000000..03bfbf1a5b --- /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> callback) { + run(callback, new Action>() { + public List run(ReviewDb db) throws OrmException { + final List result; + if (amAdmin(db)) { + result = db.projects().all().toList(); + } else { + result = myOwnedProjects(db); + Collections.sort(result, new Comparator() { + 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 callback) { + run(callback, new Action() { + 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 callback) { + run(callback, new Action() { + 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 callback) { + run(callback, new Action() { + 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 myOwnedProjects(final ReviewDb db) + throws OrmException { + final List own = new ArrayList(); + 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 0000000000..dc004651e3 --- /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 85b4828aa1..e9eea299b7 100644 --- a/appwar/src/main/webapp/WEB-INF/web.xml +++ b/appwar/src/main/webapp/WEB-INF/web.xml @@ -122,6 +122,16 @@ PatchDetailService /rpc/PatchDetailService + + + ProjectAdminService + com.google.gerrit.server.ProjectAdminServiceSrv + 1 + + + ProjectAdminService + /rpc/ProjectAdminService +