Support changing of group options via REST

The options of a group can now be set by PUT on
'/groups/<group>/options'.

The WebUI was adapted to use this new REST endpoint.

There is a new JSON entity to describe the group options, which is also
included in the GroupInfo, instead of having the options directly in
the GroupInfo.

Change-Id: I5ea30b6ca397a1a377a038bd551092eccabecd40
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
index e3461cd..8fd06b0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.client.admin;
 
 import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.VoidResult;
 import com.google.gerrit.client.groups.GroupApi;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
@@ -22,7 +23,6 @@
 import com.google.gerrit.client.ui.RPCSuggestOracle;
 import com.google.gerrit.client.ui.SmallHeading;
 import com.google.gerrit.common.data.GroupDetail;
-import com.google.gerrit.common.data.GroupOptions;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -33,7 +33,6 @@
 import com.google.gwtexpui.clippy.client.CopyableLabel;
 import com.google.gwtexpui.globalkey.client.NpTextArea;
 import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.common.VoidResult;
 
 public class AccountGroupInfoScreen extends AccountGroupScreen {
   private CopyableLabel groupUUIDLabel;
@@ -131,8 +130,8 @@
         final String newOwner = ownerTxt.getText().trim();
         if (newOwner.length() > 0) {
           GroupApi.setGroupOwner(getGroupUUID(), newOwner,
-              new GerritCallback<com.google.gerrit.client.VoidResult>() {
-                public void onSuccess(final com.google.gerrit.client.VoidResult result) {
+              new GerritCallback<VoidResult>() {
+                public void onSuccess(final VoidResult result) {
                   saveOwner.setEnabled(false);
                 }
               });
@@ -162,8 +161,8 @@
       public void onClick(final ClickEvent event) {
         final String txt = descTxt.getText().trim();
         GroupApi.setGroupDescription(getGroupUUID(), txt,
-            new GerritCallback<com.google.gerrit.client.VoidResult>() {
-              public void onSuccess(final com.google.gerrit.client.VoidResult result) {
+            new GerritCallback<VoidResult>() {
+              public void onSuccess(final VoidResult result) {
                 saveDesc.setEnabled(false);
               }
             });
@@ -191,10 +190,8 @@
     saveGroupOptions.addClickHandler(new ClickHandler() {
       @Override
       public void onClick(final ClickEvent event) {
-        final GroupOptions groupOptions =
-            new GroupOptions(visibleToAllCheckBox.getValue());
-        Util.GROUP_SVC.changeGroupOptions(getGroupId(), groupOptions,
-            new GerritCallback<VoidResult>() {
+        GroupApi.setGroupOptions(getGroupUUID(),
+            visibleToAllCheckBox.getValue(), new GerritCallback<VoidResult>() {
               public void onSuccess(final VoidResult result) {
                 saveGroupOptions.setEnabled(false);
               }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
index 83959c6..225dd51 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
@@ -119,7 +119,7 @@
       table.setHTML(row, 1, Util.highlight(k.name(), toHighlight));
     }
     table.setText(row, 2, k.description());
-    if (k.isVisibleToAll()) {
+    if (k.options().isVisibleToAll()) {
       table.setWidget(row, 3, new Image(Gerrit.RESOURCES.greenCheck()));
     }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
index 9300d79..53e8624 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupApi.java
@@ -65,6 +65,14 @@
     group(group).view("owner").put(in, cb);
   }
 
+  /** Set the options for a group */
+  public static void setGroupOptions(AccountGroup.UUID group,
+      boolean isVisibleToAll, AsyncCallback<VoidResult> cb) {
+    GroupOptionsInput in = GroupOptionsInput.create();
+    in.isVisibleToAll(isVisibleToAll);
+    group(group).view("options").put(in, cb);
+  }
+
   /** Add member to a group. */
   public static void addMember(AccountGroup.UUID group, String member,
       AsyncCallback<MemberInfo> cb) {
@@ -186,6 +194,16 @@
     }
   }
 
+  private static class GroupOptionsInput extends JavaScriptObject {
+    final native void isVisibleToAll(boolean v) /*-{ if(v)this.is_visible_to_all=v; }-*/;
+
+    static GroupOptionsInput create() {
+      return (GroupOptionsInput) createObject();
+    }
+
+    protected GroupOptionsInput() {
+    }
+  }
 
   private static class MemberInput extends JavaScriptObject {
     final native void init() /*-{ this.members = []; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
index 33849a8..bcfe6c1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/groups/GroupInfo.java
@@ -29,7 +29,7 @@
 
   public final native String id() /*-{ return this.id; }-*/;
   public final native String name() /*-{ return this.name; }-*/;
-  public final native boolean isVisibleToAll() /*-{ return this['visible_to_all'] ? true : false; }-*/;
+  public final native GroupOptionsInfo options() /*-{ return this.options; }-*/;
   public final native String description() /*-{ return this.description; }-*/;
   public final native String url() /*-{ return this.url; }-*/;
 
@@ -46,4 +46,11 @@
 
   protected GroupInfo() {
   }
+
+  public static class GroupOptionsInfo extends JavaScriptObject {
+    public final native boolean isVisibleToAll() /*-{ return this['is_visible_to_all'] ? true : false; }-*/;
+
+    protected GroupOptionsInfo() {
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java
new file mode 100644
index 0000000..3fbfc70
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GetOptions.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.group;
+
+import com.google.gerrit.extensions.restapi.RestReadView;
+
+public class GetOptions implements RestReadView<GroupResource> {
+
+  @Override
+  public GroupOptionsInfo apply(GroupResource resource) {
+    return new GroupOptionsInfo(resource.getGroup());
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupInfo.java
index 4e894e4..537a44c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupInfo.java
@@ -25,7 +25,7 @@
   public String id;
   public String name;
   public String url;
-  public Boolean visibleToAll;
+  public GroupOptionsInfo options;
 
   // These fields are only supplied for internal groups.
   public String description;
@@ -36,7 +36,7 @@
     id = Url.encode(group.getGroupUUID().get());
     name = Strings.emptyToNull(group.getName());
     url = Strings.emptyToNull(group.getUrl());
-    visibleToAll = group.isVisibleToAll() ? true : null;
+    options = new GroupOptionsInfo(group);
 
     AccountGroup internalGroup = GroupDescriptions.toAccountGroup(group);
     if (internalGroup != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupOptionsInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupOptionsInfo.java
new file mode 100644
index 0000000..94d2597
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupOptionsInfo.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.group;
+
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+public class GroupOptionsInfo {
+  final String kind = "gerritcodereview#groupoptions";
+  public Boolean isVisibleToAll;
+
+  public GroupOptionsInfo(GroupDescription.Basic group) {
+    isVisibleToAll = group.isVisibleToAll() ? true : null;
+  }
+
+  public GroupOptionsInfo(AccountGroup group) {
+    isVisibleToAll = group.isVisibleToAll() ? true : null;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
index 5425c77..33043fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
@@ -49,6 +49,8 @@
     put(GROUP_KIND, "name").to(PutName.class);
     get(GROUP_KIND, "owner").to(GetOwner.class);
     put(GROUP_KIND, "owner").to(PutOwner.class);
+    get(GROUP_KIND, "options").to(GetOptions.class);
+    put(GROUP_KIND, "options").to(PutOptions.class);
 
     child(GROUP_KIND, "members").to(MembersCollection.class);
     get(MEMBER_KIND).to(GetMember.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java
new file mode 100644
index 0000000..643e8c8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/PutOptions.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// 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.group;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.group.PutOptions.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+import java.util.Collections;
+
+public class PutOptions implements RestModifyView<GroupResource, Input> {
+  static class Input {
+    Boolean isVisibleToAll;
+  }
+
+  private final GroupCache groupCache;
+  private final ReviewDb db;
+
+  @Inject
+  PutOptions(GroupCache groupCache, ReviewDb db) {
+    this.groupCache = groupCache;
+    this.db = db;
+  }
+
+  @Override
+  public GroupOptionsInfo apply(GroupResource resource, Input input)
+      throws MethodNotAllowedException, AuthException, BadRequestException,
+      ResourceNotFoundException, OrmException {
+    if (resource.toAccountGroup() == null) {
+      throw new MethodNotAllowedException();
+    } else if (!resource.getControl().isOwner()) {
+      throw new AuthException("Not group owner");
+    }
+
+    if (input == null) {
+      throw new BadRequestException("options are required");
+    }
+    if (input.isVisibleToAll == null) {
+      input.isVisibleToAll = false;
+    }
+
+    AccountGroup group = db.accountGroups().get(
+        resource.toAccountGroup().getId());
+    if (group == null) {
+      throw new ResourceNotFoundException();
+    }
+
+    group.setVisibleToAll(input.isVisibleToAll);
+    db.accountGroups().update(Collections.singleton(group));
+    groupCache.evict(group);
+
+    return new GroupOptionsInfo(group);
+  }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
index f0fcc35..f9f7e8b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListGroupsCommand.java
@@ -88,8 +88,8 @@
           formatter.addColumn(Strings.nullToEmpty(info.description));
           formatter.addColumn(o != null ? o.getName() : "n/a");
           formatter.addColumn(o != null ? o.getGroupUUID().get() : "");
-          formatter.addColumn(Boolean.toString(
-              Objects.firstNonNull(info.visibleToAll, Boolean.FALSE)));
+          formatter.addColumn(Boolean.toString(Objects.firstNonNull(
+              info.options.isVisibleToAll, Boolean.FALSE)));
         }
         formatter.nextLine();
       }