Return inherited boolean values for settings from /projects/*/config

For the boolean project settings (use contributor agreements, etc.) the
ProjectInfoScreen shows the configured value ('TRUE', 'FALSE',
'INHERITED') and the boolean value that is inherited from the parent
project (so that the users knows which value applies if 'INHERITED' is
chosen). Include both values (the configured value and the inherited
value) in addition to the effective value into the result of the
get project configuration REST endpoint. This allows to use this REST
endpoint to populate the ProjectInfoScreen.

This change is incompatible with the previous result format of the get
project configuration REST endpoint. However this REST endpoint was
newly added in 2.7 and there is no final release yet that contains this
version of the REST endpoint.

In the REST documentation the description of the config-info entity is
moved up because the entities are sorted alphabetically.

Change-Id: I063d258c03d7e199e119dbc8921845f4970546de
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index f207d43..35caac4 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -436,10 +436,26 @@
   )]}'
   {
     "kind": "gerritcodereview#project_config",
-    "use_contributor_agreements": false,
-    "use_content_merge": true,
-    "use_signed_off_by": false,
-    "require_change_id": true,
+    "use_contributor_agreements": {
+      "value": true,
+      "configured_value": "TRUE",
+      "inherited_value": false
+    },
+    "use_content_merge": {
+      "value": true,
+      "configured_value": "INHERIT",
+      "inherited_value": true
+    },
+    "use_signed_off_by": {
+      "value": false,
+      "configured_value": "INHERIT",
+      "inherited_value": false
+    },
+    "require_change_id": {
+      "value": false,
+      "configured_value": "FALSE",
+      "inherited_value": true
+    }
     "commentlinks": {}
   }
 ----
@@ -729,6 +745,43 @@
 JSON Entities
 -------------
 
+[[config-info]]
+ConfigInfo
+~~~~~~~~~~
+The `ConfigInfo` entity contains information about the effective project
+configuration.
+
+Fields marked with * are only visible to users who have read access to
+`refs/meta/config`.
+
+[options="header",width="50%",cols="1,6"]
+|======================================
+|Field Name                   |Description
+|`use_contributor_agreements*`|
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+authors must complete a contributor agreement on the site before
+pushing any commits or changes to this project.
+|`use_content_merge*`|
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+Gerrit will try to perform a 3-way merge of text file content when a
+file has been modified by both the destination branch and the change
+being submitted. This option only takes effect if submit type is not
+FAST_FORWARD_ONLY.
+|`use_signed_off_by*`|
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether
+each change must contain a Signed-off-by line from either the author or
+the uploader in the commit message.
+|`require_change_id*`|
+link:#inherited-boolean-info[InheritedBooleanInfo] that tells whether a
+valid link:user-changeid.html[Change-Id] footer in any commit uploaded
+for review is required. This does not apply to commits pushed directly
+to a branch or tag.
+|`commentlinks`|
+Comment link configuration for the project. Has the same format as the
+link:config-gerrit.html#_a_id_commentlink_a_section_commentlink[commentlink section]
+of `gerrit.config`.
+|======================================
+
 [[dashboard-info]]
 DashboardInfo
 ~~~~~~~~~~~~~
@@ -811,6 +864,23 @@
 omitted.
 |============================
 
+[[inherited-boolean-info]]
+InheritedBooleanInfo
+~~~~~~~~~~~~~~~~~~~~
+A boolean value that can also be inherited.
+
+[options="header",width="50%",cols="1,^2,4"]
+|================================
+|Field Name         ||Description
+|`value`            ||
+The effective boolean value.
+|`configured_value` ||
+The configured value, can be `TRUE`, `FALSE` or `INHERITED`.
+|`inherited_value`  |optional|
+The boolean value inherited from the parent. +
+Not set if there is no parent.
+|================================
+
 [[project-description-input]]
 ProjectDescriptionInput
 ~~~~~~~~~~~~~~~~~~~~~~~
@@ -932,39 +1002,6 @@
 |`size_of_packed_objects`  |Size of packed objects in bytes.
 |======================================
 
-[[config-info]]
-ConfigInfo
-~~~~~~~~~~
-The `ConfigInfo` entity contains information about the effective project
-configuration.
-
-Fields marked with * are only visible to users who have read access to
-`refs/meta/config`.
-
-[options="header",width="50%",cols="1,6"]
-|======================================
-|Field Name                   |Description
-|`use_contributor_agreements*`|
-If set, authors must complete a contributor agreement on the site
-before pushing any commits or changes to this project.
-|`use_content_merge*`|
-If set, Gerrit will try to perform a 3-way merge of text file content
-when a file has been modified by both the destination branch and the
-change being submitted. This option only takes effect if submit type is
-not FAST_FORWARD_ONLY.
-|`use_signed_off_by*`|
-If set, each change must contain a Signed-off-by line from either the
-author or the uploader in the commit message.
-|`require_change_id*`|
-If set, require a valid link:user-changeid.html[Change-Id] footer in any
-commit uploaded for review. This does not apply to commits pushed
-directly to a branch or tag.
-|`commentlinks`|
-Comment link configuration for the project. Has the same format as the
-link:config-gerrit.html#_a_id_commentlink_a_section_commentlink[commentlink section]
-of `gerrit.config`.
-|======================================
-
 
 GERRIT
 ------
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
index 3ae78b9..cd5e5d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
@@ -14,47 +14,81 @@
 
 package com.google.gerrit.server.project;
 
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.Project.InheritableBoolean;
 import com.google.gerrit.server.git.GitRepositoryManager;
 
 import java.util.Map;
 
 public class GetConfig implements RestReadView<ProjectResource> {
-  public static class ConfigInfo {
-    public final String kind = "gerritcodereview#project_config";
-
-    public Boolean useContributorAgreements;
-    public Boolean useContentMerge;
-    public Boolean useSignedOffBy;
-    public Boolean requireChangeId;
-
-    public Map<String, CommentLinkInfo> commentlinks;
-    public ThemeInfo theme;
-  }
 
   @Override
   public ConfigInfo apply(ProjectResource resource) {
     ConfigInfo result = new ConfigInfo();
     RefControl refConfig = resource.getControl()
         .controlForRef(GitRepositoryManager.REF_CONFIG);
-    ProjectState project = resource.getControl().getProjectState();
+    ProjectState state = resource.getControl().getProjectState();
     if (refConfig.isVisible()) {
-      result.useContributorAgreements = project.isUseContributorAgreements();
-      result.useContentMerge = project.isUseContentMerge();
-      result.useSignedOffBy = project.isUseSignedOffBy();
-      result.requireChangeId = project.isRequireChangeID();
+      InheritedBooleanInfo useContributorAgreements = new InheritedBooleanInfo();
+      InheritedBooleanInfo useSignedOffBy = new InheritedBooleanInfo();
+      InheritedBooleanInfo useContentMerge = new InheritedBooleanInfo();
+      InheritedBooleanInfo requireChangeId = new InheritedBooleanInfo();
+
+      useContributorAgreements.value = state.isUseContributorAgreements();
+      useSignedOffBy.value = state.isUseSignedOffBy();
+      useContentMerge.value = state.isUseContentMerge();
+      requireChangeId.value = state.isRequireChangeID();
+
+      Project p = state.getProject();
+      useContributorAgreements.configuredValue = p.getUseContributorAgreements();
+      useSignedOffBy.configuredValue = p.getUseSignedOffBy();
+      useContentMerge.configuredValue = p.getUseContentMerge();
+      requireChangeId.configuredValue = p.getRequireChangeID();
+
+      ProjectState parentState = Iterables.getFirst(state.parents(), null);
+      if (parentState != null) {
+        useContributorAgreements.inheritedValue = parentState.isUseContributorAgreements();
+        useSignedOffBy.inheritedValue = parentState.isUseSignedOffBy();
+        useContentMerge.inheritedValue = parentState.isUseContentMerge();
+        requireChangeId.inheritedValue = parentState.isRequireChangeID();
+      }
+
+      result.useContributorAgreements = useContributorAgreements;
+      result.useSignedOffBy = useSignedOffBy;
+      result.useContentMerge = useContentMerge;
+      result.requireChangeId = requireChangeId;
     }
 
     // commentlinks are visible to anyone, as they are used for linkification
     // on the client side.
     result.commentlinks = Maps.newLinkedHashMap();
-    for (CommentLinkInfo cl : project.getCommentLinks()) {
+    for (CommentLinkInfo cl : state.getCommentLinks()) {
       result.commentlinks.put(cl.name, cl);
     }
 
     // Themes are visible to anyone, as they are rendered client-side.
-    result.theme = project.getTheme();
+    result.theme = state.getTheme();
     return result;
   }
+
+  public static class ConfigInfo {
+    public final String kind = "gerritcodereview#project_config";
+
+    public InheritedBooleanInfo useContributorAgreements;
+    public InheritedBooleanInfo useContentMerge;
+    public InheritedBooleanInfo useSignedOffBy;
+    public InheritedBooleanInfo requireChangeId;
+
+    public Map<String, CommentLinkInfo> commentlinks;
+    public ThemeInfo theme;
+  }
+
+  public static class InheritedBooleanInfo {
+    public Boolean value;
+    public InheritableBoolean configuredValue;
+    public Boolean inheritedValue;
+  }
 }