Merge branch 'stable'
* stable:
Update 2.1.7 release notes
ExportReviewNotes: Default to 2 threads
Change-Id: Icc576083ec6ffa5cbbe5163ab34b8c843dc3ab66
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 71c0d1f..e00e45b 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -13,7 +13,6 @@
[--branch <REF>] \
[\--owner <GROUP> ...] \
[\--parent <NAME>] \
- [\--permissions-only] \
[\--description <DESC>] \
[\--submit-type <TYPE>] \
[\--use-content-merge] \
@@ -71,11 +70,6 @@
through. If not specified, the parent is set to the default
project `\-- All Projects \--`.
-\--permissions-only::
- Create the project only to serve as a parent for other
- projects. The new project's Git repository will not be
- initialized, and cannot be cloned.
-
\--description::
Initial description of the project. If not specified,
no description is stored.
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 226ffbe..cec2783 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -485,6 +485,21 @@
+
Default is true, enabled.
+cache.projects.checkFrequency::
++
+How often project configuration should be checked for update from Git.
+Gerrit Code Review caches project access rules and configuration in
+memory, checking the refs/meta/config branch every checkFrequency
+minutes to see if a new revision should be loaded and used for future
+access. Values can be specified using standard time unit abbreviations
+('ms', 'sec', 'min', etc.).
++
+If set to 0, checks occur every time, which may slow down operations.
+Administrators may force the cache to flush with
+link:cmd-flush-caches.html[gerrit flush-caches].
++
+Default is 5 minutes.
+
[[commentlink]]Section commentlink
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt
index 9644e92..d3094fd 100644
--- a/Documentation/pgm-init.txt
+++ b/Documentation/pgm-init.txt
@@ -11,7 +11,6 @@
'java' -jar gerrit.war 'init'
-d <SITE_PATH>
[\--batch]
- [\--import-projects]
[\--no-auto-start]
DESCRIPTION
@@ -21,7 +20,7 @@
into a newly created `$site_path`.
If run an an existing `$site_path`, init will upgrade some resources
-as necessary. This can be useful to import newly created projects.
+as necessary.
OPTIONS
-------
@@ -30,12 +29,6 @@
configuration defaults are chosen based on the whims of
the Gerrit developers.
-\--import-projects::
- Recursively search
- link:config-gerrit.html#gerrit.basePath[gerrit.basePath]
- for any Git repositories not yet registered as a project,
- and initializes a new project for them.
-
\--no-auto-start::
Don't automatically start the daemon after initializing a
newly created site path. This permits the administartor
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index efe311f..db4f348 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -54,6 +54,10 @@
return "change," + ps.getParentKey().toString() + ",patchset=" + ps.get();
}
+ public static String toProjectAcceess(final Project.NameKey p) {
+ return "admin,project," + p.get() + ",access";
+ }
+
public static String toAccountDashboard(final AccountInfo acct) {
return toAccountDashboard(acct.getId());
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java
new file mode 100644
index 0000000..44a00d5
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java
@@ -0,0 +1,140 @@
+// Copyright (C) 2010 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.common.data;
+
+import com.google.gerrit.reviewdb.Project;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/** Portion of a {@link Project} describing access rules. */
+public class AccessSection implements Comparable<AccessSection> {
+ /** Pattern that matches all references in a project. */
+ public static final String ALL = "refs/*";
+
+ /** Pattern that matches all branches in a project. */
+ public static final String HEADS = "refs/heads/*";
+
+ /** Prefix that triggers a regular expression pattern. */
+ public static final String REGEX_PREFIX = "^";
+
+ /** @return true if the name is likely to be a valid access section name. */
+ public static boolean isAccessSection(String name) {
+ return name.startsWith("refs/") || name.startsWith("^refs/");
+ }
+
+ protected String refPattern;
+ protected List<Permission> permissions;
+
+ protected AccessSection() {
+ }
+
+ public AccessSection(String refPattern) {
+ setRefPattern(refPattern);
+ }
+
+ public String getRefPattern() {
+ return refPattern;
+ }
+
+ public void setRefPattern(String refPattern) {
+ this.refPattern = refPattern;
+ }
+
+ public List<Permission> getPermissions() {
+ if (permissions == null) {
+ permissions = new ArrayList<Permission>();
+ }
+ return permissions;
+ }
+
+ public void setPermissions(List<Permission> list) {
+ Set<String> names = new HashSet<String>();
+ for (Permission p : list) {
+ if (!names.add(p.getName().toLowerCase())) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ permissions = list;
+ }
+
+ public Permission getPermission(String name) {
+ return getPermission(name, false);
+ }
+
+ public Permission getPermission(String name, boolean create) {
+ for (Permission p : getPermissions()) {
+ if (p.getName().equalsIgnoreCase(name)) {
+ return p;
+ }
+ }
+
+ if (create) {
+ Permission p = new Permission(name);
+ permissions.add(p);
+ return p;
+ } else {
+ return null;
+ }
+ }
+
+ public void remove(Permission permission) {
+ if (permission != null) {
+ removePermission(permission.getName());
+ }
+ }
+
+ public void removePermission(String name) {
+ if (permissions != null) {
+ for (Iterator<Permission> itr = permissions.iterator(); itr.hasNext();) {
+ if (name.equalsIgnoreCase(itr.next().getName())) {
+ itr.remove();
+ }
+ }
+ }
+ }
+
+ public void mergeFrom(AccessSection section) {
+ for (Permission src : section.getPermissions()) {
+ Permission dst = getPermission(src.getName());
+ if (dst != null) {
+ dst.mergeFrom(src);
+ } else {
+ permissions.add(dst);
+ }
+ }
+ }
+
+ @Override
+ public int compareTo(AccessSection o) {
+ return comparePattern().compareTo(o.comparePattern());
+ }
+
+ private String comparePattern() {
+ if (getRefPattern().startsWith(REGEX_PREFIX)) {
+ return getRefPattern().substring(REGEX_PREFIX.length());
+ }
+ return getRefPattern();
+ }
+
+ @Override
+ public String toString() {
+ return "AccessSection[" + getRefPattern() + "]";
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java
index ea9aedb..7d03457 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java
@@ -31,6 +31,7 @@
protected short maxNegative;
protected short maxPositive;
+ private transient List<Integer> intList;
private transient Map<Short, ApprovalCategoryValue> byValue;
protected ApprovalType() {
@@ -56,6 +57,9 @@
maxPositive = values.get(values.size() - 1).getValue();
}
}
+
+ // Force the label name to pre-compute so we don't have data race conditions.
+ getCategory().getLabelName();
}
public ApprovalCategory getCategory() {
@@ -107,4 +111,16 @@
}
}
}
+
+ public List<Integer> getValuesAsList() {
+ if (intList == null) {
+ intList = new ArrayList<Integer>(values.size());
+ for (ApprovalCategoryValue acv : values) {
+ intList.add(Integer.valueOf(acv.getValue()));
+ }
+ Collections.sort(intList);
+ Collections.reverse(intList);
+ }
+ return intList;
+ }
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java
index 1b6d4a3..0518010 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java
@@ -19,20 +19,17 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
public class ApprovalTypes {
protected List<ApprovalType> approvalTypes;
- protected List<ApprovalType> actionTypes;
- private transient Map<ApprovalCategory.Id, ApprovalType> byCategoryId;
+ private transient Map<ApprovalCategory.Id, ApprovalType> byId;
+ private transient Map<String, ApprovalType> byLabel;
protected ApprovalTypes() {
}
- public ApprovalTypes(final List<ApprovalType> approvals,
- final List<ApprovalType> actions) {
+ public ApprovalTypes(final List<ApprovalType> approvals) {
approvalTypes = approvals;
- actionTypes = actions;
byCategory();
}
@@ -40,33 +37,35 @@
return approvalTypes;
}
- public List<ApprovalType> getActionTypes() {
- return actionTypes;
- }
-
- public ApprovalType getApprovalType(final ApprovalCategory.Id id) {
+ public ApprovalType byId(final ApprovalCategory.Id id) {
return byCategory().get(id);
}
- public Set<ApprovalCategory.Id> getApprovalCategories() {
- return byCategory().keySet();
- }
-
private Map<ApprovalCategory.Id, ApprovalType> byCategory() {
- if (byCategoryId == null) {
- byCategoryId = new HashMap<ApprovalCategory.Id, ApprovalType>();
- if (actionTypes != null) {
- for (final ApprovalType t : actionTypes) {
- byCategoryId.put(t.getCategory().getId(), t);
- }
- }
-
+ if (byId == null) {
+ byId = new HashMap<ApprovalCategory.Id, ApprovalType>();
if (approvalTypes != null) {
for (final ApprovalType t : approvalTypes) {
- byCategoryId.put(t.getCategory().getId(), t);
+ byId.put(t.getCategory().getId(), t);
}
}
}
- return byCategoryId;
+ return byId;
+ }
+
+ public ApprovalType byLabel(String labelName) {
+ return byLabel().get(labelName.toLowerCase());
+ }
+
+ private Map<String, ApprovalType> byLabel() {
+ if (byLabel == null) {
+ byLabel = new HashMap<String, ApprovalType>();
+ if (approvalTypes != null) {
+ for (ApprovalType t : approvalTypes) {
+ byLabel.put(t.getCategory().getLabelName().toLowerCase(), t);
+ }
+ }
+ }
+ return byLabel;
}
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
index 572fd7a..f196c05 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -39,10 +39,10 @@
protected List<PatchSet> patchSets;
protected List<ApprovalDetail> approvals;
protected Set<ApprovalCategory.Id> missingApprovals;
+ protected boolean canSubmit;
protected List<ChangeMessage> messages;
protected PatchSet.Id currentPatchSetId;
protected PatchSetDetail currentDetail;
- protected Set<ApprovalCategory.Id> currentActions;
public ChangeDetail() {
}
@@ -87,6 +87,14 @@
canRevert = a;
}
+ public boolean canSubmit() {
+ return canSubmit;
+ }
+
+ public void setCanSubmit(boolean a) {
+ canSubmit = a;
+ }
+
public Change getChange() {
return change;
}
@@ -153,14 +161,6 @@
missingApprovals = a;
}
- public Set<ApprovalCategory.Id> getCurrentActions() {
- return currentActions;
- }
-
- public void setCurrentActions(Set<ApprovalCategory.Id> a) {
- currentActions = a;
- }
-
public boolean isCurrentPatchSet(final PatchSetDetail detail) {
return currentPatchSetId != null
&& detail.getPatchSet().getId().equals(currentPatchSetId);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
index ce508cc..6d4b889 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
@@ -21,8 +21,8 @@
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.RpcImpl;
-import com.google.gwtjsonrpc.client.VoidResult;
import com.google.gwtjsonrpc.client.RpcImpl.Version;
+import com.google.gwtjsonrpc.client.VoidResult;
import java.util.List;
import java.util.Set;
@@ -36,7 +36,8 @@
void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
@SignInRequired
- void groupDetail(AccountGroup.Id groupId, AsyncCallback<GroupDetail> callback);
+ void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID uuid,
+ AsyncCallback<GroupDetail> callback);
@SignInRequired
void changeGroupDescription(AccountGroup.Id groupId, String description,
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
new file mode 100644
index 0000000..cd6e172
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2010 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.common.data;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+
+/** Describes a group within a projects {@link AccessSection}s. */
+public class GroupReference implements Comparable<GroupReference> {
+ /** @return a new reference to the given group description. */
+ public static GroupReference forGroup(AccountGroup group) {
+ return new GroupReference(group.getGroupUUID(), group.getName());
+ }
+
+ protected String uuid;
+ protected String name;
+
+ protected GroupReference() {
+ }
+
+ public GroupReference(AccountGroup.UUID uuid, String name) {
+ setUUID(uuid);
+ setName(name);
+ }
+
+ public AccountGroup.UUID getUUID() {
+ return uuid != null ? new AccountGroup.UUID(uuid) : null;
+ }
+
+ public void setUUID(AccountGroup.UUID newUUID) {
+ uuid = newUUID != null ? newUUID.get() : null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String newName) {
+ this.name = newName;
+ }
+
+ @Override
+ public int compareTo(GroupReference o) {
+ return uuid(this).compareTo(uuid(o));
+ }
+
+ private static String uuid(GroupReference a) {
+ return a.getUUID() != null ? a.getUUID().get() : "?";
+ }
+
+ @Override
+ public int hashCode() {
+ return uuid(this).hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof GroupReference && compareTo((GroupReference) o) == 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Group[" + getName() + " / " + getUUID() + "]";
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/InheritedRefRight.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/InheritedRefRight.java
deleted file mode 100644
index 4dc998b..0000000
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/InheritedRefRight.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2010 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.common.data;
-
-import com.google.gerrit.reviewdb.RefRight;
-
-/**
- * Additional data about a {@link RefRight} not normally loaded: defines if a
- * right is inherited from a parent structure (e.g. a parent project).
- */
-public class InheritedRefRight {
- private RefRight right;
- private boolean inherited;
- private boolean owner;
-
- /**
- * Creates a instance of a {@link RefRight} with data about inheritance
- */
- protected InheritedRefRight() {
- }
-
- /**
- * Creates a instance of a {@link RefRight} with data about inheritance
- *
- * @param right the right
- * @param inherited true if the right is inherited, false otherwise
- * @param owner true if right is owned by current user, false otherwise
- */
- public InheritedRefRight(RefRight right, boolean inherited, boolean owner) {
- this.right = right;
- this.inherited = inherited;
- this.owner = owner;
- }
-
- public RefRight getRight() {
- return right;
- }
-
- public boolean isInherited() {
- return inherited;
- }
-
- public boolean isOwner() {
- return owner;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof InheritedRefRight) {
- InheritedRefRight a = this;
- InheritedRefRight b = (InheritedRefRight) o;
- return a.getRight().equals(b.getRight())
- && a.isInherited() == b.isInherited();
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return getRight().hashCode();
- }
-}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
index 7a0ada3..273f18d 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
@@ -15,39 +15,35 @@
package com.google.gerrit.common.data;
import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchLineComment;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.PatchSetInfo;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
public class PatchSetPublishDetail {
protected AccountInfoCache accounts;
protected PatchSetInfo patchSetInfo;
protected Change change;
protected List<PatchLineComment> drafts;
- protected Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
- protected Map<ApprovalCategory.Id, PatchSetApproval> given;
- protected boolean isSubmitAllowed;
+ protected List<PermissionRange> labels;
+ protected List<PatchSetApproval> given;
+ protected boolean canSubmit;
- public Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> getAllowed() {
- return allowed;
+ public List<PermissionRange> getLabels() {
+ return labels;
}
- public void setAllowed(
- Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed) {
- this.allowed = allowed;
+ public void setLabels(List<PermissionRange> labels) {
+ this.labels = labels;
}
- public Map<ApprovalCategory.Id, PatchSetApproval> getGiven() {
+ public List<PatchSetApproval> getGiven() {
return given;
}
- public void setGiven(Map<ApprovalCategory.Id, PatchSetApproval> given) {
+ public void setGiven(List<PatchSetApproval> given) {
this.given = given;
}
@@ -67,8 +63,8 @@
this.drafts = drafts;
}
- public void setSubmitAllowed(boolean allowed) {
- isSubmitAllowed = allowed;
+ public void setCanSubmit(boolean allowed) {
+ canSubmit = allowed;
}
public AccountInfoCache getAccounts() {
@@ -87,20 +83,25 @@
return drafts;
}
- public boolean isAllowed(final ApprovalCategory.Id id) {
- final Set<ApprovalCategoryValue.Id> s = getAllowed(id);
- return s != null && !s.isEmpty();
+ public PermissionRange getRange(final String permissionName) {
+ for (PermissionRange s : labels) {
+ if (s.getName().equals(permissionName)) {
+ return s;
+ }
+ }
+ return null;
}
- public Set<ApprovalCategoryValue.Id> getAllowed(final ApprovalCategory.Id id) {
- return allowed.get(id);
+ public PatchSetApproval getChangeApproval(ApprovalCategory.Id id) {
+ for (PatchSetApproval a : given) {
+ if (a.getCategoryId().equals(id)) {
+ return a;
+ }
+ }
+ return null;
}
- public PatchSetApproval getChangeApproval(final ApprovalCategory.Id id) {
- return given.get(id);
- }
-
- public boolean isSubmitAllowed() {
- return isSubmitAllowed;
+ public boolean canSubmit() {
+ return canSubmit;
}
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
new file mode 100644
index 0000000..c27d9d9
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
@@ -0,0 +1,201 @@
+// Copyright (C) 2010 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.common.data;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/** A single permission within an {@link AccessSection} of a project. */
+public class Permission implements Comparable<Permission> {
+ public static final String CREATE = "create";
+ public static final String FORGE_AUTHOR = "forgeAuthor";
+ public static final String FORGE_COMMITTER = "forgeCommitter";
+ public static final String FORGE_SERVER = "forgeServerAsCommitter";
+ public static final String LABEL = "label-";
+ public static final String OWNER = "owner";
+ public static final String PUSH = "push";
+ public static final String PUSH_MERGE = "pushMerge";
+ public static final String PUSH_TAG = "pushTag";
+ public static final String READ = "read";
+ public static final String SUBMIT = "submit";
+
+ private static final List<String> NAMES_LC;
+
+ static {
+ NAMES_LC = new ArrayList<String>();
+ NAMES_LC.add(OWNER.toLowerCase());
+ NAMES_LC.add(READ.toLowerCase());
+ NAMES_LC.add(CREATE.toLowerCase());
+ NAMES_LC.add(FORGE_AUTHOR.toLowerCase());
+ NAMES_LC.add(FORGE_COMMITTER.toLowerCase());
+ NAMES_LC.add(FORGE_SERVER.toLowerCase());
+ NAMES_LC.add(PUSH.toLowerCase());
+ NAMES_LC.add(PUSH_MERGE.toLowerCase());
+ NAMES_LC.add(PUSH_TAG.toLowerCase());
+ NAMES_LC.add(LABEL.toLowerCase());
+ NAMES_LC.add(SUBMIT.toLowerCase());
+ }
+
+ /** @return true if the name is recognized as a permission name. */
+ public static boolean isPermission(String varName) {
+ String lc = varName.toLowerCase();
+ if (lc.startsWith(LABEL)) {
+ return LABEL.length() < lc.length();
+ }
+ return NAMES_LC.contains(lc);
+ }
+
+ /** @return true if the permission name is actually for a review label. */
+ public static boolean isLabel(String varName) {
+ return varName.startsWith(LABEL) && LABEL.length() < varName.length();
+ }
+
+ /** @return permission name for the given review label. */
+ public static String forLabel(String labelName) {
+ return LABEL + labelName;
+ }
+
+ protected String name;
+ protected boolean exclusiveGroup;
+ protected List<PermissionRule> rules;
+
+ protected Permission() {
+ }
+
+ public Permission(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isLabel() {
+ return isLabel(getName());
+ }
+
+ public String getLabel() {
+ if (isLabel()) {
+ return getName().substring(LABEL.length());
+ }
+ return null;
+ }
+
+ public Boolean getExclusiveGroup() {
+ // Only permit exclusive group behavior on non OWNER permissions,
+ // otherwise an owner might lose access to a delegated subspace.
+ //
+ return exclusiveGroup && !OWNER.equals(getName());
+ }
+
+ public void setExclusiveGroup(Boolean newExclusiveGroup) {
+ exclusiveGroup = newExclusiveGroup;
+ }
+
+ public List<PermissionRule> getRules() {
+ initRules();
+ return rules;
+ }
+
+ public void setRules(List<PermissionRule> list) {
+ rules = list;
+ }
+
+ public void add(PermissionRule rule) {
+ initRules();
+ rules.add(rule);
+ }
+
+ public void remove(PermissionRule rule) {
+ if (rule != null) {
+ removeRule(rule.getGroup());
+ }
+ }
+
+ public void removeRule(GroupReference group) {
+ if (rules != null) {
+ for (Iterator<PermissionRule> itr = rules.iterator(); itr.hasNext();) {
+ if (sameGroup(itr.next(), group)) {
+ itr.remove();
+ }
+ }
+ }
+ }
+
+ public PermissionRule getRule(GroupReference group) {
+ return getRule(group, false);
+ }
+
+ public PermissionRule getRule(GroupReference group, boolean create) {
+ initRules();
+
+ for (PermissionRule r : rules) {
+ if (sameGroup(r, group)) {
+ return r;
+ }
+ }
+
+ if (create) {
+ PermissionRule r = new PermissionRule(group);
+ rules.add(r);
+ return r;
+ } else {
+ return null;
+ }
+ }
+
+ void mergeFrom(Permission src) {
+ for (PermissionRule srcRule : src.getRules()) {
+ PermissionRule dstRule = getRule(srcRule.getGroup());
+ if (dstRule != null) {
+ dstRule.mergeFrom(srcRule);
+ } else {
+ add(srcRule);
+ }
+ }
+ }
+
+ private static boolean sameGroup(PermissionRule rule, GroupReference group) {
+ if (group.getUUID() != null) {
+ return group.getUUID().equals(rule.getGroup().getUUID());
+
+ } else if (group.getName() != null) {
+ return group.getName().equals(rule.getGroup().getName());
+
+ } else {
+ return false;
+ }
+ }
+
+ private void initRules() {
+ if (rules == null) {
+ rules = new ArrayList<PermissionRule>(4);
+ }
+ }
+
+ @Override
+ public int compareTo(Permission b) {
+ int cmp = index(this) - index(b);
+ if (cmp == 0) getName().compareTo(b.getName());
+ return cmp;
+ }
+
+ private static int index(Permission a) {
+ String lc = a.isLabel() ? Permission.LABEL : a.getName().toLowerCase();
+ int index = NAMES_LC.indexOf(lc);
+ return 0 <= index ? index : NAMES_LC.size();
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java
new file mode 100644
index 0000000..bc2dadd
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2010 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.common.data;
+
+public class PermissionRange implements Comparable<PermissionRange> {
+ protected String name;
+ protected int min;
+ protected int max;
+
+ protected PermissionRange() {
+ }
+
+ public PermissionRange(String name, int min, int max) {
+ this.name = name;
+
+ if (min <= max) {
+ this.min = min;
+ this.max = max;
+ } else {
+ this.min = max;
+ this.max = min;
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isLabel() {
+ return Permission.isLabel(getName());
+ }
+
+ public String getLabel() {
+ return isLabel() ? getName().substring(Permission.LABEL.length()) : null;
+ }
+
+ public int getMin() {
+ return min;
+ }
+
+ public int getMax() {
+ return max;
+ }
+
+ /** True if the value is within the range. */
+ public boolean contains(int value) {
+ return getMin() <= value && value <= getMax();
+ }
+
+ /** Normalize the value to fit within the bounds of the range. */
+ public int squash(int value) {
+ return Math.min(Math.max(getMin(), value), getMax());
+ }
+
+ /** True both {@link #getMin()} and {@link #getMax()} are 0. */
+ public boolean isEmpty() {
+ return getMin() == 0 && getMax() == 0;
+ }
+
+ @Override
+ public int compareTo(PermissionRange o) {
+ return getName().compareTo(o.getName());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder r = new StringBuilder();
+ if (getMin() < 0 && getMax() == 0) {
+ r.append(getMin());
+ r.append(' ');
+ } else {
+ if (getMin() != getMax()) {
+ if (0 <= getMin()) r.append('+');
+ r.append(getMin());
+ r.append("..");
+ }
+ if (0 <= getMax()) r.append('+');
+ r.append(getMax());
+ r.append(' ');
+ }
+ return r.toString();
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
new file mode 100644
index 0000000..5c73c52
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
@@ -0,0 +1,203 @@
+// Copyright (C) 2010 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.common.data;
+
+public class PermissionRule implements Comparable<PermissionRule> {
+ public static enum Action {
+ ALLOW, DENY;
+ }
+
+ protected boolean deny;
+ protected boolean force;
+ protected int min;
+ protected int max;
+ protected GroupReference group;
+
+ public PermissionRule() {
+ }
+
+ public PermissionRule(GroupReference group) {
+ this.group = group;
+ }
+
+ public Action getAction() {
+ return deny ? Action.DENY : Action.ALLOW;
+ }
+
+ public void setAction(Action action) {
+ if (action == null) {
+ throw new NullPointerException("action");
+ }
+ setDeny(action == Action.DENY);
+ }
+
+ public boolean getDeny() {
+ return deny;
+ }
+
+ public void setDeny(boolean newDeny) {
+ deny = newDeny;
+ }
+
+ public Boolean getForce() {
+ return force;
+ }
+
+ public void setForce(Boolean newForce) {
+ force = newForce;
+ }
+
+ public Integer getMin() {
+ return min;
+ }
+
+ public void setMin(Integer min) {
+ this.min = min;
+ }
+
+ public void setMax(Integer max) {
+ this.max = max;
+ }
+
+ public Integer getMax() {
+ return max;
+ }
+
+ public void setRange(int newMin, int newMax) {
+ if (newMax < newMin) {
+ min = newMax;
+ max = newMin;
+ } else {
+ min = newMin;
+ max = newMax;
+ }
+ }
+
+ public GroupReference getGroup() {
+ return group;
+ }
+
+ public void setGroup(GroupReference newGroup) {
+ group = newGroup;
+ }
+
+ void mergeFrom(PermissionRule src) {
+ setDeny(getDeny() || src.getDeny());
+ setForce(getForce() || src.getForce());
+ setRange(Math.min(getMin(), src.getMin()), Math.max(getMax(), src.getMax()));
+ }
+
+ @Override
+ public int compareTo(PermissionRule o) {
+ int cmp = deny(this) - deny(o);
+ if (cmp == 0) cmp = range(o) - range(this);
+ if (cmp == 0) cmp = group(this).compareTo(group(o));
+ return cmp;
+ }
+
+ private static int deny(PermissionRule a) {
+ return a.getDeny() ? 1 : 0;
+ }
+
+ private static int range(PermissionRule a) {
+ return Math.abs(a.getMin()) + Math.abs(a.getMax());
+ }
+
+ private static String group(PermissionRule a) {
+ return a.getGroup().getName() != null ? a.getGroup().getName() : "";
+ }
+
+ @Override
+ public String toString() {
+ return asString(true);
+ }
+
+ public String asString(boolean canUseRange) {
+ StringBuilder r = new StringBuilder();
+
+ if (getDeny()) {
+ r.append("deny ");
+ }
+
+ if (getForce()) {
+ r.append("+force ");
+ }
+
+ if (canUseRange && (getMin() != 0 || getMax() != 0)) {
+ if (0 <= getMin()) r.append('+');
+ r.append(getMin());
+ r.append("..");
+ if (0 <= getMax()) r.append('+');
+ r.append(getMax());
+ r.append(' ');
+ }
+
+ r.append("group ");
+ r.append(getGroup().getName());
+
+ return r.toString();
+ }
+
+ public static PermissionRule fromString(String src, boolean mightUseRange) {
+ final String orig = src;
+ final PermissionRule rule = new PermissionRule();
+
+ src = src.trim();
+
+ if (src.startsWith("deny ")) {
+ rule.setDeny(true);
+ src = src.substring(5).trim();
+ }
+
+ if (src.startsWith("+force ")) {
+ rule.setForce(true);
+ src = src.substring("+force ".length()).trim();
+ }
+
+ if (mightUseRange && !src.startsWith("group ")) {
+ int sp = src.indexOf(' ');
+ String range = src.substring(0, sp);
+
+ if (range.matches("^([+-]\\d+)\\.\\.([+-]\\d)$")) {
+ int dotdot = range.indexOf("..");
+ int min = parseInt(range.substring(0, dotdot));
+ int max = parseInt(range.substring(dotdot + 2));
+ rule.setRange(min, max);
+ } else {
+ throw new IllegalArgumentException("Invalid range in rule: " + orig);
+ }
+
+ src = src.substring(sp + 1).trim();
+ }
+
+ if (src.startsWith("group ")) {
+ src = src.substring(6).trim();
+ GroupReference group = new GroupReference();
+ group.setName(src);
+ rule.setGroup(group);
+ } else {
+ throw new IllegalArgumentException("Rule must include group: " + orig);
+ }
+
+ return rule;
+ }
+
+ private static int parseInt(String value) {
+ if (value.startsWith("+")) {
+ value = value.substring(1);
+ }
+ return Integer.parseInt(value);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
new file mode 100644
index 0000000..2fbf512
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2011 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.common.data;
+
+import com.google.gerrit.reviewdb.Project;
+
+import java.util.List;
+import java.util.Set;
+
+public class ProjectAccess {
+ protected String revision;
+ protected Project.NameKey inheritsFrom;
+ protected List<AccessSection> local;
+ protected Set<String> ownerOf;
+
+ public ProjectAccess() {
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String name) {
+ revision = name;
+ }
+
+ public Project.NameKey getInheritsFrom() {
+ return inheritsFrom;
+ }
+
+ public void setInheritsFrom(Project.NameKey name) {
+ inheritsFrom = name;
+ }
+
+ public List<AccessSection> getLocal() {
+ return local;
+ }
+
+ public void setLocal(List<AccessSection> as) {
+ local = as;
+ }
+
+ public boolean isOwnerOf(AccessSection section) {
+ return getOwnerOf().contains(section.getRefPattern());
+ }
+
+ public Set<String> getOwnerOf() {
+ return ownerOf;
+ }
+
+ public void setOwnerOf(Set<String> refs) {
+ ownerOf = refs;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
index b5a986f..cd02785 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
@@ -15,10 +15,8 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import com.google.gwtjsonrpc.client.RpcImpl;
@@ -34,18 +32,17 @@
void projectDetail(Project.NameKey projectName,
AsyncCallback<ProjectDetail> callback);
+ void projectAccess(Project.NameKey projectName,
+ AsyncCallback<ProjectAccess> callback);
+
@SignInRequired
void changeProjectSettings(Project update,
AsyncCallback<ProjectDetail> callback);
@SignInRequired
- void deleteRight(Project.NameKey projectName, Set<RefRight.Key> ids,
- AsyncCallback<ProjectDetail> callback);
-
- @SignInRequired
- void addRight(Project.NameKey projectName, ApprovalCategory.Id categoryId,
- String groupName, String refName, short min, short max,
- AsyncCallback<ProjectDetail> callback);
+ void changeProjectAccess(Project.NameKey projectName, String baseRevision,
+ String message, List<AccessSection> sections,
+ AsyncCallback<ProjectAccess> callback);
void listBranches(Project.NameKey projectName,
AsyncCallback<ListBranchesResult> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java
index 2aa8c62..02aaf80 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java
@@ -14,16 +14,10 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.Project;
-import java.util.List;
-import java.util.Map;
-
public class ProjectDetail {
public Project project;
- public Map<AccountGroup.Id, AccountGroup> groups;
- public List<InheritedRefRight> rights;
public boolean canModifyDescription;
public boolean canModifyMergeType;
public boolean canModifyAgreements;
@@ -36,14 +30,6 @@
project = p;
}
- public void setGroups(final Map<AccountGroup.Id, AccountGroup> g) {
- groups = g;
- }
-
- public void setRights(final List<InheritedRefRight> r) {
- rights = r;
- }
-
public void setCanModifyDescription(final boolean cmd) {
canModifyDescription = cmd;
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
index 9dae169..976004f 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
@@ -14,7 +14,6 @@
package com.google.gerrit.common.data;
-import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.Project;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
@@ -32,5 +31,5 @@
AsyncCallback<List<AccountInfo>> callback);
void suggestAccountGroup(String query, int limit,
- AsyncCallback<List<AccountGroupName>> callback);
+ AsyncCallback<List<GroupReference>> callback);
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java
index 4e117b1..179bc3a 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java
@@ -26,10 +26,18 @@
this(key, null);
}
+ public NoSuchGroupException(final AccountGroup.UUID key) {
+ this(key, null);
+ }
+
public NoSuchGroupException(final AccountGroup.Id key, final Throwable why) {
super(MESSAGE + key.toString(), why);
}
+ public NoSuchGroupException(final AccountGroup.UUID key, final Throwable why) {
+ super(MESSAGE + key.toString(), why);
+ }
+
public NoSuchGroupException(final AccountGroup.NameKey k) {
this(k, null);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
index ec0f74f..d3d990c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<module rename-to="gerrit">
+ <inherits name='com.google.gwt.editor.Editor'/>
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.resources.Resources'/>
<inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 2b3ace4..8b82a30 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -87,6 +87,10 @@
return "admin,group," + id.toString();
}
+ public static String toGroup(final AccountGroup.UUID uuid) {
+ return "admin,group,uuid-" + uuid.toString();
+ }
+
public static String toProjectAdmin(final Project.NameKey n, final String tab) {
return "admin,project," + n.toString() + "," + tab;
}
@@ -411,6 +415,10 @@
private Screen select() {
String p;
+ p = "admin,group,uuid-";
+ if (token.startsWith(p))
+ return new AccountGroupScreen(AccountGroup.UUID.parse(skip(p, token)));
+
p = "admin,group,";
if (token.startsWith(p))
return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessRightEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessRightEditor.java
deleted file mode 100644
index 8f6831b..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessRightEditor.java
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright (C) 2010 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.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.HintTextBox;
-import com.google.gerrit.client.ui.RPCSuggestOracle;
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.ProjectDetail;
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.SuggestBox;
-
-public class AccessRightEditor extends Composite
- implements HasValueChangeHandlers<ProjectDetail> {
- private Project.NameKey projectKey;
- private ListBox catBox;
- private HintTextBox nameTxt;
- private SuggestBox nameSug;
- private HintTextBox referenceTxt;
- private ListBox topBox;
- private ListBox botBox;
- private Button addBut;
- private Button clearBut;
-
- public AccessRightEditor(final Project.NameKey key) {
- projectKey = key;
-
- initWidgets();
- initCategories();
-
- final Grid grid = new Grid(5, 2);
- grid.setText(0, 0, Util.C.columnApprovalCategory() + ":");
- grid.setWidget(0, 1, catBox);
-
- grid.setText(1, 0, Util.C.columnGroupName() + ":");
- grid.setWidget(1, 1, nameSug);
-
- grid.setText(2, 0, Util.C.columnRefName() + ":");
- grid.setWidget(2, 1, referenceTxt);
-
- grid.setText(3, 0, Util.C.columnRightRange() + ":");
- grid.setWidget(3, 1, topBox);
-
- grid.setText(4, 0, "");
- grid.setWidget(4, 1, botBox);
-
- FlowPanel fp = new FlowPanel();
- fp.setStyleName(Gerrit.RESOURCES.css().addSshKeyPanel());
-
- fp.add(grid);
- fp.add(addBut);
- fp.add(clearBut);
- initWidget(fp);
- }
-
- protected void initWidgets() {
- catBox = new ListBox();
- catBox.addChangeHandler(new ChangeHandler() {
- @Override
- public void onChange(final ChangeEvent event) {
- updateCategorySelection();
- }
- });
-
- nameTxt = new HintTextBox();
- nameSug = new SuggestBox(new RPCSuggestOracle(
- new AccountGroupSuggestOracle()), nameTxt);
- nameTxt.setVisibleLength(50);
- nameTxt.setHintText(Util.C.defaultAccountGroupName());
-
- referenceTxt = new HintTextBox();
- referenceTxt.setVisibleLength(50);
- referenceTxt.setText("");
- referenceTxt.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doAddNewRight();
- }
- }
- });
-
- topBox = new ListBox();
- botBox = new ListBox();
-
- addBut = new Button(Util.C.buttonAddProjectRight());
- addBut.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddNewRight();
- }
- });
-
- clearBut = new Button(Util.C.buttonClearProjectRight());
- clearBut.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- clear();
- }
- });
- }
-
- protected void initCategories() {
- for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes()
- .getApprovalTypes()) {
- final ApprovalCategory c = at.getCategory();
- catBox.addItem(c.getName(), c.getId().get());
- }
- for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes()
- .getActionTypes()) {
- final ApprovalCategory c = at.getCategory();
- if (Gerrit.getConfig().getWildProject().equals(projectKey)
- && !c.getId().canBeOnWildProject()) {
- // Giving out control of the WILD_PROJECT to other groups beyond
- // Administrators is dangerous. Having control over WILD_PROJECT
- // is about the same as having Administrator access as users are
- // able to affect grants in all projects on the system.
- //
- continue;
- }
- catBox.addItem(c.getName(), c.getId().get());
- }
-
- if (catBox.getItemCount() > 0) {
- catBox.setSelectedIndex(0);
- updateCategorySelection();
- }
- }
-
- public void enableForm(final boolean on) {
- final boolean canAdd = on && catBox.getItemCount() > 0;
- addBut.setEnabled(canAdd);
- clearBut.setEnabled(canAdd);
- nameTxt.setEnabled(canAdd);
- referenceTxt.setEnabled(canAdd);
- catBox.setEnabled(canAdd);
- topBox.setEnabled(canAdd);
- botBox.setEnabled(canAdd);
- }
-
- public void clear() {
- setCat(null);
- setName("");
- setReference("");
- }
-
- public void load(final RefRight right, final AccountGroup group) {
- final ApprovalType atype =
- Gerrit.getConfig().getApprovalTypes().getApprovalType(
- right.getApprovalCategoryId());
-
- setCat(atype != null ? atype.getCategory().getName()
- : right.getApprovalCategoryId().get() );
-
- setName(group.getName());
- setReference(right.getRefPatternForDisplay());
-
- setRange(atype.getCategory().isRange() ? atype.getValue(right.getMinValue())
- : null, atype.getValue(right.getMaxValue()) );
- }
-
- protected void doAddNewRight() {
- final ApprovalType at = getApprovalType();
- ApprovalCategoryValue min = getMin(at);
- ApprovalCategoryValue max = getMax(at);
-
- if (at == null || min == null || max == null) {
- return;
- }
-
- final String groupName = nameSug.getText();
- if ("".equals(groupName)
- || Util.C.defaultAccountGroupName().equals(groupName)) {
- return;
- }
-
- final String refPattern = referenceTxt.getText();
-
- addBut.setEnabled(false);
- Util.PROJECT_SVC.addRight(projectKey, at.getCategory().getId(),
- groupName, refPattern, min.getValue(), max.getValue(),
- new GerritCallback<ProjectDetail>() {
- public void onSuccess(final ProjectDetail result) {
- addBut.setEnabled(true);
- nameSug.setText("");
- referenceTxt.setText("");
- ValueChangeEvent.fire(AccessRightEditor.this, result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- addBut.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- protected void updateCategorySelection() {
- final ApprovalType at = getApprovalType();
-
- if (at == null || at.getValues().isEmpty()) {
- topBox.setEnabled(false);
- botBox.setEnabled(false);
- referenceTxt.setEnabled(false);
- addBut.setEnabled(false);
- clearBut.setEnabled(false);
- return;
- }
-
- updateRanges(at);
- }
-
- protected void updateRanges(final ApprovalType at) {
- ApprovalCategoryValue min = null, max = null, last = null;
-
- topBox.clear();
- botBox.clear();
-
- for(final ApprovalCategoryValue v : at.getValues()) {
- final int nval = v.getValue();
- final String vStr = String.valueOf(nval);
-
- String nStr = vStr + ": " + v.getName();
- if (nval > 0) {
- nStr = "+" + nStr;
- }
-
- topBox.addItem(nStr, vStr);
- botBox.addItem(nStr, vStr);
-
- if (min == null || nval < 0) {
- min = v;
- } else if (max == null && nval > 0) {
- max = v;
- }
- last = v;
- }
-
- if (max == null) {
- max = last;
- }
-
- if (ApprovalCategory.READ.equals(at.getCategory().getId())) {
- // Special case; for READ the most logical range is just
- // +1 READ, so assume that as the default for both.
- min = max;
- }
-
- if (! at.getCategory().isRange()) {
- max = null;
- }
-
- setRange(min, max);
- }
-
- protected void setCat(final String cat) {
- if (cat == null) {
- catBox.setSelectedIndex(0);
- } else {
- setSelectedText(catBox, cat);
- }
- updateCategorySelection();
- }
-
- protected void setName(final String name) {
- nameTxt.setText(name);
- }
-
- protected void setReference(final String ref) {
- referenceTxt.setText(ref);
- }
-
- protected void setRange(final ApprovalCategoryValue min,
- final ApprovalCategoryValue max) {
- if (min == null || max == null) {
- botBox.setVisible(false);
- if (max != null) {
- setSelectedValue(topBox, "" + max.getValue());
- return;
- }
- } else {
- botBox.setVisible(true);
- setSelectedValue(botBox, "" + max.getValue());
- }
- setSelectedValue(topBox, "" + min.getValue());
- }
-
- private ApprovalType getApprovalType() {
- int idx = catBox.getSelectedIndex();
- if (idx < 0) {
- return null;
- }
- return Gerrit.getConfig().getApprovalTypes().getApprovalType(
- new ApprovalCategory.Id(catBox.getValue(idx)));
- }
-
- public ApprovalCategoryValue getMin(ApprovalType at) {
- final ApprovalCategoryValue top = getTop(at);
- final ApprovalCategoryValue bot = getBot(at);
- if (bot == null) {
- for (final ApprovalCategoryValue v : at.getValues()) {
- if (0 <= v.getValue() && v.getValue() <= top.getValue()) {
- return v;
- }
- }
- return at.getMin();
- }
-
- if (top.getValue() > bot.getValue()) {
- return bot;
- }
- return top;
- }
-
- public ApprovalCategoryValue getMax(ApprovalType at) {
- final ApprovalCategoryValue top = getTop(at);
- final ApprovalCategoryValue bot = getBot(at);
- if (bot == null || bot.getValue() < top.getValue()) {
- return top;
- }
- return bot;
- }
-
- protected ApprovalCategoryValue getTop(ApprovalType at) {
- int idx = topBox.getSelectedIndex();
- if (idx < 0) {
- return null;
- }
- return at.getValue(Short.parseShort(topBox.getValue(idx)));
- }
-
- protected ApprovalCategoryValue getBot(ApprovalType at) {
- int idx = botBox.getSelectedIndex();
- if (idx < 0 || ! botBox.isVisible()) {
- return null;
- }
- return at.getValue(Short.parseShort(botBox.getValue(idx)));
- }
-
- public HandlerRegistration addValueChangeHandler(
- final ValueChangeHandler<ProjectDetail> handler) {
- return addHandler(handler, ValueChangeEvent.getType());
- }
-
- public static boolean setSelectedText(ListBox box, String text) {
- if (text == null) {
- return false;
- }
- for (int i =0 ; i < box.getItemCount(); i++) {
- if (text.equals(box.getItemText(i))) {
- box.setSelectedIndex(i);
- return true;
- }
- }
- return false;
- }
-
- public static boolean setSelectedValue(ListBox box, String value) {
- if (value == null) {
- return false;
- }
- for (int i =0 ; i < box.getItemCount(); i++) {
- if (value.equals(box.getValue(i))) {
- box.setSelectedIndex(i);
- return true;
- }
- }
- return false;
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
new file mode 100644
index 0000000..fdcaa00
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -0,0 +1,238 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.ProjectAccess;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.EditorDelegate;
+import com.google.gwt.editor.client.ValueAwareEditor;
+import com.google.gwt.editor.client.adapters.EditorSource;
+import com.google.gwt.editor.client.adapters.ListEditor;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.ValueListBox;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccessSectionEditor extends Composite implements
+ Editor<AccessSection>, ValueAwareEditor<AccessSection> {
+ interface Binder extends UiBinder<HTMLPanel, AccessSectionEditor> {
+ }
+
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ @UiField
+ ValueEditor<String> refPattern;
+
+ @UiField
+ FlowPanel permissionContainer;
+ ListEditor<Permission, PermissionEditor> permissions;
+
+ @UiField
+ DivElement addContainer;
+ @UiField(provided = true)
+ @Editor.Ignore
+ ValueListBox<String> permissionSelector;
+
+ @UiField
+ SpanElement deletedName;
+
+ @UiField
+ Anchor deleteSection;
+
+ @UiField
+ DivElement normal;
+ @UiField
+ DivElement deleted;
+
+ private final ProjectAccess projectAccess;
+ private AccessSection value;
+ private boolean editing;
+ private boolean readOnly;
+ private boolean isDeleted;
+
+ public AccessSectionEditor(ProjectAccess access) {
+ projectAccess = access;
+
+ permissionSelector =
+ new ValueListBox<String>(PermissionNameRenderer.INSTANCE);
+ permissionSelector.addValueChangeHandler(new ValueChangeHandler<String>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<String> event) {
+ if (!Util.C.addPermission().equals(event.getValue())) {
+ onAddPermission(event.getValue());
+ }
+ }
+ });
+
+ initWidget(uiBinder.createAndBindUi(this));
+ permissions = ListEditor.of(new PermissionEditorSource());
+ }
+
+ @UiHandler("deleteSection")
+ void onDeleteHover(MouseOverEvent event) {
+ normal.addClassName(AdminResources.I.css().deleteSectionHover());
+ }
+
+ @UiHandler("deleteSection")
+ void onDeleteNonHover(MouseOutEvent event) {
+ normal.removeClassName(AdminResources.I.css().deleteSectionHover());
+ }
+
+ @UiHandler("deleteSection")
+ void onDeleteSection(ClickEvent event) {
+ isDeleted = true;
+ deletedName.setInnerText(refPattern.getValue());
+ normal.getStyle().setDisplay(Display.NONE);
+ deleted.getStyle().setDisplay(Display.BLOCK);
+ }
+
+ @UiHandler("undoDelete")
+ void onUndoDelete(ClickEvent event) {
+ isDeleted = false;
+ deleted.getStyle().setDisplay(Display.NONE);
+ normal.getStyle().setDisplay(Display.BLOCK);
+ }
+
+ void onAddPermission(String varName) {
+ Permission p = value.getPermission(varName, true);
+ permissions.getList().add(p);
+ rebuildPermissionSelector();
+ }
+
+ void editRefPattern() {
+ refPattern.edit();
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ refPattern.setFocus(true);
+ }});
+ }
+
+ void enableEditing() {
+ readOnly = false;
+ addContainer.getStyle().setDisplay(Display.BLOCK);
+ rebuildPermissionSelector();
+ }
+
+ boolean isDeleted() {
+ return isDeleted;
+ }
+
+ @Override
+ public void setValue(AccessSection value) {
+ this.value = value;
+ this.readOnly = !editing || !projectAccess.isOwnerOf(value);
+
+ refPattern.setEnabled(!readOnly);
+ deleteSection.setVisible(!readOnly);
+
+ if (readOnly) {
+ addContainer.getStyle().setDisplay(Display.NONE);
+ } else {
+ enableEditing();
+ }
+ }
+
+ void setEditing(final boolean editing) {
+ this.editing = editing;
+ }
+
+ private void rebuildPermissionSelector() {
+ List<String> perms = new ArrayList<String>();
+ for (ApprovalType t : Gerrit.getConfig().getApprovalTypes()
+ .getApprovalTypes()) {
+ String varName = Permission.LABEL + t.getCategory().getLabelName();
+ if (value.getPermission(varName) == null) {
+ perms.add(varName);
+ }
+ }
+ for (String varName : Util.C.permissionNames().keySet()) {
+ if (value.getPermission(varName) == null) {
+ perms.add(varName);
+ }
+ }
+ if (perms.isEmpty()) {
+ addContainer.getStyle().setDisplay(Display.NONE);
+ } else {
+ addContainer.getStyle().setDisplay(Display.BLOCK);
+ perms.add(0, Util.C.addPermission());
+ permissionSelector.setValue(Util.C.addPermission());
+ permissionSelector.setAcceptableValues(perms);
+ }
+ }
+
+ @Override
+ public void flush() {
+ List<Permission> src = permissions.getList();
+ List<Permission> keep = new ArrayList<Permission>(src.size());
+
+ for (int i = 0; i < src.size(); i++) {
+ PermissionEditor e = (PermissionEditor) permissionContainer.getWidget(i);
+ if (!e.isDeleted()) {
+ keep.add(src.get(i));
+ }
+ }
+ value.setPermissions(keep);
+ }
+
+ @Override
+ public void onPropertyChange(String... paths) {
+ }
+
+ @Override
+ public void setDelegate(EditorDelegate<AccessSection> delegate) {
+ }
+
+ private class PermissionEditorSource extends EditorSource<PermissionEditor> {
+ @Override
+ public PermissionEditor create(int index) {
+ PermissionEditor subEditor = new PermissionEditor(readOnly, value);
+ permissionContainer.insert(subEditor, index);
+ return subEditor;
+ }
+
+ @Override
+ public void dispose(PermissionEditor subEditor) {
+ subEditor.removeFromParent();
+ }
+
+ @Override
+ public void setIndex(PermissionEditor subEditor, int index) {
+ permissionContainer.insert(subEditor, index);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml
new file mode 100644
index 0000000..fdae4ed
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2011 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:e='urn:import:com.google.gwt.editor.ui.client'
+ xmlns:my='urn:import:com.google.gerrit.client.admin'
+ ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
+ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
+ ui:generateLocales='default,en'
+ >
+<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
+<ui:style>
+ @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
+ @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
+
+ .panel {
+ position: relative;
+ }
+
+ .content {
+ margin-top: 4px;
+ margin-bottom: 4px;
+ padding-bottom: 2px;
+ }
+
+ .normal {
+ background-color: trimColor;
+ }
+
+ .deleted {
+ padding-left: 7px;
+ padding-bottom: 2px;
+ }
+
+ .header {
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+ .headerText {
+ vertical-align: top;
+ white-space: nowrap;
+ font-weight: bold;
+ }
+ .headerTable {
+ border: 0;
+ width: 100%;
+ padding-right: 40px;
+ }
+
+ .header:hover {
+ background-color: selectionColor;
+ }
+
+ .refName {
+ width: 100%;
+ }
+ .refNameEdit {
+ width: 100%;
+ }
+
+ .permissionList {
+ margin-left: 5px;
+ margin-right: 5px;
+ }
+
+ .addContainer {
+ padding-left: 16px;
+ padding-right: 16px;
+ font-size: 80%;
+ }
+ .addContainer:hover {
+ background-color: selectionColor;
+ }
+ .addSelector {
+ font-size: 80%;
+ }
+
+ .deleteIcon {
+ position: absolute;
+ top: 5px;
+ right: 17px;
+ }
+
+ .undoIcon {
+ position: absolute;
+ top: 2px;
+ right: 17px;
+ }
+</ui:style>
+
+<g:HTMLPanel styleName='{style.panel}'>
+<div ui:field='normal' class='{style.normal} {style.content}'>
+ <div class='{style.header}'>
+ <table class='{style.headerTable}'><tr>
+ <td class='{style.headerText}'><ui:msg>Reference:</ui:msg></td>
+ <td width='100%'>
+ <my:ValueEditor
+ ui:field='refPattern'
+ addStyleNames='{style.refName}'
+ editTitle='Edit reference pattern'>
+ <ui:attribute name='editTitle'/>
+ <my:editor>
+ <my:RefPatternBox styleName='{style.refNameEdit}'/>
+ </my:editor>
+ </my:ValueEditor>
+ </td>
+ </tr></table>
+
+ <g:Anchor
+ ui:field='deleteSection'
+ href='javascript:void'
+ styleName='{style.deleteIcon} {res.css.deleteIcon}'
+ title='Delete this section (and nested rules)'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+ </div>
+
+ <g:FlowPanel
+ ui:field='permissionContainer'
+ styleName='{style.permissionList}'/>
+ <div ui:field='addContainer' class='{style.addContainer}'>
+ <g:ValueListBox
+ ui:field='permissionSelector'
+ styleName='{style.addSelector}' />
+ </div>
+</div>
+
+<div
+ ui:field='deleted'
+ class='{style.deleted} {res.css.deleted}'
+ style='display: none'>
+ <ui:msg>Reference <span ui:field='deletedName'/> was deleted</ui:msg>
+ <g:Anchor
+ ui:field='undoDelete'
+ href='javascript:void'
+ styleName='{style.undoIcon} {res.css.undoIcon}'
+ title='Undo deletion'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+</div>
+</g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
index 593ab34..9f8117a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
@@ -63,7 +63,9 @@
import java.util.List;
public class AccountGroupScreen extends AccountScreen {
- private final AccountGroup.Id groupId;
+ private AccountGroup.Id groupId;
+ private AccountGroup.UUID groupUUID;
+
private AccountInfoCache accounts = AccountInfoCache.empty();
private GroupInfoCache groups = GroupInfoCache.empty();
private MemberTable members;
@@ -106,24 +108,31 @@
groupId = toShow;
}
+ public AccountGroupScreen(final AccountGroup.UUID toShow) {
+ groupUUID = toShow;
+ }
+
@Override
protected void onLoad() {
super.onLoad();
- Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback<GroupDetail>(
- this) {
- @Override
- protected void preDisplay(final GroupDetail result) {
- enableForm(result.canModify);
- saveName.setVisible(result.canModify);
- saveOwner.setVisible(result.canModify);
- saveDesc.setVisible(result.canModify);
- saveGroupOptions.setVisible(result.canModify);
- delMember.setVisible(result.canModify);
- saveType.setVisible(result.canModify);
- delInclude.setVisible(result.canModify);
- display(result);
- }
- });
+ Util.GROUP_SVC.groupDetail(groupId, groupUUID,
+ new ScreenLoadCallback<GroupDetail>(this) {
+ @Override
+ protected void preDisplay(final GroupDetail result) {
+ groupId = result.group.getId();
+ groupUUID = result.group.getGroupUUID();
+ display(result);
+
+ enableForm(result.canModify);
+ saveName.setVisible(result.canModify);
+ saveOwner.setVisible(result.canModify);
+ saveDesc.setVisible(result.canModify);
+ saveGroupOptions.setVisible(result.canModify);
+ delMember.setVisible(result.canModify);
+ saveType.setVisible(result.canModify);
+ delInclude.setVisible(result.canModify);
+ }
+ });
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index 4631a29..4f95a70 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -16,6 +16,8 @@
import com.google.gwt.i18n.client.Constants;
+import java.util.Map;
+
public interface AdminConstants extends Constants {
String defaultAccountName();
String defaultAccountGroupName();
@@ -101,4 +103,14 @@
String noGroupSelected();
String errorNoMatchingGroups();
String errorNoGitRepository();
+
+ String addPermission();
+ Map<String,String> permissionNames();
+
+ String refErrorEmpty();
+ String refErrorBeginSlash();
+ String refErrorDoubleSlash();
+ String refErrorNoSpace();
+ String refErrorPrintable();
+ String errorsMustBeFixed();
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 4bad368..e9575cf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -83,3 +83,36 @@
noGroupSelected = (No group selected)
errorNoMatchingGroups = No Matching Groups
errorNoGitRepository = No Git Repository
+
+
+addPermission = Add Permission ...
+
+# Permission Names
+permissionNames = \
+ create, \
+ forgeAuthor, \
+ forgeCommitter, \
+ forgeServerAsCommitter, \
+ owner, \
+ push, \
+ pushMerge, \
+ pushTag, \
+ read, \
+ submit
+create = Create Reference
+forgeAuthor = Forge Author Identity
+forgeCommitter = Forge Committer Identity
+forgeServerAsCommitter = Forge Server Identity
+owner = Owner
+push = Push
+pushMerge = Push Merge Commit
+pushTag = Push Annotated Tag
+read = Read
+submit = Submit
+
+refErrorEmpty = Reference must be supplied
+refErrorBeginSlash = Reference must not start with '/'
+refErrorDoubleSlash = References cannot contain '//'
+refErrorNoSpace = References cannot contain spaces
+refErrorPrintable = References may contain only printable characters
+errorsMustBeFixed = Errors must be fixed before committing changes.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java
similarity index 69%
rename from gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java
rename to gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java
index 0977ee9..55a9bf3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java
@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.schema;
+package com.google.gerrit.client.admin;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
+import com.google.gwt.resources.client.CssResource;
-public class Schema_49 extends SchemaVersion {
+public interface AdminCss extends CssResource {
+ String deleteIcon();
+ String undoIcon();
- @Inject
- Schema_49(Provider<Schema_48> prior) {
- super(prior);
- }
+ String deleted();
+ String deletedBorder();
+
+ String deleteSectionHover();
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
index ab3541e..9ce3ccf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
@@ -18,6 +18,7 @@
public interface AdminMessages extends Messages {
String group(String name);
+ String label(String name);
String project(String name);
String deletedGroup(int id);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
index 6feb69a..60d9c70 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
@@ -1,3 +1,4 @@
group = Group {0}
+label = Label {0}
project = Project {0}
deletedGroup = Deleted Group {0}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java
new file mode 100644
index 0000000..cd366f3
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.ImageResource;
+
+public interface AdminResources extends ClientBundle {
+ public static final AdminResources I = GWT.create(AdminResources.class);
+
+ @Source("admin.css")
+ AdminCss css();
+
+ @Source("editText.png")
+ public ImageResource editText();
+
+ @Source("deleteNormal.png")
+ public ImageResource deleteNormal();
+
+ @Source("deleteHover.png")
+ public ImageResource deleteHover();
+
+ @Source("undoNormal.png")
+ public ImageResource undoNormal();
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
new file mode 100644
index 0000000..b51f0c0
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
@@ -0,0 +1,143 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
+import com.google.gerrit.client.ui.RPCSuggestOracle;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.event.logical.shared.HasCloseHandlers;
+import com.google.gwt.event.logical.shared.HasSelectionHandlers;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Focusable;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
+import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+public class GroupReferenceBox extends Composite implements
+ LeafValueEditor<GroupReference>, HasSelectionHandlers<GroupReference>,
+ HasCloseHandlers<GroupReferenceBox>, Focusable {
+ private final DefaultSuggestionDisplay suggestions;
+ private final NpTextBox textBox;
+ private final AccountGroupSuggestOracle oracle;
+ private final SuggestBox suggestBox;
+
+ private boolean submitOnSelection;
+
+ public GroupReferenceBox() {
+ suggestions = new DefaultSuggestionDisplay();
+ textBox = new NpTextBox();
+ oracle = new AccountGroupSuggestOracle();
+ suggestBox = new SuggestBox( //
+ new RPCSuggestOracle(oracle), //
+ textBox, //
+ suggestions);
+ initWidget(suggestBox);
+
+ suggestBox.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ submitOnSelection = false;
+
+ if (event.getCharCode() == KeyCodes.KEY_ENTER) {
+ if (suggestions.isSuggestionListShowing()) {
+ submitOnSelection = true;
+ } else {
+ SelectionEvent.fire(GroupReferenceBox.this, getValue());
+ }
+ }
+ }
+ });
+ suggestBox.addKeyUpHandler(new KeyUpHandler() {
+ @Override
+ public void onKeyUp(KeyUpEvent event) {
+ if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
+ suggestBox.setText("");
+ CloseEvent.fire(GroupReferenceBox.this, GroupReferenceBox.this);
+ }
+ }
+ });
+ suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() {
+ @Override
+ public void onSelection(SelectionEvent<Suggestion> event) {
+ if (submitOnSelection) {
+ submitOnSelection = false;
+ SelectionEvent.fire(GroupReferenceBox.this, getValue());
+ }
+ }
+ });
+ }
+
+ public void setVisibleLength(int len) {
+ textBox.setVisibleLength(len);
+ }
+
+ @Override
+ public HandlerRegistration addSelectionHandler(
+ SelectionHandler<GroupReference> handler) {
+ return addHandler(handler, SelectionEvent.getType());
+ }
+
+ @Override
+ public HandlerRegistration addCloseHandler(
+ CloseHandler<GroupReferenceBox> handler) {
+ return addHandler(handler, CloseEvent.getType());
+ }
+
+ @Override
+ public GroupReference getValue() {
+ String name = suggestBox.getText();
+ if (name != null && !name.isEmpty()) {
+ return new GroupReference(oracle.getUUID(name), name);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void setValue(GroupReference value) {
+ suggestBox.setText(value != null ? value.getName() : "");
+ }
+
+ @Override
+ public int getTabIndex() {
+ return suggestBox.getTabIndex();
+ }
+
+ @Override
+ public void setTabIndex(int index) {
+ suggestBox.setTabIndex(index);
+ }
+
+ public void setFocus(boolean focused) {
+ suggestBox.setFocus(focused);
+ }
+
+ @Override
+ public void setAccessKey(char key) {
+ suggestBox.setAccessKey(key);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
new file mode 100644
index 0000000..c21844c
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
@@ -0,0 +1,297 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.SuggestUtil;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.EditorDelegate;
+import com.google.gwt.editor.client.ValueAwareEditor;
+import com.google.gwt.editor.client.adapters.EditorSource;
+import com.google.gwt.editor.client.adapters.ListEditor;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.MouseOutEvent;
+import com.google.gwt.event.dom.client.MouseOverEvent;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.ValueLabel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PermissionEditor extends Composite implements Editor<Permission>,
+ ValueAwareEditor<Permission> {
+ interface Binder extends UiBinder<HTMLPanel, PermissionEditor> {
+ }
+
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ @UiField(provided = true)
+ @Path("name")
+ ValueLabel<String> normalName;
+
+ @UiField(provided = true)
+ @Path("name")
+ ValueLabel<String> deletedName;
+
+ @UiField
+ CheckBox exclusiveGroup;
+
+ @UiField
+ FlowPanel ruleContainer;
+ ListEditor<PermissionRule, PermissionRuleEditor> rules;
+
+ @UiField
+ DivElement addContainer;
+ @UiField
+ DivElement addStage1;
+ @UiField
+ DivElement addStage2;
+ @UiField
+ Anchor beginAddRule;
+ @UiField
+ @Editor.Ignore
+ GroupReferenceBox groupToAdd;
+ @UiField
+ Button addRule;
+
+ @UiField
+ Anchor deletePermission;
+
+ @UiField
+ DivElement normal;
+ @UiField
+ DivElement deleted;
+
+ private final boolean readOnly;
+ private final AccessSection section;
+ private Permission value;
+ private ApprovalType rangeType;
+ private boolean isDeleted;
+
+ public PermissionEditor(boolean readOnly, AccessSection section) {
+ this.readOnly = readOnly;
+ this.section = section;
+
+ normalName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE);
+ deletedName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE);
+
+ initWidget(uiBinder.createAndBindUi(this));
+ rules = ListEditor.of(new RuleEditorSource());
+
+ exclusiveGroup.setEnabled(!readOnly);
+
+ if (readOnly) {
+ addContainer.removeFromParent();
+ addContainer = null;
+
+ deletePermission.removeFromParent();
+ deletePermission = null;
+ }
+ }
+
+ @UiHandler("deletePermission")
+ void onDeleteHover(MouseOverEvent event) {
+ addStyleName(AdminResources.I.css().deleteSectionHover());
+ }
+
+ @UiHandler("deletePermission")
+ void onDeleteNonHover(MouseOutEvent event) {
+ removeStyleName(AdminResources.I.css().deleteSectionHover());
+ }
+
+ @UiHandler("deletePermission")
+ void onDeletePermission(ClickEvent event) {
+ isDeleted = true;
+ normal.getStyle().setDisplay(Display.NONE);
+ deleted.getStyle().setDisplay(Display.BLOCK);
+ }
+
+ @UiHandler("undoDelete")
+ void onUndoDelete(ClickEvent event) {
+ isDeleted = false;
+ deleted.getStyle().setDisplay(Display.NONE);
+ normal.getStyle().setDisplay(Display.BLOCK);
+ }
+
+ @UiHandler("beginAddRule")
+ void onBeginAddRule(ClickEvent event) {
+ addStage1.getStyle().setDisplay(Display.NONE);
+ addStage2.getStyle().setDisplay(Display.BLOCK);
+
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ groupToAdd.setFocus(true);
+ }
+ });
+ }
+
+ @UiHandler("addRule")
+ void onAddGroupByClick(ClickEvent event) {
+ GroupReference ref = groupToAdd.getValue();
+ if (ref != null) {
+ addGroup(ref);
+ } else {
+ groupToAdd.setFocus(true);
+ }
+ }
+
+ @UiHandler("groupToAdd")
+ void onAddGroupByEnter(SelectionEvent<GroupReference> event) {
+ GroupReference ref = event.getSelectedItem();
+ if (ref != null) {
+ addGroup(ref);
+ }
+ }
+
+ @UiHandler("groupToAdd")
+ void onAbortAddGroup(CloseEvent<GroupReferenceBox> event) {
+ hideAddGroup();
+ }
+
+ @UiHandler("hideAddGroup")
+ void hideAddGroup(ClickEvent event) {
+ hideAddGroup();
+ }
+
+ private void hideAddGroup() {
+ addStage1.getStyle().setDisplay(Display.BLOCK);
+ addStage2.getStyle().setDisplay(Display.NONE);
+ }
+
+ private void addGroup(GroupReference ref) {
+ if (ref.getUUID() != null) {
+ if (value.getRule(ref) == null) {
+ PermissionRule newRule = value.getRule(ref, true);
+ if (rangeType != null) {
+ int min = rangeType.getMin().getValue();
+ int max = rangeType.getMax().getValue();
+ newRule.setRange(min, max);
+ }
+ rules.getList().add(newRule);
+ }
+ groupToAdd.setValue(null);
+ groupToAdd.setFocus(true);
+
+ } else {
+ // If the oracle didn't get to complete a UUID, resolve it now.
+ //
+ addRule.setEnabled(false);
+ SuggestUtil.SVC.suggestAccountGroup(ref.getName(), 1,
+ new GerritCallback<List<GroupReference>>() {
+ @Override
+ public void onSuccess(List<GroupReference> result) {
+ addRule.setEnabled(true);
+ if (result.size() == 1) {
+ addGroup(result.get(0));
+ } else {
+ groupToAdd.setFocus(true);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ addRule.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+ }
+
+ boolean isDeleted() {
+ return isDeleted;
+ }
+
+ @Override
+ public void setValue(Permission value) {
+ this.value = value;
+ if (value.isLabel()) {
+ rangeType =
+ Gerrit.getConfig().getApprovalTypes().byLabel(value.getLabel());
+ } else {
+ rangeType = null;
+ }
+
+ if (value != null && Permission.OWNER.equals(value.getName())) {
+ exclusiveGroup.setEnabled(false);
+ } else {
+ exclusiveGroup.setEnabled(!readOnly);
+ }
+ }
+
+ @Override
+ public void flush() {
+ List<PermissionRule> src = rules.getList();
+ List<PermissionRule> keep = new ArrayList<PermissionRule>(src.size());
+
+ for (int i = 0; i < src.size(); i++) {
+ PermissionRuleEditor e =
+ (PermissionRuleEditor) ruleContainer.getWidget(i);
+ if (!e.isDeleted()) {
+ keep.add(src.get(i));
+ }
+ }
+ value.setRules(keep);
+ }
+
+ @Override
+ public void onPropertyChange(String... paths) {
+ }
+
+ @Override
+ public void setDelegate(EditorDelegate<Permission> delegate) {
+ }
+
+ private class RuleEditorSource extends EditorSource<PermissionRuleEditor> {
+ @Override
+ public PermissionRuleEditor create(int index) {
+ PermissionRuleEditor subEditor =
+ new PermissionRuleEditor(readOnly, section, value, rangeType);
+ ruleContainer.insert(subEditor, index);
+ return subEditor;
+ }
+
+ @Override
+ public void dispose(PermissionRuleEditor subEditor) {
+ subEditor.removeFromParent();
+ }
+
+ @Override
+ public void setIndex(PermissionRuleEditor subEditor, int index) {
+ ruleContainer.insert(subEditor, index);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml
new file mode 100644
index 0000000..25995d9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2011 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:e='urn:import:com.google.gwt.editor.ui.client'
+ xmlns:my='urn:import:com.google.gerrit.client.admin'
+ ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
+ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
+ ui:generateLocales='default,en'
+ >
+<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
+<ui:style>
+ @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
+ @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
+
+ .panel {
+ position: relative;
+ }
+
+ .normal {
+ border: 1px solid backgroundColor;
+ margin-top: -1px;
+ margin-bottom: -1px;
+ }
+
+ .header {
+ padding-left: 5px;
+ padding-right: 5px;
+ padding-bottom: 1px;
+ white-space: nowrap;
+ }
+
+ .header:hover {
+ background-color: selectionColor;
+ }
+
+ .name {
+ font-style: italic;
+ }
+
+ .exclusiveGroup {
+ position: absolute;
+ top: 0;
+ right: 36px;
+ width: 7em;
+ font-size: 80%;
+ }
+
+ .addContainer {
+ padding-left: 10px;
+ position: relative;
+ }
+ .addContainer:hover {
+ background-color: selectionColor;
+ }
+ .addLink {
+ font-size: 80%;
+ }
+
+ .deleteIcon {
+ position: absolute;
+ top: 1px;
+ right: 12px;
+ }
+</ui:style>
+
+<g:HTMLPanel stylePrimaryName='{style.panel}'>
+<div ui:field='normal' class='{style.normal}'>
+ <div class='{style.header}'>
+ <g:ValueLabel styleName='{style.name}' ui:field='normalName'/>
+ <g:CheckBox
+ ui:field='exclusiveGroup'
+ addStyleNames='{style.exclusiveGroup}'
+ text='Exclusive'>
+ <ui:attribute name='text'/>
+ </g:CheckBox>
+ <g:Anchor
+ ui:field='deletePermission'
+ href='javascript:void'
+ styleName='{style.deleteIcon} {res.css.deleteIcon}'
+ title='Delete this permission (and nested rules)'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+ </div>
+ <g:FlowPanel ui:field='ruleContainer'/>
+ <div ui:field='addContainer' class='{style.addContainer}'>
+ <div ui:field='addStage1'>
+ <g:Anchor
+ ui:field='beginAddRule'
+ styleName='{style.addLink}'
+ href='javascript:void'
+ text='Add Group'>
+ <ui:attribute name='text'/>
+ </g:Anchor>
+ </div>
+ <div ui:field='addStage2' style='display: none'>
+ <ui:msg>Group Name: <my:GroupReferenceBox
+ ui:field='groupToAdd'
+ visibleLength='45'/></ui:msg>
+ <g:Button
+ ui:field='addRule'
+ text='Add'>
+ <ui:attribute name='text'/>
+ </g:Button>
+ <g:Anchor
+ ui:field='hideAddGroup'
+ href='javascript:void'
+ styleName='{style.deleteIcon} {res.css.deleteIcon}'
+ title='Cancel additional group'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+ </div>
+ </div>
+</div>
+
+<div
+ ui:field='deleted'
+ class='{res.css.deleted} {res.css.deletedBorder}'
+ style='display: none'>
+ <ui:msg>Permission <g:ValueLabel styleName='{style.name}' ui:field='deletedName'/> was deleted</ui:msg>
+ <g:Anchor
+ ui:field='undoDelete'
+ href='javascript:void'
+ styleName='{style.deleteIcon} {res.css.undoIcon}'
+ title='Undo deletion'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+</div>
+</g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java
new file mode 100644
index 0000000..1dea1fb
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gerrit.common.data.Permission;
+import com.google.gwt.text.shared.Renderer;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+class PermissionNameRenderer implements Renderer<String> {
+ static final PermissionNameRenderer INSTANCE = new PermissionNameRenderer();
+
+ private static Map<String, String> LC;
+
+ @Override
+ public String render(String varName) {
+ if (Permission.isLabel(varName)) {
+ return Util.M.label(new Permission(varName).getLabel());
+ }
+
+ Map<String, String> m = Util.C.permissionNames();
+ String desc = m.get(varName);
+ if (desc == null) {
+ if (LC == null) {
+ LC = new HashMap<String, String>();
+ for (Map.Entry<String, String> e : m.entrySet()) {
+ LC.put(e.getKey().toLowerCase(), e.getValue());
+ }
+ }
+ desc = LC.get(varName.toLowerCase());
+ }
+ return desc != null ? desc : varName;
+ }
+
+ @Override
+ public void render(String object, Appendable appendable) throws IOException {
+ appendable.append(render(object));
+ }
+}
\ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
new file mode 100644
index 0000000..12b71c985
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
@@ -0,0 +1,200 @@
+// Copyright (C) 2011 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.client.admin;
+
+import static com.google.gerrit.common.data.Permission.PUSH;
+import static com.google.gerrit.common.data.Permission.PUSH_TAG;
+
+import com.google.gerrit.client.Dispatcher;
+import com.google.gerrit.client.ui.Hyperlink;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.EditorDelegate;
+import com.google.gwt.editor.client.ValueAwareEditor;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.text.shared.Renderer;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.ValueListBox;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class PermissionRuleEditor extends Composite implements
+ Editor<PermissionRule>, ValueAwareEditor<PermissionRule> {
+ interface Binder extends UiBinder<HTMLPanel, PermissionRuleEditor> {
+ }
+
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ @UiField(provided = true)
+ ValueListBox<PermissionRule.Action> action;
+
+ @UiField(provided = true)
+ ValueListBox<Integer> min;
+
+ @UiField(provided = true)
+ ValueListBox<Integer> max;
+
+ @UiField
+ CheckBox force;
+
+ @UiField
+ Hyperlink normalGroupName;
+ @UiField
+ SpanElement deletedGroupName;
+
+ @UiField
+ Anchor deleteRule;
+
+ @UiField
+ DivElement normal;
+ @UiField
+ DivElement deleted;
+
+ @UiField
+ SpanElement rangeEditor;
+
+ private boolean isDeleted;
+
+ public PermissionRuleEditor(boolean readOnly, AccessSection section,
+ Permission permission, ApprovalType labelRange) {
+ action = new ValueListBox<PermissionRule.Action>(actionRenderer);
+ min = new ValueListBox<Integer>(rangeRenderer);
+ max = new ValueListBox<Integer>(rangeRenderer);
+
+ if (labelRange != null){
+ min.setValue((int) labelRange.getMin().getValue());
+ max.setValue((int) labelRange.getMax().getValue());
+
+ min.setAcceptableValues(labelRange.getValuesAsList());
+ max.setAcceptableValues(labelRange.getValuesAsList());
+ } else {
+ action.setValue(PermissionRule.Action.ALLOW);
+ action.setAcceptableValues(Arrays.asList(PermissionRule.Action.values()));
+ }
+
+ initWidget(uiBinder.createAndBindUi(this));
+
+ String name = permission.getName();
+ boolean canForce = PUSH.equals(name) || PUSH_TAG.equals(name);
+ if (canForce) {
+ String ref = section.getRefPattern();
+ canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/");
+ }
+ force.setVisible(canForce);
+ force.setEnabled(!readOnly);
+
+ if (labelRange != null) {
+ action.getElement().getStyle().setDisplay(Display.NONE);
+ DOM.setElementPropertyBoolean(min.getElement(), "disabled", readOnly);
+ DOM.setElementPropertyBoolean(max.getElement(), "disabled", readOnly);
+ } else {
+ rangeEditor.getStyle().setDisplay(Display.NONE);
+ DOM.setElementPropertyBoolean(action.getElement(), "disabled", readOnly);
+ }
+
+ if (readOnly) {
+ deleteRule.removeFromParent();
+ deleteRule = null;
+ }
+ }
+
+ boolean isDeleted() {
+ return isDeleted;
+ }
+
+ @UiHandler("deleteRule")
+ void onDeleteRule(ClickEvent event) {
+ isDeleted = true;
+ normal.getStyle().setDisplay(Display.NONE);
+ deleted.getStyle().setDisplay(Display.BLOCK);
+ }
+
+ @UiHandler("undoDelete")
+ void onUndoDelete(ClickEvent event) {
+ isDeleted = false;
+ deleted.getStyle().setDisplay(Display.NONE);
+ normal.getStyle().setDisplay(Display.BLOCK);
+ }
+
+ @Override
+ public void setValue(PermissionRule value) {
+ GroupReference ref = value.getGroup();
+ normalGroupName.setTargetHistoryToken(Dispatcher.toGroup(ref.getUUID()));
+ normalGroupName.setText(ref.getName());
+ deletedGroupName.setInnerText(ref.getName());
+ }
+
+ @Override
+ public void setDelegate(EditorDelegate<PermissionRule> delegate) {
+ }
+
+ @Override
+ public void flush() {
+ }
+
+ @Override
+ public void onPropertyChange(String... paths) {
+ }
+
+ private static class ActionRenderer implements
+ Renderer<PermissionRule.Action> {
+ @Override
+ public String render(PermissionRule.Action object) {
+ return object != null ? object.toString() : "";
+ }
+
+ @Override
+ public void render(PermissionRule.Action object, Appendable appendable)
+ throws IOException {
+ appendable.append(render(object));
+ }
+ }
+
+ private static class RangeRenderer implements Renderer<Integer> {
+ @Override
+ public String render(Integer object) {
+ if (0 <= object) {
+ return "+" + object;
+ } else {
+ return String.valueOf(object);
+ }
+ }
+
+ @Override
+ public void render(Integer object, Appendable appendable)
+ throws IOException {
+ appendable.append(render(object));
+ }
+ }
+
+ private static final ActionRenderer actionRenderer = new ActionRenderer();
+ private static final RangeRenderer rangeRenderer = new RangeRenderer();
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
new file mode 100644
index 0000000..447c99f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2011 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:e='urn:import:com.google.gwt.editor.ui.client'
+ xmlns:my='urn:import:com.google.gerrit.client.admin'
+ xmlns:q='urn:import:com.google.gerrit.client.ui'
+ ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
+ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
+ ui:generateLocales='default,en'
+ >
+<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
+<ui:style>
+ @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
+
+ .panel {
+ position: relative;
+ height: 1.5em;
+ }
+
+ .panel:hover {
+ background-color: selectionColor;
+ }
+
+ .normal {
+ padding-left: 10px;
+ white-space: nowrap;
+ height: 100%;
+ }
+
+ .deleted {
+ height: 100%;
+ }
+
+ .actionList, .minmax {
+ font-size: 80%;
+ }
+
+ .forcePush {
+ position: absolute;
+ top: 0;
+ right: 36px;
+ width: 7em;
+ font-size: 80%;
+ }
+
+ .deleteIcon {
+ position: absolute;
+ top: 2px;
+ right: 11px;
+ }
+
+ .groupName {
+ display: inline;
+ }
+</ui:style>
+
+<g:HTMLPanel styleName='{style.panel}'>
+<div ui:field='normal' class='{style.normal}'>
+ <g:ValueListBox ui:field='action' styleName='{style.actionList}'/>
+ <span ui:field='rangeEditor'>
+ <g:ValueListBox ui:field='min' styleName='{style.minmax}'/>
+ <g:ValueListBox ui:field='max' styleName='{style.minmax}'/>
+ </span>
+
+ <q:Hyperlink ui:field='normalGroupName' styleName='{style.groupName}'/>
+ <g:CheckBox
+ ui:field='force'
+ addStyleNames='{style.forcePush}'
+ text='Force Push'>
+ <ui:attribute name='text'/>
+ </g:CheckBox>
+
+ <g:Anchor
+ ui:field='deleteRule'
+ href='javascript:void'
+ styleName='{style.deleteIcon} {res.css.deleteIcon}'
+ title='Delete this rule'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+</div>
+
+<div
+ ui:field='deleted'
+ class='{res.css.deleted} {style.deleted}'
+ style='display: none'>
+ <ui:msg>Group <span ui:field='deletedGroupName'/> was deleted</ui:msg>
+ <g:Anchor
+ ui:field='undoDelete'
+ href='javascript:void'
+ styleName='{style.deleteIcon} {res.css.undoIcon}'
+ title='Undo deletion'>
+ <ui:attribute name='title'/>
+ </g:Anchor>
+</div>
+</g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
new file mode 100644
index 0000000..c550d8c
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
@@ -0,0 +1,150 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gerrit.client.Dispatcher;
+import com.google.gerrit.client.ui.Hyperlink;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.ProjectAccess;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.editor.client.Editor;
+import com.google.gwt.editor.client.EditorDelegate;
+import com.google.gwt.editor.client.ValueAwareEditor;
+import com.google.gwt.editor.client.adapters.EditorSource;
+import com.google.gwt.editor.client.adapters.ListEditor;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTMLPanel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProjectAccessEditor extends Composite implements
+ Editor<ProjectAccess>, ValueAwareEditor<ProjectAccess> {
+ interface Binder extends UiBinder<HTMLPanel, ProjectAccessEditor> {
+ }
+
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ @UiField
+ DivElement inheritsFrom;
+
+ @UiField
+ Hyperlink parentProject;
+
+ @UiField
+ FlowPanel localContainer;
+ ListEditor<AccessSection, AccessSectionEditor> local;
+
+ @UiField
+ Anchor addSection;
+
+ private ProjectAccess value;
+
+ private boolean editing;
+
+ public ProjectAccessEditor() {
+ initWidget(uiBinder.createAndBindUi(this));
+ local = ListEditor.of(new Source(localContainer));
+ }
+
+ @UiHandler("addSection")
+ void onAddSection(ClickEvent event) {
+ int index = local.getList().size();
+ local.getList().add(new AccessSection("refs/heads/*"));
+
+ AccessSectionEditor editor = local.getEditors().get(index);
+ editor.enableEditing();
+ editor.editRefPattern();
+ }
+
+ @Override
+ public void setValue(ProjectAccess value) {
+ this.value = value;
+
+ Project.NameKey parent = value.getInheritsFrom();
+ if (parent != null) {
+ inheritsFrom.getStyle().setDisplay(Display.BLOCK);
+ parentProject.setText(parent.get());
+ parentProject.setTargetHistoryToken( //
+ Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS));
+ } else {
+ inheritsFrom.getStyle().setDisplay(Display.NONE);
+ }
+
+ addSection.setVisible(value != null && editing && !value.getOwnerOf().isEmpty());
+ }
+
+ @Override
+ public void flush() {
+ List<AccessSection> src = local.getList();
+ List<AccessSection> keep = new ArrayList<AccessSection>(src.size());
+
+ for (int i = 0; i < src.size(); i++) {
+ AccessSectionEditor e = (AccessSectionEditor) localContainer.getWidget(i);
+ if (!e.isDeleted()) {
+ keep.add(src.get(i));
+ }
+ }
+ value.setLocal(keep);
+ }
+
+ @Override
+ public void onPropertyChange(String... paths) {
+ }
+
+ @Override
+ public void setDelegate(EditorDelegate<ProjectAccess> delegate) {
+ }
+
+ void setEditing(final boolean editing) {
+ this.editing = editing;
+ addSection.setVisible(editing);
+ }
+
+ private class Source extends EditorSource<AccessSectionEditor> {
+ private final FlowPanel container;
+
+ Source(FlowPanel container) {
+ this.container = container;
+ }
+
+ @Override
+ public AccessSectionEditor create(int index) {
+ AccessSectionEditor subEditor = new AccessSectionEditor(value);
+ subEditor.setEditing(editing);
+ container.insert(subEditor, index);
+ return subEditor;
+ }
+
+ @Override
+ public void dispose(AccessSectionEditor subEditor) {
+ subEditor.removeFromParent();
+ }
+
+ @Override
+ public void setIndex(AccessSectionEditor subEditor, int index) {
+ container.insert(subEditor, index);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
new file mode 100644
index 0000000..9360daa
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2011 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:q='urn:import:com.google.gerrit.client.ui'
+ ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
+ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
+ ui:generateLocales='default,en'
+ >
+<ui:style>
+ .inheritsFrom {
+ margin-bottom: 0.5em;
+ }
+ .parentTitle {
+ font-weight: bold;
+ }
+ .parentLink {
+ display: inline;
+ }
+
+ .addContainer {
+ margin-top: 5px;
+ font-size: 80%;
+ }
+ .addContainer:hover {
+ background-color: selectionColor;
+ }
+</ui:style>
+
+<g:HTMLPanel>
+ <div ui:field='inheritsFrom' class='{style.inheritsFrom}'>
+ <span class='{style.parentTitle}'><ui:msg>Rights Inherit From:</ui:msg></span>
+ <q:Hyperlink ui:field='parentProject' styleName='{style.parentLink}'/>
+ </div>
+
+ <g:FlowPanel ui:field='localContainer'/>
+ <div class='{style.addContainer}'>
+ <g:Anchor
+ ui:field='addSection'
+ href='javascript:void'
+ text='Add Reference'>
+ <ui:attribute name='text'/>
+ </g:Anchor>
+ </div>
+</g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
index b87f6f1..e9d3889 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2008 The Android Open Source Project
+// Copyright (C) 2011 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.
@@ -14,46 +14,60 @@
package com.google.gerrit.client.admin;
-import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.Hyperlink;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.GerritConfig;
-import com.google.gerrit.common.data.InheritedRefRight;
-import com.google.gerrit.common.data.ProjectDetail;
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.ProjectAccess;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
public class ProjectAccessScreen extends ProjectScreen {
- private Panel parentPanel;
- private Hyperlink parentName;
+ interface Binder extends UiBinder<HTMLPanel, ProjectAccessScreen> {
+ }
- private RightsTable rights;
- private Button delRight;
- private AccessRightEditor rightEditor;
- private CheckBox showInherited;
+ private static final Binder uiBinder = GWT.create(Binder.class);
+
+ interface Driver extends SimpleBeanEditorDriver< //
+ ProjectAccess, //
+ ProjectAccessEditor> {
+ }
+
+ @UiField
+ DivElement editTools;
+
+ @UiField
+ Button edit;
+
+ @UiField
+ Button cancel;
+
+ @UiField
+ ProjectAccessEditor accessEditor;
+
+ @UiField
+ DivElement commitTools;
+
+ @UiField
+ NpTextArea commitMessage;
+
+ @UiField
+ Button commit;
+
+ private Driver driver;
+
+ private ProjectAccess access;
public ProjectAccessScreen(final Project.NameKey toShow) {
super(toShow);
@@ -62,266 +76,87 @@
@Override
protected void onInitUI() {
super.onInitUI();
- initParent();
- initRights();
+ add(uiBinder.createAndBindUi(this));
+
+ driver = GWT.create(Driver.class);
+ accessEditor.setEditing(false);
+ driver.initialize(accessEditor);
}
@Override
protected void onLoad() {
super.onLoad();
- Util.PROJECT_SVC.projectDetail(getProjectKey(),
- new ScreenLoadCallback<ProjectDetail>(this) {
- public void preDisplay(final ProjectDetail result) {
- enableForm(true);
- display(result);
+ Util.PROJECT_SVC.projectAccess(getProjectKey(),
+ new ScreenLoadCallback<ProjectAccess>(this) {
+ @Override
+ public void preDisplay(ProjectAccess access) {
+ edit(access);
}
});
}
- private void enableForm(final boolean on) {
- delRight.setEnabled(on);
- rightEditor.enableForm(on);
+ void edit(ProjectAccess access) {
+ this.access = access;
+ final boolean editing = !edit.isEnabled();
+ accessEditor.setEditing(editing);
+ UIObject.setVisible(editTools, !access.getOwnerOf().isEmpty());
+ cancel.setVisible(editing);
+ UIObject.setVisible(commitTools, editing);
+ driver.edit(access);
}
- private void initParent() {
- parentName = new Hyperlink("", "");
-
- showInherited = new CheckBox();
- showInherited.setValue(true);
- showInherited.addClickHandler(new ClickHandler() {
- public void onClick(ClickEvent event) {
- rights.showInherited(showInherited.getValue());
- }
- });
-
- Grid g = new Grid(2, 3);
- g.setWidget(0, 0, new SmallHeading(Util.C.headingParentProjectName()));
- g.setWidget(1, 0, parentName);
- g.setWidget(1, 1, showInherited);
- g.setText(1, 2, Util.C.headingShowInherited());
-
- parentPanel = new VerticalPanel();
- parentPanel.add(g);
- add(parentPanel);
+ @UiHandler("edit")
+ void onEdit(ClickEvent event) {
+ edit.setEnabled(false);
+ cancel.setVisible(true);
+ UIObject.setVisible(commitTools, true);
+ accessEditor.setEditing(true);
+ driver.edit(access);
}
- private void initRights() {
- rights = new RightsTable();
-
- delRight = new Button(Util.C.buttonDeleteGroupMembers());
- delRight.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final HashSet<RefRight.Key> refRightIds = rights.getRefRightIdsChecked();
- doDeleteRefRights(refRightIds);
- }
- });
-
- rightEditor = new AccessRightEditor(getProjectKey());
- rightEditor.addValueChangeHandler(new ValueChangeHandler<ProjectDetail>() {
- @Override
- public void onValueChange(ValueChangeEvent<ProjectDetail> event) {
- display(event.getValue());
- }
- });
-
- add(new SmallHeading(Util.C.headingAccessRights()));
- add(rights);
- add(delRight);
- add(rightEditor);
+ @UiHandler(value={"cancel", "cancel2"})
+ void onCancel(ClickEvent event) {
+ Gerrit.display(PageLinks.toProjectAcceess(getProjectKey()));
}
- void display(final ProjectDetail result) {
- final Project project = result.project;
+ @UiHandler("commit")
+ void onCommit(ClickEvent event) {
+ ProjectAccess access = driver.flush();
- final Project.NameKey wildKey = Gerrit.getConfig().getWildProject();
- final boolean isWild = wildKey.equals(project.getNameKey());
- Project.NameKey parent = project.getParent();
- if (parent == null) {
- parent = wildKey;
+ if (driver.hasErrors()) {
+ Window.alert(Util.C.errorsMustBeFixed());
+ return;
}
- parentPanel.setVisible(!isWild);
- parentName.setTargetHistoryToken(Dispatcher.toProjectAdmin(parent, ACCESS));
- parentName.setText(parent.get());
+ String message = commitMessage.getText().trim();
+ if ("".equals(message)) {
+ message = null;
+ }
- rights.display(result.groups, result.rights);
+ enable(false);
+ Util.PROJECT_SVC.changeProjectAccess( //
+ getProjectKey(), //
+ access.getRevision(), //
+ message, //
+ access.getLocal(), //
+ new GerritCallback<ProjectAccess>() {
+ @Override
+ public void onSuccess(ProjectAccess access) {
+ commitMessage.setText("");
+ edit(access);
+ enable(true);
+ }
- rightEditor.setVisible(result.canModifyAccess);
- delRight.setVisible(rights.getCanDelete());
+ @Override
+ public void onFailure(Throwable caught) {
+ enable(true);
+ super.onFailure(caught);
+ }
+ });
}
- private void doDeleteRefRights(final HashSet<RefRight.Key> refRightIds) {
- if (!refRightIds.isEmpty()) {
- Util.PROJECT_SVC.deleteRight(getProjectKey(), refRightIds,
- new GerritCallback<ProjectDetail>() {
- @Override
- public void onSuccess(final ProjectDetail result) {
- //The user could no longer modify access after deleting a ref right.
- display(result);
- }
- });
- }
- }
-
- private class RightsTable extends FancyFlexTable<InheritedRefRight> {
- boolean canDelete;
- Map<AccountGroup.Id, AccountGroup> groups;
-
- RightsTable() {
- table.setWidth("");
- table.setText(0, 2, Util.C.columnRightOrigin());
- table.setText(0, 3, Util.C.columnApprovalCategory());
- table.setText(0, 4, Util.C.columnGroupName());
- table.setText(0, 5, Util.C.columnRefName());
- table.setText(0, 6, Util.C.columnRightRange());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader());
- fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 5, Gerrit.RESOURCES.css().dataHeader());
- fmt.addStyleName(0, 6, Gerrit.RESOURCES.css().dataHeader());
-
- table.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- onOpenRow(table.getCellForEvent(event).getRowIndex());
- }
- });
- }
-
- HashSet<RefRight.Key> getRefRightIdsChecked() {
- final HashSet<RefRight.Key> refRightIds = new HashSet<RefRight.Key>();
- for (int row = 1; row < table.getRowCount(); row++) {
- RefRight r = getRowItem(row).getRight();
- if (r != null && table.getWidget(row, 1) instanceof CheckBox
- && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- refRightIds.add(r.getKey());
- }
- }
- return refRightIds;
- }
-
- void display(final Map<AccountGroup.Id, AccountGroup> grps,
- final List<InheritedRefRight> refRights) {
- groups = grps;
- canDelete = false;
-
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final InheritedRefRight r : refRights) {
- final int row = table.getRowCount();
- table.insertRow(row);
- if (! showInherited.getValue() && r.isInherited()) {
- table.getRowFormatter().setVisible(row, false);
- }
- applyDataRowStyle(row);
- populate(row, r);
- }
- }
-
- protected void onOpenRow(final int row) {
- if (row > 0) {
- RefRight right = getRowItem(row).getRight();
- rightEditor.load(right, groups.get(right.getAccountGroupId()));
- }
- }
-
- void populate(final int row, final InheritedRefRight r) {
- final GerritConfig config = Gerrit.getConfig();
- final RefRight right = r.getRight();
- final ApprovalType ar =
- config.getApprovalTypes().getApprovalType(
- right.getApprovalCategoryId());
- final AccountGroup group = groups.get(right.getAccountGroupId());
-
- if (r.isInherited() || !r.isOwner()) {
- table.setText(row, 1, "");
- } else {
- table.setWidget(row, 1, new CheckBox());
- canDelete = true;
- }
-
- if (r.isInherited()) {
- Project.NameKey fromProject = right.getKey().getProjectNameKey();
- table.setWidget(row, 2, new Hyperlink(fromProject.get(), Dispatcher
- .toProjectAdmin(fromProject, ACCESS)));
- } else {
- table.setText(row, 2, "");
- }
-
- table.setText(row, 3, ar != null ? ar.getCategory().getName()
- : right.getApprovalCategoryId().get() );
-
- if (group != null) {
- table.setWidget(row, 4, new Hyperlink(group.getName(), Dispatcher
- .toAccountGroup(group.getId())));
- } else {
- table.setText(row, 4, Util.M.deletedGroup(right.getAccountGroupId()
- .get()));
- }
-
- table.setText(row, 5, right.getRefPatternForDisplay());
-
- {
- final SafeHtmlBuilder m = new SafeHtmlBuilder();
- final ApprovalCategoryValue min, max;
- min = ar != null ? ar.getValue(right.getMinValue()) : null;
- max = ar != null ? ar.getValue(right.getMaxValue()) : null;
-
- if (ar != null && ar.getCategory().isRange()) {
- formatValue(m, right.getMinValue(), min);
- m.br();
- }
- formatValue(m, right.getMaxValue(), max);
- SafeHtml.set(table, row, 6, m);
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell());
- fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 5, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 6, Gerrit.RESOURCES.css().dataCell());
- fmt.addStyleName(row, 6, Gerrit.RESOURCES.css()
- .projectAdminApprovalCategoryRangeLine());
-
- setRowItem(row, r);
- }
-
- public void showInherited(boolean visible) {
- for (int r = 0; r < table.getRowCount(); r++) {
- if (getRowItem(r) != null && getRowItem(r).isInherited()) {
- table.getRowFormatter().setVisible(r, visible);
- }
- }
- }
-
- private void formatValue(final SafeHtmlBuilder m, final short v,
- final ApprovalCategoryValue e) {
- m.openSpan();
- m
- .setStyleName(Gerrit.RESOURCES.css()
- .projectAdminApprovalCategoryValue());
- if (v == 0) {
- m.append(' ');
- } else if (v > 0) {
- m.append('+');
- }
- m.append(v);
- m.closeSpan();
- if (e != null) {
- m.append(": ");
- m.append(e.getName());
- }
- }
-
- private boolean getCanDelete() {
- return canDelete;
- }
+ private void enable(boolean enabled) {
+ commitMessage.setEnabled(enabled);
+ commit.setEnabled(enabled);
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml
new file mode 100644
index 0000000..9a74610
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2011 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:my='urn:import:com.google.gerrit.client.admin'
+ xmlns:expui='urn:import:com.google.gwtexpui.globalkey.client'
+ ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
+ ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator'
+ ui:generateLocales='default,en'
+ >
+<ui:style>
+ @external .gwt-TextArea;
+
+ .commitMessage {
+ margin-top: 2em;
+ }
+ .commitMessage .gwt-TextArea {
+ margin: 5px 5px 5px 5px;
+ }
+</ui:style>
+
+<g:HTMLPanel>
+ <div ui:field='editTools'>
+ <g:Button
+ ui:field='edit'
+ text='Edit'>
+ <ui:attribute name='text'/>
+ </g:Button>
+ <g:Button
+ ui:field='cancel'
+ text='Cancel'>
+ <ui:attribute name='text'/>
+ </g:Button>
+ </div>
+ <my:ProjectAccessEditor ui:field='accessEditor'/>
+ <div ui:field='commitTools'>
+ <div class='{style.commitMessage}'>
+ <ui:msg>Commit Message (optional):</ui:msg><br/>
+ <expui:NpTextArea
+ ui:field='commitMessage'
+ visibleLines='4'
+ characterWidth='60'
+ spellCheck='true'
+ />
+ </div>
+ <g:Button
+ ui:field='commit'
+ text='Save Changes'>
+ <ui:attribute name='text'/>
+ </g:Button>
+ <g:Button
+ ui:field='cancel2'
+ text='Cancel'>
+ <ui:attribute name='text'/>
+ </g:Button>
+ </div>
+ <div style='width: 35em; visibility: hidden;' />
+</g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java
new file mode 100644
index 0000000..a0a3d17
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.text.shared.Parser;
+import com.google.gwt.text.shared.Renderer;
+import com.google.gwt.user.client.ui.ValueBox;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+
+import java.io.IOException;
+import java.text.ParseException;
+
+public class RefPatternBox extends ValueBox<String> {
+ private static final Renderer<String> RENDERER = new Renderer<String>() {
+ public String render(String ref) {
+ return ref;
+ }
+
+ public void render(String ref, Appendable dst) throws IOException {
+ dst.append(render(ref));
+ }
+ };
+
+ private static final Parser<String> PARSER = new Parser<String>() {
+ public String parse(CharSequence text) throws ParseException {
+ String ref = text.toString();
+
+ if (ref.isEmpty()) {
+ throw new ParseException(Util.C.refErrorEmpty(), 0);
+ }
+
+ if (ref.charAt(0) == '/') {
+ throw new ParseException(Util.C.refErrorBeginSlash(), 0);
+ }
+
+ final boolean re = ref.charAt(0) == '^';
+ if (re && !ref.startsWith("^refs/")) {
+ ref = "^refs/heads/" + ref.substring(1);
+ } else if (!ref.startsWith("refs/")) {
+ ref = "refs/heads/" + ref;
+ }
+
+ for (int i = 0; i < ref.length(); i++) {
+ final char c = ref.charAt(i);
+
+ if (c == '/' && 0 < i && ref.charAt(i - 1) == '/') {
+ throw new ParseException(Util.C.refErrorDoubleSlash(), i);
+ }
+
+ if (c == ' ') {
+ throw new ParseException(Util.C.refErrorNoSpace(), i);
+ }
+
+ if (c < ' ') {
+ throw new ParseException(Util.C.refErrorPrintable(), i);
+ }
+ }
+ return ref;
+ }
+ };
+
+ public RefPatternBox() {
+ super(Document.get().createTextInputElement(), RENDERER, PARSER);
+ addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
+ addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ if (event.getCharCode() == ' ') {
+ event.preventDefault();
+ }
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
index 4167116..c599ee9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
@@ -32,6 +32,8 @@
PROJECT_SVC = GWT.create(ProjectAdminService.class);
JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
+
+ AdminResources.I.css().ensureInjected();
}
public static String toLongString(final Project.SubmitType type) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java
new file mode 100644
index 0000000..d8b8c5d
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java
@@ -0,0 +1,206 @@
+// Copyright (C) 2011 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.client.admin;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.DivElement;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.editor.client.EditorError;
+import com.google.gwt.editor.client.HasEditorErrors;
+import com.google.gwt.editor.client.IsEditor;
+import com.google.gwt.editor.client.LeafValueEditor;
+import com.google.gwt.editor.ui.client.adapters.ValueBoxEditor;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DoubleClickEvent;
+import com.google.gwt.event.dom.client.DoubleClickHandler;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiChild;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Focusable;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.ValueBoxBase;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.text.ParseException;
+import java.util.List;
+
+public class ValueEditor<T> extends Composite implements HasEditorErrors<T>,
+ IsEditor<ValueBoxEditor<T>>, LeafValueEditor<T>, Focusable {
+ interface Binder extends UiBinder<Widget, ValueEditor<?>> {
+ }
+
+ static final Binder uiBinder = GWT.create(Binder.class);
+
+ @UiField
+ SimplePanel textPanel;
+ private Label textLabel;
+ private StartEditHandlers startHandlers;
+
+ @UiField
+ Image editIcon;
+
+ @UiField
+ SimplePanel editPanel;
+
+ @UiField
+ DivElement errorLabel;
+
+ private ValueBoxBase<T> editChild;
+ private ValueBoxEditor<T> editProxy;
+
+ public ValueEditor() {
+ startHandlers = new StartEditHandlers();
+ initWidget(uiBinder.createAndBindUi(this));
+ editPanel.setVisible(false);
+ editIcon.addClickHandler(startHandlers);
+ }
+
+ public void edit() {
+ textPanel.removeFromParent();
+ textPanel = null;
+ textLabel = null;
+
+ editIcon.removeFromParent();
+ editIcon = null;
+ startHandlers = null;
+
+ editPanel.setVisible(true);
+ }
+
+ public ValueBoxEditor<T> asEditor() {
+ if (editProxy == null) {
+ editProxy = new EditorProxy();
+ }
+ return editProxy;
+ }
+
+ @Override
+ public T getValue() {
+ return asEditor().getValue();
+ }
+
+ @Override
+ public void setValue(T value) {
+ asEditor().setValue(value);
+ }
+
+ public void setEditTitle(String title) {
+ editIcon.setTitle(title);
+ }
+
+ @UiChild(limit = 1, tagname = "display")
+ public void setDisplay(Label widget) {
+ textLabel = widget;
+ textPanel.add(textLabel);
+
+ textLabel.addClickHandler(startHandlers);
+ textLabel.addDoubleClickHandler(startHandlers);
+ }
+
+ @UiChild(limit = 1, tagname = "editor")
+ public void setEditor(ValueBoxBase<T> widget) {
+ editChild = widget;
+ editPanel.add(editChild);
+ editProxy = null;
+ }
+
+ public void setEnabled(boolean enabled) {
+ editIcon.setVisible(enabled);
+ startHandlers.enabled = enabled;
+ }
+
+ public void showErrors(List<EditorError> errors) {
+ StringBuilder buf = new StringBuilder();
+ for (EditorError error : errors) {
+ if (error.getEditor().equals(editProxy)) {
+ buf.append("\n");
+ if (error.getUserData() instanceof ParseException) {
+ buf.append(((ParseException) error.getUserData()).getMessage());
+ } else {
+ buf.append(error.getMessage());
+ }
+ }
+ }
+
+ if (0 < buf.length()) {
+ errorLabel.setInnerText(buf.substring(1));
+ errorLabel.getStyle().setDisplay(Display.BLOCK);
+ } else {
+ errorLabel.setInnerText("");
+ errorLabel.getStyle().setDisplay(Display.NONE);
+ }
+ }
+
+ @Override
+ public void setAccessKey(char key) {
+ editChild.setAccessKey(key);
+ }
+
+ @Override
+ public void setFocus(boolean focused) {
+ editChild.setFocus(focused);
+ if (focused) {
+ editChild.setCursorPos(editChild.getText().length());
+ }
+ }
+
+ @Override
+ public int getTabIndex() {
+ return editChild.getTabIndex();
+ }
+
+ @Override
+ public void setTabIndex(int index) {
+ editChild.setTabIndex(index);
+ }
+
+ private class StartEditHandlers implements ClickHandler, DoubleClickHandler {
+ boolean enabled;
+
+ @Override
+ public void onClick(ClickEvent event) {
+ if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
+ edit();
+ }
+ }
+
+ @Override
+ public void onDoubleClick(DoubleClickEvent event) {
+ if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) {
+ edit();
+ }
+ }
+ }
+
+ private class EditorProxy extends ValueBoxEditor<T> {
+ EditorProxy() {
+ super(editChild);
+ }
+
+ @Override
+ public void setValue(T value) {
+ super.setValue(value);
+ if (textLabel == null) {
+ setDisplay(new Label());
+ }
+ textLabel.setText(editChild.getText());
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml
new file mode 100644
index 0000000..9862848
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2011 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.
+-->
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ >
+<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/>
+<ui:style>
+ .panel {
+ position: relative;
+ white-space: nowrap;
+ }
+
+ .textPanel {
+ width: 100%;
+ padding-right: 21px;
+ }
+
+ .editIcon {
+ position: absolute;
+ top: 0;
+ right: 5px;
+ }
+
+ .editPanel {
+ width: 100%;
+ }
+
+ .errorLabel {
+ display: none;
+ color: red;
+ white-space: pre;
+ }
+</ui:style>
+<g:HTMLPanel stylePrimaryName='{style.panel}'>
+ <g:Image
+ ui:field='editIcon'
+ resource='{res.editText}'
+ stylePrimaryName='{style.editIcon}'
+ title='Edit'>
+ <ui:attribute name='title'/>
+ </g:Image>
+ <g:SimplePanel ui:field='textPanel' stylePrimaryName='{style.textPanel}'/>
+
+ <g:SimplePanel ui:field='editPanel' stylePrimaryName='{style.editPanel}'/>
+ <div
+ ui:field='errorLabel'
+ class='{style.errorLabel}'/>
+</g:HTMLPanel>
+</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css
new file mode 100644
index 0000000..eca4823
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css
@@ -0,0 +1,53 @@
+/* Copyright (C) 2009 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.
+ */
+
+@eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
+@eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
+@def deletedBackground #a9a9a9;
+
+@sprite .deleteIcon {
+ gwt-image: 'deleteNormal';
+ border: none;
+}
+
+@sprite .deleteIcon:hover {
+ gwt-image: 'deleteHover';
+ border: none;
+}
+
+@sprite .undoIcon {
+ gwt-image: 'undoNormal';
+ border: none;
+}
+
+.deleted {
+ background-color: deletedBackground;
+ color: #ffffff;
+ white-space: nowrap;
+ padding-left: 50px;
+}
+
+.deleted:hover {
+ background-color: selectionColor;
+ color: textColor;
+ }
+
+.deletedBorder {
+ background: 1px solid deletedBackground;
+}
+
+.deleteSectionHover {
+ background-color: selectionColor !important;
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
new file mode 100644
index 0000000..839e8ef
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
new file mode 100644
index 0000000..ffddb6f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png
new file mode 100644
index 0000000..188e1c1
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png
new file mode 100644
index 0000000..8b0fef9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index 364d710..4caf8f8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -27,7 +27,8 @@
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountDiffPreference;
import com.google.gerrit.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.ChangeMessage;
import com.google.gerrit.reviewdb.Patch;
@@ -35,8 +36,6 @@
import com.google.gerrit.reviewdb.PatchSetInfo;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.UserIdentity;
-import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand;
-import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -54,7 +53,6 @@
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwtexpui.clippy.client.CopyableLabel;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -402,12 +400,8 @@
private void populateActions(final PatchSetDetail detail) {
final boolean isOpen = changeDetail.getChange().getStatus().isOpen();
- Set<ApprovalCategory.Id> allowed = changeDetail.getCurrentActions();
- if (allowed == null) {
- allowed = Collections.emptySet();
- }
- if (isOpen && allowed.contains(ApprovalCategory.SUBMIT)) {
+ if (isOpen && changeDetail.canSubmit()) {
final Button b =
new Button(Util.M
.submitPatchSet(detail.getPatchSet().getPatchSetId()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
index 0ed58b4..f7f8ae1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
@@ -25,8 +25,11 @@
import com.google.gerrit.client.ui.SmallHeading;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Change;
@@ -39,11 +42,11 @@
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import com.google.gwtjsonrpc.client.VoidResult;
@@ -54,7 +57,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
public class PublishCommentScreen extends AccountScreen implements
ClickHandler, CommentEditorContainer {
@@ -218,16 +220,25 @@
}
private void initApprovals(final PatchSetPublishDetail r, final Panel body) {
- for (final ApprovalType ct : Gerrit.getConfig().getApprovalTypes()
- .getApprovalTypes()) {
- if (r.isAllowed(ct.getCategory().getId())) {
- initApprovalType(r, body, ct);
+ ApprovalTypes types = Gerrit.getConfig().getApprovalTypes();
+
+ for (ApprovalType type : types.getApprovalTypes()) {
+ String permission = Permission.forLabel(type.getCategory().getLabelName());
+ PermissionRange range = r.getRange(permission);
+ if (range != null && !range.isEmpty()) {
+ initApprovalType(r, body, type, range);
+ }
+ }
+
+ for (PermissionRange range : r.getLabels()) {
+ if (!range.isEmpty() && types.byLabel(range.getLabel()) == null) {
+ // TODO: this is a non-standard label. Offer it without the type.
}
}
}
private void initApprovalType(final PatchSetPublishDetail r,
- final Panel body, final ApprovalType ct) {
+ final Panel body, final ApprovalType ct, final PermissionRange range) {
body.add(new SmallHeading(ct.getCategory().getName() + ":"));
final VerticalPanel vp = new VerticalPanel();
@@ -236,11 +247,10 @@
new ArrayList<ApprovalCategoryValue>(ct.getValues());
Collections.reverse(lst);
final ApprovalCategory.Id catId = ct.getCategory().getId();
- final Set<ApprovalCategoryValue.Id> allowed = r.getAllowed(catId);
final PatchSetApproval prior = r.getChangeApproval(catId);
for (final ApprovalCategoryValue buttonValue : lst) {
- if (!allowed.contains(buttonValue.getId())) {
+ if (!range.contains(buttonValue.getValue())) {
continue;
}
@@ -306,7 +316,7 @@
}
}
- submit.setVisible(r.isSubmitAllowed());
+ submit.setVisible(r.canSubmit());
}
private void onSend(final boolean submit) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
index 0a9cedf..5d8ee8b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
@@ -16,26 +16,34 @@
import com.google.gerrit.client.RpcStatus;
import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.reviewdb.AccountGroupName;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/** Suggestion Oracle for AccountGroup entities. */
public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
+ private Map<String, AccountGroup.UUID> priorResults =
+ new HashMap<String, AccountGroup.UUID>();
+
@Override
public void onRequestSuggestions(final Request req, final Callback callback) {
RpcStatus.hide(new Runnable() {
public void run() {
SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
- new GerritCallback<List<AccountGroupName>>() {
- public void onSuccess(final List<AccountGroupName> result) {
+ new GerritCallback<List<GroupReference>>() {
+ public void onSuccess(final List<GroupReference> result) {
+ priorResults.clear();
final ArrayList<AccountGroupSuggestion> r =
new ArrayList<AccountGroupSuggestion>(result.size());
- for (final AccountGroupName p : result) {
+ for (final GroupReference p : result) {
r.add(new AccountGroupSuggestion(p));
+ priorResults.put(p.getName(), p.getUUID());
}
callback.onSuggestionsReady(req, new Response(r));
}
@@ -46,9 +54,9 @@
private static class AccountGroupSuggestion implements
SuggestOracle.Suggestion {
- private final AccountGroupName info;
+ private final GroupReference info;
- AccountGroupSuggestion(final AccountGroupName k) {
+ AccountGroupSuggestion(final GroupReference k) {
info = k;
}
@@ -60,4 +68,9 @@
return info.getName();
}
}
+
+ /** @return the group UUID, or null if it cannot be found. */
+ public AccountGroup.UUID getUUID(String name) {
+ return priorResults.get(name);
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java
index 6c18db1..1261b42 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java
@@ -25,6 +25,10 @@
public class Hyperlink extends com.google.gwt.user.client.ui.Hyperlink {
static final HyperlinkImpl impl = GWT.create(HyperlinkImpl.class);
+ /** Initialize a default hyperlink with no target and no text. */
+ public Hyperlink() {
+ }
+
/**
* Creates a hyperlink with its text and target history token specified.
*
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
index 379fee6..adc48f8 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
@@ -15,12 +15,12 @@
package com.google.gerrit.httpd.rpc;
import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.SuggestService;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.AccountExternalId;
import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.AccountGroup.Id;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
@@ -30,8 +30,10 @@
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.ProjectControl;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
@@ -52,23 +54,28 @@
private static final String MAX_SUFFIX = "\u9fa5";
private final AuthConfig authConfig;
+ private final ProjectControl.Factory projectControlFactory;
private final ProjectCache projectCache;
private final AccountCache accountCache;
private final GroupControl.Factory groupControlFactory;
private final IdentifiedUser.GenericFactory userFactory;
private final Provider<CurrentUser> currentUser;
private final SuggestAccountsEnum suggestAccounts;
+ private final GroupCache groupCache;
@Inject
SuggestServiceImpl(final Provider<ReviewDb> schema,
final AuthConfig authConfig,
+ final ProjectControl.Factory projectControlFactory,
final ProjectCache projectCache, final AccountCache accountCache,
final GroupControl.Factory groupControlFactory,
final IdentifiedUser.GenericFactory userFactory,
final Provider<CurrentUser> currentUser,
- @GerritServerConfig final Config cfg) {
+ @GerritServerConfig final Config cfg,
+ final GroupCache groupCache) {
super(schema, currentUser);
this.authConfig = authConfig;
+ this.projectControlFactory = projectControlFactory;
this.projectCache = projectCache;
this.accountCache = accountCache;
this.groupControlFactory = groupControlFactory;
@@ -76,28 +83,29 @@
this.currentUser = currentUser;
this.suggestAccounts =
cfg.getEnum("suggest", null, "accounts", SuggestAccountsEnum.ALL);
+ this.groupCache = groupCache;
}
public void suggestProjectNameKey(final String query, final int limit,
final AsyncCallback<List<Project.NameKey>> callback) {
- run(callback, new Action<List<Project.NameKey>>() {
- public List<Project.NameKey> run(final ReviewDb db) throws OrmException {
- final String a = query;
- final String b = a + MAX_SUFFIX;
- final int max = 10;
- final int n = limit <= 0 ? max : Math.min(limit, max);
+ final int max = 10;
+ final int n = limit <= 0 ? max : Math.min(limit, max);
- final CurrentUser user = currentUser.get();
- final List<Project.NameKey> r = new ArrayList<Project.NameKey>();
- for (final Project p : db.projects().suggestByName(a, b, n)) {
- final ProjectState e = projectCache.get(p.getNameKey());
- if (e != null && e.controlFor(user).isVisible()) {
- r.add(p.getNameKey());
- }
- }
- return r;
+ final List<Project.NameKey> r = new ArrayList<Project.NameKey>(n);
+ for (final Project.NameKey nameKey : projectCache.byName(query)) {
+ final ProjectControl ctl;
+ try {
+ ctl = projectControlFactory.validateFor(nameKey);
+ } catch (NoSuchProjectException e) {
+ continue;
}
- });
+
+ r.add(ctl.getProject().getNameKey());
+ if (r.size() == n) {
+ break;
+ }
+ }
+ callback.onSuccess(r);
}
public void suggestAccount(final String query, final Boolean active,
@@ -154,10 +162,11 @@
map.put(account.getId(), info);
break;
case SAME_GROUP: {
- Set<AccountGroup.Id> usersGroups = groupsOf(account);
- usersGroups.removeAll(authConfig.getRegisteredGroups());
+ Set<AccountGroup.UUID> usersGroups = groupsOf(account);
+ usersGroups.remove(AccountGroup.ANONYMOUS_USERS);
+ usersGroups.remove(AccountGroup.REGISTERED_USERS);
usersGroups.remove(authConfig.getBatchUsersGroup());
- for (AccountGroup.Id myGroup : currentUser.get().getEffectiveGroups()) {
+ for (AccountGroup.UUID myGroup : currentUser.get().getEffectiveGroups()) {
if (usersGroups.contains(myGroup)) {
map.put(account.getId(), info);
break;
@@ -166,10 +175,11 @@
break;
}
case VISIBLE_GROUP: {
- Set<AccountGroup.Id> usersGroups = groupsOf(account);
- usersGroups.removeAll(authConfig.getRegisteredGroups());
+ Set<AccountGroup.UUID> usersGroups = groupsOf(account);
+ usersGroups.remove(AccountGroup.ANONYMOUS_USERS);
+ usersGroups.remove(AccountGroup.REGISTERED_USERS);
usersGroups.remove(authConfig.getBatchUsersGroup());
- for (AccountGroup.Id usersGroup : usersGroups) {
+ for (AccountGroup.UUID usersGroup : usersGroups) {
try {
if (groupControlFactory.controlFor(usersGroup).isVisible()) {
map.put(account.getId(), info);
@@ -188,33 +198,36 @@
}
}
- private Set<Id> groupsOf(Account account) {
+ private Set<AccountGroup.UUID> groupsOf(Account account) {
IdentifiedUser user = userFactory.create(account.getId());
- return new HashSet<AccountGroup.Id>(user.getEffectiveGroups());
+ return new HashSet<AccountGroup.UUID>(user.getEffectiveGroups());
}
public void suggestAccountGroup(final String query, final int limit,
- final AsyncCallback<List<AccountGroupName>> callback) {
- run(callback, new Action<List<AccountGroupName>>() {
- public List<AccountGroupName> run(final ReviewDb db) throws OrmException {
+ final AsyncCallback<List<GroupReference>> callback) {
+ run(callback, new Action<List<GroupReference>>() {
+ public List<GroupReference> run(final ReviewDb db) throws OrmException {
final String a = query;
final String b = a + MAX_SUFFIX;
final int max = 10;
final int n = limit <= 0 ? max : Math.min(limit, max);
- Set<AccountGroup.Id> memberOf = currentUser.get().getEffectiveGroups();
- List<AccountGroupName> names = new ArrayList<AccountGroupName>(n);
+ Set<AccountGroup.UUID> memberOf = currentUser.get().getEffectiveGroups();
+ List<GroupReference> r = new ArrayList<GroupReference>(n);
for (AccountGroupName group : db.accountGroupNames()
.suggestByName(a, b, n)) {
try {
if (memberOf.contains(group.getId())
|| groupControlFactory.controlFor(group.getId()).isVisible()) {
- names.add(group);
+ AccountGroup g = groupCache.get(group.getId());
+ if (g != null && g.getGroupUUID() != null) {
+ r.add(GroupReference.forGroup(g));
+ }
}
} catch (NoSuchGroupException e) {
continue;
}
}
- return names;
+ return r;
}
});
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
index f638d48..4f62330 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
@@ -22,6 +22,7 @@
import com.google.gerrit.reviewdb.ContributorAgreement;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.GroupCache;
import com.google.inject.Inject;
import java.util.ArrayList;
@@ -36,13 +37,16 @@
}
private final ReviewDb db;
+ private final GroupCache groupCache;
private final IdentifiedUser user;
private AgreementInfo info;
@Inject
- AgreementInfoFactory(final ReviewDb db, final IdentifiedUser user) {
+ AgreementInfoFactory(final ReviewDb db, final GroupCache groupCache,
+ final IdentifiedUser user) {
this.db = db;
+ this.groupCache = groupCache;
this.user = user;
}
@@ -55,9 +59,14 @@
final List<AccountGroupAgreement> groupAccepted =
new ArrayList<AccountGroupAgreement>();
- for (final AccountGroup.Id groupId : user.getEffectiveGroups()) {
+ for (final AccountGroup.UUID groupUUID : user.getEffectiveGroups()) {
+ AccountGroup group = groupCache.get(groupUUID);
+ if (group == null) {
+ continue;
+ }
+
final List<AccountGroupAgreement> temp =
- db.accountGroupAgreements().byGroup(groupId).toList();
+ db.accountGroupAgreements().byGroup(group.getId()).toList();
Collections.reverse(temp);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
index a564907..578e866 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -118,8 +119,14 @@
createGroupFactory.create(newName).to(callback);
}
- public void groupDetail(final AccountGroup.Id groupId,
- final AsyncCallback<GroupDetail> callback) {
+ public void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID groupUUID,
+ AsyncCallback<GroupDetail> callback) {
+ if (groupId == null && groupUUID != null) {
+ AccountGroup g = groupCache.get(groupUUID);
+ if (g != null) {
+ groupId = g.getId();
+ }
+ }
groupDetailFactory.create(groupId).to(callback);
}
@@ -281,7 +288,7 @@
Collections.singleton(new AccountGroupIncludeAudit(m,
getAccountId())));
db.accountGroupIncludes().insert(Collections.singleton(m));
- groupIncludeCache.evictInclude(a.getId());
+ groupIncludeCache.evictInclude(a.getGroupUUID());
}
return groupDetailFactory.create(groupId).call();
@@ -361,6 +368,7 @@
}
final Account.Id me = getAccountId();
+ final Set<AccountGroup.Id> groupsToEvict = new HashSet<AccountGroup.Id>();
for (final AccountGroupInclude.Key k : keys) {
final AccountGroupInclude m =
db.accountGroupIncludes().get(k);
@@ -385,9 +393,12 @@
Collections.singleton(audit));
}
db.accountGroupIncludes().delete(Collections.singleton(m));
- groupIncludeCache.evictInclude(m.getIncludeId());
+ groupsToEvict.add(k.getIncludeId());
}
}
+ for (AccountGroup group : db.accountGroups().get(groupsToEvict)) {
+ groupIncludeCache.evictInclude(group.getGroupUUID());
+ }
return VoidResult.INSTANCE;
}
});
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java
index b3e993e..088fae2 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java
@@ -42,10 +42,10 @@
@Override
public List<AccountGroup> call() throws Exception {
- final Set<AccountGroup.Id> effective = user.getEffectiveGroups();
+ final Set<AccountGroup.UUID> effective = user.getEffectiveGroups();
final int cnt = effective.size();
final List<AccountGroup> groupList = new ArrayList<AccountGroup>(cnt);
- for (final AccountGroup.Id groupId : effective) {
+ for (final AccountGroup.UUID groupId : effective) {
groupList.add(groupCache.get(groupId));
}
Collections.sort(groupList, new Comparator<AccountGroup>() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java
index d62f0c0..c8bf519 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java
@@ -21,14 +21,19 @@
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.git.RenameGroupOp;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Collections;
+import java.util.Date;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
class RenameGroup extends Handler<GroupDetail> {
interface Factory {
@@ -39,6 +44,8 @@
private final GroupCache groupCache;
private final GroupControl.Factory groupControlFactory;
private final GroupDetailFactory.Factory groupDetailFactory;
+ private final RenameGroupOp.Factory renameGroupOpFactory;
+ private final IdentifiedUser currentUser;
private final AccountGroup.Id groupId;
private final String newName;
@@ -47,11 +54,15 @@
RenameGroup(final ReviewDb db, final GroupCache groupCache,
final GroupControl.Factory groupControlFactory,
final GroupDetailFactory.Factory groupDetailFactory,
+ final RenameGroupOp.Factory renameGroupOpFactory,
+ final IdentifiedUser currentUser,
@Assisted final AccountGroup.Id groupId, @Assisted final String newName) {
this.db = db;
this.groupCache = groupCache;
this.groupControlFactory = groupControlFactory;
this.groupDetailFactory = groupDetailFactory;
+ this.renameGroupOpFactory = renameGroupOpFactory;
+ this.currentUser = currentUser;
this.groupId = groupId;
this.newName = newName;
}
@@ -94,6 +105,10 @@
groupCache.evict(group);
groupCache.evictAfterRename(old);
+ renameGroupOpFactory.create( //
+ currentUser.newCommitterIdent(new Date(), TimeZone.getDefault()), //
+ group.getGroupUUID(), //
+ old.get(), newName).start(0, TimeUnit.MILLISECONDS);
return groupDetailFactory.create(groupId).call();
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
index 560b155..d2d4ce0 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.CanSubmitResult;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.workflow.CategoryFunction;
@@ -94,6 +95,7 @@
if (patch == null) {
throw new NoSuchEntityException();
}
+ final CanSubmitResult canSubmitResult = control.canSubmit(patch.getId());
aic.want(change.getOwner());
@@ -103,6 +105,7 @@
detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
+ detail.setCanSubmit(canSubmitResult == CanSubmitResult.OK);
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
changeId));
@@ -141,23 +144,13 @@
final Set<ApprovalCategory.Id> missingApprovals =
new HashSet<ApprovalCategory.Id>();
- final Set<ApprovalCategory.Id> currentActions =
- new HashSet<ApprovalCategory.Id>();
-
for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
CategoryFunction.forCategory(at.getCategory()).run(at, fs);
if (!fs.isValid(at)) {
missingApprovals.add(at.getCategory().getId());
}
}
- for (final ApprovalType at : approvalTypes.getActionTypes()) {
- if (CategoryFunction.forCategory(at.getCategory()).isValid(
- control.getCurrentUser(), at, fs)) {
- currentActions.add(at.getCategory().getId());
- }
- }
detail.setMissingApprovals(missingApprovals);
- detail.setCurrentActions(currentActions);
}
final boolean canRemoveReviewers = detail.getChange().getStatus().isOpen() //
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
index 779475e..58daa09 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
@@ -15,19 +15,14 @@
package com.google.gerrit.httpd.rpc.changedetail;
import com.google.gerrit.common.data.AccountInfoCache;
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchLineComment;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.PatchSetInfo;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
@@ -36,27 +31,20 @@
import com.google.gerrit.server.project.CanSubmitResult;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.RefControl;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail> {
interface Factory {
PatchSetPublishDetailFactory create(PatchSet.Id patchSetId);
}
- private final ProjectCache projectCache;
private final PatchSetInfoFactory infoFactory;
- private final ApprovalTypes approvalTypes;
private final ReviewDb db;
private final ChangeControl.Factory changeControlFactory;
private final AccountInfoCacheFactory aic;
@@ -68,19 +56,14 @@
private PatchSetInfo patchSetInfo;
private Change change;
private List<PatchLineComment> drafts;
- private Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
- private Map<ApprovalCategory.Id, PatchSetApproval> given;
@Inject
PatchSetPublishDetailFactory(final PatchSetInfoFactory infoFactory,
- final ProjectCache projectCache, final ApprovalTypes approvalTypes,
final ReviewDb db,
final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
final ChangeControl.Factory changeControlFactory,
final IdentifiedUser user, @Assisted final PatchSet.Id patchSetId) {
- this.projectCache = projectCache;
this.infoFactory = infoFactory;
- this.approvalTypes = approvalTypes;
this.db = db;
this.changeControlFactory = changeControlFactory;
this.aic = accountInfoCacheFactory.create();
@@ -98,15 +81,17 @@
patchSetInfo = infoFactory.get(patchSetId);
drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList();
- allowed = new HashMap<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>>();
- given = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ List<PermissionRange> allowed = Collections.emptyList();
+ List<PatchSetApproval> given = Collections.emptyList();
+
if (change.getStatus().isOpen()
&& patchSetId.equals(change.currentPatchSetId())) {
- computeAllowed();
- for (final PatchSetApproval a : db.patchSetApprovals().byPatchSetUser(
- patchSetId, user.getAccountId())) {
- given.put(a.getCategoryId(), a);
- }
+ allowed = new ArrayList<PermissionRange>(control.getLabelRanges());
+ Collections.sort(allowed);
+
+ given = db.patchSetApprovals() //
+ .byPatchSetUser(patchSetId, user.getAccountId()) //
+ .toList();
}
aic.want(change.getOwner());
@@ -117,46 +102,12 @@
detail.setPatchSetInfo(patchSetInfo);
detail.setChange(change);
detail.setDrafts(drafts);
- detail.setAllowed(allowed);
+ detail.setLabels(allowed);
detail.setGiven(given);
final CanSubmitResult canSubmitResult = control.canSubmit(patchSetId);
- detail.setSubmitAllowed(canSubmitResult == CanSubmitResult.OK);
+ detail.setCanSubmit(canSubmitResult == CanSubmitResult.OK);
return detail;
}
-
- private void computeAllowed() {
- final Set<AccountGroup.Id> am = user.getEffectiveGroups();
- final ProjectState pe = projectCache.get(change.getProject());
- for (ApprovalCategory.Id category : approvalTypes.getApprovalCategories()) {
- RefControl rc = pe.controlFor(user).controlForRef(change.getDest());
- List<RefRight> categoryRights = rc.getApplicableRights(category);
- computeAllowed(am, categoryRights, category);
- }
- }
-
- private void computeAllowed(final Set<AccountGroup.Id> am,
- final List<RefRight> list, ApprovalCategory.Id category) {
-
- Set<ApprovalCategoryValue.Id> s = allowed.get(category);
- if (s == null) {
- s = new HashSet<ApprovalCategoryValue.Id>();
- allowed.put(category, s);
- }
-
- for (final RefRight r : list) {
- if (!am.contains(r.getAccountGroupId())) {
- continue;
- }
- final ApprovalType at =
- approvalTypes.getApprovalType(r.getApprovalCategoryId());
- for (short m = r.getMinValue(); m <= r.getMaxValue(); m++) {
- final ApprovalCategoryValue v = at.getValue(m);
- if (v != null) {
- s.add(v.getId());
- }
- }
- }
- }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
index da52596..c81c2e9 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
@@ -187,11 +187,13 @@
for (final PatchSetApproval ca : db.patchSetApprovals()
.byPatchSetUser(ps_id, aid)) {
final ApprovalCategory.Id category = ca.getCategoryId();
- if (change.getStatus().isOpen()) {
- fs.normalize(approvalTypes.getApprovalType(category), ca);
+ if (ApprovalCategory.SUBMIT.equals(category)) {
+ continue;
}
- if (ca.getValue() == 0
- || ApprovalCategory.SUBMIT.equals(category)) {
+ if (change.getStatus().isOpen()) {
+ fs.normalize(approvalTypes.byId(category), ca);
+ }
+ if (ca.getValue() == 0) {
continue;
}
psas.put(category, ca);
@@ -231,11 +233,13 @@
for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(ps_id)) {
final ApprovalCategory.Id category = ca.getCategoryId();
- if (change.getStatus().isOpen()) {
- fs.normalize(approvalTypes.getApprovalType(category), ca);
+ if (ApprovalCategory.SUBMIT.equals(category)) {
+ continue;
}
- if (ca.getValue() == 0
- || ApprovalCategory.SUBMIT.equals(category)) {
+ if (change.getStatus().isOpen()) {
+ fs.normalize(approvalTypes.byId(category), ca);
+ }
+ if (ca.getValue() == 0) {
continue;
}
boolean keep = true;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddRefRight.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddRefRight.java
deleted file mode 100644
index 358b542..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddRefRight.java
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (C) 2010 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.httpd.rpc.project;
-
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.common.data.ProjectDetail;
-import com.google.gerrit.common.errors.InvalidNameException;
-import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.NoSuchRefException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.project.RefControl;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
-
-import java.util.Collections;
-
-import javax.annotation.Nullable;
-
-class AddRefRight extends Handler<ProjectDetail> {
- interface Factory {
- AddRefRight create(@Assisted Project.NameKey projectName,
- @Assisted ApprovalCategory.Id categoryId,
- @Assisted("groupName") String groupName,
- @Nullable @Assisted("refPattern") String refPattern,
- @Assisted("min") short min, @Assisted("max") short max);
- }
-
- private final ProjectDetailFactory.Factory projectDetailFactory;
- private final ProjectControl.Factory projectControlFactory;
- private final ProjectCache projectCache;
- private final GroupCache groupCache;
- private final ReviewDb db;
- private final ApprovalTypes approvalTypes;
-
- private final Project.NameKey projectName;
- private final ApprovalCategory.Id categoryId;
- private final AccountGroup.NameKey groupName;
- private final String refPattern;
- private final short min;
- private final short max;
-
- @Inject
- AddRefRight(final ProjectDetailFactory.Factory projectDetailFactory,
- final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final GroupCache groupCache,
- final ReviewDb db, final ApprovalTypes approvalTypes,
-
- @Assisted final Project.NameKey projectName,
- @Assisted final ApprovalCategory.Id categoryId,
- @Assisted("groupName") final String groupName,
- @Nullable @Assisted("refPattern") final String refPattern,
- @Assisted("min") final short min, @Assisted("max") final short max) {
- this.projectDetailFactory = projectDetailFactory;
- this.projectControlFactory = projectControlFactory;
- this.projectCache = projectCache;
- this.groupCache = groupCache;
- this.approvalTypes = approvalTypes;
- this.db = db;
-
- this.projectName = projectName;
- this.categoryId = categoryId;
- this.groupName = new AccountGroup.NameKey(groupName);
- this.refPattern = refPattern != null ? refPattern.trim() : null;
-
- if (min <= max) {
- this.min = min;
- this.max = max;
- } else {
- this.min = max;
- this.max = min;
- }
- }
-
- @Override
- public ProjectDetail call() throws NoSuchProjectException, OrmException,
- NoSuchGroupException, InvalidNameException, NoSuchRefException {
- final ProjectControl projectControl =
- projectControlFactory.controlFor(projectName);
-
- final ApprovalType at = approvalTypes.getApprovalType(categoryId);
- if (at == null || at.getValue(min) == null || at.getValue(max) == null) {
- throw new IllegalArgumentException("Invalid category " + categoryId
- + " or range " + min + ".." + max);
- }
-
- String refPattern = this.refPattern;
- if (refPattern == null || refPattern.isEmpty()) {
- if (categoryId.equals(ApprovalCategory.SUBMIT)
- || categoryId.equals(ApprovalCategory.PUSH_HEAD)) {
- // Explicitly related to a branch head.
- refPattern = Constants.R_HEADS + "*";
-
- } else if (!at.getCategory().isAction()) {
- // Non actions are approval votes on a change, assume these apply
- // to branch heads only.
- refPattern = Constants.R_HEADS + "*";
-
- } else if (categoryId.equals(ApprovalCategory.PUSH_TAG)) {
- // Explicitly related to the tag namespace.
- refPattern = Constants.R_TAGS + "*";
-
- } else if (categoryId.equals(ApprovalCategory.READ)
- || categoryId.equals(ApprovalCategory.OWN)) {
- // Currently these are project-wide rights, so apply that way.
- refPattern = RefRight.ALL;
-
- } else {
- // Assume project wide for the default.
- refPattern = RefRight.ALL;
- }
- }
-
- boolean exclusive = refPattern.startsWith("-");
- if (exclusive) {
- refPattern = refPattern.substring(1);
- }
-
- while (refPattern.startsWith("/")) {
- refPattern = refPattern.substring(1);
- }
-
- if (refPattern.startsWith(RefRight.REGEX_PREFIX)) {
- String example = RefControl.shortestExample(refPattern);
-
- if (!example.startsWith(Constants.R_REFS)) {
- refPattern = RefRight.REGEX_PREFIX + Constants.R_HEADS
- + refPattern.substring(RefRight.REGEX_PREFIX.length());
- example = RefControl.shortestExample(refPattern);
- }
-
- if (!Repository.isValidRefName(example)) {
- throw new InvalidNameException();
- }
-
- } else {
- if (!refPattern.startsWith(Constants.R_REFS)) {
- refPattern = Constants.R_HEADS + refPattern;
- }
-
- if (refPattern.endsWith("/*")) {
- final String prefix = refPattern.substring(0, refPattern.length() - 2);
- if (!"refs".equals(prefix) && !Repository.isValidRefName(prefix)) {
- throw new InvalidNameException();
- }
- } else {
- if (!Repository.isValidRefName(refPattern)) {
- throw new InvalidNameException();
- }
- }
- }
-
- if (!projectControl.controlForRef(refPattern).isOwner()) {
- throw new NoSuchRefException(refPattern);
- }
-
- if (exclusive) {
- refPattern = "-" + refPattern;
- }
-
- final AccountGroup group = groupCache.get(groupName);
- if (group == null) {
- throw new NoSuchGroupException(groupName);
- }
- final RefRight.Key key =
- new RefRight.Key(projectName, new RefRight.RefPattern(refPattern),
- categoryId, group.getId());
- RefRight rr = db.refRights().get(key);
- if (rr == null) {
- rr = new RefRight(key);
- rr.setMinValue(min);
- rr.setMaxValue(max);
- db.refRights().insert(Collections.singleton(rr));
- } else {
- rr.setMinValue(min);
- rr.setMaxValue(max);
- db.refRights().update(Collections.singleton(rr));
- }
- projectCache.evictAll();
- return projectDetailFactory.create(projectName).call();
- }
-}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
new file mode 100644
index 0000000..ffe44a3
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -0,0 +1,206 @@
+// Copyright (C) 2010 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.httpd.rpc.project;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.common.data.ProjectAccess;
+import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.RefControl;
+import com.google.gwtorm.client.OrmConcurrencyException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+class ChangeProjectAccess extends Handler<ProjectAccess> {
+ interface Factory {
+ ChangeProjectAccess create(@Assisted Project.NameKey projectName,
+ @Assisted ObjectId base, @Assisted List<AccessSection> sectionList,
+ @Nullable @Assisted String message);
+ }
+
+ private final ProjectAccessFactory.Factory projectAccessFactory;
+ private final ProjectControl.Factory projectControlFactory;
+ private final ProjectCache projectCache;
+ private final GroupCache groupCache;
+ private final MetaDataUpdate.User metaDataUpdateFactory;
+
+ private final Project.NameKey projectName;
+ private final ObjectId base;
+ private List<AccessSection> sectionList;
+ private String message;
+
+ @Inject
+ ChangeProjectAccess(final ProjectAccessFactory.Factory projectAccessFactory,
+ final ProjectControl.Factory projectControlFactory,
+ final ProjectCache projectCache, final GroupCache groupCache,
+ final MetaDataUpdate.User metaDataUpdateFactory,
+
+ @Assisted final Project.NameKey projectName,
+ @Assisted final ObjectId base, @Assisted List<AccessSection> sectionList,
+ @Nullable @Assisted String message) {
+ this.projectAccessFactory = projectAccessFactory;
+ this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
+ this.groupCache = groupCache;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+
+ this.projectName = projectName;
+ this.base = base;
+ this.sectionList = sectionList;
+ this.message = message;
+ }
+
+ @Override
+ public ProjectAccess call() throws NoSuchProjectException, IOException,
+ ConfigInvalidException, InvalidNameException, NoSuchGroupException,
+ OrmConcurrencyException {
+ final ProjectControl projectControl =
+ projectControlFactory.controlFor(projectName);
+
+ final MetaDataUpdate md;
+ try {
+ md = metaDataUpdateFactory.create(projectName);
+ } catch (RepositoryNotFoundException notFound) {
+ throw new NoSuchProjectException(projectName);
+ }
+ try {
+ ProjectConfig config = ProjectConfig.read(md, base);
+ Set<String> toDelete = scanSectionNames(config);
+
+ for (AccessSection section : mergeSections(sectionList)) {
+ final String name = section.getRefPattern();
+ if (!projectControl.controlForRef(name).isOwner()) {
+ continue;
+ }
+
+ if (name.startsWith(AccessSection.REGEX_PREFIX)) {
+ if (!Repository.isValidRefName(RefControl.shortestExample(name))) {
+ throw new InvalidNameException();
+ }
+
+ } else if (name.equals(AccessSection.ALL)) {
+ // This is a special case we have to allow, it fails below.
+
+ } else if (name.endsWith("/*")) {
+ String prefix = name.substring(0, name.length() - 2);
+ if (!Repository.isValidRefName(prefix)) {
+ throw new InvalidNameException();
+ }
+
+ } else if (!Repository.isValidRefName(name)) {
+ throw new InvalidNameException();
+ }
+
+ for (Permission permission : section.getPermissions()) {
+ for (PermissionRule rule : permission.getRules()) {
+ lookupGroup(rule);
+ }
+ }
+
+ config.replace(section);
+ toDelete.remove(section.getRefPattern());
+ }
+
+ for (String name : toDelete) {
+ if (projectControl.controlForRef(name).isOwner()) {
+ config.remove(config.getAccessSection(name));
+ }
+ }
+
+ if (message != null && !message.isEmpty()) {
+ if (!message.endsWith("\n")) {
+ message += "\n";
+ }
+ md.setMessage(message);
+ } else {
+ md.setMessage("Modify access rules\n");
+ }
+
+ if (config.commit(md)) {
+ projectCache.evict(config.getProject());
+ return projectAccessFactory.create(projectName).call();
+
+ } else {
+ throw new OrmConcurrencyException("Cannot update " + projectName);
+ }
+ } finally {
+ md.close();
+ }
+ }
+
+ private static List<AccessSection> mergeSections(List<AccessSection> src) {
+ Map<String, AccessSection> map = new LinkedHashMap<String, AccessSection>();
+ for (AccessSection section : src) {
+ if (section.getPermissions().isEmpty()) {
+ continue;
+ }
+
+ AccessSection prior = map.get(section.getRefPattern());
+ if (prior != null) {
+ prior.mergeFrom(section);
+ } else {
+ map.put(section.getRefPattern(), section);
+ }
+ }
+ return new ArrayList<AccessSection>(map.values());
+ }
+
+ private static Set<String> scanSectionNames(ProjectConfig config) {
+ Set<String> names = new HashSet<String>();
+ for (AccessSection section : config.getAccessSections()) {
+ names.add(section.getRefPattern());
+ }
+ return names;
+ }
+
+ private void lookupGroup(PermissionRule rule) throws NoSuchGroupException {
+ GroupReference ref = rule.getGroup();
+ if (ref.getUUID() == null) {
+ AccountGroup.NameKey name = new AccountGroup.NameKey(ref.getName());
+ AccountGroup group = groupCache.get(name);
+ if (group == null) {
+ throw new NoSuchGroupException(name);
+ }
+ ref.setUUID(group.getGroupUUID());
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java
index 88c85b8f..2b0f856 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java
@@ -17,16 +17,21 @@
import com.google.gerrit.common.data.ProjectDetail;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtorm.client.OrmConcurrencyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.util.Collections;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+
+import java.io.IOException;
class ChangeProjectSettings extends Handler<ProjectDetail> {
interface Factory {
@@ -36,8 +41,8 @@
private final ProjectDetailFactory.Factory projectDetailFactory;
private final ProjectControl.Factory projectControlFactory;
private final ProjectCache projectCache;
- private final ReviewDb db;
- private final GitRepositoryManager repoManager;
+ private final GitRepositoryManager mgr;
+ private final MetaDataUpdate.User metaDataUpdateFactory;
private final Project update;
@@ -45,14 +50,14 @@
ChangeProjectSettings(
final ProjectDetailFactory.Factory projectDetailFactory,
final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final ReviewDb db,
- final GitRepositoryManager grm,
+ final ProjectCache projectCache, final GitRepositoryManager mgr,
+ final MetaDataUpdate.User metaDataUpdateFactory,
@Assisted final Project update) {
this.projectDetailFactory = projectDetailFactory;
this.projectControlFactory = projectControlFactory;
this.projectCache = projectCache;
- this.db = db;
- this.repoManager = grm;
+ this.mgr = mgr;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
this.update = update;
}
@@ -60,20 +65,34 @@
@Override
public ProjectDetail call() throws NoSuchProjectException, OrmException {
final Project.NameKey projectName = update.getNameKey();
- final ProjectControl projectControl =
- projectControlFactory.ownerFor(projectName);
+ projectControlFactory.ownerFor(projectName);
- final Project proj = db.projects().get(projectName);
- if (proj == null) {
+ final MetaDataUpdate md;
+ try {
+ md = metaDataUpdateFactory.create(projectName);
+ } catch (RepositoryNotFoundException notFound) {
throw new NoSuchProjectException(projectName);
}
+ try {
+ // TODO We really should take advantage of the Git commit DAG and
+ // ensure the current version matches the old version the caller read.
+ //
+ ProjectConfig config = ProjectConfig.read(md);
+ config.getProject().copySettingsFrom(update);
- proj.copySettingsFrom(update);
- db.projects().update(Collections.singleton(proj));
- projectCache.evict(proj);
-
- if (!projectControl.getProjectState().isSpecialWildProject()) {
- repoManager.setProjectDescription(projectName, update.getDescription());
+ md.setMessage("Modified project settings\n");
+ if (config.commit(md)) {
+ mgr.setProjectDescription(projectName, update.getDescription());
+ projectCache.evict(config.getProject());
+ } else {
+ throw new OrmConcurrencyException("Cannot update " + projectName);
+ }
+ } catch (ConfigInvalidException err) {
+ throw new OrmException("Cannot read project " + projectName, err);
+ } catch (IOException err) {
+ throw new OrmException("Cannot update project " + projectName, err);
+ } finally {
+ md.close();
}
return projectDetailFactory.create(projectName).call();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteRefRights.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteRefRights.java
deleted file mode 100644
index ae8b98b..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteRefRights.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2010 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.httpd.rpc.project;
-
-import com.google.gerrit.common.data.ProjectDetail;
-import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.NoSuchRefException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-import java.util.Set;
-
-class DeleteRefRights extends Handler<ProjectDetail> {
- interface Factory {
- DeleteRefRights create(@Assisted Project.NameKey projectName,
- @Assisted Set<RefRight.Key> toRemove);
- }
-
- private final ProjectDetailFactory.Factory projectDetailFactory;
- private final ProjectControl.Factory projectControlFactory;
- private final ProjectCache projectCache;
- private final ReviewDb db;
-
- private final Project.NameKey projectName;
- private final Set<RefRight.Key> toRemove;
-
- @Inject
- DeleteRefRights(final ProjectDetailFactory.Factory projectDetailFactory,
- final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final ReviewDb db,
-
- @Assisted final Project.NameKey projectName,
- @Assisted final Set<RefRight.Key> toRemove) {
- this.projectDetailFactory = projectDetailFactory;
- this.projectControlFactory = projectControlFactory;
- this.projectCache = projectCache;
- this.db = db;
-
- this.projectName = projectName;
- this.toRemove = toRemove;
- }
-
- @Override
- public ProjectDetail call() throws NoSuchProjectException, OrmException,
- NoSuchRefException {
- final ProjectControl projectControl =
- projectControlFactory.controlFor(projectName);
-
- for (final RefRight.Key k : toRemove) {
- if (!projectName.equals(k.getProjectNameKey())) {
- throw new IllegalArgumentException("All keys must be from same project");
- }
- String refPattern = k.getRefPattern();
- if (refPattern.startsWith("-")) {
- refPattern = refPattern.substring(1);
- }
- if (!projectControl.controlForRef(refPattern).isOwner()) {
- throw new NoSuchRefException(refPattern);
- }
- }
-
- for (final RefRight.Key k : toRemove) {
- final RefRight m = db.refRights().get(k);
- if (m != null) {
- db.refRights().delete(Collections.singleton(m));
- }
- }
- projectCache.evictAll();
- return projectDetailFactory.create(projectName).call();
- }
-}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
new file mode 100644
index 0000000..3ae3ac5
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -0,0 +1,135 @@
+// Copyright (C) 2011 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.httpd.rpc.project;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.ProjectAccess;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.config.WildProjectName;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.RefControl;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class ProjectAccessFactory extends Handler<ProjectAccess> {
+ interface Factory {
+ ProjectAccessFactory create(@Assisted Project.NameKey name);
+ }
+
+ private final GroupCache groupCache;
+ private final ProjectCache projectCache;
+ private final ProjectControl.Factory projectControlFactory;
+ private final MetaDataUpdate.Server metaDataUpdateFactory;
+ private final Project.NameKey wildProject;
+
+ private final Project.NameKey projectName;
+ private ProjectControl pc;
+
+ @Inject
+ ProjectAccessFactory(final GroupCache groupCache,
+ final ProjectCache projectCache,
+ final ProjectControl.Factory projectControlFactory,
+ final MetaDataUpdate.Server metaDataUpdateFactory,
+ @WildProjectName final Project.NameKey wildProject,
+
+
+ @Assisted final Project.NameKey name) {
+ this.groupCache = groupCache;
+ this.projectCache = projectCache;
+ this.projectControlFactory = projectControlFactory;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.wildProject = wildProject;
+
+ this.projectName = name;
+ }
+
+ @Override
+ public ProjectAccess call() throws NoSuchProjectException, IOException,
+ ConfigInvalidException {
+ pc = open();
+
+ // Load the current configuration from the repository, ensuring its the most
+ // recent version available. If it differs from what was in the project
+ // state, force a cache flush now.
+ //
+ ProjectConfig config;
+ MetaDataUpdate md = metaDataUpdateFactory.create(projectName);
+ try {
+ config = ProjectConfig.read(md);
+
+ if (config.updateGroupNames(groupCache)) {
+ md.setMessage("Update group names\n");
+ if (config.commit(md)) {
+ projectCache.evict(config.getProject());
+ pc = open();
+ }
+ } else if (config.getRevision() != null
+ && !config.getRevision().equals(
+ pc.getProjectState().getConfig().getRevision())) {
+ projectCache.evict(config.getProject());
+ pc = open();
+ }
+ } finally {
+ md.close();
+ }
+
+ List<AccessSection> local = new ArrayList<AccessSection>();
+ Set<String> ownerOf = new HashSet<String>();
+ for (AccessSection section : config.getAccessSections()) {
+ RefControl rc = pc.controlForRef(section.getRefPattern());
+ if (rc.isOwner()) {
+ local.add(section);
+ ownerOf.add(section.getRefPattern());
+ } else if (rc.isVisible()) {
+ local.add(section);
+ }
+ }
+
+ final ProjectAccess detail = new ProjectAccess();
+ detail.setRevision(config.getRevision().name());
+ detail.setLocal(local);
+ detail.setOwnerOf(ownerOf);
+
+ if (projectName.equals(wildProject)) {
+ detail.setInheritsFrom(null);
+ } else if (config.getProject().getParent() != null) {
+ detail.setInheritsFrom(config.getProject().getParent());
+ } else {
+ detail.setInheritsFrom(wildProject);
+ }
+
+ return detail;
+ }
+
+ private ProjectControl open() throws NoSuchProjectException {
+ return projectControlFactory.validateFor( //
+ projectName, //
+ ProjectControl.OWNER | ProjectControl.VISIBLE);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
index 9b06dd9..0f9ffff 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
@@ -14,46 +14,48 @@
package com.google.gerrit.httpd.rpc.project;
+import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ListBranchesResult;
+import com.google.gerrit.common.data.ProjectAccess;
import com.google.gerrit.common.data.ProjectAdminService;
import com.google.gerrit.common.data.ProjectDetail;
-import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;
+import org.eclipse.jgit.lib.ObjectId;
+
import java.util.List;
import java.util.Set;
class ProjectAdminServiceImpl implements ProjectAdminService {
private final AddBranch.Factory addBranchFactory;
+ private final ChangeProjectAccess.Factory changeProjectAccessFactory;
private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
private final DeleteBranches.Factory deleteBranchesFactory;
private final ListBranches.Factory listBranchesFactory;
private final VisibleProjects.Factory visibleProjectsFactory;
+ private final ProjectAccessFactory.Factory projectAccessFactory;
private final ProjectDetailFactory.Factory projectDetailFactory;
- private final AddRefRight.Factory addRefRightFactory;
- private final DeleteRefRights.Factory deleteRefRightsFactory;
@Inject
ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
+ final ChangeProjectAccess.Factory changeProjectAccessFactory,
final ChangeProjectSettings.Factory changeProjectSettingsFactory,
final DeleteBranches.Factory deleteBranchesFactory,
final ListBranches.Factory listBranchesFactory,
final VisibleProjects.Factory visibleProjectsFactory,
- final ProjectDetailFactory.Factory projectDetailFactory,
- final AddRefRight.Factory addRefRightFactory,
- final DeleteRefRights.Factory deleteRefRightsFactory) {
+ final ProjectAccessFactory.Factory projectAccessFactory,
+ final ProjectDetailFactory.Factory projectDetailFactory) {
this.addBranchFactory = addBranchFactory;
+ this.changeProjectAccessFactory = changeProjectAccessFactory;
this.changeProjectSettingsFactory = changeProjectSettingsFactory;
this.deleteBranchesFactory = deleteBranchesFactory;
this.listBranchesFactory = listBranchesFactory;
this.visibleProjectsFactory = visibleProjectsFactory;
+ this.projectAccessFactory = projectAccessFactory;
this.projectDetailFactory = projectDetailFactory;
- this.addRefRightFactory = addRefRightFactory;
- this.deleteRefRightsFactory = deleteRefRightsFactory;
}
@Override
@@ -68,24 +70,23 @@
}
@Override
+ public void projectAccess(final Project.NameKey projectName,
+ final AsyncCallback<ProjectAccess> callback) {
+ projectAccessFactory.create(projectName).to(callback);
+ }
+
+ @Override
public void changeProjectSettings(final Project update,
final AsyncCallback<ProjectDetail> callback) {
changeProjectSettingsFactory.create(update).to(callback);
}
@Override
- public void deleteRight(final Project.NameKey projectName,
- final Set<RefRight.Key> toRemove, final AsyncCallback<ProjectDetail> callback) {
- deleteRefRightsFactory.create(projectName, toRemove).to(callback);
- }
-
- @Override
- public void addRight(final Project.NameKey projectName,
- final ApprovalCategory.Id categoryId, final String groupName,
- final String refPattern, final short min, final short max,
- final AsyncCallback<ProjectDetail> callback) {
- addRefRightFactory.create(projectName, categoryId, groupName, refPattern,
- min, max).to(callback);
+ public void changeProjectAccess(Project.NameKey projectName,
+ String baseRevision, String msg, List<AccessSection> sections,
+ AsyncCallback<ProjectAccess> cb) {
+ ObjectId base = ObjectId.fromString(baseRevision);
+ changeProjectAccessFactory.create(projectName, base, sections, msg).to(cb);
}
@Override
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java
index ef632c4..1eb940b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java
@@ -14,50 +14,28 @@
package com.google.gerrit.httpd.rpc.project;
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.common.data.InheritedRefRight;
import com.google.gerrit.common.data.ProjectDetail;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.project.RefControl;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
class ProjectDetailFactory extends Handler<ProjectDetail> {
interface Factory {
ProjectDetailFactory create(@Assisted Project.NameKey name);
}
- private final ApprovalTypes approvalTypes;
- private final GroupCache groupCache;
private final ProjectControl.Factory projectControlFactory;
private final Project.NameKey projectName;
- private Map<AccountGroup.Id, AccountGroup> groups;
@Inject
- ProjectDetailFactory(final ApprovalTypes approvalTypes,
- final GroupCache groupCache,
- final ProjectControl.Factory projectControlFactory,
+ ProjectDetailFactory(final ProjectControl.Factory projectControlFactory,
@Assisted final Project.NameKey name) {
- this.approvalTypes = approvalTypes;
- this.groupCache = groupCache;
this.projectControlFactory = projectControlFactory;
this.projectName = name;
@@ -72,88 +50,13 @@
final ProjectDetail detail = new ProjectDetail();
detail.setProject(projectState.getProject());
- groups = new HashMap<AccountGroup.Id, AccountGroup>();
- final List<InheritedRefRight> refRights = new ArrayList<InheritedRefRight>();
-
- for (final RefRight r : projectState.getInheritedRights()) {
- RefControl rc = pc.controlForRef(r.getRefPattern());
- boolean isOwner = rc.isOwner();
-
- if (!isOwner && !rc.isVisible()) {
- continue;
- }
-
- InheritedRefRight refRight = new InheritedRefRight(r, true, isOwner);
- if (!refRights.contains(refRight)) {
- refRights.add(refRight);
- wantGroup(r.getAccountGroupId());
- }
- }
-
- for (final RefRight r : projectState.getLocalRights()) {
- RefControl rc = pc.controlForRef(r.getRefPattern());
- boolean isOwner = rc.isOwner();
-
- if (!isOwner && !rc.isVisible()) {
- continue;
- }
-
- refRights.add(new InheritedRefRight(r, false, isOwner));
- wantGroup(r.getAccountGroupId());
- }
-
- loadGroups();
-
- Collections.sort(refRights, new Comparator<InheritedRefRight>() {
- @Override
- public int compare(final InheritedRefRight a, final InheritedRefRight b) {
- final RefRight right1 = a.getRight();
- final RefRight right2 = b.getRight();
- int rc = categoryOf(right1).compareTo(categoryOf(right2));
- if (rc == 0) {
- rc = right1.getRefPattern().compareTo(right2.getRefPattern());
- }
- if (rc == 0) {
- rc = groupOf(right1).compareTo(groupOf(right2));
- }
- return rc;
- }
-
- private String categoryOf(final RefRight r) {
- final ApprovalType type =
- approvalTypes.getApprovalType(r.getApprovalCategoryId());
- if (type == null) {
- return r.getApprovalCategoryId().get();
- }
- return type.getCategory().getName();
- }
-
- private String groupOf(final RefRight r) {
- return groups.get(r.getAccountGroupId()).getName();
- }
- });
-
final boolean userIsOwner = pc.isOwner();
final boolean userIsOwnerAnyRef = pc.isOwnerAnyRef();
- detail.setRights(refRights);
- detail.setGroups(groups);
detail.setCanModifyAccess(userIsOwnerAnyRef);
detail.setCanModifyAgreements(userIsOwner);
detail.setCanModifyDescription(userIsOwner);
detail.setCanModifyMergeType(userIsOwner);
return detail;
}
-
- private void wantGroup(final AccountGroup.Id id) {
- groups.put(id, null);
- }
-
- private void loadGroups() {
- final Set<AccountGroup.Id> toGet = groups.keySet();
- groups = new HashMap<AccountGroup.Id, AccountGroup>();
- for (AccountGroup.Id groupId : toGet) {
- groups.put(groupId, groupCache.get(groupId));
- }
- }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
index 7932c79..68b3625 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
@@ -29,12 +29,12 @@
@Override
protected void configure() {
factory(AddBranch.Factory.class);
- factory(AddRefRight.Factory.class);
+ factory(ChangeProjectAccess.Factory.class);
factory(ChangeProjectSettings.Factory.class);
factory(DeleteBranches.Factory.class);
- factory(DeleteRefRights.Factory.class);
factory(ListBranches.Factory.class);
factory(VisibleProjects.Factory.class);
+ factory(ProjectAccessFactory.Factory.class);
factory(ProjectDetailFactory.Factory.class);
}
});
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java
index 2588350..31ba77e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java
@@ -17,11 +17,9 @@
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
-import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import java.util.ArrayList;
@@ -35,33 +33,26 @@
}
private final ProjectControl.Factory projectControlFactory;
- private final CurrentUser user;
- private final ReviewDb db;
+ private final ProjectCache projectCache;
@Inject
VisibleProjects(final ProjectControl.Factory projectControlFactory,
- final CurrentUser user, final ReviewDb db) {
+ final ProjectCache projectCache) {
this.projectControlFactory = projectControlFactory;
- this.user = user;
- this.db = db;
+ this.projectCache = projectCache;
}
@Override
- public List<Project> call() throws OrmException {
- final List<Project> result;
- if (user.isAdministrator()) {
- result = db.projects().all().toList();
- } else {
- result = new ArrayList<Project>();
- for (Project p : db.projects().all().toList()) {
- try {
- ProjectControl c = projectControlFactory.controlFor(p.getNameKey());
- if (c.isVisible() || c.isOwner()) {
- result.add(p);
- }
- } catch (NoSuchProjectException e) {
- continue;
+ public List<Project> call() {
+ List<Project> result = new ArrayList<Project>();
+ for (Project.NameKey p : projectCache.all()) {
+ try {
+ ProjectControl c = projectControlFactory.controlFor(p);
+ if (c.isVisible() || c.isOwner()) {
+ result.add(c.getProject());
}
+ } catch (NoSuchProjectException e) {
+ continue;
}
}
Collections.sort(result, new Comparator<Project>() {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
index 71512f2..94cad95 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
@@ -24,8 +24,6 @@
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.GroupCacheImpl;
import com.google.gerrit.server.cache.CachePool;
@@ -47,7 +45,6 @@
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
@@ -94,13 +91,10 @@
gitInjector = dbInjector.createChildInjector(new AbstractModule() {
@Override
protected void configure() {
- bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in(
Scopes.SINGLETON);
bind(String.class).annotatedWith(CanonicalWebUrl.class)
.toProvider(CanonicalWebUrlProvider.class).in(Scopes.SINGLETON);
- bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class)
- .toProvider(GerritPersonIdentProvider.class).in(Scopes.SINGLETON);
bind(CachePool.class);
install(AccountCacheImpl.module());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 7127027..68ac33d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -31,9 +31,7 @@
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.git.GitProjectImporter;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.schema.SchemaUpdater;
import com.google.gerrit.server.schema.UpdateUI;
import com.google.gerrit.server.util.HostPlatform;
@@ -62,9 +60,6 @@
@Option(name = "--batch", usage = "Batch mode; skip interactive prompting")
private boolean batchMode;
- @Option(name = "--import-projects", usage = "Import git repositories as projects")
- private boolean importProjects;
-
@Option(name = "--no-auto-start", usage = "Don't automatically start daemon after init")
private boolean noAutoStart;
@@ -73,7 +68,6 @@
ErrorLogFile.errorOnlyConsole();
final SiteInit init = createSiteInit();
- init.flags.importProjects = importProjects;
init.flags.autoStart = !noAutoStart && init.site.isNew;
final SiteRun run;
@@ -83,7 +77,6 @@
run = createSiteRun(init);
run.upgradeSchema();
- run.importGit();
} catch (Exception failure) {
if (init.flags.deleteOnFailure) {
recursiveDelete(getSitePath());
@@ -166,7 +159,6 @@
final SchemaUpdater schemaUpdater;
final SchemaFactory<ReviewDb> schema;
final GitRepositoryManager repositoryManager;
- final GitProjectImporter gitProjectImporter;
final Browser browser;
@Inject
@@ -174,14 +166,13 @@
final SchemaUpdater schemaUpdater,
final SchemaFactory<ReviewDb> schema,
final GitRepositoryManager repositoryManager,
- final GitProjectImporter gitProjectImporter, final Browser browser) {
+ final Browser browser) {
this.ui = ui;
this.site = site;
this.flags = flags;
this.schemaUpdater = schemaUpdater;
this.schema = schema;
this.repositoryManager = repositoryManager;
- this.gitProjectImporter = gitProjectImporter;
this.browser = browser;
}
@@ -241,23 +232,6 @@
}
}
- void importGit() throws OrmException, IOException {
- if (flags.importProjects) {
- gitProjectImporter.run(new GitProjectImporter.Messages() {
- @Override
- public void info(String msg) {
- System.err.println(msg);
- System.err.flush();
- }
-
- @Override
- public void warning(String msg) {
- info(msg);
- }
- });
- }
- }
-
void start() throws Exception {
if (flags.autoStart) {
if (HostPlatform.isWin32()) {
@@ -317,9 +291,6 @@
protected void configure() {
bind(ConsoleUI.class).toInstance(init.ui);
bind(InitFlags.class).toInstance(init.flags);
-
- bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
- bind(GitProjectImporter.class);
}
});
return createDbInjector(SINGLE_USER).createChildInjector(modules);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java
index beeed24..45f560a 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
@@ -26,7 +25,6 @@
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
@@ -56,7 +54,6 @@
private List<Change> todo;
private Injector dbInjector;
- private Injector gitInjector;
@Inject
private TrackingFooters footers;
@@ -74,17 +71,9 @@
}
dbInjector = createDbInjector(MULTI_USER);
- gitInjector = dbInjector.createChildInjector(new LifecycleModule() {
- @Override
- protected void configure() {
- bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
- listener().to(LocalDiskRepositoryManager.Lifecycle.class);
- }
- });
-
- manager.add(dbInjector, gitInjector);
+ manager.add(dbInjector);
manager.start();
- gitInjector.injectMembers(this);
+ dbInjector.injectMembers(this);
final ReviewDb db = database.open();
try {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
index 992c616..5d71b48 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
@@ -30,9 +30,6 @@
/** Recursively delete the site path if initialization fails. */
public boolean deleteOnFailure;
- /** Run the Git project importer after initialization. */
- public boolean importProjects;
-
/** Run the daemon (and open the web UI in a browser) after initialization. */
public boolean autoStart;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java
index 2d95577..f0cd31f 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java
@@ -25,14 +25,11 @@
/** Initialize the GitRepositoryManager configuration section. */
@Singleton
class InitGitManager implements InitStep {
- private final InitFlags flags;
private final ConsoleUI ui;
private final Section gerrit;
@Inject
- InitGitManager(final InitFlags flags, final ConsoleUI ui,
- final Section.Factory sections) {
- this.flags = flags;
+ InitGitManager(final ConsoleUI ui, final Section.Factory sections) {
this.ui = ui;
this.gerrit = sections.get("gerrit");
}
@@ -44,11 +41,7 @@
if (d == null) {
throw die("gerrit.basePath is required");
}
- if (d.exists()) {
- if (!flags.importProjects && d.list() != null && d.list().length > 0) {
- flags.importProjects = ui.yesno(true, "Import existing repositories");
- }
- } else if (!d.mkdirs()) {
+ if (!d.exists() && !d.mkdirs()) {
throw die("Cannot create " + d);
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
index 8e3306b..340168c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.DatabaseModule;
+import com.google.gerrit.server.schema.SchemaModule;
import com.google.gwtorm.client.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.CreationException;
@@ -162,6 +163,7 @@
});
modules.add(new GerritServerConfigModule());
modules.add(new DatabaseModule());
+ modules.add(new SchemaModule());
try {
return Guice.createInjector(PRODUCTION, modules);
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java
index d2aceaa..5d8a4b9 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java
@@ -46,6 +46,39 @@
}
}
+ /** Globally unique identifier. */
+ public static class UUID extends
+ StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(id = 1, length = 40)
+ protected String uuid;
+
+ protected UUID() {
+ }
+
+ public UUID(final String n) {
+ uuid = n;
+ }
+
+ @Override
+ public String get() {
+ return uuid;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ uuid = newValue;
+ }
+
+ /** Parse an AccountGroup.UUID out of a string representation. */
+ public static UUID parse(final String str) {
+ final UUID r = new UUID();
+ r.fromString(str);
+ return r;
+ }
+ }
+
/** Distinguished name, within organization directory server. */
public static class ExternalNameKey extends
StringKey<com.google.gwtorm.client.Key<?>> {
@@ -140,6 +173,18 @@
LDAP;
}
+ /** Common UUID assigned to the "Project Owners" placeholder group. */
+ public static final AccountGroup.UUID PROJECT_OWNERS =
+ new AccountGroup.UUID("global:Project-Owners");
+
+ /** Common UUID assigned to the "Anonymous Users" group. */
+ public static final AccountGroup.UUID ANONYMOUS_USERS =
+ new AccountGroup.UUID("global:Anonymous-Users");
+
+ /** Common UUID assigned to the "Registered Users" group. */
+ public static final AccountGroup.UUID REGISTERED_USERS =
+ new AccountGroup.UUID("global:Registered-Users");
+
/** Unique name of this group within the system. */
@Column(id = 1)
protected NameKey name;
@@ -176,15 +221,20 @@
@Column(id = 8)
protected boolean emailOnlyAuthors;
+ /** Globally unique identifier name for this group. */
+ @Column(id = 9)
+ protected UUID groupUUID;
+
protected AccountGroup() {
}
public AccountGroup(final AccountGroup.NameKey newName,
- final AccountGroup.Id newId) {
+ final AccountGroup.Id newId, final AccountGroup.UUID uuid) {
name = newName;
groupId = newId;
ownerGroupId = groupId;
visibleToAll = false;
+ groupUUID = uuid;
setType(Type.INTERNAL);
}
@@ -251,4 +301,12 @@
public void setEmailOnlyAuthors(boolean emailOnlyAuthors) {
this.emailOnlyAuthors = emailOnlyAuthors;
}
+
+ public AccountGroup.UUID getGroupUUID() {
+ return groupUUID;
+ }
+
+ public void setGroupUUID(AccountGroup.UUID uuid) {
+ groupUUID = uuid;
+ }
}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java
index 2530654..7eb7ed2 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java
@@ -25,6 +25,9 @@
@PrimaryKey("groupId")
AccountGroup get(AccountGroup.Id id) throws OrmException;
+ @Query("WHERE groupUUID = ?")
+ ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) throws OrmException;
+
@Query("WHERE externalName = ?")
ResultSet<AccountGroup> byExternalName(AccountGroup.ExternalNameKey name)
throws OrmException;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java
index 29ea97e..d7e5024 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java
@@ -24,33 +24,6 @@
public static final ApprovalCategory.Id SUBMIT =
new ApprovalCategory.Id("SUBM");
- /** Id of the special "Read" action (and category). */
- public static final ApprovalCategory.Id READ =
- new ApprovalCategory.Id("READ");
-
- /** Id of the special "Own" category; manages a project. */
- public static final ApprovalCategory.Id OWN = new ApprovalCategory.Id("OWN");
-
- /** Id of the special "Push Annotated Tag" action (and category). */
- public static final ApprovalCategory.Id PUSH_TAG =
- new ApprovalCategory.Id("pTAG");
- public static final short PUSH_TAG_SIGNED = 1;
- public static final short PUSH_TAG_ANNOTATED = 2;
-
- /** Id of the special "Push Branch" action (and category). */
- public static final ApprovalCategory.Id PUSH_HEAD =
- new ApprovalCategory.Id("pHD");
- public static final short PUSH_HEAD_UPDATE = 1;
- public static final short PUSH_HEAD_CREATE = 2;
- public static final short PUSH_HEAD_REPLACE = 3;
-
- /** Id of the special "Forge Identity" category. */
- public static final ApprovalCategory.Id FORGE_IDENTITY =
- new ApprovalCategory.Id("FORG");
- public static final short FORGE_AUTHOR = 1;
- public static final short FORGE_COMMITTER = 2;
- public static final short FORGE_SERVER = 3;
-
public static class Id extends StringKey<Key<?>> {
private static final long serialVersionUID = 1L;
@@ -73,15 +46,6 @@
protected void set(String newValue) {
id = newValue;
}
-
- /** True if the right can be assigned on the wild project. */
- public boolean canBeOnWildProject() {
- if (OWN.equals(this)) {
- return false;
- } else {
- return true;
- }
- }
}
/** Internal short unique identifier for this category. */
@@ -96,16 +60,7 @@
@Column(id = 3, length = 4, notNull = false)
protected String abbreviatedName;
- /**
- * Order of this category within the Approvals table when presented.
- * <p>
- * If < 0 (e.g. -1) this category is not shown in the Approvals table but is
- * instead considered to be an action that the user might be able to perform,
- * e.g. "Submit".
- * <p>
- * If >= 0 this category is shown in the Approvals table, sorted along with
- * its siblings by <code>position, name</code>.
- */
+ /** Order of this category within the Approvals table when presented. */
@Column(id = 4)
protected short position;
@@ -117,6 +72,9 @@
@Column(id = 6)
protected boolean copyMinScore;
+ /** Computed name derived from {@link #name}. */
+ protected String labelName;
+
protected ApprovalCategory() {
}
@@ -136,6 +94,26 @@
public void setName(final String n) {
name = n;
+ labelName = null;
+ }
+
+ /** Clean version of {@link #getName()}, e.g. "Code Review" is "Code-Review". */
+ public String getLabelName() {
+ if (labelName == null) {
+ StringBuilder r = new StringBuilder();
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ if (('0' <= c && c <= '9') //
+ || ('a' <= c && c <= 'z') //
+ || ('A' <= c && c <= 'Z')) {
+ r.append(c);
+ } else if (c == ' ') {
+ r.append('-');
+ }
+ }
+ labelName = r.toString();
+ }
+ return labelName;
}
public String getAbbreviatedName() {
@@ -154,14 +132,6 @@
position = p;
}
- public boolean isAction() {
- return position < 0;
- }
-
- public boolean isRange() {
- return !isAction();
- }
-
public String getFunctionName() {
return functionName;
}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
index 409547a..49f3333 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
@@ -21,7 +21,7 @@
public final class Project {
/** Project name key */
public static class NameKey extends
- StringKey<com.google.gwtorm.client.Key<?>> {
+ StringKey<com.google.gwtorm.client.Key<?>> implements Comparable<NameKey> {
private static final long serialVersionUID = 1L;
@Column(id = 1)
@@ -44,6 +44,11 @@
name = newValue;
}
+ @Override
+ public int compareTo(NameKey other) {
+ return get().compareTo(other.get());
+ }
+
/** Parse a Project.NameKey out of a string representation. */
public static NameKey parse(final String str) {
final NameKey r = new NameKey();
@@ -53,65 +58,37 @@
}
public static enum SubmitType {
- FAST_FORWARD_ONLY('F'),
+ FAST_FORWARD_ONLY,
- MERGE_IF_NECESSARY('M'),
+ MERGE_IF_NECESSARY,
- MERGE_ALWAYS('A'),
+ MERGE_ALWAYS,
- CHERRY_PICK('C');
-
- private final char code;
-
- private SubmitType(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static SubmitType forCode(final char c) {
- for (final SubmitType s : SubmitType.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
+ CHERRY_PICK;
}
- @Column(id = 1)
protected NameKey name;
- @Column(id = 2, length = Integer.MAX_VALUE, notNull = false)
protected String description;
- @Column(id = 3)
protected boolean useContributorAgreements;
- @Column(id = 4)
protected boolean useSignedOffBy;
- @Column(id = 5)
- protected char submitType;
+ protected SubmitType submitType;
- @Column(id = 6, notNull = false, name = "parent_name")
protected NameKey parent;
- @Column(id = 7)
protected boolean requireChangeID;
- @Column(id = 8)
protected boolean useContentMerge;
protected Project() {
}
- public Project(final Project.NameKey newName) {
- name = newName;
- useContributorAgreements = true;
- setSubmitType(SubmitType.MERGE_IF_NECESSARY);
+ public Project(Project.NameKey nameKey) {
+ name = nameKey;
+ submitType = SubmitType.MERGE_IF_NECESSARY;
}
public Project.NameKey getNameKey() {
@@ -119,7 +96,7 @@
}
public String getName() {
- return name.get();
+ return name != null ? name.get() : null;
}
public String getDescription() {
@@ -163,11 +140,11 @@
}
public SubmitType getSubmitType() {
- return SubmitType.forCode(submitType);
+ return submitType;
}
public void setSubmitType(final SubmitType type) {
- submitType = type.getCode();
+ submitType = type;
}
public void copySettingsFrom(final Project update) {
@@ -183,7 +160,11 @@
return parent;
}
- public void setParent(final Project.NameKey parentProjectName) {
- parent = parentProjectName;
+ public String getParentName() {
+ return parent != null ? parent.get() : null;
+ }
+
+ public void setParentName(String n) {
+ parent = n != null ? new NameKey(n) : null;
}
}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
deleted file mode 100644
index b9adada..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 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.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface ProjectAccess extends Access<Project, Project.NameKey> {
- @PrimaryKey("name")
- Project get(Project.NameKey name) throws OrmException;
-
- @Query("ORDER BY name")
- ResultSet<Project> all() 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/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRight.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRight.java
deleted file mode 100644
index 97ee219..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRight.java
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright (C) 2010 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.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-import com.google.gwtorm.client.StringKey;
-
-import java.util.Comparator;
-
-/** Grant to use an {@link ApprovalCategory} in the scope of a git ref. */
-public final class RefRight {
- /** Pattern that matches all references in a project. */
- public static final String ALL = "refs/*";
-
- /** Prefix that triggers a regular expression pattern. */
- public static final String REGEX_PREFIX = "^";
-
- public static class RefPattern extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1)
- protected String pattern;
-
- protected RefPattern() {
- }
-
- public RefPattern(final String pattern) {
- this.pattern = pattern;
- }
-
- @Override
- public String get() {
- return pattern;
- }
-
- @Override
- protected void set(String pattern) {
- this.pattern = pattern;
- }
- }
-
- public static class Key extends CompoundKey<Project.NameKey> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1)
- protected Project.NameKey projectName;
-
- @Column(id = 2)
- protected RefPattern refPattern;
-
- @Column(id = 3)
- protected ApprovalCategory.Id categoryId;
-
- @Column(id = 4)
- protected AccountGroup.Id groupId;
-
- protected Key() {
- projectName = new Project.NameKey();
- refPattern = new RefPattern();
- categoryId = new ApprovalCategory.Id();
- groupId = new AccountGroup.Id();
- }
-
- public Key(final Project.NameKey projectName, final RefPattern refPattern,
- final ApprovalCategory.Id categoryId, final AccountGroup.Id groupId) {
- this.projectName = projectName;
- this.refPattern = refPattern;
- this.categoryId = categoryId;
- this.groupId = groupId;
- }
-
- @Override
- public Project.NameKey getParentKey() {
- return projectName;
- }
-
- public Project.NameKey getProjectNameKey() {
- return projectName;
- }
-
- public String getRefPattern() {
- return refPattern.get();
- }
-
- public void setGroupId(AccountGroup.Id groupId) {
- this.groupId = groupId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {refPattern, categoryId,
- groupId};
- }
- }
-
- @Column(id = 1, name = Column.NONE)
- protected Key key;
-
- @Column(id = 2)
- protected short minValue;
-
- @Column(id = 3)
- protected short maxValue;
-
- protected RefRight() {
- }
-
- public RefRight(RefRight.Key key) {
- this.key = key;
- }
-
- public RefRight(final RefRight refRight, final AccountGroup.Id groupId) {
- this(new RefRight.Key(refRight.getKey().projectName,
- refRight.getKey().refPattern, refRight.getKey().categoryId, groupId));
- setMinValue(refRight.getMinValue());
- setMaxValue(refRight.getMaxValue());
- }
-
- public RefRight.Key getKey() {
- return key;
- }
-
- public String getRefPattern() {
- if (isExclusive()) {
- return key.refPattern.get().substring(1);
- }
- return key.refPattern.get();
- }
-
- public String getRefPatternForDisplay() {
- return key.refPattern.get();
- }
-
- public Project.NameKey getProjectNameKey() {
- return getKey().getProjectNameKey();
- }
-
- public boolean isExclusive() {
- return key.refPattern.get().startsWith("-");
- }
-
- public ApprovalCategory.Id getApprovalCategoryId() {
- return key.categoryId;
- }
-
- public AccountGroup.Id getAccountGroupId() {
- return key.groupId;
- }
-
- public short getMinValue() {
- return minValue;
- }
-
- public void setMinValue(final short m) {
- minValue = m;
- }
-
- public short getMaxValue() {
- return maxValue;
- }
-
- public void setMaxValue(final short m) {
- maxValue = m;
- }
-
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("{group :");
- s.append(getAccountGroupId().get());
- s.append(", proj :");
- s.append(getProjectNameKey().get());
- s.append(", cat :");
- s.append(getApprovalCategoryId().get());
- s.append(", pattern :");
- s.append(getRefPatternForDisplay());
- s.append(", min :");
- s.append(getMinValue());
- s.append(", max :");
- s.append(getMaxValue());
- s.append("}");
- return s.toString();
- }
-
- @Override
- public int hashCode() {
- return getKey().hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof RefRight) {
- RefRight a = this;
- RefRight b = (RefRight) o;
- return a.getKey().equals(b.getKey())
- && a.getMinValue() == b.getMinValue()
- && a.getMaxValue() == b.getMaxValue();
- }
- return false;
- }
-
- public static final Comparator<RefRight> REF_PATTERN_ORDER =
- new Comparator<RefRight>() {
-
- @Override
- public int compare(RefRight a, RefRight b) {
- int aLength = a.getRefPattern().length();
- int bLength = b.getRefPattern().length();
- if (bLength == aLength) {
- ApprovalCategory.Id aCat = a.getApprovalCategoryId();
- ApprovalCategory.Id bCat = b.getApprovalCategoryId();
- if (aCat.get().equals(bCat.get())) {
- return a.getRefPattern().compareTo(b.getRefPattern());
- }
- return a.getApprovalCategoryId().get()
- .compareTo(b.getApprovalCategoryId().get());
- }
- return bLength - aLength;
- }
- };
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRightAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRightAccess.java
deleted file mode 100644
index a42ff2c..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRightAccess.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2010 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.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface RefRightAccess extends Access<RefRight, RefRight.Key> {
- @PrimaryKey("key")
- RefRight get(RefRight.Key refRight) throws OrmException;
-
- @Query("WHERE key.projectName = ?")
- ResultSet<RefRight> byProject(Project.NameKey project) throws OrmException;
-
- @Query("WHERE key.categoryId = ? AND key.groupId = ?")
- ResultSet<RefRight> byCategoryGroup(ApprovalCategory.Id cat,
- AccountGroup.Id group) throws OrmException;
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java
index 49e07ff..b75b91b 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java
@@ -24,7 +24,6 @@
* <p>
* Root entities that are at the top level of some important data graph:
* <ul>
- * <li>{@link Project}: Configuration for a single Git repository.</li>
* <li>{@link Account}: Per-user account registration, preferences, identity.</li>
* <li>{@link Change}: All review information about a single proposed change.</li>
* <li>{@link SystemConfig}: Server-wide settings, managed by administrator.</li>
@@ -94,9 +93,6 @@
AccountPatchReviewAccess accountPatchReviews();
@Relation
- ProjectAccess projects();
-
- @Relation
ChangeAccess changes();
@Relation
@@ -115,9 +111,6 @@
PatchLineCommentAccess patchComments();
@Relation
- RefRightAccess refRights();
-
- @Relation
TrackingIdAccess trackingIds();
/** Create the next unique id for an {@link Account}. */
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
index 6ff23ed..229d173 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
@@ -65,6 +65,8 @@
/** Identity of the administration group; those with full access. */
@Column(id = 4)
public AccountGroup.Id adminGroupId;
+ @Column(id = 10)
+ public AccountGroup.UUID adminGroupUUID;
/** Identity of the anonymous group, which permits anyone. */
@Column(id = 5)
@@ -81,6 +83,8 @@
/** Identity of the batch users group */
@Column(id = 8)
public AccountGroup.Id batchUsersGroupId;
+ @Column(id = 11)
+ public AccountGroup.UUID batchUsersGroupUUID;
/** Identity of the owner group, which permits any project owner. */
@Column(id = 9)
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql
index d33d24d..9784a4d 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql
@@ -158,14 +158,6 @@
-- *********************************************************************
--- RefRightAccess
--- @PrimaryKey covers: byProject
--- covers: byCategoryGroup
-CREATE INDEX ref_rights_byCatGroup
-ON ref_rights (category_id, group_id);
-
-
--- *********************************************************************
-- TrackingIdAccess
--
CREATE INDEX tracking_ids_byTrkId
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql
index 8e3cead..db6894d 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql
@@ -240,14 +240,6 @@
-- *********************************************************************
--- RefRightAccess
--- @PrimaryKey covers: byProject
--- covers: byCategoryGroup
-CREATE INDEX ref_rights_byCatGroup
-ON ref_rights (category_id, group_id);
-
-
--- *********************************************************************
-- TrackingIdAccess
--
CREATE INDEX tracking_ids_byTrkId
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
index 1a4a863..8205946 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -433,8 +433,10 @@
Entry<ApprovalCategory.Id, ApprovalCategoryValue.Id> approval) {
ApprovalAttribute a = new ApprovalAttribute();
a.type = approval.getKey().get();
- final ApprovalType at = approvalTypes.getApprovalType(approval.getKey());
- a.description = at.getCategory().getName();
+ ApprovalType at = approvalTypes.byId(approval.getKey());
+ if (at != null) {
+ a.description = at.getCategory().getName();
+ }
a.value = Short.toString(approval.getValue().get());
return a;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java
index 1bd2066..3a450ed 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java
@@ -34,8 +34,8 @@
}
@Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
- return authConfig.getAnonymousGroups();
+ public Set<AccountGroup.UUID> getEffectiveGroups() {
+ return Collections.singleton(AccountGroup.ANONYMOUS_USERS);
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java
index 0d8d19a..512de00 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java
@@ -56,7 +56,7 @@
*
* @return active groups for this user.
*/
- public abstract Set<AccountGroup.Id> getEffectiveGroups();
+ public abstract Set<AccountGroup.UUID> getEffectiveGroups();
/** Set of changes starred by this user. */
public abstract Set<Change.Id> getStarredChanges();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
index e46957d..5fb5a7f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -43,11 +43,14 @@
import java.net.MalformedURLException;
import java.net.SocketAddress;
import java.net.URL;
+import java.util.AbstractSet;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.Set;
@@ -139,6 +142,28 @@
private static final Logger log =
LoggerFactory.getLogger(IdentifiedUser.class);
+ private static final Set<AccountGroup.UUID> registeredGroups =
+ new AbstractSet<AccountGroup.UUID>() {
+ private final List<AccountGroup.UUID> groups =
+ Collections.unmodifiableList(Arrays.asList(new AccountGroup.UUID[] {
+ AccountGroup.ANONYMOUS_USERS, AccountGroup.REGISTERED_USERS}));
+
+ @Override
+ public boolean contains(Object o) {
+ return groups.contains(o);
+ }
+
+ @Override
+ public Iterator<AccountGroup.UUID> iterator() {
+ return groups.iterator();
+ }
+
+ @Override
+ public int size() {
+ return groups.size();
+ }
+ };
+
private final Provider<String> canonicalUrl;
private final Realm realm;
private final AccountCache accountCache;
@@ -154,7 +179,7 @@
private AccountState state;
private Set<String> emailAddresses;
- private Set<AccountGroup.Id> effectiveGroups;
+ private Set<AccountGroup.UUID> effectiveGroups;
private Set<Change.Id> starredChanges;
private Collection<AccountProjectWatch> notificationFilters;
@@ -217,14 +242,14 @@
}
@Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
+ public Set<AccountGroup.UUID> getEffectiveGroups() {
if (effectiveGroups == null) {
- Set<AccountGroup.Id> seedGroups;
+ Set<AccountGroup.UUID> seedGroups;
if (authConfig.isIdentityTrustable(state().getExternalIds())) {
seedGroups = realm.groups(state());
} else {
- seedGroups = authConfig.getRegisteredGroups();
+ seedGroups = registeredGroups;
}
effectiveGroups = getIncludedGroups(seedGroups);
@@ -233,14 +258,14 @@
return effectiveGroups;
}
- private Set<AccountGroup.Id> getIncludedGroups(Set<AccountGroup.Id> seedGroups) {
- Set<AccountGroup.Id> includes = new HashSet<AccountGroup.Id> (seedGroups);
- Queue<AccountGroup.Id> groupQueue = new LinkedList<AccountGroup.Id> (seedGroups);
+ private Set<AccountGroup.UUID> getIncludedGroups(Set<AccountGroup.UUID> seedGroups) {
+ Set<AccountGroup.UUID> includes = new HashSet<AccountGroup.UUID> (seedGroups);
+ Queue<AccountGroup.UUID> groupQueue = new LinkedList<AccountGroup.UUID> (seedGroups);
while (groupQueue.size() > 0) {
- AccountGroup.Id id = groupQueue.remove();
+ AccountGroup.UUID id = groupQueue.remove();
- for (final AccountGroup.Id groupId : groupIncludeCache.getByInclude(id)) {
+ for (final AccountGroup.UUID groupId : groupIncludeCache.getByInclude(id)) {
if (includes.add(groupId)) {
groupQueue.add(groupId);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java
index 26dec09..a4a72a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java
@@ -36,21 +36,21 @@
PeerDaemonUser create(@Assisted SocketAddress peer);
}
- private final Set<AccountGroup.Id> effectiveGroups;
+ private final Set<AccountGroup.UUID> effectiveGroups;
private final SocketAddress peer;
@Inject
protected PeerDaemonUser(AuthConfig authConfig, @Assisted SocketAddress peer) {
super(AccessPath.SSH_COMMAND, authConfig);
- final HashSet<AccountGroup.Id> g = new HashSet<AccountGroup.Id>();
+ final HashSet<AccountGroup.UUID> g = new HashSet<AccountGroup.UUID>();
g.add(authConfig.getAdministratorsGroup());
this.effectiveGroups = Collections.unmodifiableSet(g);
this.peer = peer;
}
@Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
+ public Set<AccountGroup.UUID> getEffectiveGroups() {
return effectiveGroups;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java
index 5c44bfe..afb8800 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java
@@ -28,18 +28,18 @@
public class ReplicationUser extends CurrentUser {
/** Magic set of groups enabling read of any project and reference. */
- public static final Set<AccountGroup.Id> EVERYTHING_VISIBLE =
- Collections.unmodifiableSet(new HashSet<AccountGroup.Id>(0));
+ public static final Set<AccountGroup.UUID> EVERYTHING_VISIBLE =
+ Collections.unmodifiableSet(new HashSet<AccountGroup.UUID>(0));
public interface Factory {
- ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups);
+ ReplicationUser create(@Assisted Set<AccountGroup.UUID> authGroups);
}
- private final Set<AccountGroup.Id> effectiveGroups;
+ private final Set<AccountGroup.UUID> effectiveGroups;
@Inject
protected ReplicationUser(AuthConfig authConfig,
- @Assisted Set<AccountGroup.Id> authGroups) {
+ @Assisted Set<AccountGroup.UUID> authGroups) {
super(AccessPath.REPLICATION, authConfig);
if (authGroups == EVERYTHING_VISIBLE) {
@@ -53,12 +53,12 @@
}
}
- private static Set<AccountGroup.Id> copy(Set<AccountGroup.Id> groups) {
- return Collections.unmodifiableSet(new HashSet<AccountGroup.Id>(groups));
+ private static Set<AccountGroup.UUID> copy(Set<AccountGroup.UUID> groups) {
+ return Collections.unmodifiableSet(new HashSet<AccountGroup.UUID>(groups));
}
@Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
+ public Set<AccountGroup.UUID> getEffectiveGroups() {
return effectiveGroups;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
index 52ccc66..aea13c7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -22,7 +22,6 @@
import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator;
-import com.google.gerrit.server.config.AuthConfig;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
@@ -90,18 +89,13 @@
static class ByIdLoader extends EntryCreator<Account.Id, AccountState> {
private final SchemaFactory<ReviewDb> schema;
- private final Set<AccountGroup.Id> registered;
- private final Set<AccountGroup.Id> anonymous;
private final GroupCache groupCache;
private final Cache<String, Account.Id> byName;
@Inject
- ByIdLoader(SchemaFactory<ReviewDb> sf, AuthConfig auth,
- GroupCache groupCache,
+ ByIdLoader(SchemaFactory<ReviewDb> sf, GroupCache groupCache,
@Named(BYUSER_NAME) Cache<String, Account.Id> byUsername) {
this.schema = sf;
- this.registered = auth.getRegisteredGroups();
- this.anonymous = auth.getAnonymousGroups();
this.groupCache = groupCache;
this.byName = byUsername;
}
@@ -133,21 +127,18 @@
Collections.unmodifiableCollection(db.accountExternalIds().byAccount(
who).toList());
- Set<AccountGroup.Id> internalGroups = new HashSet<AccountGroup.Id>();
+ Set<AccountGroup.UUID> internalGroups = new HashSet<AccountGroup.UUID>();
for (AccountGroupMember g : db.accountGroupMembers().byAccount(who)) {
final AccountGroup.Id groupId = g.getAccountGroupId();
final AccountGroup group = groupCache.get(groupId);
if (group != null && group.getType() == AccountGroup.Type.INTERNAL) {
- internalGroups.add(groupId);
+ internalGroups.add(group.getGroupUUID());
}
}
- if (internalGroups.isEmpty()) {
- internalGroups = registered;
- } else {
- internalGroups.addAll(registered);
- internalGroups = Collections.unmodifiableSet(internalGroups);
- }
+ internalGroups.add(AccountGroup.REGISTERED_USERS);
+ internalGroups.add(AccountGroup.ANONYMOUS_USERS);
+ internalGroups = Collections.unmodifiableSet(internalGroups);
return new AccountState(account, internalGroups, externalIds);
}
@@ -156,6 +147,8 @@
public AccountState missing(final Account.Id accountId) {
final Account account = new Account(accountId);
final Collection<AccountExternalId> ids = Collections.emptySet();
+ final Set<AccountGroup.UUID> anonymous =
+ Collections.singleton(AccountGroup.ANONYMOUS_USERS);
return new AccountState(account, anonymous, ids);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index 5cb8f36..a388eef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -275,7 +275,9 @@
// is going to be the site's administrator and just make them that
// to bootstrap the authentication database.
//
- final AccountGroup.Id admin = authConfig.getAdministratorsGroup();
+ final AccountGroup.UUID uuid = authConfig.getAdministratorsGroup();
+ final AccountGroup g = db.accountGroups().byUUID(uuid).iterator().next();
+ final AccountGroup.Id admin = g.getId();
final AccountGroupMember m =
new AccountGroupMember(new AccountGroupMember.Key(newId, admin));
db.accountGroupMembersAudit().insert(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
index 9393227..1b036d8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
@@ -26,11 +26,11 @@
public class AccountState {
private final Account account;
- private final Set<AccountGroup.Id> internalGroups;
+ private final Set<AccountGroup.UUID> internalGroups;
private final Collection<AccountExternalId> externalIds;
public AccountState(final Account account,
- final Set<AccountGroup.Id> actualGroups,
+ final Set<AccountGroup.UUID> actualGroups,
final Collection<AccountExternalId> externalIds) {
this.account = account;
this.internalGroups = actualGroups;
@@ -89,7 +89,7 @@
}
/** The set of groups maintained directly within the Gerrit database. */
- public Set<AccountGroup.Id> getInternalGroups() {
+ public Set<AccountGroup.UUID> getInternalGroups() {
return internalGroups;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
index a836f54..77afb13 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -51,7 +51,7 @@
}
@Override
- public Set<AccountGroup.Id> groups(final AccountState who) {
+ public Set<AccountGroup.UUID> groups(final AccountState who) {
return who.getInternalGroups();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
index 978d9c2..6dce197 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
@@ -24,6 +24,8 @@
public AccountGroup get(AccountGroup.NameKey name);
+ public AccountGroup get(AccountGroup.UUID uuid);
+
public Collection<AccountGroup> get(AccountGroup.ExternalNameKey externalName);
public void evict(AccountGroup group);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
index d948aef..4d6dbc1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -20,7 +20,6 @@
import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator;
-import com.google.gerrit.server.config.AuthConfig;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -29,12 +28,14 @@
import com.google.inject.name.Named;
import java.util.Collection;
+import java.util.List;
/** Tracks group objects in memory for efficient access. */
@Singleton
public class GroupCacheImpl implements GroupCache {
private static final String BYID_NAME = "groups";
private static final String BYNAME_NAME = "groups_byname";
+ private static final String BYUUID_NAME = "groups_byuuid";
private static final String BYEXT_NAME = "groups_byext";
public static Module module() {
@@ -49,6 +50,10 @@
new TypeLiteral<Cache<AccountGroup.NameKey, AccountGroup>>() {};
core(byName, BYNAME_NAME).populateWith(ByNameLoader.class);
+ final TypeLiteral<Cache<AccountGroup.UUID, AccountGroup>> byUUID =
+ new TypeLiteral<Cache<AccountGroup.UUID, AccountGroup>>() {};
+ core(byUUID, BYUUID_NAME).populateWith(ByUUIDLoader.class);
+
final TypeLiteral<Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>>> byExternalName =
new TypeLiteral<Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>>>() {};
core(byExternalName, BYEXT_NAME) //
@@ -62,15 +67,18 @@
private final Cache<AccountGroup.Id, AccountGroup> byId;
private final Cache<AccountGroup.NameKey, AccountGroup> byName;
+ private final Cache<AccountGroup.UUID, AccountGroup> byUUID;
private final Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>> byExternalName;
@Inject
GroupCacheImpl(
@Named(BYID_NAME) Cache<AccountGroup.Id, AccountGroup> byId,
@Named(BYNAME_NAME) Cache<AccountGroup.NameKey, AccountGroup> byName,
+ @Named(BYUUID_NAME) Cache<AccountGroup.UUID, AccountGroup> byUUID,
@Named(BYEXT_NAME) Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>> byExternalName) {
this.byId = byId;
this.byName = byName;
+ this.byUUID = byUUID;
this.byExternalName = byExternalName;
}
@@ -81,6 +89,7 @@
public void evict(final AccountGroup group) {
byId.remove(group.getId());
byName.remove(group.getNameKey());
+ byUUID.remove(group.getGroupUUID());
byExternalName.remove(group.getExternalNameKey());
}
@@ -92,6 +101,10 @@
return byName.get(name);
}
+ public AccountGroup get(final AccountGroup.UUID uuid) {
+ return byUUID.get(uuid);
+ }
+
public Collection<AccountGroup> get(
final AccountGroup.ExternalNameKey externalName) {
return byExternalName.get(externalName);
@@ -99,12 +112,10 @@
static class ByIdLoader extends EntryCreator<AccountGroup.Id, AccountGroup> {
private final SchemaFactory<ReviewDb> schema;
- private final AccountGroup.Id administrators;
@Inject
- ByIdLoader(final SchemaFactory<ReviewDb> sf, final AuthConfig authConfig) {
+ ByIdLoader(final SchemaFactory<ReviewDb> sf) {
schema = sf;
- administrators = authConfig.getAdministratorsGroup();
}
@Override
@@ -126,9 +137,8 @@
public AccountGroup missing(final AccountGroup.Id key) {
final AccountGroup.NameKey name =
new AccountGroup.NameKey("Deleted Group" + key.toString());
- final AccountGroup g = new AccountGroup(name, key);
+ final AccountGroup g = new AccountGroup(name, key, null);
g.setType(AccountGroup.Type.SYSTEM);
- g.setOwnerGroupId(administrators);
return g;
}
}
@@ -160,6 +170,32 @@
}
}
+ static class ByUUIDLoader extends
+ EntryCreator<AccountGroup.UUID, AccountGroup> {
+ private final SchemaFactory<ReviewDb> schema;
+
+ @Inject
+ ByUUIDLoader(final SchemaFactory<ReviewDb> sf) {
+ schema = sf;
+ }
+
+ @Override
+ public AccountGroup createEntry(final AccountGroup.UUID uuid)
+ throws Exception {
+ final ReviewDb db = schema.open();
+ try {
+ List<AccountGroup> r = db.accountGroups().byUUID(uuid).toList();
+ if (r.size() == 1) {
+ return r.get(0);
+ } else {
+ return null;
+ }
+ } finally {
+ db.close();
+ }
+ }
+ }
+
static class ByExternalNameLoader extends
EntryCreator<AccountGroup.ExternalNameKey, Collection<AccountGroup>> {
private final SchemaFactory<ReviewDb> schema;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
index 602b593..722a9e2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -39,11 +39,20 @@
if (group == null) {
throw new NoSuchGroupException(groupId);
}
- return new GroupControl(user.get(), group);
+ return new GroupControl(groupCache, user.get(), group);
+ }
+
+ public GroupControl controlFor(final AccountGroup.UUID groupId)
+ throws NoSuchGroupException {
+ final AccountGroup group = groupCache.get(groupId);
+ if (group == null) {
+ throw new NoSuchGroupException(groupId);
+ }
+ return new GroupControl(groupCache, user.get(), group);
}
public GroupControl controlFor(final AccountGroup group) {
- return new GroupControl(user.get(), group);
+ return new GroupControl(groupCache, user.get(), group);
}
public GroupControl validateFor(final AccountGroup.Id groupId)
@@ -56,10 +65,13 @@
}
}
+ private final GroupCache groupCache;
private final CurrentUser user;
private final AccountGroup group;
+ private Boolean isOwner;
- GroupControl(final CurrentUser who, final AccountGroup gc) {
+ GroupControl(GroupCache g, CurrentUser who, AccountGroup gc) {
+ groupCache = g;
user = who;
group = gc;
}
@@ -78,9 +90,13 @@
}
public boolean isOwner() {
- final AccountGroup.Id owner = group.getOwnerGroupId();
- return getCurrentUser().getEffectiveGroups().contains(owner)
- || getCurrentUser().isAdministrator();
+ if (isOwner == null) {
+ AccountGroup g = groupCache.get(group.getOwnerGroupId());
+ AccountGroup.UUID ownerUUID = g != null ? g.getGroupUUID() : null;
+ isOwner = getCurrentUser().getEffectiveGroups().contains(ownerUUID)
+ || getCurrentUser().isAdministrator();
+ }
+ return isOwner;
}
public boolean canAddMember(final Account.Id id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java
index 3088806..e5f73a3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java
@@ -20,8 +20,8 @@
/** Tracks group inclusions in memory for efficient access. */
public interface GroupIncludeCache {
- public Collection<AccountGroup.Id> getByInclude(AccountGroup.Id groupId);
+ public Collection<AccountGroup.UUID> getByInclude(AccountGroup.UUID groupId);
- public void evictInclude(AccountGroup.Id groupId);
+ public void evictInclude(AccountGroup.UUID groupId);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index 76e9231..830d01c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -21,16 +21,17 @@
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator;
import com.google.gwtorm.client.SchemaFactory;
-
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/** Tracks group inclusions in memory for efficient access. */
@Singleton
@@ -41,8 +42,8 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<AccountGroup.Id, Collection<AccountGroup.Id>>> byInclude =
- new TypeLiteral<Cache<AccountGroup.Id, Collection<AccountGroup.Id>>>() {};
+ final TypeLiteral<Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>>> byInclude =
+ new TypeLiteral<Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>>>() {};
core(byInclude, BYINCLUDE_NAME).populateWith(ByIncludeLoader.class);
bind(GroupIncludeCacheImpl.class);
@@ -51,23 +52,24 @@
};
}
- private final Cache<AccountGroup.Id, Collection<AccountGroup.Id>> byInclude;
+ private final Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>> byInclude;
@Inject
GroupIncludeCacheImpl(
- @Named(BYINCLUDE_NAME) Cache<AccountGroup.Id, Collection<AccountGroup.Id>> byInclude) {
+ @Named(BYINCLUDE_NAME) Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>> byInclude) {
this.byInclude = byInclude;
}
- public Collection<AccountGroup.Id> getByInclude(final AccountGroup.Id groupId) {
+ public Collection<AccountGroup.UUID> getByInclude(AccountGroup.UUID groupId) {
return byInclude.get(groupId);
}
- public void evictInclude(AccountGroup.Id groupId) {
+ public void evictInclude(AccountGroup.UUID groupId) {
byInclude.remove(groupId);
}
- static class ByIncludeLoader extends EntryCreator<AccountGroup.Id, Collection<AccountGroup.Id>> {
+ static class ByIncludeLoader extends
+ EntryCreator<AccountGroup.UUID, Collection<AccountGroup.UUID>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -76,14 +78,23 @@
}
@Override
- public Collection<AccountGroup.Id> createEntry(final AccountGroup.Id key) throws Exception {
+ public Collection<AccountGroup.UUID> createEntry(final AccountGroup.UUID key) throws Exception {
final ReviewDb db = schema.open();
try {
- ArrayList<AccountGroup.Id> groupArray = new ArrayList<AccountGroup.Id> ();
- for (AccountGroupInclude agi : db.accountGroupIncludes().byInclude(key)) {
- groupArray.add(agi.getGroupId());
+ List<AccountGroup> group = db.accountGroups().byUUID(key).toList();
+ if (group.size() != 1) {
+ return Collections.emptyList();
}
+ Set<AccountGroup.Id> ids = new HashSet<AccountGroup.Id>();
+ for (AccountGroupInclude agi : db.accountGroupIncludes().byInclude(group.get(0).getId())) {
+ ids.add(agi.getGroupId());
+ }
+
+ Set<AccountGroup.UUID> groupArray = new HashSet<AccountGroup.UUID> ();
+ for (AccountGroup g : db.accountGroups().get(ids)) {
+ groupArray.add(g.getGroupUUID());
+ }
return Collections.unmodifiableCollection(groupArray);
} finally {
db.close();
@@ -91,7 +102,7 @@
}
@Override
- public Collection<AccountGroup.Id> missing(final AccountGroup.Id key) {
+ public Collection<AccountGroup.UUID> missing(final AccountGroup.UUID key) {
return Collections.emptyList();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupUUID.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupUUID.java
new file mode 100644
index 0000000..9eec1df
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupUUID.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2010 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.account;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+
+import java.security.MessageDigest;
+
+public class GroupUUID {
+ public static AccountGroup.UUID make(String groupName, PersonIdent creator) {
+ MessageDigest md = Constants.newMessageDigest();
+ md.update(Constants.encode("group " + groupName + "\n"));
+ md.update(Constants.encode("creator " + creator.toExternalString() + "\n"));
+ return new AccountGroup.UUID(ObjectId.fromRaw(md.digest()).name());
+ }
+
+ private GroupUUID() {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
index aab1cda..a017588 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
@@ -23,14 +23,18 @@
import com.google.gerrit.reviewdb.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gwtorm.client.OrmDuplicateKeyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
+import org.eclipse.jgit.lib.PersonIdent;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
public class PerformCreateGroup {
@@ -43,14 +47,18 @@
private final AccountCache accountCache;
private final GroupIncludeCache groupIncludeCache;
private final IdentifiedUser currentUser;
+ private final PersonIdent serverIdent;
@Inject
PerformCreateGroup(final ReviewDb db, final AccountCache accountCache,
- final GroupIncludeCache groupIncludeCache, final IdentifiedUser currentUser) {
+ final GroupIncludeCache groupIncludeCache,
+ final IdentifiedUser currentUser,
+ @GerritPersonIdent final PersonIdent serverIdent) {
this.db = db;
this.accountCache = accountCache;
this.groupIncludeCache = groupIncludeCache;
this.currentUser = currentUser;
+ this.serverIdent = serverIdent;
}
/**
@@ -81,7 +89,11 @@
final AccountGroup.Id groupId =
new AccountGroup.Id(db.nextAccountGroupId());
final AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
- final AccountGroup group = new AccountGroup(nameKey, groupId);
+ final AccountGroup.UUID uuid = GroupUUID.make(groupName,
+ currentUser.newCommitterIdent(
+ serverIdent.getWhen(),
+ serverIdent.getTimeZone()));
+ final AccountGroup group = new AccountGroup(nameKey, groupId, uuid);
group.setVisibleToAll(visibleToAll);
if (ownerGroupId != null) {
group.setOwnerGroupId(ownerGroupId);
@@ -149,8 +161,9 @@
db.accountGroupIncludes().insert(includeList);
db.accountGroupIncludesAudit().insert(includesAudit);
- for (AccountGroup.Id includeId : groups) {
- groupIncludeCache.evictInclude(includeId);
+ for (AccountGroup group : db.accountGroups().get(
+ new HashSet<AccountGroup.Id>(groups))) {
+ groupIncludeCache.evictInclude(group.getGroupUUID());
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
index e42d4ae..072f796 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
@@ -27,7 +27,7 @@
public void onCreateAccount(AuthRequest who, Account account);
- public Set<AccountGroup.Id> groups(AccountState who);
+ public Set<AccountGroup.UUID> groups(AccountState who);
/**
* Locate an account whose local username is the given account name.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
index 675202c..da4f63a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -133,7 +133,7 @@
}
}
- Set<AccountGroup.Id> queryForGroups(final DirContext ctx,
+ Set<AccountGroup.UUID> queryForGroups(final DirContext ctx,
final String username, LdapQuery.Result account)
throws NamingException, AccountException {
final LdapSchema schema = getSchema(ctx);
@@ -175,12 +175,12 @@
}
}
- final Set<AccountGroup.Id> actual = new HashSet<AccountGroup.Id>();
+ final Set<AccountGroup.UUID> actual = new HashSet<AccountGroup.UUID>();
for (String dn : groupDNs) {
for (AccountGroup group : groupCache
.get(new AccountGroup.ExternalNameKey(dn))) {
if (group.getType() == AccountGroup.Type.LDAP) {
- actual.add(group.getId());
+ actual.add(group.getGroupUUID());
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
index 810df28..d41fe82 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
@@ -32,8 +32,8 @@
@Override
protected void configure() {
- final TypeLiteral<Cache<String, Set<AccountGroup.Id>>> groups =
- new TypeLiteral<Cache<String, Set<AccountGroup.Id>>>() {};
+ final TypeLiteral<Cache<String, Set<AccountGroup.UUID>>> groups =
+ new TypeLiteral<Cache<String, Set<AccountGroup.UUID>>>() {};
core(groups, GROUP_CACHE).maxAge(1, HOURS) //
.populateWith(LdapRealm.MemberLoader.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index de33b44..cd3f2c0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -68,14 +68,14 @@
private final Cache<String, Account.Id> usernameCache;
private final Set<Account.FieldName> readOnlyAccountFields;
- private final Cache<String, Set<AccountGroup.Id>> membershipCache;
+ private final Cache<String, Set<AccountGroup.UUID>> membershipCache;
@Inject
LdapRealm(
final Helper helper,
final AuthConfig authConfig,
final EmailExpander emailExpander,
- @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.Id>> membershipCache,
+ @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.UUID>> membershipCache,
@Named(LdapModule.USERNAME_CACHE) final Cache<String, Account.Id> usernameCache,
@GerritServerConfig final Config config) {
this.helper = helper;
@@ -241,8 +241,8 @@
}
@Override
- public Set<AccountGroup.Id> groups(final AccountState who) {
- final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>();
+ public Set<AccountGroup.UUID> groups(final AccountState who) {
+ final HashSet<AccountGroup.UUID> r = new HashSet<AccountGroup.UUID>();
r.addAll(membershipCache.get(findId(who.getExternalIds())));
r.addAll(who.getInternalGroups());
return r;
@@ -324,7 +324,7 @@
}
}
- static class MemberLoader extends EntryCreator<String, Set<AccountGroup.Id>> {
+ static class MemberLoader extends EntryCreator<String, Set<AccountGroup.UUID>> {
private final Helper helper;
@Inject
@@ -333,7 +333,7 @@
}
@Override
- public Set<AccountGroup.Id> createEntry(final String username)
+ public Set<AccountGroup.UUID> createEntry(final String username)
throws Exception {
final DirContext ctx = helper.open();
try {
@@ -348,7 +348,7 @@
}
@Override
- public Set<AccountGroup.Id> missing(final String key) {
+ public Set<AccountGroup.UUID> missing(final String key) {
return Collections.emptySet();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
index db5bceb..0afaa05 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
@@ -39,8 +39,7 @@
@Override
public ApprovalTypes get() {
- List<ApprovalType> approvalTypes = new ArrayList<ApprovalType>(2);
- List<ApprovalType> actionTypes = new ArrayList<ApprovalType>(2);
+ List<ApprovalType> types = new ArrayList<ApprovalType>(2);
try {
final ReviewDb db = schema.open();
@@ -48,12 +47,7 @@
for (final ApprovalCategory c : db.approvalCategories().all()) {
final List<ApprovalCategoryValue> values =
db.approvalCategoryValues().byCategory(c.getId()).toList();
- final ApprovalType type = new ApprovalType(c, values);
- if (type.getCategory().isAction()) {
- actionTypes.add(type);
- } else {
- approvalTypes.add(type);
- }
+ types.add(new ApprovalType(c, values));
}
} finally {
db.close();
@@ -62,8 +56,6 @@
throw new ProvisionException("Cannot query approval categories", e);
}
- approvalTypes = Collections.unmodifiableList(approvalTypes);
- actionTypes = Collections.unmodifiableList(actionTypes);
- return new ApprovalTypes(approvalTypes, actionTypes);
+ return new ApprovalTypes(Collections.unmodifiableList(types));
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
index 16a6a7c..a747ea4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -29,9 +29,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/** Authentication related settings from {@code gerrit.config}. */
@Singleton
@@ -45,10 +43,8 @@
private final boolean cookieSecure;
private final SignedToken emailReg;
- private final AccountGroup.Id administratorGroup;
- private final Set<AccountGroup.Id> anonymousGroups;
- private final Set<AccountGroup.Id> registeredGroups;
- private final AccountGroup.Id batchUsersGroup;
+ private final AccountGroup.UUID administratorGroup;
+ private final AccountGroup.UUID batchUsersGroup;
private final boolean allowGoogleAccountUpgrade;
@@ -64,13 +60,8 @@
cookieSecure = cfg.getBoolean("auth", "cookiesecure", false);
emailReg = new SignedToken(5 * 24 * 60 * 60, s.registerEmailPrivateKey);
- final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>(2);
- r.add(s.anonymousGroupId);
- r.add(s.registeredGroupId);
- registeredGroups = Collections.unmodifiableSet(r);
- anonymousGroups = Collections.singleton(s.anonymousGroupId);
- administratorGroup = s.adminGroupId;
- batchUsersGroup = s.batchUsersGroupId;
+ administratorGroup = s.adminGroupUUID;
+ batchUsersGroup = s.batchUsersGroupUUID;
if (authType == AuthType.OPENID) {
allowGoogleAccountUpgrade =
@@ -127,25 +118,15 @@
}
/** Identity of the magic group with full powers. */
- public AccountGroup.Id getAdministratorsGroup() {
+ public AccountGroup.UUID getAdministratorsGroup() {
return administratorGroup;
}
/** Identity of the group whose service is degraded to lower priority. */
- public AccountGroup.Id getBatchUsersGroup() {
+ public AccountGroup.UUID getBatchUsersGroup() {
return batchUsersGroup;
}
- /** Groups that all users, including anonymous users, belong to. */
- public Set<AccountGroup.Id> getAnonymousGroups() {
- return anonymousGroups;
- }
-
- /** Groups that all users who have created an account belong to. */
- public Set<AccountGroup.Id> getRegisteredGroups() {
- return registeredGroups;
- }
-
/** OpenID identities which the server permits for authentication. */
public List<OpenIdProviderPattern> getAllowedOpenIDs() {
return allowedOpenIDs;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
index f24dcc5..3c102ba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
@@ -310,10 +310,10 @@
* @return the actual groups resolved from the database. If no groups are
* found, returns an empty {@code Set}, never {@code null}.
*/
- public static Set<AccountGroup.Id> groupsFor(
+ public static Set<AccountGroup.UUID> groupsFor(
SchemaFactory<ReviewDb> dbfactory, String[] groupNames, Logger log,
String groupNotFoundWarning) {
- final Set<AccountGroup.Id> result = new HashSet<AccountGroup.Id>();
+ final Set<AccountGroup.UUID> result = new HashSet<AccountGroup.UUID>();
try {
final ReviewDb db = dbfactory.open();
try {
@@ -322,9 +322,16 @@
db.accountGroupNames().get(new AccountGroup.NameKey(name));
if (group == null) {
log.warn(MessageFormat.format(groupNotFoundWarning, name));
- } else {
- result.add(group.getId());
+ continue;
}
+
+ AccountGroup ag = db.accountGroups().get(group.getId());
+ if (ag == null) {
+ log.warn(MessageFormat.format(groupNotFoundWarning, name));
+ continue;
+ }
+
+ result.add(ag.getGroupUUID());
}
} finally {
db.close();
@@ -345,7 +352,7 @@
* @return the actual groups resolved from the database. If no groups are
* found, returns an empty {@code Set}, never {@code null}.
*/
- public static Set<AccountGroup.Id> groupsFor(
+ public static Set<AccountGroup.UUID> groupsFor(
SchemaFactory<ReviewDb> dbfactory, String[] groupNames, Logger log) {
return groupsFor(dbfactory, groupNames, log,
"Group \"{0}\" not in database, skipping.");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 5426f34..3f400a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -23,8 +23,6 @@
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.FileTypeRegistry;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
import com.google.gerrit.server.ReplicationUser;
@@ -41,8 +39,7 @@
import com.google.gerrit.server.cache.CachePool;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.git.ChangeMergeQueue;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.gerrit.server.git.GitModule;
import com.google.gerrit.server.git.MergeQueue;
import com.google.gerrit.server.git.PushAllProjectsOp;
import com.google.gerrit.server.git.PushReplication;
@@ -71,7 +68,6 @@
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.RuntimeConstants;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
import java.util.Properties;
@@ -143,9 +139,6 @@
SINGLETON);
bind(AnonymousUser.class);
- bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider(
- GerritPersonIdentProvider.class);
-
bind(IdGenerator.class);
bind(CachePool.class);
install(AccountByEmailCacheImpl.module());
@@ -155,13 +148,13 @@
install(PatchListCacheImpl.module());
install(ProjectCacheImpl.module());
install(new AccessControlModule());
+ install(new GitModule());
factory(AccountInfoCacheFactory.Factory.class);
factory(GroupInfoCacheFactory.Factory.class);
factory(ProjectState.Factory.class);
factory(RefControl.Factory.class);
- bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
bind(WorkQueue.class);
bind(ToolsCatalog.class);
@@ -189,7 +182,6 @@
install(new LifecycleModule() {
@Override
protected void configure() {
- listener().to(LocalDiskRepositoryManager.Lifecycle.class);
listener().to(CachePool.Lifecycle.class);
listener().to(WorkQueue.Lifecycle.class);
listener().to(VelocityLifecycle.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index f6dad7d..52525e7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -24,6 +24,7 @@
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.server.git.CreateCodeReviewNotes;
import com.google.gerrit.server.git.MergeOp;
+import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.mail.AbandonedSender;
import com.google.gerrit.server.mail.AddReviewerSender;
@@ -49,6 +50,7 @@
bind(ReviewDb.class).toProvider(RequestScopedReviewDbProvider.class).in(
RequestScoped.class);
bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
+ bind(MetaDataUpdate.User.class).in(RequestScoped.class);
bind(AccountResolver.class);
bind(ChangeQueryRewriter.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
index 9af6d62..1c13b9e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
@@ -22,21 +22,17 @@
import org.eclipse.jgit.lib.Config;
import java.util.Collections;
-import java.util.HashSet;
public class GitReceivePackGroupsProvider extends GroupSetProvider {
@Inject
public GitReceivePackGroupsProvider(@GerritServerConfig Config config,
- AuthConfig authConfig, SchemaFactory<ReviewDb> db) {
+ SchemaFactory<ReviewDb> db) {
super(config, db, "receive", null, "allowGroup");
// If no group was set, default to "registered users"
//
if (groupIds.isEmpty()) {
- HashSet<AccountGroup.Id> all = new HashSet<AccountGroup.Id>();
- all.addAll(authConfig.getRegisteredGroups());
- all.removeAll(authConfig.getAnonymousGroups());
- groupIds = Collections.unmodifiableSet(all);
+ groupIds = Collections.singleton(AccountGroup.REGISTERED_USERS);
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
index bfb09a5..65e6900 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
@@ -27,15 +27,15 @@
public class GitUploadPackGroupsProvider extends GroupSetProvider {
@Inject
public GitUploadPackGroupsProvider(@GerritServerConfig Config config,
- AuthConfig authConfig, SchemaFactory<ReviewDb> db) {
+ SchemaFactory<ReviewDb> db) {
super(config, db, "upload", null, "allowGroup");
// If no group was set, default to "registered users" and "anonymous"
//
if (groupIds.isEmpty()) {
- HashSet<AccountGroup.Id> all = new HashSet<AccountGroup.Id>();
- all.addAll(authConfig.getRegisteredGroups());
- all.addAll(authConfig.getAnonymousGroups());
+ HashSet<AccountGroup.UUID> all = new HashSet<AccountGroup.UUID>();
+ all.add(AccountGroup.REGISTERED_USERS);
+ all.add(AccountGroup.ANONYMOUS_USERS);
groupIds = Collections.unmodifiableSet(all);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
index 373fdb5..6ff977f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
@@ -30,11 +30,11 @@
import java.util.Set;
public abstract class GroupSetProvider implements
- Provider<Set<AccountGroup.Id>> {
+ Provider<Set<AccountGroup.UUID>> {
private static final Logger log =
LoggerFactory.getLogger(GroupSetProvider.class);
- protected Set<AccountGroup.Id> groupIds;
+ protected Set<AccountGroup.UUID> groupIds;
@Inject
protected GroupSetProvider(@GerritServerConfig Config config,
@@ -44,7 +44,7 @@
}
@Override
- public Set<AccountGroup.Id> get() {
+ public Set<AccountGroup.UUID> get() {
return groupIds;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java
index 381c914..e58e8bf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.config;
import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
@@ -37,11 +36,11 @@
public class ProjectCreatorGroupsProvider extends GroupSetProvider {
@Inject
public ProjectCreatorGroupsProvider(@GerritServerConfig final Config config,
- final SystemConfig systemConfig, final SchemaFactory<ReviewDb> db) {
+ final AuthConfig authConfig, final SchemaFactory<ReviewDb> db) {
super(config, db, "repository", "*", "createGroup");
if (groupIds.isEmpty()) {
- groupIds = Collections.singleton(systemConfig.adminGroupId);
+ groupIds = Collections.singleton(authConfig.getAdministratorsGroup());
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
index c457d73..4d5ad0c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
@@ -37,7 +37,7 @@
public class ProjectOwnerGroupsProvider extends GroupSetProvider {
@Inject
public ProjectOwnerGroupsProvider(
- @ProjectCreatorGroups final Set<AccountGroup.Id> creatorGroups,
+ @ProjectCreatorGroups final Set<AccountGroup.UUID> creatorGroups,
@GerritServerConfig final Config config, final SchemaFactory<ReviewDb> db) {
super(config, db, "repository", "*", "ownerGroup");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathFromSystemConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathFromSystemConfigProvider.java
deleted file mode 100644
index 5b5dcc1..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathFromSystemConfigProvider.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2009 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.config;
-
-import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.io.File;
-
-/** Provides {@link java.io.File} annotated with {@link SitePath}. */
-public class SitePathFromSystemConfigProvider implements Provider<File> {
- private final File path;
-
- @Inject
- SitePathFromSystemConfigProvider(final SystemConfig config) {
- final String p = config.sitePath;
- path = new File(p != null && p.length() > 0 ? p : ".").getAbsoluteFile();
- }
-
- @Override
- public File get() {
- return path;
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index 573e6bb..beeaa59 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -212,7 +212,7 @@
a.by = asAccountAttribute(approval.getAccountId());
a.grantedOn = approval.getGranted().getTime() / 1000L;
- ApprovalType at = approvalTypes.getApprovalType(approval.getCategoryId());
+ ApprovalType at = approvalTypes.byId(approval.getCategoryId());
if (at != null) {
a.description = at.getCategory().getName();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
index 976ec2e..fb88ad6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
@@ -16,6 +16,7 @@
import static com.google.gerrit.server.git.GitRepositoryManager.REFS_NOTES_REVIEW;
+import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Change;
@@ -197,10 +198,13 @@
} else if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
submit = a;
} else {
- formatter.appendApproval(
- approvalTypes.getApprovalType(a.getCategoryId()).getCategory(),
- a.getValue(),
- accountCache.get(a.getAccountId()).getAccount());
+ ApprovalType type = approvalTypes.byId(a.getCategoryId());
+ if (type != null) {
+ formatter.appendApproval(
+ type.getCategory(),
+ a.getValue(),
+ accountCache.get(a.getAccountId()).getAccount());
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
similarity index 63%
copy from gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
index 0977ee9..7947d30 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.schema;
+package com.google.gerrit.server.git;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
+import com.google.gerrit.server.config.FactoryModule;
-public class Schema_49 extends SchemaVersion {
-
- @Inject
- Schema_49(Provider<Schema_48> prior) {
- super(prior);
+/** Configures the Git support. */
+public class GitModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ factory(RenameGroupOp.Factory.class);
+ factory(MetaDataUpdate.InternalFactory.class);
+ bind(MetaDataUpdate.Server.class);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java
deleted file mode 100644
index 3f2ff0d..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2009 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.git;
-
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.Project.SubmitType;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-
-import org.eclipse.jgit.lib.RepositoryCache.FileKey;
-import org.eclipse.jgit.util.FS;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/** Imports all projects found within the repository manager. */
-public class GitProjectImporter {
- public interface Messages {
- void info(String msg);
- void warning(String msg);
- }
-
- private final LocalDiskRepositoryManager repositoryManager;
- private final SchemaFactory<ReviewDb> schema;
- private Messages messages;
-
- @Inject
- GitProjectImporter(final LocalDiskRepositoryManager repositoryManager,
- final SchemaFactory<ReviewDb> schema) {
- this.repositoryManager = repositoryManager;
- this.schema = schema;
- }
-
- public void run(final Messages msg) throws OrmException, IOException {
- messages = msg;
- messages.info("Scanning " + repositoryManager.getBasePath());
- final ReviewDb db = schema.open();
- try {
- final HashSet<String> have = new HashSet<String>();
- for (Project p : db.projects().all()) {
- have.add(p.getName());
- }
- importProjects(repositoryManager.getBasePath(), "", db, have);
- } finally {
- db.close();
- }
- }
-
- private void importProjects(final File dir, final String prefix,
- final ReviewDb db, final Set<String> have) throws OrmException,
- IOException {
- final File[] ls = dir.listFiles();
- if (ls == null) {
- return;
- }
-
- for (File f : ls) {
- String name = f.getName();
- if (".".equals(name) || "..".equals(name)) {
- continue;
- }
-
- if (FileKey.isGitRepository(f, FS.DETECTED)) {
- if (name.equals(".git")) {
- if ("".equals(prefix)) {
- // If the git base path is itself a git repository working
- // directory, this is a bit nonsensical for Gerrit Code Review.
- // Skip the path and do the next one.
- messages.warning("Skipping " + f.getAbsolutePath());
- continue;
- }
- name = prefix.substring(0, prefix.length() - 1);
-
- } else if (name.endsWith(".git")) {
- name = prefix + name.substring(0, name.length() - 4);
-
- } else {
- name = prefix + name;
- if (!have.contains(name)) {
- messages.warning("Importing non-standard name '" + name + "'");
- }
- }
-
- if (have.contains(name)) {
- continue;
- }
-
- final Project.NameKey nameKey = new Project.NameKey(name);
- final Project p = new Project(nameKey);
-
- p.setDescription(repositoryManager.getProjectDescription(nameKey));
- p.setSubmitType(SubmitType.MERGE_IF_NECESSARY);
- p.setUseContributorAgreements(false);
- p.setUseSignedOffBy(false);
- p.setUseContentMerge(false);
- p.setRequireChangeID(false);
- db.projects().insert(Collections.singleton(p));
-
- } else if (f.isDirectory()) {
- importProjects(f, prefix + f.getName() + "/", db, have);
- }
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
index a19b722..3e3d929 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
@@ -21,7 +21,7 @@
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
-
+import java.util.SortedSet;
/**
* Manages Git repositories for the Gerrit server process.
@@ -37,6 +37,9 @@
/** Note tree listing commits we refuse {@code refs/meta/reject-commits} */
public static final String REF_REJECT_COMMITS = "refs/meta/reject-commits";
+ /** Configuration settings for a project {@code refs/meta/config} */
+ public static final String REF_CONFIG = "refs/meta/config";
+
/**
* Get (or open) a repository by name.
*
@@ -61,6 +64,9 @@
public abstract Repository createRepository(Project.NameKey name)
throws RepositoryNotFoundException;
+ /** @return set of all known projects, sorted by natural NameKey order. */
+ public abstract SortedSet<Project.NameKey> list();
+
/**
* Read the {@code GIT_DIR/description} file for gitweb.
* <p>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
index 7e98348..1d348f7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -23,10 +23,12 @@
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.storage.file.LockFile;
import org.eclipse.jgit.storage.file.WindowCache;
import org.eclipse.jgit.storage.file.WindowCacheConfig;
@@ -39,6 +41,9 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.Collections;
+import java.util.SortedSet;
+import java.util.TreeSet;
/** Manages Git repositories stored on the local filesystem. */
@Singleton
@@ -129,10 +134,19 @@
}
loc = FileKey.exact(new File(basePath, n), FS.DETECTED);
}
- return RepositoryCache.open(loc, false);
+
+ Repository db = RepositoryCache.open(loc, false);
+ db.create(true /* bare */);
+
+ StoredConfig config = db.getConfig();
+ config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
+ null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
+ config.save();
+
+ return db;
} catch (IOException e1) {
final RepositoryNotFoundException e2;
- e2 = new RepositoryNotFoundException("Cannot open repository " + name);
+ e2 = new RepositoryNotFoundException("Cannot create repository " + name);
e2.initCause(e1);
throw e2;
}
@@ -142,41 +156,48 @@
throws RepositoryNotFoundException, IOException {
final Repository e = openRepository(name);
try {
- final File d = new File(e.getDirectory(), "description");
-
- String description;
- try {
- description = RawParseUtils.decode(IO.readFully(d));
- } catch (FileNotFoundException err) {
- return null;
- }
-
- if (description != null) {
- description = description.trim();
- if (description.isEmpty()) {
- description = null;
- }
- if (UNNAMED.equals(description)) {
- description = null;
- }
- }
- return description;
+ return getProjectDescription(e);
} finally {
e.close();
}
}
+ private String getProjectDescription(final Repository e) throws IOException {
+ final File d = new File(e.getDirectory(), "description");
+
+ String description;
+ try {
+ description = RawParseUtils.decode(IO.readFully(d));
+ } catch (FileNotFoundException err) {
+ return null;
+ }
+
+ if (description != null) {
+ description = description.trim();
+ if (description.isEmpty()) {
+ description = null;
+ }
+ if (UNNAMED.equals(description)) {
+ description = null;
+ }
+ }
+ return description;
+ }
+
public void setProjectDescription(final Project.NameKey name,
final String description) {
// Update git's description file, in case gitweb is being used
//
try {
- final Repository e;
- final LockFile f;
-
- e = openRepository(name);
+ final Repository e = openRepository(name);
try {
- f = new LockFile(new File(e.getDirectory(), "description"), FS.DETECTED);
+ final String old = getProjectDescription(e);
+ if ((old == null && description == null)
+ || (old != null && old.equals(description))) {
+ return;
+ }
+
+ final LockFile f = new LockFile(new File(e.getDirectory(), "description"), FS.DETECTED);
if (f.lock()) {
String d = description;
if (d != null) {
@@ -216,4 +237,46 @@
return false; // is a reasonable name
}
+
+ @Override
+ public SortedSet<Project.NameKey> list() {
+ SortedSet<Project.NameKey> names = new TreeSet<Project.NameKey>();
+ scanProjects(basePath, "", names);
+ return Collections.unmodifiableSortedSet(names);
+ }
+
+ private void scanProjects(final File dir, final String prefix,
+ final SortedSet<Project.NameKey> names) {
+ final File[] ls = dir.listFiles();
+ if (ls == null) {
+ return;
+ }
+
+ for (File f : ls) {
+ String fileName = f.getName();
+ if (FileKey.isGitRepository(f, FS.DETECTED)) {
+ String projectName;
+ if (fileName.equals(Constants.DOT_GIT)) {
+ projectName = prefix.substring(0, prefix.length() - 1);
+
+ } else if (fileName.endsWith(Constants.DOT_GIT_EXT)) {
+ int newLen = fileName.length() - Constants.DOT_GIT_EXT.length();
+ projectName = prefix + fileName.substring(0, newLen);
+
+ } else {
+ projectName = prefix + fileName;
+ }
+
+ Project.NameKey nameKey = new Project.NameKey(projectName);
+ if (isUnreasonableName(nameKey)) {
+ log.warn("Ignoring unreasonably named repository " + f.getAbsolutePath());
+ } else {
+ names.add(nameKey);
+ }
+
+ } else if (f.isDirectory()) {
+ scanProjects(f, prefix + f.getName() + "/", names);
+ }
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index b66fcb59..c69119b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -805,7 +805,7 @@
tag = "Tested-by";
} else {
final ApprovalType at =
- approvalTypes.getApprovalType(a.getCategoryId());
+ approvalTypes.byId(a.getCategoryId());
if (at == null) {
// A deprecated/deleted approval type, ignore it.
continue;
@@ -882,6 +882,17 @@
private void updateBranch() throws MergeException {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
+ if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
+ try {
+ ProjectConfig cfg = new ProjectConfig(destProject.getNameKey());
+ cfg.load(db, mergeTip);
+ } catch (Exception e) {
+ throw new MergeException("Submit would store invalid"
+ + " project configuration " + mergeTip.name() + " for "
+ + destProject.getName(), e);
+ }
+ }
+
branchUpdate.setForceUpdate(false);
branchUpdate.setNewObjectId(mergeTip);
branchUpdate.setRefLogMessage("merged", true);
@@ -889,6 +900,13 @@
switch (branchUpdate.update(rw)) {
case NEW:
case FAST_FORWARD:
+ if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
+ projectCache.evict(destProject);
+ ProjectState ps = projectCache.get(destProject.getNameKey());
+ repoManager.setProjectDescription(destProject.getNameKey(), //
+ ps.getProject().getDescription());
+ }
+
replication.scheduleUpdate(destBranch.getParentKey(), branchUpdate
.getName());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java
new file mode 100644
index 0000000..568c8cf9
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java
@@ -0,0 +1,128 @@
+// Copyright (C) 2011 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.git;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+/** Helps with the updating of a {@link VersionedMetaData}. */
+public class MetaDataUpdate {
+ public static class User {
+ private final InternalFactory factory;
+ private final GitRepositoryManager mgr;
+ private final PersonIdent serverIdent;
+ private final PersonIdent userIdent;
+
+ @Inject
+ User(InternalFactory factory, GitRepositoryManager mgr,
+ @GerritPersonIdent PersonIdent serverIdent, IdentifiedUser currentUser) {
+ this.factory = factory;
+ this.mgr = mgr;
+ this.serverIdent = serverIdent;
+ this.userIdent = currentUser.newCommitterIdent( //
+ serverIdent.getWhen(), //
+ serverIdent.getTimeZone());
+ }
+
+ public PersonIdent getUserPersonIdent() {
+ return userIdent;
+ }
+
+ public MetaDataUpdate create(Project.NameKey name)
+ throws RepositoryNotFoundException {
+ MetaDataUpdate md = factory.create(name, mgr.openRepository(name));
+ md.getCommitBuilder().setAuthor(userIdent);
+ md.getCommitBuilder().setCommitter(serverIdent);
+ return md;
+ }
+ }
+
+ public static class Server {
+ private final InternalFactory factory;
+ private final GitRepositoryManager mgr;
+ private final PersonIdent serverIdent;
+
+ @Inject
+ Server(InternalFactory factory, GitRepositoryManager mgr,
+ @GerritPersonIdent PersonIdent serverIdent) {
+ this.factory = factory;
+ this.mgr = mgr;
+ this.serverIdent = serverIdent;
+ }
+
+ public MetaDataUpdate create(Project.NameKey name)
+ throws RepositoryNotFoundException {
+ MetaDataUpdate md = factory.create(name, mgr.openRepository(name));
+ md.getCommitBuilder().setAuthor(serverIdent);
+ md.getCommitBuilder().setCommitter(serverIdent);
+ return md;
+ }
+ }
+
+ interface InternalFactory {
+ MetaDataUpdate create(@Assisted Project.NameKey projectName,
+ @Assisted Repository db);
+ }
+
+ private final ReplicationQueue replication;
+ private final Project.NameKey projectName;
+ private final Repository db;
+ private final CommitBuilder commit;
+
+ @Inject
+ public MetaDataUpdate(ReplicationQueue replication,
+ @Assisted Project.NameKey projectName, @Assisted Repository db) {
+ this.replication = replication;
+ this.projectName = projectName;
+ this.db = db;
+ this.commit = new CommitBuilder();
+ }
+
+ /** Set the commit message used when committing the update. */
+ public void setMessage(String message) {
+ getCommitBuilder().setMessage(message);
+ }
+
+ /** Close the cached Repository handle. */
+ public void close() {
+ getRepository().close();
+ }
+
+ Project.NameKey getProjectName() {
+ return projectName;
+ }
+
+ Repository getRepository() {
+ return db;
+ }
+
+ public CommitBuilder getCommitBuilder() {
+ return commit;
+ }
+
+ void replicate(String ref) {
+ if (replication.isEnabled()) {
+ replication.scheduleUpdate(projectName, ref);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/NoReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/NoReplication.java
new file mode 100644
index 0000000..ceb70d2
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/NoReplication.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2011 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.git;
+
+import com.google.gerrit.reviewdb.Project;
+
+/** A disabled {@link ReplicationQueue}. */
+public final class NoReplication implements ReplicationQueue {
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+
+ @Override
+ public void scheduleUpdate(Project.NameKey project, String ref) {
+ }
+
+ @Override
+ public void scheduleFullSync(Project.NameKey project, String urlMatch) {
+ }
+
+ @Override
+ public void replicateNewProject(Project.NameKey project, String head) {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
new file mode 100644
index 0000000..651fc6a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -0,0 +1,391 @@
+// Copyright (C) 2010 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.git;
+
+import static com.google.gerrit.common.data.AccessSection.isAccessSection;
+import static com.google.gerrit.common.data.Permission.isPermission;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.Project.SubmitType;
+import com.google.gerrit.server.account.GroupCache;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ProjectConfig extends VersionedMetaData {
+ private static final String PROJECT_CONFIG = "project.config";
+ private static final String GROUP_LIST = "groups";
+
+ private static final String PROJECT = "project";
+ private static final String KEY_DESCRIPTION = "description";
+
+ private static final String ACCESS = "access";
+ private static final String KEY_INHERIT_FROM = "inheritFrom";
+ private static final String KEY_GROUP_PERMISSIONS = "exclusiveGroupPermissions";
+
+ private static final String RECEIVE = "receive";
+ private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy";
+ private static final String KEY_REQUIRE_CHANGE_ID = "requireChangeId";
+ private static final String KEY_REQUIRE_CONTRIBUTOR_AGREEMENT =
+ "requireContributorAgreement";
+
+ private static final String SUBMIT = "submit";
+ private static final String KEY_ACTION = "action";
+ private static final String KEY_MERGE_CONTENT = "mergeContent";
+
+ private static final SubmitType defaultSubmitAction =
+ SubmitType.MERGE_IF_NECESSARY;
+
+ private Project.NameKey projectName;
+ private Project project;
+ private Map<AccountGroup.UUID, GroupReference> groupsByUUID;
+ private Map<String, AccessSection> accessSections;
+
+ public static ProjectConfig read(MetaDataUpdate update) throws IOException,
+ ConfigInvalidException {
+ ProjectConfig r = new ProjectConfig(update.getProjectName());
+ r.load(update);
+ return r;
+ }
+
+ public static ProjectConfig read(MetaDataUpdate update, ObjectId id)
+ throws IOException, ConfigInvalidException {
+ ProjectConfig r = new ProjectConfig(update.getProjectName());
+ r.load(update, id);
+ return r;
+ }
+
+ public ProjectConfig(Project.NameKey projectName) {
+ this.projectName = projectName;
+ }
+
+ public Project getProject() {
+ return project;
+ }
+
+ public AccessSection getAccessSection(String name) {
+ return getAccessSection(name, false);
+ }
+
+ public AccessSection getAccessSection(String name, boolean create) {
+ AccessSection as = accessSections.get(name);
+ if (as == null && create) {
+ as = new AccessSection(name);
+ accessSections.put(name, as);
+ }
+ return as;
+ }
+
+ public Collection<AccessSection> getAccessSections() {
+ return sort(accessSections.values());
+ }
+
+ public void remove(AccessSection section) {
+ if (section != null) {
+ accessSections.remove(section.getRefPattern());
+ }
+ }
+
+ public void replace(AccessSection section) {
+ for (Permission permission : section.getPermissions()) {
+ for (PermissionRule rule : permission.getRules()) {
+ rule.setGroup(resolve(rule.getGroup()));
+ }
+ }
+
+ accessSections.put(section.getRefPattern(), section);
+ }
+
+ public GroupReference resolve(AccountGroup group) {
+ return resolve(GroupReference.forGroup(group));
+ }
+
+ public GroupReference resolve(GroupReference group) {
+ if (group != null) {
+ GroupReference ref = groupsByUUID.get(group.getUUID());
+ if (ref != null) {
+ return ref;
+ }
+ groupsByUUID.put(group.getUUID(), group);
+ }
+ return group;
+ }
+
+ /** @return the group reference, if the group is used by at least one rule. */
+ public GroupReference getGroup(AccountGroup.UUID uuid) {
+ return groupsByUUID.get(uuid);
+ }
+
+ /**
+ * Check all GroupReferences use current group name, repairing stale ones.
+ *
+ * @param groupCache cache to use when looking up group information by UUID.
+ * @return true if one or more group names was stale.
+ */
+ public boolean updateGroupNames(GroupCache groupCache) {
+ boolean dirty = false;
+ for (GroupReference ref : groupsByUUID.values()) {
+ AccountGroup g = groupCache.get(ref.getUUID());
+ if (g != null && !g.getName().equals(ref.getName())) {
+ dirty = true;
+ ref.setName(g.getName());
+ }
+ }
+ return dirty;
+ }
+
+ @Override
+ protected String getRefName() {
+ return GitRepositoryManager.REF_CONFIG;
+ }
+
+ @Override
+ protected void onLoad() throws IOException, ConfigInvalidException {
+ Map<String,GroupReference> groupsByName = readGroupList();
+
+ Config rc = readConfig(PROJECT_CONFIG);
+ project = new Project(projectName);
+
+ Project p = project;
+ p.setDescription(rc.getString(PROJECT, null, KEY_DESCRIPTION));
+ if (p.getDescription() == null) {
+ p.setDescription("");
+ }
+ p.setParentName(rc.getString(ACCESS, null, KEY_INHERIT_FROM));
+
+ p.setUseContributorAgreements(rc.getBoolean(RECEIVE, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, false));
+ p.setUseSignedOffBy(rc.getBoolean(RECEIVE, KEY_REQUIRE_SIGNED_OFF_BY, false));
+ p.setRequireChangeID(rc.getBoolean(RECEIVE, KEY_REQUIRE_CHANGE_ID, false));
+
+ p.setSubmitType(rc.getEnum(SUBMIT, null, KEY_ACTION, defaultSubmitAction));
+ p.setUseContentMerge(rc.getBoolean(SUBMIT, null, KEY_MERGE_CONTENT, false));
+
+ accessSections = new HashMap<String, AccessSection>();
+ for (String refName : rc.getSubsections(ACCESS)) {
+ if (isAccessSection(refName)) {
+ AccessSection as = getAccessSection(refName, true);
+
+ for (String varName : rc.getStringList(ACCESS, refName, KEY_GROUP_PERMISSIONS)) {
+ for (String n : varName.split("[, \t]{1,}")) {
+ if (isPermission(n)) {
+ as.getPermission(n, true).setExclusiveGroup(true);
+ }
+ }
+ }
+
+ for (String varName : rc.getNames(ACCESS, refName)) {
+ if (isPermission(varName)) {
+ Permission perm = as.getPermission(varName, true);
+
+ boolean useRange = perm.isLabel();
+ for (String ruleString : rc.getStringList(ACCESS, refName, varName)) {
+ PermissionRule rule;
+ try {
+ rule = PermissionRule.fromString(ruleString, useRange);
+ } catch (IllegalArgumentException notRule) {
+ throw new ConfigInvalidException("Invalid rule in " + ACCESS
+ + "." + refName + "." + varName + ": "
+ + notRule.getMessage(), notRule);
+ }
+
+ GroupReference ref = groupsByName.get(rule.getGroup().getName());
+ if (ref == null) {
+ // The group wasn't mentioned in the groups table, so there is
+ // no valid UUID for it. Pool the reference anyway so at least
+ // all rules in the same file share the same GroupReference.
+ //
+ ref = rule.getGroup();
+ groupsByName.put(ref.getName(), ref);
+ }
+
+ rule.setGroup(ref);
+ perm.add(rule);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private Map<String, GroupReference> readGroupList() throws IOException,
+ ConfigInvalidException {
+ groupsByUUID = new HashMap<AccountGroup.UUID, GroupReference>();
+ Map<String, GroupReference> groupsByName =
+ new HashMap<String, GroupReference>();
+
+ BufferedReader br = new BufferedReader(new StringReader(readUTF8(GROUP_LIST)));
+ String s;
+ while ((s = br.readLine()) != null) {
+ if (s.isEmpty() || s.startsWith("#")) {
+ continue;
+ }
+
+ int tab = s.indexOf('\t');
+ if (tab < 0) {
+ throw new ConfigInvalidException("Invalid group line: " + s);
+ }
+
+ AccountGroup.UUID uuid = new AccountGroup.UUID(s.substring(0, tab).trim());
+ String name = s.substring(tab + 1).trim();
+ GroupReference ref = new GroupReference(uuid, name);
+
+ groupsByUUID.put(uuid, ref);
+ groupsByName.put(name, ref);
+ }
+ return groupsByName;
+ }
+
+ @Override
+ protected void onSave(CommitBuilder commit) throws IOException,
+ ConfigInvalidException {
+ if (commit.getMessage() == null || "".equals(commit.getMessage())) {
+ commit.setMessage("Updated project configuration\n");
+ }
+
+ Config rc = readConfig(PROJECT_CONFIG);
+ Project p = project;
+
+ if (p.getDescription() != null && !p.getDescription().isEmpty()) {
+ rc.setString(PROJECT, null, KEY_DESCRIPTION, p.getDescription());
+ } else {
+ rc.unset(PROJECT, null, KEY_DESCRIPTION);
+ }
+ set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName());
+
+ set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.isUseContributorAgreements());
+ set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.isUseSignedOffBy());
+ set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.isRequireChangeID());
+
+ set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
+ set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge());
+
+ Set<AccountGroup.UUID> keepGroups = new HashSet<AccountGroup.UUID>();
+ for (AccessSection as : sort(accessSections.values())) {
+ String refName = as.getRefPattern();
+
+ StringBuilder doNotInherit = new StringBuilder();
+ for (Permission perm : sort(as.getPermissions())) {
+ if (perm.getExclusiveGroup()) {
+ if (0 < doNotInherit.length()) {
+ doNotInherit.append(' ');
+ }
+ doNotInherit.append(perm.getName());
+ }
+ }
+ if (0 < doNotInherit.length()) {
+ rc.setString(ACCESS, refName, KEY_GROUP_PERMISSIONS, doNotInherit.toString());
+ } else {
+ rc.unset(ACCESS, refName, KEY_GROUP_PERMISSIONS);
+ }
+
+ Set<String> have = new HashSet<String>();
+ for (Permission permission : sort(as.getPermissions())) {
+ have.add(permission.getName().toLowerCase());
+
+ boolean needRange = permission.isLabel();
+ List<String> rules = new ArrayList<String>();
+ for (PermissionRule rule : sort(permission.getRules())) {
+ GroupReference group = rule.getGroup();
+ if (group.getUUID() != null) {
+ keepGroups.add(group.getUUID());
+ }
+ rules.add(rule.asString(needRange));
+ }
+ rc.setStringList(ACCESS, refName, permission.getName(), rules);
+ }
+
+ for (String varName : rc.getNames(ACCESS, refName)) {
+ if (isPermission(varName) && !have.contains(varName.toLowerCase())) {
+ rc.unset(ACCESS, refName, varName);
+ }
+ }
+ }
+
+ for (String name : rc.getSubsections(ACCESS)) {
+ if (isAccessSection(name) && !accessSections.containsKey(name)) {
+ rc.unsetSection(ACCESS, name);
+ }
+ }
+ groupsByUUID.keySet().retainAll(keepGroups);
+
+ saveConfig(PROJECT_CONFIG, rc);
+ saveGroupList();
+ }
+
+ private void saveGroupList() throws IOException {
+ if (groupsByUUID.isEmpty()) {
+ saveFile(GROUP_LIST, null);
+ return;
+ }
+
+ final int uuidLen = 40;
+ StringBuilder buf = new StringBuilder();
+ buf.append(pad(uuidLen, "# UUID"));
+ buf.append('\t');
+ buf.append("Group Name");
+ buf.append('\n');
+
+ buf.append('#');
+ buf.append('\n');
+
+ for (GroupReference g : sort(groupsByUUID.values())) {
+ if (g.getUUID() != null && g.getName() != null) {
+ buf.append(pad(uuidLen, g.getUUID().get()));
+ buf.append('\t');
+ buf.append(g.getName());
+ buf.append('\n');
+ }
+ }
+ saveUTF8(GROUP_LIST, buf.toString());
+ }
+
+ private static String pad(int len, String src) {
+ if (len <= src.length()) {
+ return src;
+ }
+
+ StringBuilder r = new StringBuilder(len);
+ r.append(src);
+ while (r.length() < len) {
+ r.append(' ');
+ }
+ return r.toString();
+ }
+
+ private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
+ ArrayList<T> r = new ArrayList<T>(m);
+ Collections.sort(r);
+ return r;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java
index 8985f29..b5f991c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java
@@ -15,10 +15,7 @@
package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.WildProjectName;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -37,20 +34,16 @@
private static final Logger log =
LoggerFactory.getLogger(PushAllProjectsOp.class);
- private final SchemaFactory<ReviewDb> schema;
+ private final ProjectCache projectCache;
private final ReplicationQueue replication;
- private final Project.NameKey wildProject;
private final String urlMatch;
@Inject
- public PushAllProjectsOp(final WorkQueue wq,
- final SchemaFactory<ReviewDb> sf, final ReplicationQueue rq,
- @WildProjectName final Project.NameKey wp,
- @Assisted @Nullable final String urlMatch) {
+ public PushAllProjectsOp(final WorkQueue wq, final ProjectCache projectCache,
+ final ReplicationQueue rq, @Assisted @Nullable final String urlMatch) {
super(wq);
- this.schema = sf;
+ this.projectCache = projectCache;
this.replication = rq;
- this.wildProject = wp;
this.urlMatch = urlMatch;
}
@@ -63,17 +56,10 @@
public void run() {
try {
- final ReviewDb db = schema.open();
- try {
- for (final Project project : db.projects().all()) {
- if (!project.getNameKey().equals(wildProject)) {
- replication.scheduleFullSync(project.getNameKey(), urlMatch);
- }
- }
- } finally {
- db.close();
+ for (final Project.NameKey nameKey : projectCache.all()) {
+ replication.scheduleFullSync(nameKey, urlMatch);
}
- } catch (OrmException e) {
+ } catch (RuntimeException e) {
log.error("Cannot enumerate known projects", e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
index fd7649a..cb005aa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
@@ -356,7 +356,7 @@
String[] authGroupNames =
cfg.getStringList("remote", rc.getName(), "authGroup");
- final Set<AccountGroup.Id> authGroups;
+ final Set<AccountGroup.UUID> authGroups;
if (authGroupNames.length > 0) {
authGroups = ConfigUtil.groupsFor(db, authGroupNames, //
log, "Group \"{0}\" not in database, removing from authGroup");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 4380ff0..97ad546 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -40,6 +40,7 @@
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.mail.CreateChangeSender;
@@ -48,7 +49,9 @@
import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefControl;
import com.google.gwtorm.client.AtomicUpdate;
import com.google.gwtorm.client.OrmException;
@@ -141,6 +144,9 @@
private final ReplicationQueue replication;
private final PatchSetInfoFactory patchSetInfoFactory;
private final ChangeHookRunner hooks;
+ private final GitRepositoryManager repoManager;
+ private final ProjectCache projectCache;
+ private final GroupCache groupCache;
private final String canonicalWebUrl;
private final PersonIdent gerritIdent;
private final TrackingFooters trackingFooters;
@@ -175,6 +181,9 @@
final ReplicationQueue replication,
final PatchSetInfoFactory patchSetInfoFactory,
final ChangeHookRunner hooks,
+ final ProjectCache projectCache,
+ final GitRepositoryManager repoManager,
+ final GroupCache groupCache,
@CanonicalWebUrl @Nullable final String canonicalWebUrl,
@GerritPersonIdent final PersonIdent gerritIdent,
final TrackingFooters trackingFooters,
@@ -191,6 +200,9 @@
this.replication = replication;
this.patchSetInfoFactory = patchSetInfoFactory;
this.hooks = hooks;
+ this.projectCache = projectCache;
+ this.repoManager = repoManager;
+ this.groupCache = groupCache;
this.canonicalWebUrl = canonicalWebUrl;
this.gerritIdent = gerritIdent;
this.trackingFooters = trackingFooters;
@@ -376,6 +388,13 @@
}
}
+ if (isConfig(c)) {
+ projectCache.evict(project);
+ ProjectState ps = projectCache.get(project.getNameKey());
+ repoManager.setProjectDescription(project.getNameKey(), //
+ ps.getProject().getDescription());
+ }
+
if (!c.getRefName().startsWith(NEW_CHANGE)) {
// We only schedule direct refs updates for replication.
// Change refs are scheduled when they are created.
@@ -402,9 +421,14 @@
AbstractAgreement bestAgreement = null;
ContributorAgreement bestCla = null;
- OUTER: for (AccountGroup.Id groupId : currentUser.getEffectiveGroups()) {
+ OUTER: for (AccountGroup.UUID groupUUID : currentUser.getEffectiveGroups()) {
+ AccountGroup group = groupCache.get(groupUUID);
+ if (group == null) {
+ continue;
+ }
+
final List<AccountGroupAgreement> temp =
- db.accountGroupAgreements().byGroup(groupId).toList();
+ db.accountGroupAgreements().byGroup(group.getId()).toList();
Collections.reverse(temp);
@@ -555,24 +579,59 @@
switch (cmd.getType()) {
case CREATE:
parseCreate(cmd);
- continue;
+ break;
case UPDATE:
parseUpdate(cmd);
- continue;
+ break;
case DELETE:
parseDelete(cmd);
- continue;
+ break;
case UPDATE_NONFASTFORWARD:
parseRewind(cmd);
+ break;
+
+ default:
+ reject(cmd);
continue;
}
- // Everything else is bogus as far as we are concerned.
- //
- reject(cmd);
+ if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED){
+ continue;
+ }
+
+ if (isConfig(cmd)) {
+ if (!projectControl.isOwner()) {
+ reject(cmd, "not project owner");
+ continue;
+ }
+
+ switch (cmd.getType()) {
+ case CREATE:
+ case UPDATE:
+ case UPDATE_NONFASTFORWARD:
+ try {
+ ProjectConfig cfg = new ProjectConfig(project.getNameKey());
+ cfg.load(repo, cmd.getNewId());
+ } catch (Exception e) {
+ reject(cmd, "invalid project configuration");
+ log.error("User " + currentUser.getUserName()
+ + " tried to push invalid project configuration "
+ + cmd.getNewId().name() + " for " + project.getName(), e);
+ continue;
+ }
+ break;
+
+ case DELETE:
+ break;
+
+ default:
+ reject(cmd);
+ continue;
+ }
+ }
}
}
@@ -1299,15 +1358,18 @@
oldCC.add(a.getAccountId());
}
- final ApprovalType type =
- approvalTypes.getApprovalType(a.getCategoryId());
- if (a.getPatchSetId().equals(priorPatchSet)
- && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
- // If there was a negative vote on the prior patch set, carry it
- // into this patch set.
- //
- db.patchSetApprovals().insert(
- Collections.singleton(new PatchSetApproval(ps.getId(), a)));
+ // ApprovalCategory.SUBMIT is still in db but not relevant in git-store
+ if (!ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
+ final ApprovalType type =
+ approvalTypes.byId(a.getCategoryId());
+ if (a.getPatchSetId().equals(priorPatchSet)
+ && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
+ // If there was a negative vote on the prior patch set, carry it
+ // into this patch set.
+ //
+ db.patchSetApprovals().insert(
+ Collections.singleton(new PatchSetApproval(ps.getId(), a)));
+ }
}
if (!haveAuthor && authorId != null && a.getAccountId().equals(authorId)) {
@@ -1929,4 +1991,9 @@
private static boolean isHead(final ReceiveCommand cmd) {
return cmd.getRefName().startsWith(Constants.R_HEADS);
}
+
+ private static boolean isConfig(final ReceiveCommand cmd) {
+ return cmd.getRefName().equals(GitRepositoryManager.REF_CONFIG);
+ }
+
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/RenameGroupOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/RenameGroupOp.java
new file mode 100644
index 0000000..cedcb0b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/RenameGroupOp.java
@@ -0,0 +1,153 @@
+// Copyright (C) 2011 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.git;
+
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.Project.NameKey;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class RenameGroupOp extends DefaultQueueOp {
+ public interface Factory {
+ RenameGroupOp create(@Assisted("author") PersonIdent author,
+ @Assisted AccountGroup.UUID uuid, @Assisted("oldName") String oldName,
+ @Assisted("newName") String newName);
+ }
+
+ private static final int MAX_TRIES = 10;
+ private static final Logger log =
+ LoggerFactory.getLogger(RenameGroupOp.class);
+
+ private final ProjectCache projectCache;
+ private final MetaDataUpdate.Server metaDataUpdateFactory;
+
+ private final PersonIdent author;
+ private final AccountGroup.UUID uuid;
+ private final String oldName;
+ private final String newName;
+ private final List<Project.NameKey> retryOn;
+
+ private boolean tryingAgain;
+
+ @Inject
+ public RenameGroupOp(WorkQueue workQueue, ProjectCache projectCache,
+ MetaDataUpdate.Server metaDataUpdateFactory,
+
+ @Assisted("author") PersonIdent author, @Assisted AccountGroup.UUID uuid,
+ @Assisted("oldName") String oldName, @Assisted("newName") String newName) {
+ super(workQueue);
+ this.projectCache = projectCache;
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+
+ this.author = author;
+ this.uuid = uuid;
+ this.oldName = oldName;
+ this.newName = newName;
+ this.retryOn = new ArrayList<Project.NameKey>();
+ }
+
+ @Override
+ public void run() {
+ Iterable<NameKey> names = tryingAgain ? retryOn : projectCache.all();
+ for (Project.NameKey projectName : names) {
+ ProjectConfig config = projectCache.get(projectName).getConfig();
+ GroupReference ref = config.getGroup(uuid);
+ if (ref == null || newName.equals(ref.getName())) {
+ continue;
+ }
+
+ try {
+ MetaDataUpdate md = metaDataUpdateFactory.create(projectName);
+ try {
+ rename(md);
+ } finally {
+ md.close();
+ }
+ } catch (RepositoryNotFoundException noProject) {
+ continue;
+ } catch (ConfigInvalidException err) {
+ log.error("Cannot rename group " + oldName + " in " + projectName, err);
+ } catch (IOException err) {
+ log.error("Cannot rename group " + oldName + " in " + projectName, err);
+ }
+ }
+
+ // If one or more projects did not update, wait 5 minutes
+ // and give it another attempt.
+ if (!retryOn.isEmpty() && !tryingAgain) {
+ tryingAgain = true;
+ start(5, TimeUnit.MINUTES);
+ }
+ }
+
+ private void rename(MetaDataUpdate md) throws IOException,
+ ConfigInvalidException {
+ boolean success = false;
+ for (int attempts = 0; !success && attempts < MAX_TRIES; attempts++) {
+ ProjectConfig config = ProjectConfig.read(md);
+
+ // The group isn't referenced, or its name has been fixed already.
+ //
+ GroupReference ref = config.getGroup(uuid);
+ if (ref == null || newName.equals(ref.getName())) {
+ projectCache.evict(config.getProject());
+ return;
+ }
+
+ ref.setName(newName);
+ md.getCommitBuilder().setAuthor(author);
+ md.setMessage("Rename group " + oldName + " to " + newName + "\n");
+ if (config.commit(md)) {
+ projectCache.evict(config.getProject());
+ success = true;
+
+ } else {
+ try {
+ Thread.sleep(25 /* milliseconds */);
+ } catch (InterruptedException wakeUp) {
+ continue;
+ }
+ }
+ }
+
+ if (!success) {
+ if (tryingAgain) {
+ log.warn("Could not rename group " + oldName + " to " + newName
+ + " in " + md.getProjectName().get());
+ } else {
+ retryOn.add(md.getProjectName());
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Rename Group " + oldName;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java
index d54dcec..8997e2b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java
@@ -51,8 +51,7 @@
void appendApproval(ApprovalCategory category,
short value, Account user) {
- // TODO: use category.getLabel() when available
- sb.append(category.getName().replace(' ', '-'));
+ sb.append(category.getLabelName());
sb.append(value < 0 ? "-" : "+").append(Math.abs(value)).append(": ");
appendUserData(user);
sb.append("\n");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
new file mode 100644
index 0000000..bb91044
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -0,0 +1,318 @@
+// Copyright (C) 2010 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.git;
+
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
+import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.RawParseUtils;
+
+import java.io.IOException;
+
+/**
+ * Support for metadata stored within a version controlled branch.
+ * <p>
+ * Implementors are responsible for supplying implementations of the onLoad and
+ * onSave methods to read from the repository, or format an update that can
+ * later be written back to the repository.
+ */
+public abstract class VersionedMetaData {
+ private RevCommit revision;
+ private ObjectReader reader;
+ private ObjectInserter inserter;
+ private DirCache newTree;
+
+ /** @return name of the reference storing this configuration. */
+ protected abstract String getRefName();
+
+ protected abstract void onLoad() throws IOException, ConfigInvalidException;
+
+ protected abstract void onSave(CommitBuilder commit) throws IOException,
+ ConfigInvalidException;
+
+ /** @return revision of the metadata that was loaded. */
+ public ObjectId getRevision() {
+ return revision.copy();
+ }
+
+ /** Initialize in-memory as though the repository branch doesn't exist. */
+ public void createInMemory() {
+ try {
+ revision = null;
+ onLoad();
+ } catch (IOException err) {
+ throw new RuntimeException("Unexpected IOException", err);
+ } catch (ConfigInvalidException err) {
+ throw new RuntimeException("Unexpected ConfigInvalidException", err);
+ }
+ }
+
+ /**
+ * Load the current version from the branch.
+ * <p>
+ * The repository is not held after the call completes, allowing the
+ * application to retain this object for long periods of time.
+ *
+ * @param db repository to access.
+ * @throws IOException
+ * @throws ConfigInvalidException
+ */
+ public void load(Repository db) throws IOException, ConfigInvalidException {
+ Ref ref = db.getRef(getRefName());
+ load(db, ref != null ? ref.getObjectId() : null);
+ }
+
+ /**
+ * Load a specific version from the repository.
+ * <p>
+ * This method is primarily useful for applying updates to a specific revision
+ * that was shown to an end-user in the user interface. If there are conflicts
+ * with another user's concurrent changes, these will be automatically
+ * detected at commit time.
+ * <p>
+ * The repository is not held after the call completes, allowing the
+ * application to retain this object for long periods of time.
+ *
+ * @param db repository to access.
+ * @param id revision to load.
+ * @throws IOException
+ * @throws ConfigInvalidException
+ */
+ public void load(Repository db, ObjectId id) throws IOException,
+ ConfigInvalidException {
+ if (id != null) {
+ reader = db.newObjectReader();
+ try {
+ revision = new RevWalk(reader).parseCommit(id);
+ onLoad();
+ } finally {
+ reader.release();
+ reader = null;
+ }
+ } else {
+ // The branch does not yet exist.
+ revision = null;
+ onLoad();
+ }
+ }
+
+ public void load(MetaDataUpdate update) throws IOException,
+ ConfigInvalidException {
+ load(update.getRepository());
+ }
+
+ public void load(MetaDataUpdate update, ObjectId id) throws IOException,
+ ConfigInvalidException {
+ load(update.getRepository(), id);
+ }
+
+ /**
+ * Update this metadata branch, recording a new commit on its reference.
+ *
+ * @param update helper information to define the update that will occur.
+ * @return true if the update was successful, false if it failed because of a
+ * concurrent update to the same reference.
+ * @throws IOException if there is a storage problem and the update cannot be
+ * executed as requested.
+ */
+ public boolean commit(MetaDataUpdate update) throws IOException {
+ final Repository db = update.getRepository();
+ final CommitBuilder commit = update.getCommitBuilder();
+
+ reader = db.newObjectReader();
+ inserter = db.newObjectInserter();
+ try {
+ final RevWalk rw = new RevWalk(reader);
+ final RevTree src = revision != null ? rw.parseTree(revision) : null;
+ final ObjectId res = writeTree(src, commit);
+
+ if (res.equals(src)) {
+ // If there are no changes to the content, don't create the commit.
+ return true;
+ }
+
+ commit.setTreeId(res);
+ if (revision != null) {
+ commit.setParentId(revision);
+ }
+
+ RefUpdate ru = db.updateRef(getRefName());
+ if (revision != null) {
+ ru.setExpectedOldObjectId(revision);
+ } else {
+ ru.setExpectedOldObjectId(ObjectId.zeroId());
+ }
+ ru.setNewObjectId(inserter.insert(commit));
+ ru.disableRefLog();
+ inserter.flush();
+
+ switch (ru.update(rw)) {
+ case NEW:
+ case FAST_FORWARD:
+ revision = rw.parseCommit(ru.getNewObjectId());
+ update.replicate(ru.getName());
+ return true;
+
+ case LOCK_FAILURE:
+ return false;
+
+ default:
+ throw new IOException("Cannot update " + ru.getName() + " in "
+ + db.getDirectory() + ": " + ru.getResult());
+ }
+ } catch (ConfigInvalidException e) {
+ throw new IOException("Cannot update " + getRefName() + " in "
+ + db.getDirectory() + ": " + e.getMessage(), e);
+ } finally {
+ inserter.release();
+ inserter = null;
+
+ reader.release();
+ reader = null;
+ }
+ }
+
+ private ObjectId writeTree(RevTree srcTree, CommitBuilder commit)
+ throws IOException, MissingObjectException, IncorrectObjectTypeException,
+ UnmergedPathException, ConfigInvalidException {
+ try {
+ newTree = readTree(srcTree);
+ onSave(commit);
+ return newTree.writeTree(inserter);
+ } finally {
+ newTree = null;
+ }
+ }
+
+ private DirCache readTree(RevTree tree) throws IOException,
+ MissingObjectException, IncorrectObjectTypeException {
+ DirCache dc = DirCache.newInCore();
+ if (tree != null) {
+ DirCacheBuilder b = dc.builder();
+ b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, tree);
+ b.finish();
+ }
+ return dc;
+ }
+
+ protected Config readConfig(String fileName) throws IOException,
+ ConfigInvalidException {
+ Config rc = new Config();
+ String text = readUTF8(fileName);
+ if (!text.isEmpty()) {
+ try {
+ rc.fromText(text);
+ } catch (ConfigInvalidException err) {
+ throw new ConfigInvalidException("Invalid config file " + fileName
+ + " in commit" + revision.name(), err);
+ }
+ }
+ return rc;
+ }
+
+ protected String readUTF8(String fileName) throws IOException {
+ byte[] raw = readFile(fileName);
+ return raw.length != 0 ? RawParseUtils.decode(raw) : "";
+ }
+
+ protected byte[] readFile(String fileName) throws IOException {
+ if (revision == null) {
+ return new byte[] {};
+ }
+
+ TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree());
+ if (tw != null) {
+ ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB);
+ return obj.getCachedBytes(Integer.MAX_VALUE);
+
+ } else {
+ return new byte[] {};
+ }
+ }
+
+ protected static void set(Config rc, String section, String subsection,
+ String name, String value) {
+ if (value != null) {
+ rc.setString(section, subsection, name, value);
+ } else {
+ rc.unset(section, subsection, name);
+ }
+ }
+
+ protected static void set(Config rc, String section, String subsection,
+ String name, boolean value) {
+ if (value) {
+ rc.setBoolean(section, subsection, name, value);
+ } else {
+ rc.unset(section, subsection, name);
+ }
+ }
+
+ protected static <E extends Enum<?>> void set(Config rc, String section,
+ String subsection, String name, E value, E defaultValue) {
+ if (value != defaultValue) {
+ rc.setEnum(section, subsection, name, value);
+ } else {
+ rc.unset(section, subsection, name);
+ }
+ }
+
+ protected void saveConfig(String fileName, Config cfg) throws IOException {
+ saveUTF8(fileName, cfg.toText());
+ }
+
+ protected void saveUTF8(String fileName, String text) throws IOException {
+ saveFile(fileName, text != null ? Constants.encode(text) : null);
+ }
+
+ protected void saveFile(String fileName, byte[] raw) throws IOException {
+ DirCacheEditor editor = newTree.editor();
+ if (raw != null && 0 < raw.length) {
+ final ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, raw);
+ editor.add(new PathEdit(fileName) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.REGULAR_FILE);
+ ent.setObjectId(blobId);
+ }
+ });
+ } else {
+ editor.add(new DeletePath(fileName));
+ }
+ editor.finish();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index f5a7870..ebbe686 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -69,8 +69,8 @@
/** Is the from user in an email squelching group? */
final IdentifiedUser user = args.identifiedUserFactory.create(id);
- final Set<AccountGroup.Id> gids = user.getEffectiveGroups();
- for (final AccountGroup.Id gid : gids) {
+ final Set<AccountGroup.UUID> gids = user.getEffectiveGroups();
+ for (final AccountGroup.UUID gid : gids) {
if (args.groupCache.get(gid).isEmailOnlyAuthors()) {
emailOnlyAuthors = true;
break;
@@ -273,11 +273,11 @@
}
/** Get the groups which own the project. */
- protected Set<AccountGroup.Id> getProjectOwners() {
+ protected Set<AccountGroup.UUID> getProjectOwners() {
final ProjectState r;
r = args.projectCache.get(change.getProject());
- return r != null ? r.getOwners() : Collections.<AccountGroup.Id> emptySet();
+ return r != null ? r.getOwners() : Collections.<AccountGroup.UUID> emptySet();
}
/** TO or CC all vested parties (change owner, patch set uploader, author). */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
index c14ff1b..8bb68df 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
@@ -20,6 +20,7 @@
import com.google.gerrit.reviewdb.AccountProjectWatch;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.AccountProjectWatch.NotifyType;
+import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
@@ -34,10 +35,13 @@
public CreateChangeSender create(Change change);
}
+ private final GroupCache groupCache;
+
@Inject
public CreateChangeSender(EmailArguments ea, SshInfo sshInfo,
- @Assisted Change c) {
+ GroupCache groupCache, @Assisted Change c) {
super(ea, sshInfo, c);
+ this.groupCache = groupCache;
}
@Override
@@ -52,10 +56,13 @@
// Try to mark interested owners with a TO and not a BCC line.
//
final Set<Account.Id> owners = new HashSet<Account.Id>();
- for (AccountGroup.Id g : getProjectOwners()) {
- for (AccountGroupMember m : args.db.get().accountGroupMembers()
- .byGroup(g)) {
- owners.add(m.getAccountId());
+ for (AccountGroup.UUID uuid : getProjectOwners()) {
+ AccountGroup group = groupCache.get(uuid);
+ if (group != null) {
+ for (AccountGroupMember m : args.db.get().accountGroupMembers()
+ .byGroup(group.getId())) {
+ owners.add(m.getAccountId());
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
index f3e2890..db297ae 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
@@ -171,7 +171,9 @@
final short o = a.getValue();
a.setValue(want.get());
a.cache(change);
- functionState.normalize(types.getApprovalType(a.getCategoryId()), a);
+ if (!ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
+ functionState.normalize(types.byId(a.getCategoryId()), a);
+ }
if (o != a.getValue()) {
// Value changed, ensure we update the database.
//
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
index 1e2e7f4..64e5299 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
@@ -33,19 +33,19 @@
public class AccessControlModule extends FactoryModule {
@Override
protected void configure() {
- bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) //
+ bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) //
.annotatedWith(ProjectCreatorGroups.class) //
.toProvider(ProjectCreatorGroupsProvider.class).in(SINGLETON);
- bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) //
+ bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) //
.annotatedWith(ProjectOwnerGroups.class) //
.toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON);
- bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) //
+ bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) //
.annotatedWith(GitUploadPackGroups.class) //
.toProvider(GitUploadPackGroupsProvider.class).in(SINGLETON);
- bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) //
+ bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) //
.annotatedWith(GitReceivePackGroups.class) //
.toProvider(GitReceivePackGroupsProvider.class).in(SINGLETON);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 3291f1a..b387e70 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -16,13 +16,12 @@
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.workflow.CategoryFunction;
@@ -31,7 +30,6 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.util.ArrayList;
import java.util.List;
@@ -164,8 +162,14 @@
return canAbandon(); // Anyone who can abandon the change can restore it back
}
- public short normalize(ApprovalCategory.Id category, short score) {
- return getRefControl().normalize(category, score);
+ /** All value ranges of any allowed label permission. */
+ public List<PermissionRange> getLabelRanges() {
+ return getRefControl().getLabelRanges();
+ }
+
+ /** The range of permitted values associated with a label permission. */
+ public PermissionRange getRange(String permission) {
+ return getRefControl().getRange(permission);
}
/** Can this user add a patch set to this change? */
@@ -240,34 +244,22 @@
return result;
}
- final List<PatchSetApproval> allApprovals =
- new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet(
- patchSetId).toList());
- final PatchSetApproval myAction =
- ChangeUtil.createSubmitApproval(patchSetId,
- (IdentifiedUser) getCurrentUser(), db);
-
- final ApprovalType actionType =
- approvalTypes.getApprovalType(myAction.getCategoryId());
- if (actionType == null || !actionType.getCategory().isAction()) {
- return new CanSubmitResult("Invalid action " + myAction.getCategoryId());
- }
+ final List<PatchSetApproval> all =
+ db.patchSetApprovals().byPatchSet(patchSetId).toList();
final FunctionState fs =
- functionStateFactory.create(change, patchSetId, allApprovals);
+ functionStateFactory.create(change, patchSetId, all);
+
for (ApprovalType c : approvalTypes.getApprovalTypes()) {
CategoryFunction.forCategory(c.getCategory()).run(c, fs);
}
- if (!CategoryFunction.forCategory(actionType.getCategory()).isValid(
- getCurrentUser(), actionType, fs)) {
- return new CanSubmitResult(actionType.getCategory().getName()
- + " not permitted");
+
+ for (ApprovalType type : approvalTypes.getApprovalTypes()) {
+ if (!fs.isValid(type)) {
+ return new CanSubmitResult("Requires " + type.getCategory().getName());
+ }
}
- fs.normalize(actionType, myAction);
- if (myAction.getValue() <= 0) {
- return new CanSubmitResult(actionType.getCategory().getName()
- + " not permitted");
- }
+
return CanSubmitResult.OK;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
index 35b5ee5..4202a62 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -29,6 +29,17 @@
/** Invalidate the cached information about the given project. */
public void evict(Project p);
- /** Invalidate the cached information about all projects. */
- public void evictAll();
+ /** @return sorted iteration of projects. */
+ public abstract Iterable<Project.NameKey> all();
+
+ /**
+ * Filter the set of registered project names by common prefix.
+ *
+ * @param prefix common prefix.
+ * @return sorted iteration of projects sharing the same prefix.
+ */
+ public abstract Iterable<Project.NameKey> byName(String prefix);
+
+ /** Notify the cache that a new project was constructed. */
+ public void onCreateProject(Project.NameKey newProjectName);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 48eef87..3990d06 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -15,11 +15,14 @@
package com.google.gerrit.server.project;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -27,21 +30,38 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
-import java.util.Collection;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+
import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
/** Cache of project information, including access rights. */
@Singleton
public class ProjectCacheImpl implements ProjectCache {
private static final String CACHE_NAME = "projects";
+ private static final String CACHE_LIST = "project_list";
public static Module module() {
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<Project.NameKey, ProjectState>> type =
+ final TypeLiteral<Cache<Project.NameKey, ProjectState>> nameType =
new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {};
- core(type, CACHE_NAME).populateWith(Loader.class);
+ core(nameType, CACHE_NAME).populateWith(Loader.class);
+
+ final TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>> listType =
+ new TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>>() {};
+ core(listType, CACHE_LIST).populateWith(Lister.class);
+
bind(ProjectCacheImpl.class);
bind(ProjectCache.class).to(ProjectCacheImpl.class);
}
@@ -49,11 +69,39 @@
}
private final Cache<Project.NameKey, ProjectState> byName;
+ private final Cache<ListKey,SortedSet<Project.NameKey>> list;
+ private final Lock listLock;
+ private volatile long generation;
@Inject
ProjectCacheImpl(
- @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName) {
+ @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
+ @Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list,
+ @GerritServerConfig final Config serverConfig) {
this.byName = byName;
+ this.list = list;
+ this.listLock = new ReentrantLock(true /* fair */);
+
+ long checkFrequencyMillis = TimeUnit.MILLISECONDS.convert(
+ ConfigUtil.getTimeUnit(serverConfig,
+ "cache", "projects", "checkFrequency",
+ 5, TimeUnit.MINUTES), TimeUnit.MINUTES);
+ if (10 < checkFrequencyMillis) {
+ // Start with generation 1 (to avoid magic 0 below).
+ generation = 1;
+ Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ // This is not exactly thread-safe, but is OK for our use.
+ // The only thread that writes the volatile is this task.
+ generation = generation + 1;
+ }
+ }, checkFrequencyMillis, checkFrequencyMillis, TimeUnit.MILLISECONDS);
+ } else {
+ // Magic generation 0 triggers ProjectState to always
+ // check on each needsRefresh() request we make to it.
+ generation = 0;
+ }
}
/**
@@ -63,7 +111,12 @@
* @return the cached data; null if no such project exists.
*/
public ProjectState get(final Project.NameKey projectName) {
- return byName.get(projectName);
+ ProjectState state = byName.get(projectName);
+ if (state != null && state.needsRefresh(generation)) {
+ byName.remove(projectName);
+ state = byName.get(projectName);
+ }
+ return state;
}
/** Invalidate the cached information about the given project. */
@@ -73,38 +126,127 @@
}
}
- /** Invalidate the cached information about all projects. */
- public void evictAll() {
- byName.removeAll();
+ @Override
+ public void onCreateProject(Project.NameKey newProjectName) {
+ listLock.lock();
+ try {
+ SortedSet<Project.NameKey> n = list.get(ListKey.ALL);
+ n = new TreeSet<Project.NameKey>(n);
+ n.add(newProjectName);
+ list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
+ } finally {
+ listLock.unlock();
+ }
+ }
+
+ @Override
+ public Iterable<Project.NameKey> all() {
+ return list.get(ListKey.ALL);
+ }
+
+ @Override
+ public Iterable<Project.NameKey> byName(final String pfx) {
+ return new Iterable<Project.NameKey>() {
+ @Override
+ public Iterator<Project.NameKey> iterator() {
+ return new Iterator<Project.NameKey>() {
+ private Project.NameKey next;
+ private Iterator<Project.NameKey> itr =
+ list.get(ListKey.ALL).tailSet(new Project.NameKey(pfx)).iterator();
+
+ @Override
+ public boolean hasNext() {
+ if (next != null) {
+ return true;
+ }
+
+ if (!itr.hasNext()) {
+ return false;
+ }
+
+ Project.NameKey r = itr.next();
+ if (r.get().startsWith(pfx)) {
+ next = r;
+ return true;
+ } else {
+ itr = Collections.<Project.NameKey> emptyList().iterator();
+ return false;
+ }
+ }
+
+ @Override
+ public Project.NameKey next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ Project.NameKey r = next;
+ next = null;
+ return r;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
}
static class Loader extends EntryCreator<Project.NameKey, ProjectState> {
private final ProjectState.Factory projectStateFactory;
private final SchemaFactory<ReviewDb> schema;
+ private final GitRepositoryManager mgr;
@Inject
- Loader(ProjectState.Factory psf, SchemaFactory<ReviewDb> sf) {
+ Loader(ProjectState.Factory psf, SchemaFactory<ReviewDb> sf,
+ GitRepositoryManager g) {
projectStateFactory = psf;
schema = sf;
+ mgr = g;
}
@Override
public ProjectState createEntry(Project.NameKey key) throws Exception {
final ReviewDb db = schema.open();
try {
- final Project p = db.projects().get(key);
- if (p == null) {
- return null;
+ Repository git = mgr.openRepository(key);
+ try {
+ final ProjectConfig cfg = new ProjectConfig(key);
+ cfg.load(git);
+ return projectStateFactory.create(cfg);
+ } finally {
+ git.close();
}
- final Collection<RefRight> rights =
- Collections.unmodifiableCollection(db.refRights().byProject(
- p.getNameKey()).toList());
+ } catch (RepositoryNotFoundException notFound) {
+ return null;
- return projectStateFactory.create(p, rights);
} finally {
db.close();
}
}
}
+
+ static class ListKey {
+ static final ListKey ALL = new ListKey();
+
+ private ListKey() {
+ }
+ }
+
+ static class Lister extends EntryCreator<ListKey, SortedSet<Project.NameKey>> {
+ private final GitRepositoryManager mgr;
+
+ @Inject
+ Lister(GitRepositoryManager mgr) {
+ this.mgr = mgr;
+ }
+
+ @Override
+ public SortedSet<Project.NameKey> createEntry(ListKey key) throws Exception {
+ return mgr.list();
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index 25778a6..4b11849 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -14,13 +14,15 @@
package com.google.gerrit.server.project;
-import static com.google.gerrit.common.CollectionsUtil.*;
+import static com.google.gerrit.common.CollectionsUtil.isAnyIncludedIn;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.ReplicationUser;
import com.google.gerrit.server.config.GitReceivePackGroups;
@@ -29,6 +31,7 @@
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -101,16 +104,18 @@
ProjectControl create(CurrentUser who, ProjectState ps);
}
- private final Set<AccountGroup.Id> uploadGroups;
- private final Set<AccountGroup.Id> receiveGroups;
+ private final Set<AccountGroup.UUID> uploadGroups;
+ private final Set<AccountGroup.UUID> receiveGroups;
private final RefControl.Factory refControlFactory;
private final CurrentUser user;
private final ProjectState state;
+ private Collection<AccessSection> access;
+
@Inject
- ProjectControl(@GitUploadPackGroups Set<AccountGroup.Id> uploadGroups,
- @GitReceivePackGroups Set<AccountGroup.Id> receiveGroups,
+ ProjectControl(@GitUploadPackGroups Set<AccountGroup.UUID> uploadGroups,
+ @GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups,
final RefControl.Factory refControlFactory,
@Assisted CurrentUser who, @Assisted ProjectState ps) {
this.uploadGroups = uploadGroups;
@@ -155,18 +160,18 @@
/** Can this user see this project exists? */
public boolean isVisible() {
return visibleForReplication()
- || canPerformOnAnyRef(ApprovalCategory.READ, (short) 1);
+ || canPerformOnAnyRef(Permission.READ);
}
public boolean canAddRefs() {
- return (canPerformOnAnyRef(ApprovalCategory.PUSH_HEAD, ApprovalCategory.PUSH_HEAD_CREATE)
+ return (canPerformOnAnyRef(Permission.CREATE)
|| isOwnerAnyRef());
}
/** Can this user see all the refs in this projects? */
public boolean allRefsAreVisible() {
return visibleForReplication()
- || canPerformOnAllRefs(ApprovalCategory.READ, (short) 1);
+ || canPerformOnAllRefs(Permission.READ);
}
/** Is this project completely visible for replication? */
@@ -177,49 +182,60 @@
/** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
public boolean isOwner() {
- return controlForRef(RefRight.ALL).isOwner()
+ return controlForRef(AccessSection.ALL).isOwner()
|| getCurrentUser().isAdministrator();
}
/** Does this user have ownership on at least one reference name? */
public boolean isOwnerAnyRef() {
- return canPerformOnAnyRef(ApprovalCategory.OWN, (short) 1)
+ return canPerformOnAnyRef(Permission.OWNER)
|| getCurrentUser().isAdministrator();
}
/** @return true if the user can upload to at least one reference */
public boolean canPushToAtLeastOneRef() {
- return canPerformOnAnyRef(ApprovalCategory.READ, (short) 2)
- || canPerformOnAnyRef(ApprovalCategory.PUSH_HEAD, (short) 1)
- || canPerformOnAnyRef(ApprovalCategory.PUSH_TAG, (short) 1);
+ return canPerformOnAnyRef(Permission.PUSH)
+ || canPerformOnAnyRef(Permission.PUSH_TAG);
}
- // TODO (anatol.pomazau): Try to merge this method with similar RefRightsForPattern#canPerform
- private boolean canPerformOnAnyRef(ApprovalCategory.Id actionId,
- short requireValue) {
- final Set<AccountGroup.Id> groups = user.getEffectiveGroups();
+ private boolean canPerformOnAnyRef(String permissionName) {
+ final Set<AccountGroup.UUID> groups = user.getEffectiveGroups();
- for (final RefRight pr : state.getAllRights(actionId, true)) {
- if (groups.contains(pr.getAccountGroupId())
- && pr.getMaxValue() >= requireValue) {
- return true;
+ for (AccessSection section : access()) {
+ Permission permission = section.getPermission(permissionName);
+ if (permission == null) {
+ continue;
+ }
+
+ for (PermissionRule rule : permission.getRules()) {
+ if (rule.getDeny()) {
+ continue;
+ }
+
+ // Being in a group that was granted this permission is only an
+ // approximation. There might be overrides and doNotInherit
+ // that would render this to be false.
+ //
+ if (groups.contains(rule.getGroup().getUUID())
+ && controlForRef(section.getRefPattern()).canPerform(permissionName)) {
+ return true;
+ }
}
}
return false;
}
- private boolean canPerformOnAllRefs(ApprovalCategory.Id actionId,
- short requireValue) {
+ private boolean canPerformOnAllRefs(String permission) {
boolean canPerform = false;
- final Set<String> patterns = allRefPatterns(actionId);
- if (patterns.contains(RefRight.ALL)) {
+ Set<String> patterns = allRefPatterns(permission);
+ if (patterns.contains(AccessSection.ALL)) {
// Only possible if granted on the pattern that
// matches every possible reference. Check all
// patterns also have the permission.
//
for (final String pattern : patterns) {
- if (controlForRef(pattern).canPerform(actionId, requireValue)) {
+ if (controlForRef(pattern).canPerform(permission)) {
canPerform = true;
} else {
return false;
@@ -229,14 +245,24 @@
return canPerform;
}
- private Set<String> allRefPatterns(ApprovalCategory.Id actionId) {
- final Set<String> all = new HashSet<String>();
- for (final RefRight pr : state.getAllRights(actionId, true)) {
- all.add(pr.getRefPattern());
+ private Set<String> allRefPatterns(String permissionName) {
+ Set<String> all = new HashSet<String>();
+ for (AccessSection section : access()) {
+ Permission permission = section.getPermission(permissionName);
+ if (permission != null) {
+ all.add(section.getRefPattern());
+ }
}
return all;
}
+ Collection<AccessSection> access() {
+ if (access == null) {
+ access = state.getAllAccessSections();
+ }
+ return access;
+ }
+
public boolean canRunUploadPack() {
return isAnyIncludedIn(uploadGroups, user.getEffectiveGroups());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
index 0b8e83a..0d297cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -14,122 +14,135 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.WildProjectName;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/** Cached information on a project. */
public class ProjectState {
public interface Factory {
- ProjectState create(Project project, Collection<RefRight> localRights);
+ ProjectState create(ProjectConfig config);
}
private final AnonymousUser anonymousUser;
private final Project.NameKey wildProject;
private final ProjectCache projectCache;
private final ProjectControl.AssistedFactory projectControlFactory;
+ private final GitRepositoryManager gitMgr;
- private final Project project;
- private final Collection<RefRight> localRights;
- private final Set<AccountGroup.Id> localOwners;
+ private final ProjectConfig config;
+ private final Set<AccountGroup.UUID> localOwners;
- private volatile Collection<RefRight> inheritedRights;
+ /** Last system time the configuration's revision was examined. */
+ private transient long lastCheckTime;
@Inject
protected ProjectState(final AnonymousUser anonymousUser,
final ProjectCache projectCache,
@WildProjectName final Project.NameKey wildProject,
final ProjectControl.AssistedFactory projectControlFactory,
- @Assisted final Project project,
- @Assisted Collection<RefRight> rights) {
+ final GitRepositoryManager gitMgr,
+ @Assisted final ProjectConfig config) {
this.anonymousUser = anonymousUser;
this.projectCache = projectCache;
this.wildProject = wildProject;
this.projectControlFactory = projectControlFactory;
+ this.gitMgr = gitMgr;
+ this.config = config;
+ this.lastCheckTime = System.currentTimeMillis();
- if (wildProject.equals(project.getNameKey())) {
- rights = new ArrayList<RefRight>(rights);
- for (Iterator<RefRight> itr = rights.iterator(); itr.hasNext();) {
- if (!itr.next().getApprovalCategoryId().canBeOnWildProject()) {
- itr.remove();
+ HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>();
+ AccessSection all = config.getAccessSection(AccessSection.ALL);
+ if (all != null) {
+ Permission owner = all.getPermission(Permission.OWNER);
+ if (owner != null) {
+ for (PermissionRule rule : owner.getRules()) {
+ GroupReference ref = rule.getGroup();
+ if (ref.getUUID() != null) {
+ groups.add(ref.getUUID());
+ }
}
}
- rights = Collections.unmodifiableCollection(rights);
- }
-
- this.project = project;
- this.localRights = rights;
-
- final HashSet<AccountGroup.Id> groups = new HashSet<AccountGroup.Id>();
- for (final RefRight right : rights) {
- if (ApprovalCategory.OWN.equals(right.getApprovalCategoryId())
- && right.getMaxValue() > 0
- && right.getRefPattern().equals(RefRight.ALL)) {
- groups.add(right.getAccountGroupId());
- }
}
localOwners = Collections.unmodifiableSet(groups);
}
+ boolean needsRefresh(long generation) {
+ if (generation <= 0) {
+ return isRevisionOutOfDate();
+ }
+ if (lastCheckTime != generation) {
+ lastCheckTime = generation;
+ return isRevisionOutOfDate();
+ }
+ return false;
+ }
+
+ private boolean isRevisionOutOfDate() {
+ try {
+ Repository git = gitMgr.openRepository(getProject().getNameKey());
+ try {
+ Ref ref = git.getRef(GitRepositoryManager.REF_CONFIG);
+ if (ref == null || ref.getObjectId() == null) {
+ return true;
+ }
+ return !ref.getObjectId().equals(config.getRevision());
+ } finally {
+ git.close();
+ }
+ } catch (IOException gone) {
+ return true;
+ }
+ }
+
public Project getProject() {
- return project;
+ return getConfig().getProject();
+ }
+
+ public ProjectConfig getConfig() {
+ return config;
}
/** Get the rights that pertain only to this project. */
- public Collection<RefRight> getLocalRights() {
- return localRights;
+ public Collection<AccessSection> getLocalAccessSections() {
+ return getConfig().getAccessSections();
}
- /**
- * Get the rights that pertain only to this project.
- *
- * @param action the category requested.
- * @return immutable collection of rights for the requested category.
- */
- public Collection<RefRight> getLocalRights(ApprovalCategory.Id action) {
- return filter(getLocalRights(), action);
- }
-
- /** Get the rights this project inherits from the wild project. */
- public Collection<RefRight> getInheritedRights() {
- if (inheritedRights == null) {
- inheritedRights = computeInheritedRights();
- }
- return inheritedRights;
- }
-
- void setInheritedRights(Collection<RefRight> all) {
- inheritedRights = all;
- }
-
- private Collection<RefRight> computeInheritedRights() {
- if (isSpecialWildProject()) {
+ /** Get the rights this project inherits. */
+ public Collection<AccessSection> getInheritedAccessSections() {
+ if (isWildProject()) {
return Collections.emptyList();
}
- List<RefRight> inherited = new ArrayList<RefRight>();
+ List<AccessSection> inherited = new ArrayList<AccessSection>();
Set<Project.NameKey> seen = new HashSet<Project.NameKey>();
- Project.NameKey parent = project.getParent();
+ Project.NameKey parent = getProject().getParent();
while (parent != null && seen.add(parent)) {
ProjectState s = projectCache.get(parent);
if (s != null) {
- inherited.addAll(s.getLocalRights());
+ inherited.addAll(s.getLocalAccessSections());
parent = s.getProject().getParent();
} else {
break;
@@ -138,76 +151,21 @@
// Wild project is the parent, or the root of the tree
if (parent == null) {
- inherited.addAll(getWildProjectRights());
- }
-
- return Collections.unmodifiableCollection(inherited);
- }
-
- private Collection<RefRight> getWildProjectRights() {
- final ProjectState s = projectCache.get(wildProject);
- return s != null ? s.getLocalRights() : Collections.<RefRight> emptyList();
- }
-
- /**
- * Utility class that is needed to filter overridden refrights
- */
- private static class Grant {
- final AccountGroup.Id group;
- final String pattern;
-
- private Grant(AccountGroup.Id group, String pattern) {
- this.group = group;
- this.pattern = pattern;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null)
- return false;
- Grant grant = (Grant) o;
- return group.equals(grant.group) && pattern.equals(grant.pattern);
- }
-
- @Override
- public int hashCode() {
- int result = group.hashCode();
- result = 31 * result + pattern.hashCode();
- return result;
- }
- }
-
- /**
- * Get the rights this project has and inherits from the wild project.
- *
- * @param action the category requested.
- * @param dropOverridden whether to remove inherited permissions in case if we have a
- * local one that matches (action,group,ref)
- * @return immutable collection of rights for the requested category.
- */
- public Collection<RefRight> getAllRights(ApprovalCategory.Id action, boolean dropOverridden) {
- Collection<RefRight> rights = new LinkedList<RefRight>(getLocalRights(action));
- rights.addAll(filter(getInheritedRights(), action));
- if (dropOverridden) {
- Set<Grant> grants = new HashSet<Grant>();
- Iterator<RefRight> iter = rights.iterator();
- while (iter.hasNext()) {
- RefRight right = iter.next();
-
- Grant grant = new Grant(right.getAccountGroupId(), right.getRefPattern());
- if (grants.contains(grant)) {
- iter.remove();
- } else {
- grants.add(grant);
- }
+ ProjectState s = projectCache.get(wildProject);
+ if (s != null) {
+ inherited.addAll(s.getLocalAccessSections());
}
}
- return Collections.unmodifiableCollection(rights);
+
+ return inherited;
}
- /** Is this the special wild project which manages inherited rights? */
- public boolean isSpecialWildProject() {
- return project.getNameKey().equals(wildProject);
+ /** Get both local and inherited access sections. */
+ public Collection<AccessSection> getAllAccessSections() {
+ List<AccessSection> all = new ArrayList<AccessSection>();
+ all.addAll(getLocalAccessSections());
+ all.addAll(getInheritedAccessSections());
+ return all;
}
/**
@@ -216,13 +174,13 @@
* are no local owners the local owners of the nearest parent project
* that has local owners are returned
*/
- public Set<AccountGroup.Id> getOwners() {
- if (!localOwners.isEmpty() || isSpecialWildProject()
- || project.getParent() == null) {
+ public Set<AccountGroup.UUID> getOwners() {
+ Project.NameKey parentName = getProject().getParent();
+ if (!localOwners.isEmpty() || parentName == null || isWildProject()) {
return localOwners;
}
- final ProjectState parent = projectCache.get(project.getParent());
+ ProjectState parent = projectCache.get(parentName);
if (parent != null) {
return parent.getOwners();
}
@@ -237,13 +195,23 @@
* owners) and all groups to which the owner privilege for 'refs/*' is
* assigned for one of the parent projects (the inherited owners).
*/
- public Set<AccountGroup.Id> getAllOwners() {
- final HashSet<AccountGroup.Id> owners = new HashSet<AccountGroup.Id>();
- for (final RefRight right : getAllRights(ApprovalCategory.OWN, true)) {
- if (right.getMaxValue() > 0 && right.getRefPattern().equals(RefRight.ALL)) {
- owners.add(right.getAccountGroupId());
+ public Set<AccountGroup.UUID> getAllOwners() {
+ HashSet<AccountGroup.UUID> owners = new HashSet<AccountGroup.UUID>();
+ owners.addAll(localOwners);
+
+ Set<Project.NameKey> seen = new HashSet<Project.NameKey>();
+ Project.NameKey parent = getProject().getParent();
+
+ while (parent != null && seen.add(parent)) {
+ ProjectState s = projectCache.get(parent);
+ if (s != null) {
+ owners.addAll(s.localOwners);
+ parent = s.getProject().getParent();
+ } else {
+ break;
}
}
+
return Collections.unmodifiableSet(owners);
}
@@ -255,20 +223,7 @@
return projectControlFactory.create(user, this);
}
- private static Collection<RefRight> filter(Collection<RefRight> all,
- ApprovalCategory.Id actionId) {
- if (all.isEmpty()) {
- return Collections.emptyList();
- }
- final Collection<RefRight> mine = new ArrayList<RefRight>(all.size());
- for (final RefRight right : all) {
- if (right.getApprovalCategoryId().equals(actionId)) {
- mine.add(right);
- }
- }
- if (mine.isEmpty()) {
- return Collections.emptyList();
- }
- return Collections.unmodifiableCollection(mine);
+ private boolean isWildProject() {
+ return wildProject.equals(getProject().getNameKey());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index 8ddf585..ff1d09f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -14,27 +14,16 @@
package com.google.gerrit.server.project;
-import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_AUTHOR;
-import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_COMMITTER;
-import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_IDENTITY;
-import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_SERVER;
-import static com.google.gerrit.reviewdb.ApprovalCategory.OWN;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_CREATE;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_REPLACE;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_UPDATE;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG_ANNOTATED;
-import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG_SIGNED;
-import static com.google.gerrit.reviewdb.ApprovalCategory.READ;
-
+import com.google.gerrit.common.CollectionsUtil;
+import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ParamertizedString;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRange;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -49,7 +38,6 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -57,8 +45,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
import java.util.regex.Pattern;
@@ -68,18 +54,18 @@
RefControl create(ProjectControl projectControl, String ref);
}
- private final SystemConfig systemConfig;
private final ProjectControl projectControl;
private final String refName;
+ private Map<String, List<PermissionRule>> permissions;
+
+ private Boolean owner;
private Boolean canForgeAuthor;
private Boolean canForgeCommitter;
@Inject
- protected RefControl(final SystemConfig systemConfig,
- @Assisted final ProjectControl projectControl,
+ protected RefControl(@Assisted final ProjectControl projectControl,
@Assisted String ref) {
- this.systemConfig = systemConfig;
if (isRE(ref)) {
ref = shortestExample(ref);
@@ -114,26 +100,29 @@
/** Is this user a ref owner? */
public boolean isOwner() {
- if (canPerform(OWN, (short) 1)) {
- return true;
- }
+ if (owner == null) {
+ if (canPerform(Permission.OWNER)) {
+ owner = true;
- // We have to prevent infinite recursion here, the project control
- // calls us to find out if there is ownership of all references in
- // order to determine project level ownership.
- //
- if (getRefName().equals(
- RefRight.ALL.substring(0, RefRight.ALL.length() - 1))) {
- return getCurrentUser().isAdministrator();
- } else {
- return getProjectControl().isOwner();
+ } else if (getRefName().equals(
+ AccessSection.ALL.substring(0, AccessSection.ALL.length() - 1))) {
+ // We have to prevent infinite recursion here, the project control
+ // calls us to find out if there is ownership of all references in
+ // order to determine project level ownership.
+ //
+ owner = getCurrentUser().isAdministrator();
+
+ } else {
+ owner = getProjectControl().isOwner();
+ }
}
+ return owner;
}
/** Can this user see this reference exists? */
public boolean isVisible() {
return getProjectControl().visibleForReplication()
- || canPerform(READ, (short) 1);
+ || canPerform(Permission.READ);
}
/**
@@ -144,27 +133,66 @@
* ref
*/
public boolean canUpload() {
- return canPerform(READ, (short) 2);
+ return getProjectControl()
+ .controlForRef("refs/for/" + getRefName())
+ .canPerform(Permission.PUSH);
}
/** @return true if this user can submit merge patch sets to this ref */
public boolean canUploadMerges() {
- return canPerform(READ, (short) 3);
+ return getProjectControl()
+ .controlForRef("refs/for/" + getRefName())
+ .canPerform(Permission.PUSH_MERGE);
}
/** @return true if this user can submit patch sets to this ref */
public boolean canSubmit() {
- return canPerform(ApprovalCategory.SUBMIT, (short) 1);
+ if (GitRepositoryManager.REF_CONFIG.equals(refName)) {
+ // Always allow project owners to submit configuration changes.
+ // Submitting configuration changes modifies the access control
+ // rules. Allowing this to be done by a non-project-owner opens
+ // a security hole enabling editing of access rules, and thus
+ // granting of powers beyond submitting to the configuration.
+ return getProjectControl().isOwner();
+ }
+ return canPerform(Permission.SUBMIT);
}
/** @return true if the user can update the reference as a fast-forward. */
public boolean canUpdate() {
- return canPerform(PUSH_HEAD, PUSH_HEAD_UPDATE);
+ if (GitRepositoryManager.REF_CONFIG.equals(refName)
+ && !getProjectControl().isOwner()) {
+ // Pushing requires being at least project owner, in addition to push.
+ // Pushing configuration changes modifies the access control
+ // rules. Allowing this to be done by a non-project-owner opens
+ // a security hole enabling editing of access rules, and thus
+ // granting of powers beyond pushing to the configuration.
+ return false;
+ }
+ return canPerform(Permission.PUSH);
}
/** @return true if the user can rewind (force push) the reference. */
public boolean canForceUpdate() {
- return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE) || canDelete();
+ return canPushWithForce() || canDelete();
+ }
+
+ private boolean canPushWithForce() {
+ if (GitRepositoryManager.REF_CONFIG.equals(refName)
+ && !getProjectControl().isOwner()) {
+ // Pushing requires being at least project owner, in addition to push.
+ // Pushing configuration changes modifies the access control
+ // rules. Allowing this to be done by a non-project-owner opens
+ // a security hole enabling editing of access rules, and thus
+ // granting of powers beyond pushing to the configuration.
+ return false;
+ }
+ for (PermissionRule rule : access(Permission.PUSH)) {
+ if (rule.getForce()) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -186,7 +214,7 @@
}
if (object instanceof RevCommit) {
- return owner || canPerform(PUSH_HEAD, PUSH_HEAD_CREATE);
+ return owner || canPerform(Permission.CREATE);
} else if (object instanceof RevTag) {
final RevTag tag = (RevTag) object;
@@ -208,7 +236,7 @@
} else {
valid = false;
}
- if (!valid && !owner && !canPerform(FORGE_IDENTITY, FORGE_COMMITTER)) {
+ if (!valid && !owner && !canForgeCommitter()) {
return false;
}
}
@@ -217,9 +245,9 @@
// than if it doesn't have a PGP signature.
//
if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
- return owner || canPerform(PUSH_TAG, PUSH_TAG_SIGNED);
+ return owner || canPerform(Permission.PUSH_TAG);
} else {
- return owner || canPerform(PUSH_TAG, PUSH_TAG_ANNOTATED);
+ return owner || canPerform(Permission.PUSH_TAG);
}
} else {
@@ -234,12 +262,21 @@
* @return {@code true} if the user specified can delete a Git ref.
*/
public boolean canDelete() {
+ if (GitRepositoryManager.REF_CONFIG.equals(refName)) {
+ // Never allow removal of the refs/meta/config branch.
+ // Deleting the branch would destroy all Gerrit specific
+ // metadata about the project, including its access rules.
+ // If a project is to be removed from Gerrit, its repository
+ // should be removed first.
+ return false;
+ }
+
switch (getCurrentUser().getAccessPath()) {
case WEB_UI:
- return isOwner() || canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
+ return isOwner() || canPushWithForce();
case GIT:
- return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
+ return canPushWithForce();
default:
return false;
@@ -249,7 +286,7 @@
/** @return true if this user can forge the author line in a commit. */
public boolean canForgeAuthor() {
if (canForgeAuthor == null) {
- canForgeAuthor = canPerform(FORGE_IDENTITY, FORGE_AUTHOR);
+ canForgeAuthor = canPerform(Permission.FORGE_AUTHOR);
}
return canForgeAuthor;
}
@@ -257,314 +294,103 @@
/** @return true if this user can forge the committer line in a commit. */
public boolean canForgeCommitter() {
if (canForgeCommitter == null) {
- canForgeCommitter = canPerform(FORGE_IDENTITY, FORGE_COMMITTER);
+ canForgeCommitter = canPerform(Permission.FORGE_COMMITTER);
}
return canForgeCommitter;
}
/** @return true if this user can forge the server on the committer line. */
public boolean canForgeGerritServerIdentity() {
- return canPerform(FORGE_IDENTITY, FORGE_SERVER);
+ return canPerform(Permission.FORGE_SERVER);
}
- public short normalize(ApprovalCategory.Id category, short score) {
- short minAllowed = 0, maxAllowed = 0;
- for (RefRight r : getApplicableRights(category)) {
- if (getCurrentUser().getEffectiveGroups().contains(r.getAccountGroupId())) {
- minAllowed = (short) Math.min(minAllowed, r.getMinValue());
- maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
+ /** All value ranges of any allowed label permission. */
+ public List<PermissionRange> getLabelRanges() {
+ List<PermissionRange> r = new ArrayList<PermissionRange>();
+ for (Map.Entry<String, List<PermissionRule>> e : permissions().entrySet()) {
+ if (Permission.isLabel(e.getKey())) {
+ r.add(toRange(e.getKey(), e.getValue()));
}
}
-
- if (score < minAllowed) {
- score = minAllowed;
- }
- if (score > maxAllowed) {
- score = maxAllowed;
- }
- return score;
+ return r;
}
- /**
- * Convenience holder class used to map a ref pattern to the list of
- * {@code RefRight}s that use it in the database.
- */
- public final static class RefRightsForPattern {
- private final List<RefRight> rights;
- private boolean containsExclusive;
-
- public RefRightsForPattern() {
- rights = new ArrayList<RefRight>();
- containsExclusive = false;
+ /** The range of permitted values associated with a label permission. */
+ public PermissionRange getRange(String permission) {
+ if (Permission.isLabel(permission)) {
+ return toRange(permission, access(permission));
}
+ return null;
+ }
- public void addRight(RefRight right) {
- rights.add(right);
- if (right.isExclusive()) {
- containsExclusive = true;
- }
+ private static PermissionRange toRange(String permissionName, List<PermissionRule> ruleList) {
+ int min = 0;
+ int max = 0;
+ for (PermissionRule rule : ruleList) {
+ min = Math.min(min, rule.getMin());
+ max = Math.max(max, rule.getMax());
}
+ return new PermissionRange(permissionName, min, max);
+ }
- public List<RefRight> getRights() {
- return Collections.unmodifiableList(rights);
- }
+ /** True if the user has this permission. Works only for non labels. */
+ boolean canPerform(String permissionName) {
+ return !access(permissionName).isEmpty();
+ }
- public boolean containsExclusive() {
- return containsExclusive;
- }
+ /** Rules for the given permission, or the empty list. */
+ private List<PermissionRule> access(String permissionName) {
+ List<PermissionRule> r = permissions().get(permissionName);
+ return r != null ? r : Collections.<PermissionRule> emptyList();
+ }
- /**
- * Returns The max allowed value for this ref pattern for all specified
- * groups.
- *
- * @param groups The groups of the user
- * @return The allowed value for this ref for all the specified groups
- */
- private boolean allowedValueForRef(Set<AccountGroup.Id> groups, short level) {
- for (RefRight right : rights) {
- if (groups.contains(right.getAccountGroupId())
- && right.getMaxValue() >= level) {
- return true;
+ /** All rules that pertain to this user, on this reference. */
+ private Map<String, List<PermissionRule>> permissions() {
+ if (permissions == null) {
+ List<AccessSection> sections = new ArrayList<AccessSection>();
+ for (AccessSection section : projectControl.access()) {
+ if (appliesToRef(section)) {
+ sections.add(section);
}
}
- return false;
- }
- }
+ Collections.sort(sections, new MostSpecificComparator(getRefName()));
- boolean canPerform(ApprovalCategory.Id actionId, short level) {
- final Set<AccountGroup.Id> groups = getCurrentUser().getEffectiveGroups();
+ Set<SeenRule> seen = new HashSet<SeenRule>();
+ Set<String> exclusiveGroupPermissions = new HashSet<String>();
- List<RefRight> allRights = new ArrayList<RefRight>();
- allRights.addAll(getAllRights(actionId));
+ permissions = new HashMap<String, List<PermissionRule>>();
+ for (AccessSection section : sections) {
+ for (Permission permission : section.getPermissions()) {
+ if (exclusiveGroupPermissions.contains(permission.getName())) {
+ continue;
+ }
- SortedMap<String, RefRightsForPattern> perPatternRights =
- sortedRightsByPattern(allRights);
-
- for (RefRightsForPattern right : perPatternRights.values()) {
- if (right.allowedValueForRef(groups, level)) {
- return true;
- }
- if (right.containsExclusive() && !actionId.equals(OWN)) {
- break;
- }
- }
- return false;
- }
-
- /**
- * Order the Ref Pattern by the most specific. This sort is done by:
- * <ul>
- * <li>1 - The minor value of Levenshtein string distance between the branch
- * name and the regex string shortest example. A shorter distance is a more
- * specific match.
- * <li>2 - Finites first, infinities after.
- * <li>3 - Number of transitions.
- * <li>4 - Length of the expression text.
- * </ul>
- *
- * Levenshtein distance is a measure of the similarity between two strings.
- * The distance is the number of deletions, insertions, or substitutions
- * required to transform one string into another.
- *
- * For example, if given refs/heads/m* and refs/heads/*, the distances are 5
- * and 6. It means that refs/heads/m* is more specific because it's closer to
- * refs/heads/master than refs/heads/*.
- *
- * Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the
- * distances are both 6. Both are infinite, but refs/heads/[a-zA-Z]* has more
- * transitions, which after all turns it more specific.
- */
- private final Comparator<String> BY_MOST_SPECIFIC_SORT =
- new Comparator<String>() {
- public int compare(final String pattern1, final String pattern2) {
- int cmp = distance(pattern1) - distance(pattern2);
- if (cmp == 0) {
- boolean p1_finite = finite(pattern1);
- boolean p2_finite = finite(pattern2);
-
- if (p1_finite && !p2_finite) {
- cmp = -1;
- } else if (!p1_finite && p2_finite) {
- cmp = 1;
- } else /* if (f1 == f2) */{
- cmp = 0;
+ for (PermissionRule rule : permission.getRules()) {
+ if (matchGroup(rule.getGroup().getUUID())) {
+ SeenRule s = new SeenRule(section, permission, rule);
+ if (seen.add(s) && !rule.getDeny()) {
+ List<PermissionRule> r = permissions.get(permission.getName());
+ if (r == null) {
+ r = new ArrayList<PermissionRule>(2);
+ permissions.put(permission.getName(), r);
+ }
+ r.add(rule);
+ }
}
}
- if (cmp == 0) {
- cmp = transitions(pattern1) - transitions(pattern2);
- }
- if (cmp == 0) {
- cmp = pattern2.length() - pattern1.length();
- }
- return cmp;
- }
- private int distance(String pattern) {
- String example;
- if (isRE(pattern)) {
- example = shortestExample(pattern);
-
- } else if (pattern.endsWith("/*")) {
- example = pattern.substring(0, pattern.length() - 1) + '1';
-
- } else if (pattern.equals(getRefName())) {
- return 0;
-
- } else {
- return Math.max(pattern.length(), getRefName().length());
- }
- return StringUtils.getLevenshteinDistance(example, getRefName());
- }
-
- private boolean finite(String pattern) {
- if (isRE(pattern)) {
- return toRegExp(pattern).toAutomaton().isFinite();
-
- } else if (pattern.endsWith("/*")) {
- return false;
-
- } else {
- return true;
+ if (permission.getExclusiveGroup()) {
+ exclusiveGroupPermissions.add(permission.getName());
}
}
-
- private int transitions(String pattern) {
- if (isRE(pattern)) {
- return toRegExp(pattern).toAutomaton().getNumberOfTransitions();
-
- } else if (pattern.endsWith("/*")) {
- return pattern.length();
-
- } else {
- return pattern.length();
- }
- }
- };
-
- /**
- * Sorts all given rights into a map, ordered by descending length of
- * ref pattern.
- *
- * For example, if given the following rights in argument:
- *
- * ["refs/heads/master", group1, -1, +1],
- * ["refs/heads/master", group2, -2, +2],
- * ["refs/heads/*", group3, -1, +1]
- * ["refs/heads/stable", group2, -1, +1]
- *
- * Then the following map is returned:
- * "refs/heads/master" => {
- * ["refs/heads/master", group1, -1, +1],
- * ["refs/heads/master", group2, -2, +2]
- * }
- * "refs/heads/stable" => {["refs/heads/stable", group2, -1, +1]}
- * "refs/heads/*" => {["refs/heads/*", group3, -1, +1]}
- *
- * @param actionRights
- * @return A sorted map keyed off the ref pattern of all rights.
- */
- private SortedMap<String, RefRightsForPattern> sortedRightsByPattern(
- List<RefRight> actionRights) {
- SortedMap<String, RefRightsForPattern> rights =
- new TreeMap<String, RefRightsForPattern>(BY_MOST_SPECIFIC_SORT);
- for (RefRight actionRight : actionRights) {
- RefRightsForPattern patternRights =
- rights.get(actionRight.getRefPattern());
- if (patternRights == null) {
- patternRights = new RefRightsForPattern();
- rights.put(actionRight.getRefPattern(), patternRights);
- }
- patternRights.addRight(actionRight);
- }
- return rights;
- }
-
- private List<RefRight> getAllRights(ApprovalCategory.Id actionId) {
- final List<RefRight> allRefRights = filter(getProjectState().getAllRights(actionId, true));
- return resolveOwnerGroups(allRefRights);
- }
-
- /**
- * Returns all applicable rights for a given approval category.
- *
- * Applicable rights are defined as the list of {@code RefRight}s which match
- * the ref for which this object was created, stopping the ref wildcard
- * matching when an exclusive ref right was encountered, for the given
- * approval category.
- * @param id The {@link ApprovalCategory.Id}.
- * @return All applicable rights.
- */
- public List<RefRight> getApplicableRights(final ApprovalCategory.Id id) {
- List<RefRight> l = new ArrayList<RefRight>();
- l.addAll(getAllRights(id));
- SortedMap<String, RefRightsForPattern> perPatternRights =
- sortedRightsByPattern(l);
- List<RefRight> applicable = new ArrayList<RefRight>();
- for (RefRightsForPattern patternRights : perPatternRights.values()) {
- applicable.addAll(patternRights.getRights());
- if (patternRights.containsExclusive()) {
- break;
}
}
- return Collections.unmodifiableList(applicable);
+ return permissions;
}
- /**
- * Resolves all refRights which assign privileges to the 'Project Owners'
- * group. All other refRights stay unchanged.
- *
- * @param refRights refRights to be resolved
- * @return the resolved refRights
- */
- private List<RefRight> resolveOwnerGroups(final List<RefRight> refRights) {
- final List<RefRight> resolvedRefRights =
- new ArrayList<RefRight>(refRights.size());
- for (final RefRight refRight : refRights) {
- resolvedRefRights.addAll(resolveOwnerGroups(refRight));
- }
- return resolvedRefRights;
- }
+ private boolean appliesToRef(AccessSection section) {
+ String refPattern = section.getRefPattern();
- /**
- * Checks if the given refRight assigns privileges to the 'Project Owners'
- * group.
- * If yes, resolves the 'Project Owners' group to the concrete groups that
- * own the project and creates new refRights for the concrete owner groups
- * which are returned.
- * If no, the given refRight is returned unchanged.
- *
- * @param refRight refRight
- * @return the resolved refRights
- */
- private Set<RefRight> resolveOwnerGroups(final RefRight refRight) {
- final Set<RefRight> resolvedRefRights = new HashSet<RefRight>();
- if (refRight.getAccountGroupId().equals(systemConfig.ownerGroupId)) {
- for (final AccountGroup.Id ownerGroup : getProjectState().getAllOwners()) {
- if (!ownerGroup.equals(systemConfig.ownerGroupId)) {
- resolvedRefRights.add(new RefRight(refRight, ownerGroup));
- }
- }
- } else {
- resolvedRefRights.add(refRight);
- }
- return resolvedRefRights;
- }
-
- private List<RefRight> filter(Collection<RefRight> all) {
- List<RefRight> mine = new ArrayList<RefRight>(all.size());
- for (RefRight right : all) {
- if (matches(right.getRefPattern())) {
- mine.add(right);
- }
- }
- return mine;
- }
-
- private ProjectState getProjectState() {
- return projectControl.getProjectState();
- }
-
- private boolean matches(String refPattern) {
if (isTemplate(refPattern)) {
ParamertizedString template = new ParamertizedString(refPattern);
HashMap<String, String> p = new HashMap<String, String>();
@@ -599,6 +425,18 @@
}
}
+ private boolean matchGroup(AccountGroup.UUID uuid) {
+ Set<AccountGroup.UUID> userGroups = getCurrentUser().getEffectiveGroups();
+
+ if (AccountGroup.PROJECT_OWNERS.equals(uuid)) {
+ ProjectState state = projectControl.getProjectState();
+ return CollectionsUtil.isAnyIncludedIn(state.getAllOwners(), userGroups);
+
+ } else {
+ return userGroups.contains(uuid);
+ }
+ }
+
private static boolean isTemplate(String refPattern) {
return 0 <= refPattern.indexOf("${");
}
@@ -611,7 +449,7 @@
}
private static boolean isRE(String refPattern) {
- return refPattern.startsWith(RefRight.REGEX_PREFIX);
+ return refPattern.startsWith(AccessSection.REGEX_PREFIX);
}
public static String shortestExample(String pattern) {
@@ -630,4 +468,143 @@
}
return new RegExp(refPattern, RegExp.NONE);
}
+
+ /** Tracks whether or not a permission has been overridden. */
+ private static class SeenRule {
+ final String refPattern;
+ final String permissionName;
+ final AccountGroup.UUID group;
+
+ SeenRule(AccessSection section, Permission permission, PermissionRule rule) {
+ refPattern = section.getRefPattern();
+ permissionName = permission.getName();
+ group = rule.getGroup().getUUID();
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = refPattern.hashCode();
+ hc = hc * 31 + permissionName.hashCode();
+ if (group != null) {
+ hc = hc * 31 + group.hashCode();
+ }
+ return hc;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof SeenRule) {
+ SeenRule a = this;
+ SeenRule b = (SeenRule) other;
+ return a.refPattern.equals(b.refPattern) //
+ && a.permissionName.equals(b.permissionName) //
+ && eq(a.group, b.group);
+ }
+ return false;
+ }
+
+ private boolean eq(AccountGroup.UUID a, AccountGroup.UUID b) {
+ return a != null && b != null && a.equals(b);
+ }
+ }
+
+ /**
+ * Order the Ref Pattern by the most specific. This sort is done by:
+ * <ul>
+ * <li>1 - The minor value of Levenshtein string distance between the branch
+ * name and the regex string shortest example. A shorter distance is a more
+ * specific match.
+ * <li>2 - Finites first, infinities after.
+ * <li>3 - Number of transitions.
+ * <li>4 - Length of the expression text.
+ * </ul>
+ *
+ * Levenshtein distance is a measure of the similarity between two strings.
+ * The distance is the number of deletions, insertions, or substitutions
+ * required to transform one string into another.
+ *
+ * For example, if given refs/heads/m* and refs/heads/*, the distances are 5
+ * and 6. It means that refs/heads/m* is more specific because it's closer to
+ * refs/heads/master than refs/heads/*.
+ *
+ * Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the
+ * distances are both 6. Both are infinite, but refs/heads/[a-zA-Z]* has more
+ * transitions, which after all turns it more specific.
+ */
+ private static final class MostSpecificComparator implements
+ Comparator<AccessSection> {
+ private final String refName;
+
+ MostSpecificComparator(String refName) {
+ this.refName = refName;
+ }
+
+ public int compare(AccessSection a, AccessSection b) {
+ return compare(a.getRefPattern(), b.getRefPattern());
+ }
+
+ private int compare(final String pattern1, final String pattern2) {
+ int cmp = distance(pattern1) - distance(pattern2);
+ if (cmp == 0) {
+ boolean p1_finite = finite(pattern1);
+ boolean p2_finite = finite(pattern2);
+
+ if (p1_finite && !p2_finite) {
+ cmp = -1;
+ } else if (!p1_finite && p2_finite) {
+ cmp = 1;
+ } else /* if (f1 == f2) */{
+ cmp = 0;
+ }
+ }
+ if (cmp == 0) {
+ cmp = transitions(pattern1) - transitions(pattern2);
+ }
+ if (cmp == 0) {
+ cmp = pattern2.length() - pattern1.length();
+ }
+ return cmp;
+ }
+
+ private int distance(String pattern) {
+ String example;
+ if (isRE(pattern)) {
+ example = shortestExample(pattern);
+
+ } else if (pattern.endsWith("/*")) {
+ example = pattern.substring(0, pattern.length() - 1) + '1';
+
+ } else if (pattern.equals(refName)) {
+ return 0;
+
+ } else {
+ return Math.max(pattern.length(), refName.length());
+ }
+ return StringUtils.getLevenshteinDistance(example, refName);
+ }
+
+ private boolean finite(String pattern) {
+ if (isRE(pattern)) {
+ return toRegExp(pattern).toAutomaton().isFinite();
+
+ } else if (pattern.endsWith("/*")) {
+ return false;
+
+ } else {
+ return true;
+ }
+ }
+
+ private int transitions(String pattern) {
+ if (isRE(pattern)) {
+ return toRegExp(pattern).toAutomaton().getNumberOfTransitions();
+
+ } else if (pattern.endsWith("/*")) {
+ return pattern.length();
+
+ } else {
+ return pattern.length();
+ }
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 577c5af..074ad19 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.IntPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryBuilder;
@@ -107,6 +108,7 @@
final Project.NameKey wildProjectName;
final PatchListCache patchListCache;
final GitRepositoryManager repoManager;
+ final ProjectCache projectCache;
@Inject
Arguments(Provider<ReviewDb> dbProvider,
@@ -118,7 +120,8 @@
AuthConfig authConfig, ApprovalTypes approvalTypes,
@WildProjectName Project.NameKey wildProjectName,
PatchListCache patchListCache,
- GitRepositoryManager repoManager) {
+ GitRepositoryManager repoManager,
+ ProjectCache projectCache) {
this.dbProvider = dbProvider;
this.rewriter = rewriter;
this.userFactory = userFactory;
@@ -131,6 +134,7 @@
this.wildProjectName = wildProjectName;
this.patchListCache = patchListCache;
this.repoManager = repoManager;
+ this.projectCache = projectCache;
}
}
@@ -340,15 +344,15 @@
//
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(who));
if (g != null) {
- return visibleto(new SingleGroupUser(args.authConfig, g.getId()));
+ return visibleto(new SingleGroupUser(args.authConfig, g.getGroupUUID()));
}
Collection<AccountGroup> matches =
args.groupCache.get(new AccountGroup.ExternalNameKey(who));
if (matches != null && !matches.isEmpty()) {
- HashSet<AccountGroup.Id> ids = new HashSet<AccountGroup.Id>();
+ HashSet<AccountGroup.UUID> ids = new HashSet<AccountGroup.UUID>();
for (AccountGroup group : matches) {
- ids.add(group.getId());
+ ids.add(group.getGroupUUID());
}
return visibleto(new SingleGroupUser(args.authConfig, ids));
}
@@ -508,23 +512,19 @@
// Try to match a project name by substring query.
final List<ProjectPredicate> predicate =
new ArrayList<ProjectPredicate>();
- try {
- for (final Project p : args.dbProvider.get().projects().all()) {
- if (p.getName().toLowerCase().contains(query.toLowerCase())) {
- predicate.add(new ProjectPredicate(args.dbProvider, p.getName()));
- }
+ for (Project.NameKey name : args.projectCache.all()) {
+ if (name.get().toLowerCase().contains(query.toLowerCase())) {
+ predicate.add(new ProjectPredicate(args.dbProvider, name.get()));
}
+ }
- // If two or more projects contains "query" as substring create an
- // OrPredicate holding predicates for all these projects, otherwise if
- // only one contains that, return only that one predicate by itself.
- if (predicate.size() == 1) {
- return predicate.get(0);
- } else if (predicate.size() > 1) {
- return Predicate.or(predicate);
- }
- } catch (OrmException e) {
- throw error("Cannot lookup project.", e);
+ // If two or more projects contains "query" as substring create an
+ // OrPredicate holding predicates for all these projects, otherwise if
+ // only one contains that, return only that one predicate by itself.
+ if (predicate.size() == 1) {
+ return predicate.get(0);
+ } else if (predicate.size() > 1) {
+ return Predicate.or(predicate);
}
throw error("Unsupported query:" + query);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
index 717b487..b31bf65 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
@@ -38,7 +38,8 @@
new ChangeQueryBuilder.Arguments( //
new InvalidProvider<ReviewDb>(), //
new InvalidProvider<ChangeQueryRewriter>(), //
- null, null, null, null, null, null, null, null, null, null), null));
+ null, null, null, null, null, null, null, //
+ null, null, null, null), null));
private final Provider<ReviewDb> dbProvider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
index e76c278..c5c974d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -16,6 +16,7 @@
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.Permission;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.ReviewDb;
@@ -33,48 +34,54 @@
private static enum Test {
EQ {
@Override
- public boolean match(short psValue, short expValue) {
+ public boolean match(int psValue, int expValue) {
return psValue == expValue;
}
},
GT_EQ {
@Override
- public boolean match(short psValue, short expValue) {
+ public boolean match(int psValue, int expValue) {
return psValue >= expValue;
}
},
LT_EQ {
@Override
- public boolean match(short psValue, short expValue) {
+ public boolean match(int psValue, int expValue) {
return psValue <= expValue;
}
};
- abstract boolean match(short psValue, short expValue);
+ abstract boolean match(int psValue, int expValue);
}
- private static ApprovalCategory.Id category(ApprovalTypes types, String toFind) {
- if (types.getApprovalType(new ApprovalCategory.Id(toFind)) != null) {
- return new ApprovalCategory.Id(toFind);
+ private static ApprovalCategory category(ApprovalTypes types, String toFind) {
+ if (types.byLabel(toFind) != null) {
+ return types.byLabel(toFind).getCategory();
+ }
+
+ if (types.byId(new ApprovalCategory.Id(toFind)) != null) {
+ return types.byId(new ApprovalCategory.Id(toFind)).getCategory();
}
for (ApprovalType at : types.getApprovalTypes()) {
- String name = at.getCategory().getName();
- if (toFind.equalsIgnoreCase(name)) {
- return at.getCategory().getId();
+ ApprovalCategory category = at.getCategory();
- } else if (toFind.equalsIgnoreCase(name.replace(" ", ""))) {
- return at.getCategory().getId();
+ if (toFind.equalsIgnoreCase(category.getName())) {
+ return category;
+
+ } else if (toFind.equalsIgnoreCase(category.getName().replace(" ", ""))) {
+ return category;
}
}
for (ApprovalType at : types.getApprovalTypes()) {
- if (toFind.equalsIgnoreCase(at.getCategory().getAbbreviatedName())) {
- return at.getCategory().getId();
+ ApprovalCategory category = at.getCategory();
+ if (toFind.equalsIgnoreCase(category.getAbbreviatedName())) {
+ return category;
}
}
- return new ApprovalCategory.Id(toFind);
+ return new ApprovalCategory(new ApprovalCategory.Id(toFind), toFind);
}
private static Test op(String op) {
@@ -92,19 +99,20 @@
}
}
- private static short value(String value) {
+ private static int value(String value) {
if (value.startsWith("+")) {
value = value.substring(1);
}
- return Short.parseShort(value);
+ return Integer.parseInt(value);
}
private final ChangeControl.GenericFactory ccFactory;
private final IdentifiedUser.GenericFactory userFactory;
private final Provider<ReviewDb> dbProvider;
private final Test test;
- private final ApprovalCategory.Id category;
- private final short expVal;
+ private final ApprovalCategory category;
+ private final String permissionName;
+ private final int expVal;
LabelPredicate(ChangeControl.GenericFactory ccFactory,
IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider,
@@ -131,13 +139,15 @@
test = Test.EQ;
expVal = 1;
}
+
+ this.permissionName = Permission.forLabel(category.getLabelName());
}
@Override
public boolean match(final ChangeData object) throws OrmException {
for (PatchSetApproval p : object.currentApprovals(dbProvider)) {
if (p.getCategoryId().equals(category)) {
- short psVal = p.getValue();
+ int psVal = p.getValue();
if (test.match(psVal, expVal)) {
// Double check the value is still permitted for the user.
//
@@ -149,7 +159,7 @@
//
continue;
}
- psVal = cc.normalize(category, psVal);
+ psVal = cc.getRange(permissionName).squash(psVal);
} catch (NoSuchChangeException e) {
// The project has disappeared.
//
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java
index 2fb6694..1c37ac4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java
@@ -26,19 +26,19 @@
import java.util.Set;
final class SingleGroupUser extends CurrentUser {
- private final Set<AccountGroup.Id> groups;
+ private final Set<AccountGroup.UUID> groups;
- SingleGroupUser(AuthConfig authConfig, AccountGroup.Id groupId) {
+ SingleGroupUser(AuthConfig authConfig, AccountGroup.UUID groupId) {
this(authConfig, Collections.singleton(groupId));
}
- SingleGroupUser(AuthConfig authConfig, Set<AccountGroup.Id> groups) {
+ SingleGroupUser(AuthConfig authConfig, Set<AccountGroup.UUID> groups) {
super(AccessPath.UNKNOWN, authConfig);
this.groups = groups;
}
@Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
+ public Set<AccountGroup.UUID> getEffectiveGroups() {
return groups;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
index 55a36d6..6b66e33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
@@ -26,8 +26,6 @@
public class DatabaseModule extends FactoryModule {
@Override
protected void configure() {
- install(new SchemaVersion.Module());
-
bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).to(
new TypeLiteral<Database<ReviewDb>>() {}).in(SINGLETON);
bind(new TypeLiteral<Database<ReviewDb>>() {}).toProvider(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
index a24471a..c5ca308 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -14,19 +14,26 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.common.Version;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountGroupName;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.workflow.NoOpFunction;
-import com.google.gerrit.server.workflow.SubmitFunction;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.NoReplication;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtjsonrpc.server.SignedToken;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.jdbc.JdbcExecutor;
@@ -37,6 +44,11 @@
import com.google.gwtorm.schema.sql.SqlDialect;
import com.google.inject.Inject;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -50,27 +62,40 @@
private final @SitePath
File site_path;
+ private final GitRepositoryManager mgr;
+ private final PersonIdent serverUser;
+
private final int versionNbr;
private final ScriptRunner index_generic;
private final ScriptRunner index_postgres;
private final ScriptRunner mysql_nextval;
+ private AccountGroup admin;
+ private AccountGroup anonymous;
+ private AccountGroup registered;
+ private AccountGroup owners;
+
@Inject
public SchemaCreator(final SitePaths site,
- @Current final SchemaVersion version) {
- this(site.site_path, version);
+ @Current final SchemaVersion version, final GitRepositoryManager mgr,
+ @GerritPersonIdent final PersonIdent au) {
+ this(site.site_path, version, mgr, au);
}
public SchemaCreator(final @SitePath File site,
- @Current final SchemaVersion version) {
+ @Current final SchemaVersion version, final GitRepositoryManager gitMgr,
+ final @GerritPersonIdent PersonIdent au) {
site_path = site;
+ mgr = gitMgr;
+ serverUser = au;
versionNbr = version.getVersionNbr();
index_generic = new ScriptRunner("index_generic.sql");
index_postgres = new ScriptRunner("index_postgres.sql");
mysql_nextval = new ScriptRunner("mysql_nextval.sql");
}
- public void create(final ReviewDb db) throws OrmException {
+ public void create(final ReviewDb db) throws OrmException, IOException,
+ ConfigInvalidException {
final JdbcSchema jdbc = (JdbcSchema) db;
final JdbcExecutor e = new JdbcExecutor(jdbc);
try {
@@ -84,15 +109,13 @@
db.schemaVersion().insert(Collections.singleton(sVer));
final SystemConfig sConfig = initSystemConfig(db);
- initOwnerCategory(db);
- initReadCategory(db, sConfig);
initVerifiedCategory(db);
initCodeReviewCategory(db, sConfig);
- initSubmitCategory(db);
- initPushTagCategory(db);
- initPushUpdateBranchCategory(db);
- initForgeIdentityCategory(db, sConfig);
- initWildCardProject(db);
+
+ if (mgr != null) {
+ // TODO This should never be null when initializing a site.
+ initWildCardProject();
+ }
final SqlDialect d = jdbc.getDialect();
if (d instanceof DialectH2) {
@@ -110,19 +133,27 @@
}
}
+ private AccountGroup newGroup(ReviewDb c, String name, AccountGroup.UUID uuid)
+ throws OrmException {
+ if (uuid == null) {
+ uuid = GroupUUID.make(name, serverUser);
+ }
+ return new AccountGroup( //
+ new AccountGroup.NameKey(name), //
+ new AccountGroup.Id(c.nextAccountGroupId()), //
+ uuid);
+ }
+
private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException {
- final AccountGroup admin =
- new AccountGroup(new AccountGroup.NameKey("Administrators"),
- new AccountGroup.Id(c.nextAccountGroupId()));
+ admin = newGroup(c, "Administrators", null);
admin.setDescription("Gerrit Site Administrators");
admin.setType(AccountGroup.Type.INTERNAL);
c.accountGroups().insert(Collections.singleton(admin));
c.accountGroupNames().insert(
Collections.singleton(new AccountGroupName(admin)));
- final AccountGroup anonymous =
- new AccountGroup(new AccountGroup.NameKey("Anonymous Users"),
- new AccountGroup.Id(c.nextAccountGroupId()));
+ anonymous =
+ newGroup(c, "Anonymous Users", AccountGroup.ANONYMOUS_USERS);
anonymous.setDescription("Any user, signed-in or not");
anonymous.setOwnerGroupId(admin.getId());
anonymous.setType(AccountGroup.Type.SYSTEM);
@@ -130,9 +161,8 @@
c.accountGroupNames().insert(
Collections.singleton(new AccountGroupName(anonymous)));
- final AccountGroup registered =
- new AccountGroup(new AccountGroup.NameKey("Registered Users"),
- new AccountGroup.Id(c.nextAccountGroupId()));
+ registered =
+ newGroup(c, "Registered Users", AccountGroup.REGISTERED_USERS);
registered.setDescription("Any signed-in user");
registered.setOwnerGroupId(admin.getId());
registered.setType(AccountGroup.Type.SYSTEM);
@@ -140,9 +170,7 @@
c.accountGroupNames().insert(
Collections.singleton(new AccountGroupName(registered)));
- final AccountGroup batchUsers =
- new AccountGroup(new AccountGroup.NameKey("Non-Interactive Users"),
- new AccountGroup.Id(c.nextAccountGroupId()));
+ final AccountGroup batchUsers = newGroup(c, "Non-Interactive Users", null);
batchUsers.setDescription("Users who perform batch actions on Gerrit");
batchUsers.setOwnerGroupId(admin.getId());
batchUsers.setType(AccountGroup.Type.INTERNAL);
@@ -150,9 +178,7 @@
c.accountGroupNames().insert(
Collections.singleton(new AccountGroupName(batchUsers)));
- final AccountGroup owners =
- new AccountGroup(new AccountGroup.NameKey("Project Owners"),
- new AccountGroup.Id(c.nextAccountGroupId()));
+ owners = newGroup(c, "Project Owners", AccountGroup.PROJECT_OWNERS);
owners.setDescription("Any owner of the project");
owners.setOwnerGroupId(admin.getId());
owners.setType(AccountGroup.Type.SYSTEM);
@@ -162,10 +188,17 @@
final SystemConfig s = SystemConfig.create();
s.registerEmailPrivateKey = SignedToken.generateRandomKey();
+
s.adminGroupId = admin.getId();
+ s.adminGroupUUID = admin.getGroupUUID();
+
s.anonymousGroupId = anonymous.getId();
+
s.registeredGroupId = registered.getId();
+
s.batchUsersGroupId = batchUsers.getId();
+ s.batchUsersGroupUUID = batchUsers.getGroupUUID();
+
s.ownerGroupId = owners.getId();
s.wildProjectName = DEFAULT_WILD_NAME;
try {
@@ -177,13 +210,64 @@
return s;
}
- private void initWildCardProject(final ReviewDb c) throws OrmException {
- final Project p;
+ private void initWildCardProject() throws IOException, ConfigInvalidException {
+ Repository git;
+ try {
+ git = mgr.openRepository(DEFAULT_WILD_NAME);
+ } catch (RepositoryNotFoundException notFound) {
+ // A repository may be missing if this project existed only to store
+ // inheritable permissions. For example '-- All Projects --'.
+ try {
+ git = mgr.createRepository(DEFAULT_WILD_NAME);
+ } catch (RepositoryNotFoundException err) {
+ final String name = DEFAULT_WILD_NAME.get();
+ throw new IOException("Cannot create repository " + name, err);
+ }
+ }
+ try {
+ MetaDataUpdate md =
+ new MetaDataUpdate(new NoReplication(), DEFAULT_WILD_NAME, git);
+ md.getCommitBuilder().setAuthor(serverUser);
+ md.getCommitBuilder().setCommitter(serverUser);
- p = new Project(DEFAULT_WILD_NAME);
- p.setDescription("Rights inherited by all other projects");
- p.setUseContributorAgreements(false);
- c.projects().insert(Collections.singleton(p));
+ ProjectConfig config = ProjectConfig.read(md);
+ Project p = config.getProject();
+ p.setDescription("Rights inherited by all other projects");
+ p.setUseContributorAgreements(false);
+
+ AccessSection all = config.getAccessSection(AccessSection.ALL, true);
+ AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
+ AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true);
+
+ PermissionRule review = rule(config, registered);
+ review.setRange(-1, 1);
+ heads.getPermission(Permission.LABEL + "Code-Review", true).add(review);
+
+ all.getPermission(Permission.READ, true) //
+ .add(rule(config, admin));
+ all.getPermission(Permission.READ, true) //
+ .add(rule(config, anonymous));
+
+ config.getAccessSection("refs/for/" + AccessSection.ALL, true) //
+ .getPermission(Permission.PUSH, true) //
+ .add(rule(config, registered));
+ all.getPermission(Permission.FORGE_AUTHOR, true) //
+ .add(rule(config, registered));
+
+ meta.getPermission(Permission.READ, true) //
+ .add(rule(config, owners));
+
+ md.setMessage("Initialized Gerrit Code Review " + Version.getVersion());
+ if (!config.commit(md)) {
+ throw new IOException("Cannot create " + DEFAULT_WILD_NAME.get());
+ }
+ } finally {
+ git.close();
+ }
+ }
+
+ private PermissionRule rule(ProjectConfig config, AccountGroup group) {
+ return new PermissionRule(config.resolve(group));
}
private void initVerifiedCategory(final ReviewDb c) throws OrmException {
@@ -218,143 +302,6 @@
vals.add(value(cat, -2, "Do not submit"));
c.approvalCategories().insert(Collections.singleton(cat));
c.approvalCategoryValues().insert(vals);
-
- final RefRight approve =
- new RefRight(new RefRight.Key(DEFAULT_WILD_NAME,
- new RefRight.RefPattern("refs/heads/*"), cat.getId(),
- sConfig.registeredGroupId));
- approve.setMaxValue((short) 1);
- approve.setMinValue((short) -1);
- c.refRights().insert(Collections.singleton(approve));
- }
-
- private void initOwnerCategory(final ReviewDb c) throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.OWN, "Owner");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 1, "Administer All Settings"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(vals);
- }
-
- private void initReadCategory(final ReviewDb c, final SystemConfig sConfig)
- throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.READ, "Read Access");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 3, "Upload merges permission"));
- vals.add(value(cat, 2, "Upload permission"));
- vals.add(value(cat, 1, "Read access"));
- vals.add(value(cat, -1, "No access"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(vals);
-
- final RefRight.RefPattern pattern = new RefRight.RefPattern(RefRight.ALL);
- {
- final RefRight read =
- new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern,
- cat.getId(), sConfig.anonymousGroupId));
- read.setMaxValue((short) 1);
- read.setMinValue((short) 1);
- c.refRights().insert(Collections.singleton(read));
- }
- {
- final RefRight read =
- new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern,
- cat.getId(), sConfig.registeredGroupId));
- read.setMaxValue((short) 2);
- read.setMinValue((short) 1);
- c.refRights().insert(Collections.singleton(read));
- }
- {
- final RefRight read =
- new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern,
- cat.getId(), sConfig.adminGroupId));
- read.setMaxValue((short) 1);
- read.setMinValue((short) 1);
- c.refRights().insert(Collections.singleton(read));
- }
- }
-
- private void initSubmitCategory(final ReviewDb c) throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.SUBMIT, "Submit");
- cat.setPosition((short) -1);
- cat.setFunctionName(SubmitFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 1, "Submit"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(vals);
- }
-
- private void initPushTagCategory(final ReviewDb c) throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.PUSH_TAG, "Push Tag");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, ApprovalCategory.PUSH_TAG_SIGNED, "Create Signed Tag"));
- vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANNOTATED,
- "Create Annotated Tag"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(vals);
- }
-
- private void initPushUpdateBranchCategory(final ReviewDb c)
- throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.PUSH_HEAD, "Push Branch");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, ApprovalCategory.PUSH_HEAD_UPDATE, "Update Branch"));
- vals.add(value(cat, ApprovalCategory.PUSH_HEAD_CREATE, "Create Branch"));
- vals.add(value(cat, ApprovalCategory.PUSH_HEAD_REPLACE,
- "Force Push Branch; Delete Branch"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(vals);
- }
-
- private void initForgeIdentityCategory(final ReviewDb c,
- final SystemConfig sConfig) throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> values;
-
- cat =
- new ApprovalCategory(ApprovalCategory.FORGE_IDENTITY, "Forge Identity");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- values = new ArrayList<ApprovalCategoryValue>();
- values.add(value(cat, ApprovalCategory.FORGE_AUTHOR,
- "Forge Author Identity"));
- values.add(value(cat, ApprovalCategory.FORGE_COMMITTER,
- "Forge Committer or Tagger Identity"));
- values.add(value(cat, ApprovalCategory.FORGE_SERVER,
- "Forge Gerrit Code Review Server Identity"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(values);
-
- RefRight right =
- new RefRight(new RefRight.Key(sConfig.wildProjectName,
- new RefRight.RefPattern(RefRight.ALL),
- ApprovalCategory.FORGE_IDENTITY, sConfig.registeredGroupId));
- right.setMinValue(ApprovalCategory.FORGE_AUTHOR);
- right.setMaxValue(ApprovalCategory.FORGE_AUTHOR);
- c.refRights().insert(Collections.singleton(right));
}
private static ApprovalCategoryValue value(final ApprovalCategory cat,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java
new file mode 100644
index 0000000..3225e13
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2009 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.schema;
+
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.GerritPersonIdentProvider;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+
+import org.eclipse.jgit.lib.PersonIdent;
+
+/** Validate the schema and connect to Git. */
+public class SchemaModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ install(new SchemaVersion.Module());
+
+ bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider(
+ GerritPersonIdentProvider.class);
+
+ bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
+ install(new LifecycleModule() {
+ @Override
+ protected void configure() {
+ listener().to(LocalDiskRepositoryManager.Lifecycle.class);
+ }
+ });
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
index f44eff5..3937aaa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -23,6 +23,8 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collections;
@@ -49,7 +51,13 @@
final SchemaVersion u = updater.get();
final CurrentSchemaVersion version = getSchemaVersion(db);
if (version == null) {
- creator.create(db);
+ try {
+ creator.create(db);
+ } catch (IOException e) {
+ throw new OrmException("Cannot initialize schema", e);
+ } catch (ConfigInvalidException e) {
+ throw new OrmException("Cannot initialize schema", e);
+ }
} else {
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 978a152..b83f540 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- private static final Class<? extends SchemaVersion> C = Schema_52.class;
+ private static final Class<? extends SchemaVersion> C = Schema_53.class;
public static class Module extends AbstractModule {
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java
deleted file mode 100644
index 7ad91ff..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2009 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.schema;
-
-import com.google.gerrit.reviewdb.CurrentSchemaVersion;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-class Schema_19 extends SchemaVersion {
- @Inject
- Schema_19() {
- super(new Provider<SchemaVersion>() {
- public SchemaVersion get() {
- throw new ProvisionException("Cannot upgrade from 18");
- }
- });
- }
-
- @Override
- protected void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr,
- ReviewDb db, boolean toTargetVersion) throws OrmException {
- throw new OrmException("Cannot upgrade from " + curr.versionNbr
- + "; manually run scripts from"
- + " http://gerrit.googlecode.com/files/schema-upgrades003_019.zip"
- + " and restart.");
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_20.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_20.java
deleted file mode 100644
index 4d8dca7..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_20.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2009 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-class Schema_20 extends SchemaVersion {
- @Inject
- Schema_20(Provider<Schema_19> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java
deleted file mode 100644
index ed50f7f..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2009 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.schema;
-
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectH2;
-import com.google.gwtorm.schema.sql.DialectMySQL;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Collections;
-
-class Schema_21 extends SchemaVersion {
- @Inject
- Schema_21(Provider<Schema_20> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- JdbcSchema jdbc = (JdbcSchema) db;
- SystemConfig sc = db.systemConfig().get(new SystemConfig.Key());
-
- Statement s = jdbc.getConnection().createStatement();
- try {
- ResultSet r;
-
- r = s.executeQuery("SELECT name FROM projects WHERE project_id = 0");
- try {
- if (!r.next()) {
- throw new OrmException("Cannot read old wild project");
- }
- sc.wildProjectName = new Project.NameKey(r.getString(1));
- } finally {
- r.close();
- }
-
- if (jdbc.getDialect() instanceof DialectMySQL) {
- try {
- s.execute("DROP FUNCTION nextval_project_id");
- } catch (SQLException se) {
- ui.message("warning: could not delete function nextval_project_id");
- }
-
- } else if (jdbc.getDialect() instanceof DialectH2) {
- s.execute("ALTER TABLE projects DROP CONSTRAINT"
- + " IF EXISTS CONSTRAINT_F3");
- }
- } finally {
- s.close();
- }
-
- db.systemConfig().update(Collections.singleton(sc));
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_22.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_22.java
deleted file mode 100644
index 8e24aa2..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_22.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2009 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.schema;
-
-import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_USERNAME;
-
-import com.google.gerrit.reviewdb.Account;
-import com.google.gerrit.reviewdb.AccountExternalId;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.AccountExternalId.Key;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectH2;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-
-class Schema_22 extends SchemaVersion {
- @Inject
- Schema_22(Provider<Schema_21> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- Statement s = ((JdbcSchema) db).getConnection().createStatement();
- try {
- ResultSet results =
- s.executeQuery(//
- "SELECT account_id, ssh_user_name"
- + " FROM accounts" //
- + " WHERE ssh_user_name IS NOT NULL"
- + " AND ssh_user_name <> ''");
- Collection<AccountExternalId> ids = new ArrayList<AccountExternalId>();
- while (results.next()) {
- final int accountId = results.getInt(1);
- final String userName = results.getString(2);
-
- final Account.Id account = new Account.Id(accountId);
- final AccountExternalId.Key key = toKey(userName);
- ids.add(new AccountExternalId(account, key));
- }
- db.accountExternalIds().insert(ids);
-
- if (((JdbcSchema) db).getDialect() instanceof DialectH2) {
- s.execute("ALTER TABLE accounts DROP CONSTRAINT"
- + " IF EXISTS CONSTRAINT_AF");
- }
- } finally {
- s.close();
- }
- }
-
- private Key toKey(final String userName) {
- return new AccountExternalId.Key(SCHEME_USERNAME, userName);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_23.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_23.java
deleted file mode 100644
index 413fa4e..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_23.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2009 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.schema;
-
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.AccountGroupName;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-
-class Schema_23 extends SchemaVersion {
- @Inject
- Schema_23(Provider<Schema_22> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- Collection<AccountGroupName> names = new ArrayList<AccountGroupName>();
- Statement queryStmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- ResultSet results =
- queryStmt.executeQuery("SELECT group_id, name FROM account_groups");
- while (results.next()) {
- final int id = results.getInt(1);
- final String name = results.getString(2);
-
- final AccountGroup.Id group = new AccountGroup.Id(id);
- final AccountGroup.NameKey key = toKey(name);
- names.add(new AccountGroupName(key, group));
- }
- } finally {
- queryStmt.close();
- }
- db.accountGroupNames().insert(names);
- }
-
- private AccountGroup.NameKey toKey(final String name) {
- return new AccountGroup.NameKey(name);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_24.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_24.java
deleted file mode 100644
index 0172921..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_24.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-class Schema_24 extends SchemaVersion {
- @Inject
- Schema_24(Provider<Schema_23> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_25.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_25.java
deleted file mode 100644
index fdfff70..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_25.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.eclipse.jgit.lib.Constants;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-class Schema_25 extends SchemaVersion {
- private Set<ApprovalCategory.Id> nonActions;
-
- @Inject
- Schema_25(Provider<Schema_24> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- nonActions = new HashSet<ApprovalCategory.Id>();
- for (ApprovalCategory c : db.approvalCategories().all()) {
- if (!c.isAction()) {
- nonActions.add(c.getId());
- }
- }
-
- List<RefRight> rights = new ArrayList<RefRight>();
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- ResultSet rs = stmt.executeQuery("SELECT * FROM project_rights");
- try {
- while (rs.next()) {
- rights.add(toRefRight(rs));
- }
- } finally {
- rs.close();
- }
-
- db.refRights().insert(rights);
- stmt.execute("CREATE INDEX ref_rights_byCatGroup"
- + " ON ref_rights (category_id, group_id)");
- } finally {
- stmt.close();
- }
- }
-
- private RefRight toRefRight(ResultSet rs) throws SQLException {
- short min_value = rs.getShort("min_value");
- short max_value = rs.getShort("max_value");
- String category_id = rs.getString("category_id");
- int group_id = rs.getInt("group_id");
- String project_name = rs.getString("project_name");
-
- ApprovalCategory.Id category = new ApprovalCategory.Id(category_id);
- Project.NameKey project = new Project.NameKey(project_name);
- AccountGroup.Id group = new AccountGroup.Id(group_id);
-
- RefRight.RefPattern ref;
- if (category.equals(ApprovalCategory.SUBMIT)
- || category.equals(ApprovalCategory.PUSH_HEAD)
- || nonActions.contains(category)) {
- // Explicitly related to a branch head.
- ref = new RefRight.RefPattern(Constants.R_HEADS + "*");
-
- } else if (category.equals(ApprovalCategory.PUSH_TAG)) {
- // Explicitly related to the tag namespace.
- ref = new RefRight.RefPattern(Constants.R_TAGS + "/*");
-
- } else if (category.equals(ApprovalCategory.READ)
- || category.equals(ApprovalCategory.OWN)) {
- // Currently these are project-wide rights, so apply that way.
- ref = new RefRight.RefPattern(RefRight.ALL);
-
- } else {
- // Assume project wide for the default.
- ref = new RefRight.RefPattern(RefRight.ALL);
- }
-
- RefRight.Key key = new RefRight.Key(project, ref, category, group);
- RefRight r = new RefRight(key);
- r.setMinValue(min_value);
- r.setMaxValue(max_value);
- return r;
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_26.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_26.java
deleted file mode 100644
index 9c76af2..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_26.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectMySQL;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-class Schema_26 extends SchemaVersion {
- @Inject
- Schema_26(Provider<Schema_25> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- if (((JdbcSchema) db).getDialect() instanceof DialectMySQL) {
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("ALTER TABLE account_group_members_audit" //
- + " MODIFY removed_on TIMESTAMP NULL DEFAULT NULL");
- stmt.execute("UPDATE account_group_members_audit" //
- + " SET removed_on = NULL" //
- + " WHERE removed_by IS NULL;");
- } finally {
- stmt.close();
- }
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_27.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_27.java
deleted file mode 100644
index f8febec..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_27.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.AccountGroupName;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectH2;
-import com.google.gwtorm.schema.sql.DialectMySQL;
-import com.google.gwtorm.schema.sql.DialectPostgreSQL;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-class Schema_27 extends SchemaVersion {
- @Inject
- Schema_27(Provider<Schema_26> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException, OrmException {
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- final SqlDialect dialect = ((JdbcSchema) db).getDialect();
- if (dialect instanceof DialectPostgreSQL) {
- stmt.execute("ALTER TABLE account_groups"
- + " ALTER COLUMN name TYPE VARCHAR(255)");
- stmt.execute("ALTER TABLE account_group_names"
- + " ALTER COLUMN name TYPE VARCHAR(255)");
-
- } else if (dialect instanceof DialectH2) {
- stmt.execute("ALTER TABLE account_groups"
- + " ALTER COLUMN name VARCHAR(255)");
- stmt.execute("ALTER TABLE account_group_names"
- + " ALTER COLUMN name VARCHAR(255) NOT NULL");
-
- } else if (dialect instanceof DialectMySQL) {
- stmt.execute("ALTER TABLE account_groups MODIFY name VARCHAR(255)");
- stmt.execute("ALTER TABLE account_group_names"
- + " MODIFY name VARCHAR(255)");
-
- } else {
- throw new OrmException("Unsupported dialect " + dialect);
- }
- } finally {
- stmt.close();
- }
-
- // Some groups might be missing their names, our older schema
- // creation logic failed to create the name objects. Do it now.
- //
- Map<AccountGroup.NameKey, AccountGroupName> names =
- db.accountGroupNames().toMap(db.accountGroupNames().all());
-
- List<AccountGroupName> insert = new ArrayList<AccountGroupName>();
- List<AccountGroupName> update = new ArrayList<AccountGroupName>();
-
- for (AccountGroup g : db.accountGroups().all()) {
- AccountGroupName n = names.get(g.getNameKey());
- if (n == null) {
- insert.add(new AccountGroupName(g));
-
- } else if (!g.getId().equals(n.getId())) {
- n.setId(g.getId());
- update.add(n);
- }
- }
-
- db.accountGroupNames().insert(insert);
- db.accountGroupNames().update(update);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_28.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_28.java
deleted file mode 100644
index cddbe4e..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_28.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gerrit.server.workflow.NoOpFunction;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collections;
-
-class Schema_28 extends SchemaVersion {
- @Inject
- Schema_28(Provider<Schema_27> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- final SystemConfig cfg = db.systemConfig().get(new SystemConfig.Key());
- ApprovalCategory cat;
-
- initForgeIdentityCategory(db, cfg);
-
- // Don't grant FORGE_COMMITTER to existing PUSH_HEAD rights. That
- // is considered a bug that we are fixing with this schema upgrade.
- // Administrators might need to relax permissions manually after the
- // upgrade if that forgery is critical to their workflow.
-
- cat = db.approvalCategories().get(ApprovalCategory.PUSH_TAG);
- if (cat != null && "Push Annotated Tag".equals(cat.getName())) {
- cat.setName("Push Tag");
- db.approvalCategories().update(Collections.singleton(cat));
- }
-
- // Since we deleted Push Tags +3, drop anything using +3 down to +2.
- //
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("UPDATE ref_rights SET max_value = "
- + ApprovalCategory.PUSH_TAG_ANNOTATED + " WHERE max_value >= 3");
- stmt.execute("UPDATE ref_rights SET min_value = "
- + ApprovalCategory.PUSH_TAG_ANNOTATED + " WHERE min_value >= 3");
- } finally {
- stmt.close();
- }
- }
-
- private void initForgeIdentityCategory(final ReviewDb c,
- final SystemConfig sConfig) throws OrmException {
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> values;
-
- cat =
- new ApprovalCategory(ApprovalCategory.FORGE_IDENTITY, "Forge Identity");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- values = new ArrayList<ApprovalCategoryValue>();
- values.add(value(cat, ApprovalCategory.FORGE_AUTHOR,
- "Forge Author Identity"));
- values.add(value(cat, ApprovalCategory.FORGE_COMMITTER,
- "Forge Committer or Tagger Identity"));
- c.approvalCategories().insert(Collections.singleton(cat));
- c.approvalCategoryValues().insert(values);
-
- RefRight right =
- new RefRight(new RefRight.Key(sConfig.wildProjectName,
- new RefRight.RefPattern(RefRight.ALL),
- ApprovalCategory.FORGE_IDENTITY, sConfig.registeredGroupId));
- right.setMinValue(ApprovalCategory.FORGE_AUTHOR);
- right.setMaxValue(ApprovalCategory.FORGE_AUTHOR);
- c.refRights().insert(Collections.singleton(right));
- }
-
- private static ApprovalCategoryValue value(final ApprovalCategory cat,
- final int value, final String name) {
- return new ApprovalCategoryValue(new ApprovalCategoryValue.Id(cat.getId(),
- (short) value), name);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_29.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_29.java
deleted file mode 100644
index 37920bf..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_29.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-class Schema_29 extends SchemaVersion {
- @Inject
- Schema_29(Provider<Schema_28> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_30.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_30.java
deleted file mode 100644
index 7285e32..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_30.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.Collections;
-
-class Schema_30 extends SchemaVersion {
- @Inject
- Schema_30(Provider<Schema_29> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- db.approvalCategoryValues().insert(
- Collections.singleton(new ApprovalCategoryValue(
- new ApprovalCategoryValue.Id(ApprovalCategory.FORGE_IDENTITY,
- ApprovalCategory.FORGE_SERVER),
- "Forge Gerrit Code Review Server Identity")));
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_31.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_31.java
deleted file mode 100644
index 62f57e2..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_31.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-class Schema_31 extends SchemaVersion {
- @Inject
- Schema_31(Provider<Schema_30> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("CREATE INDEX changes_byProject"
- + " ON changes (dest_project_name)");
- } finally {
- stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_32.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_32.java
deleted file mode 100644
index 2af5596..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_32.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_32 extends SchemaVersion {
- @Inject
- Schema_32(Provider<Schema_31> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_33.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_33.java
deleted file mode 100644
index 0c733d7..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_33.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.AccountGroupName;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.Collections;
-
-public class Schema_33 extends SchemaVersion {
- @Inject
- Schema_33(Provider<Schema_32> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- SystemConfig config = db.systemConfig().all().toList().get(0);
- final AccountGroup batchUsers =
- new AccountGroup(new AccountGroup.NameKey("Non-Interactive Users"),
- new AccountGroup.Id(db.nextAccountGroupId()));
- batchUsers.setDescription("Users who perform batch actions on Gerrit");
- batchUsers.setOwnerGroupId(config.adminGroupId);
- batchUsers.setType(AccountGroup.Type.INTERNAL);
- db.accountGroups().insert(Collections.singleton(batchUsers));
- db.accountGroupNames().insert(
- Collections.singleton(new AccountGroupName(batchUsers)));
-
- config.batchUsersGroupId = batchUsers.getId();
- db.systemConfig().update(Collections.singleton(config));
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_34.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_34.java
deleted file mode 100644
index fa94146..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_34.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gerrit.reviewdb.RefRight.RefPattern;
-import com.google.gerrit.server.project.RefControl.RefRightsForPattern;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-public class Schema_34 extends SchemaVersion {
- private static final Comparator<String> DESCENDING_SORT =
- new Comparator<String>() {
-
- @Override
- public int compare(String a, String b) {
- int aLength = a.length();
- int bLength = b.length();
- if (bLength == aLength) {
- return a.compareTo(b);
- }
- return bLength - aLength;
- }
- };
-
- @Inject
- Schema_34(Provider<Schema_33> prior) {
- super(prior);
- }
-
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
- Iterable<Project> projects = db.projects().all();
- boolean showedBanner = false;
-
- List<RefRight> toUpdate = new ArrayList<RefRight>();
- List<RefRight> toDelete = new ArrayList<RefRight>();
- for (Project p : projects) {
- boolean showedProject = false;
- List<RefRight> pr = db.refRights().byProject(p.getNameKey()).toList();
- Map<ApprovalCategory.Id, Map<String, RefRightsForPattern>> r =
- new HashMap<ApprovalCategory.Id, Map<String, RefRightsForPattern>>();
- for (RefRight right : pr) {
- ApprovalCategory.Id cat = right.getApprovalCategoryId();
- if (r.get(cat) == null) {
- Map<String, RefRightsForPattern> m =
- new TreeMap<String, RefRightsForPattern>(DESCENDING_SORT);
- r.put(cat, m);
- }
- if (r.get(cat).get(right.getRefPattern()) == null) {
- RefRightsForPattern s = new RefRightsForPattern();
- r.get(cat).put(right.getRefPattern(), s);
- }
- r.get(cat).get(right.getRefPattern()).addRight(right);
- }
-
- for (Map<String, RefRightsForPattern> categoryRights : r.values()) {
- for (RefRightsForPattern rrp : categoryRights.values()) {
- RefRight oldRight = rrp.getRights().get(0);
- if (shouldPrompt(oldRight)) {
- if (!showedBanner) {
- ui.message("Entering interactive reference rights migration tool...");
- showedBanner = true;
- }
- if (!showedProject) {
- ui.message("In project " + p.getName());
- showedProject = true;
- }
- ui.message("For category " + oldRight.getApprovalCategoryId());
- boolean isWildcard = oldRight.getRefPattern().endsWith("/*");
- boolean shouldUpdate = ui.yesno(!isWildcard,
- "Should rights for pattern "
- + oldRight.getRefPattern()
- + " be considered exclusive?");
- if (shouldUpdate) {
- RefRight.Key newKey = new RefRight.Key(oldRight.getProjectNameKey(),
- new RefPattern("-" + oldRight.getRefPattern()),
- oldRight.getApprovalCategoryId(),
- oldRight.getAccountGroupId());
- RefRight newRight = new RefRight(newKey);
- newRight.setMaxValue(oldRight.getMaxValue());
- newRight.setMinValue(oldRight.getMinValue());
- toUpdate.add(newRight);
- toDelete.add(oldRight);
- }
- }
- }
- }
- }
- db.refRights().insert(toUpdate);
- db.refRights().delete(toDelete);
- }
-
- private boolean shouldPrompt(RefRight right) {
- return !right.getRefPattern().equals("refs/*")
- && !right.getRefPattern().equals("refs/heads/*")
- && !right.getRefPattern().equals("refs/tags/*");
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_35.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_35.java
deleted file mode 100644
index 12d90c3..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_35.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_35 extends SchemaVersion {
- @Inject
- Schema_35(Provider<Schema_34> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("CREATE INDEX tracking_ids_byTrkId"
- + " ON tracking_ids (tracking_id)");
- } finally {
- stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_36.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_36.java
deleted file mode 100644
index ba6b841..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_36.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectMySQL;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_36 extends SchemaVersion {
- @Inject
- Schema_36(Provider<Schema_35> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- if (((JdbcSchema) db).getDialect() instanceof DialectMySQL) {
- stmt.execute("DROP INDEX account_project_watches_ntNew ON account_project_watches");
- stmt.execute("DROP INDEX account_project_watches_ntCmt ON account_project_watches");
- stmt.execute("DROP INDEX account_project_watches_ntSub ON account_project_watches");
- } else {
- stmt.execute("DROP INDEX account_project_watches_ntNew");
- stmt.execute("DROP INDEX account_project_watches_ntCmt");
- stmt.execute("DROP INDEX account_project_watches_ntSub");
- }
- stmt.execute("CREATE INDEX account_project_watches_byProject"
- + " ON account_project_watches (project_name)");
- } finally {
- stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_37.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_37.java
deleted file mode 100644
index 871f2e9..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_37.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_37 extends SchemaVersion {
- @Inject
- Schema_37(Provider<Schema_36> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_38.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_38.java
deleted file mode 100644
index 59d6fa2..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_38.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.Account;
-import com.google.gerrit.reviewdb.AccountDiffPreference;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.List;
-
-public class Schema_38 extends SchemaVersion {
- @Inject
- Schema_38(Provider<Schema_37> prior) {
- super(prior);
- }
-
- /**
- * Migrate the account.default_context column to account_diff_preferences.context column.
- * <p>
- * Other fields in account_diff_preferences will be filled in with their defaults as
- * defined in the {@link AccountDiffPreference#createDefault(com.google.gerrit.reviewdb.Account.Id)}
- */
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException,
- SQLException {
- List<AccountDiffPreference> newPrefs =
- new ArrayList<AccountDiffPreference>();
-
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- ResultSet result =
- stmt.executeQuery("SELECT account_id, default_context"
- + " FROM accounts WHERE default_context <> 10");
- while (result.next()) {
- int accountId = result.getInt(1);
- short defaultContext = result.getShort(2);
- AccountDiffPreference diffPref = AccountDiffPreference.createDefault(new Account.Id(accountId));
- diffPref.setContext(defaultContext);
- newPrefs.add(diffPref);
- }
- } finally {
- stmt.close();
- }
-
- db.accountDiffPreferences().insert(newPrefs);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_39.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_39.java
deleted file mode 100644
index 39ae226..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_39.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_39 extends SchemaVersion {
- @Inject
- Schema_39(Provider<Schema_38> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_40.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_40.java
deleted file mode 100644
index 7d3e4f5..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_40.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.AccountProjectWatch;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.gwtorm.schema.sql.DialectH2;
-import com.google.gwtorm.schema.sql.DialectMySQL;
-import com.google.gwtorm.schema.sql.DialectPostgreSQL;
-import com.google.gwtorm.schema.sql.SqlDialect;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_40 extends SchemaVersion {
- @Inject
- Schema_40(Provider<Schema_39> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException,
- OrmException {
- // Set to "*" the filter field of the previously watched projects
- //
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("UPDATE account_project_watches" //
- + " SET filter = '" + AccountProjectWatch.FILTER_ALL + "'" //
- + " WHERE filter IS NULL OR filter = ''");
-
- // Set the new primary key
- //
- final SqlDialect dialect = ((JdbcSchema) db).getDialect();
- if (dialect instanceof DialectPostgreSQL) {
- stmt.execute("ALTER TABLE account_project_watches "
- + "DROP CONSTRAINT account_project_watches_pkey");
- stmt.execute("ALTER TABLE account_project_watches "
- + "ADD PRIMARY KEY (account_id, project_name, filter)");
-
- } else if ((dialect instanceof DialectH2)
- || (dialect instanceof DialectMySQL)) {
- stmt.execute("ALTER TABLE account_project_watches DROP PRIMARY KEY");
- stmt.execute("ALTER TABLE account_project_watches "
- + "ADD PRIMARY KEY (account_id, project_name, filter)");
-
- } else {
- throw new OrmException("Unsupported dialect " + dialect);
- }
- } finally {
- stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_41.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_41.java
deleted file mode 100644
index 508db43..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_41.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_41 extends SchemaVersion {
- @Inject
- Schema_41(Provider<Schema_40> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_42.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_42.java
deleted file mode 100644
index 83bca7b..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_42.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_42 extends SchemaVersion {
- @Inject
- Schema_42(Provider<Schema_41> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_43.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_43.java
deleted file mode 100644
index 0edb7e5..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_43.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_43 extends SchemaVersion {
- @Inject
- Schema_43(Provider<Schema_42> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_44.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_44.java
deleted file mode 100644
index 4ab1986..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_44.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_44 extends SchemaVersion {
- @Inject
- Schema_44(Provider<Schema_43> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_45.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_45.java
deleted file mode 100644
index e37e87d..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_45.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_45 extends SchemaVersion {
- @Inject
- Schema_45(Provider<Schema_44> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_46.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_46.java
deleted file mode 100644
index e7b104c..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_46.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.AccountGroupName;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Collections;
-
-public class Schema_46 extends SchemaVersion {
-
- @Inject
- Schema_46(final Provider<Schema_45> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException,
- OrmException {
- AccountGroup.Id groupId = new AccountGroup.Id(db.nextAccountGroupId());
-
- // update system_config
- final Connection connection = ((JdbcSchema) db).getConnection();
- Statement stmt = null;
- try {
- stmt = connection.createStatement();
- stmt.execute("UPDATE system_config SET OWNER_GROUP_ID = " + groupId.get());
- final ResultSet resultSet =
- stmt.executeQuery("SELECT ADMIN_GROUP_ID FROM system_config");
- resultSet.next();
- final int adminGroupId = resultSet.getInt(1);
-
- // create 'Project Owners' group
- AccountGroup.NameKey nameKey = new AccountGroup.NameKey("Project Owners");
- AccountGroup group = new AccountGroup(nameKey, groupId);
- group.setType(AccountGroup.Type.SYSTEM);
- group.setOwnerGroupId(new AccountGroup.Id(adminGroupId));
- group.setDescription("Any owner of the project");
- AccountGroupName gn = new AccountGroupName(group);
- db.accountGroupNames().insert(Collections.singleton(gn));
- db.accountGroups().insert(Collections.singleton(group));
- } finally {
- if (stmt != null) stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_47.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_47.java
deleted file mode 100644
index 124cc02..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_47.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_47 extends SchemaVersion {
- @Inject
- Schema_47(Provider<Schema_46> prior) {
- super(prior);
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_48.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_48.java
deleted file mode 100644
index 4e8b94d..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_48.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2010 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.schema;
-
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-import java.util.Collections;
-
-public class Schema_48 extends SchemaVersion {
- @Inject
- Schema_48(Provider<Schema_47> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
- // Read +3 allows merges to be uploaded
- db.approvalCategoryValues().insert(
- Collections.singleton(new ApprovalCategoryValue(
- new ApprovalCategoryValue.Id(ApprovalCategory.READ, (short) 3),
- "Upload merges permission")));
- // Since we added Read +3, elevate any Read +2 to that level to provide
- // access equivalent to prior schema versions.
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("UPDATE ref_rights SET max_value = 3"
- + " WHERE category_id = '" + ApprovalCategory.READ.get()
- + "' AND max_value = 2");
- } finally {
- stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_50.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_50.java
deleted file mode 100644
index 4a90c1f..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_50.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2011 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.schema;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-public class Schema_50 extends SchemaVersion {
- @Inject
- Schema_50(Provider<Schema_49> prior) {
- super(prior);
- }
-}
\ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_51.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_51.java
deleted file mode 100644
index d111160..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_51.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2011 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.schema;
-
-import com.google.gerrit.reviewdb.ReviewDb;
-import com.google.gwtorm.jdbc.JdbcSchema;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.sql.SQLException;
-import java.sql.Statement;
-
-public class Schema_51 extends SchemaVersion {
- @Inject
- Schema_51(Provider<Schema_50> prior) {
- super(prior);
- }
-
- @Override
- protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException {
- Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
- try {
- stmt.execute("CREATE INDEX account_group_includes_byInclude"
- + " ON account_group_includes (include_id)");
- } finally {
- stmt.close();
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java
index 3fbbbe0..e16b95c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java
@@ -14,12 +14,28 @@
package com.google.gerrit.server.schema;
+import com.google.gerrit.reviewdb.CurrentSchemaVersion;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
public class Schema_52 extends SchemaVersion {
@Inject
- Schema_52(Provider<Schema_51> prior) {
- super(prior);
+ Schema_52() {
+ super(new Provider<SchemaVersion>() {
+ public SchemaVersion get() {
+ throw new ProvisionException("Cannot upgrade from 51");
+ }
+ });
+ }
+
+ @Override
+ protected void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr,
+ ReviewDb db, boolean toTargetVersion) throws OrmException {
+ throw new OrmException("Cannot upgrade from schema " + curr.versionNbr
+ + "; manually run init from Gerrit Code Review 2.1.7"
+ + " and restart this version to continue.");
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_53.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_53.java
new file mode 100644
index 0000000..8a04071
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_53.java
@@ -0,0 +1,473 @@
+// Copyright (C) 2010 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.schema;
+
+import static com.google.gerrit.common.data.Permission.CREATE;
+import static com.google.gerrit.common.data.Permission.FORGE_AUTHOR;
+import static com.google.gerrit.common.data.Permission.FORGE_COMMITTER;
+import static com.google.gerrit.common.data.Permission.FORGE_SERVER;
+import static com.google.gerrit.common.data.Permission.LABEL;
+import static com.google.gerrit.common.data.Permission.OWNER;
+import static com.google.gerrit.common.data.Permission.PUSH;
+import static com.google.gerrit.common.data.Permission.PUSH_MERGE;
+import static com.google.gerrit.common.data.Permission.PUSH_TAG;
+import static com.google.gerrit.common.data.Permission.READ;
+import static com.google.gerrit.common.data.Permission.SUBMIT;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.account.GroupUUID;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.NoReplication;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class Schema_53 extends SchemaVersion {
+ private final GitRepositoryManager mgr;
+ private final PersonIdent serverUser;
+
+ private SystemConfig systemConfig;
+ private Map<AccountGroup.Id, GroupReference> groupMap;
+ private Map<ApprovalCategory.Id, ApprovalCategory> categoryMap;
+ private GroupReference projectOwners;
+
+ private Map<Project.NameKey, Project.NameKey> parentsByProject;
+ private Map<Project.NameKey, List<OldRefRight>> rightsByProject;
+
+ private final String OLD_SUBMIT = "SUBM";
+ private final String OLD_READ = "READ";
+ private final String OLD_OWN = "OWN";
+ private final String OLD_PUSH_TAG = "pTAG";
+ private final String OLD_PUSH_HEAD = "pHD";
+ private final String OLD_FORGE_IDENTITY = "FORG";
+
+ @Inject
+ Schema_53(Provider<Schema_52> prior, GitRepositoryManager mgr,
+ @GerritPersonIdent PersonIdent serverUser) {
+ super(prior);
+ this.mgr = mgr;
+ this.serverUser = serverUser;
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException,
+ SQLException {
+ systemConfig = db.systemConfig().get(new SystemConfig.Key());
+ categoryMap = db.approvalCategories().toMap(db.approvalCategories().all());
+
+ assignGroupUUIDs(db);
+ readOldRefRights(db);
+ readProjectParents(db);
+ exportProjectConfig(db);
+
+ deleteActionCategories(db);
+ }
+
+ private void deleteActionCategories(ReviewDb db) throws OrmException {
+ List<ApprovalCategory> delete = new ArrayList<ApprovalCategory>();
+ for (ApprovalCategory category : categoryMap.values()) {
+ if (category.getPosition() < 0) {
+ delete.add(category);
+ }
+ }
+ db.approvalCategories().delete(delete);
+ }
+
+ private void assignGroupUUIDs(ReviewDb db) throws OrmException {
+ groupMap = new HashMap<AccountGroup.Id, GroupReference>();
+ List<AccountGroup> groups = db.accountGroups().all().toList();
+ for (AccountGroup g : groups) {
+ if (g.getId().equals(systemConfig.ownerGroupId)) {
+ g.setGroupUUID(AccountGroup.PROJECT_OWNERS);
+ projectOwners = GroupReference.forGroup(g);
+
+ } else if (g.getId().equals(systemConfig.anonymousGroupId)) {
+ g.setGroupUUID(AccountGroup.ANONYMOUS_USERS);
+
+ } else if (g.getId().equals(systemConfig.registeredGroupId)) {
+ g.setGroupUUID(AccountGroup.REGISTERED_USERS);
+
+ } else {
+ g.setGroupUUID(GroupUUID.make(g.getName(), serverUser));
+ }
+ groupMap.put(g.getId(), GroupReference.forGroup(g));
+ }
+ db.accountGroups().update(groups);
+
+ systemConfig.adminGroupUUID = toUUID(systemConfig.adminGroupId);
+ systemConfig.batchUsersGroupUUID = toUUID(systemConfig.batchUsersGroupId);
+ db.systemConfig().update(Collections.singleton(systemConfig));
+ }
+
+ private AccountGroup.UUID toUUID(AccountGroup.Id id) {
+ return groupMap.get(id).getUUID();
+ }
+
+ private void exportProjectConfig(ReviewDb db) throws OrmException,
+ SQLException {
+ Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT * FROM projects ORDER BY name");
+ while (rs.next()) {
+ final String name = rs.getString("name");
+ final Project.NameKey nameKey = new Project.NameKey(name);
+
+ Repository git;
+ try {
+ git = mgr.openRepository(nameKey);
+ } catch (RepositoryNotFoundException notFound) {
+ // A repository may be missing if this project existed only to store
+ // inheritable permissions. For example '-- All Projects --'.
+ try {
+ git = mgr.createRepository(nameKey);
+ } catch (RepositoryNotFoundException err) {
+ throw new OrmException("Cannot create repository " + name, err);
+ }
+ }
+ try {
+ MetaDataUpdate md =
+ new MetaDataUpdate(new NoReplication(), nameKey, git);
+ md.getCommitBuilder().setAuthor(serverUser);
+ md.getCommitBuilder().setCommitter(serverUser);
+
+ ProjectConfig config = ProjectConfig.read(md);
+ loadProject(rs, config.getProject());
+ config.getAccessSections().clear();
+ convertRights(config);
+
+ // Grant out read on the config branch by default.
+ //
+ if (config.getProject().getNameKey().equals(systemConfig.wildProjectName)) {
+ AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true);
+ Permission read = meta.getPermission(READ, true);
+ read.getRule(config.resolve(projectOwners), true);
+ }
+
+ md.setMessage("Import project configuration from SQL\n");
+ if (!config.commit(md)) {
+ throw new OrmException("Cannot export project " + name);
+ }
+ } catch (ConfigInvalidException err) {
+ throw new OrmException("Cannot read project " + name, err);
+ } catch (IOException err) {
+ throw new OrmException("Cannot export project " + name, err);
+ } finally {
+ git.close();
+ }
+ }
+ rs.close();
+ stmt.close();
+ }
+
+ private void loadProject(ResultSet rs, Project project) throws SQLException,
+ OrmException {
+ project.setDescription(rs.getString("description"));
+ project.setUseContributorAgreements("Y".equals(rs
+ .getString("use_contributor_agreements")));
+
+ switch (rs.getString("submit_type").charAt(0)) {
+ case 'F':
+ project.setSubmitType(Project.SubmitType.FAST_FORWARD_ONLY);
+ break;
+ case 'M':
+ project.setSubmitType(Project.SubmitType.MERGE_IF_NECESSARY);
+ break;
+ case 'A':
+ project.setSubmitType(Project.SubmitType.MERGE_ALWAYS);
+ break;
+ case 'C':
+ project.setSubmitType(Project.SubmitType.CHERRY_PICK);
+ break;
+ default:
+ throw new OrmException("Unsupported submit_type="
+ + rs.getString("submit_type") + " on project " + project.getName());
+ }
+
+ project.setUseSignedOffBy("Y".equals(rs.getString("use_signed_off_by")));
+ project.setRequireChangeID("Y".equals(rs.getString("require_change_id")));
+ project.setUseContentMerge("Y".equals(rs.getString("use_content_merge")));
+ project.setParentName(rs.getString("parent_name"));
+ }
+
+ private void readOldRefRights(ReviewDb db) throws SQLException {
+ rightsByProject = new HashMap<Project.NameKey, List<OldRefRight>>();
+
+ Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT * FROM ref_rights");
+ while (rs.next()) {
+ OldRefRight right = new OldRefRight(rs);
+ if (right.group == null || right.category == null) {
+ continue;
+ }
+
+ List<OldRefRight> list;
+
+ list = rightsByProject.get(right.project);
+ if (list == null) {
+ list = new ArrayList<OldRefRight>();
+ rightsByProject.put(right.project, list);
+ }
+ list.add(right);
+ }
+ rs.close();
+ stmt.close();
+ }
+
+ private void readProjectParents(ReviewDb db) throws SQLException {
+ parentsByProject = new HashMap<Project.NameKey, Project.NameKey>();
+ Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT * FROM projects");
+ while (rs.next()) {
+ String name = rs.getString("name");
+ String parent_name = rs.getString("parent_name");
+ if (parent_name == null) {
+ parent_name = systemConfig.wildProjectName.get();
+ }
+ parentsByProject.put(new Project.NameKey(name), //
+ new Project.NameKey(parent_name));
+ }
+ rs.close();
+ stmt.close();
+ }
+
+ private void convertRights(ProjectConfig config) {
+ List<OldRefRight> myRights =
+ rightsByProject.get(config.getProject().getNameKey());
+ if (myRights == null) {
+ return;
+ }
+
+ for (OldRefRight old : myRights) {
+ AccessSection section = config.getAccessSection(old.ref_pattern, true);
+ GroupReference group = config.resolve(old.group);
+
+ if (OLD_SUBMIT.equals(old.category)) {
+ PermissionRule submit = rule(group);
+ submit.setDeny(old.max_value <= 0);
+ add(section, SUBMIT, old.exclusive, submit);
+
+ } else if (OLD_READ.equals(old.category)) {
+ if (old.exclusive) {
+ section.getPermission(READ, true).setExclusiveGroup(true);
+ newChangePermission(config, old.ref_pattern).setExclusiveGroup(true);
+ }
+
+ PermissionRule read = rule(group);
+ read.setDeny(old.max_value <= 0);
+ add(section, READ, old.exclusive, read);
+
+ if (3 <= old.max_value) {
+ newMergePermission(config, old.ref_pattern).add(rule(group));
+ } else if (3 <= inheritedMax(config, old)) {
+ newMergePermission(config, old.ref_pattern).add(deny(group));
+ }
+
+ if (2 <= old.max_value) {
+ newChangePermission(config, old.ref_pattern).add(rule(group));
+ } else if (2 <= inheritedMax(config, old)) {
+ newChangePermission(config, old.ref_pattern).add(deny(group));
+ }
+
+ } else if (OLD_OWN.equals(old.category)) {
+ add(section, OWNER, false, rule(group));
+
+ } else if (OLD_PUSH_TAG.equals(old.category)) {
+ PermissionRule push = rule(group);
+ push.setDeny(old.max_value <= 0);
+ add(section, PUSH_TAG, old.exclusive, push);
+
+ } else if (OLD_PUSH_HEAD.equals(old.category)) {
+ if (old.exclusive) {
+ section.getPermission(PUSH, true).setExclusiveGroup(true);
+ section.getPermission(CREATE, true).setExclusiveGroup(true);
+ }
+
+ PermissionRule push = rule(group);
+ push.setDeny(old.max_value <= 0);
+ push.setForce(3 <= old.max_value);
+ add(section, PUSH, old.exclusive, push);
+
+ if (2 <= old.max_value) {
+ add(section, CREATE, old.exclusive, rule(group));
+ } else if (2 <= inheritedMax(config, old)) {
+ add(section, CREATE, old.exclusive, deny(group));
+ }
+
+ } else if (OLD_FORGE_IDENTITY.equals(old.category)) {
+ if (old.exclusive) {
+ section.getPermission(FORGE_AUTHOR, true).setExclusiveGroup(true);
+ section.getPermission(FORGE_COMMITTER, true).setExclusiveGroup(true);
+ section.getPermission(FORGE_SERVER, true).setExclusiveGroup(true);
+ }
+
+ if (1 <= old.max_value) {
+ add(section, FORGE_AUTHOR, old.exclusive, rule(group));
+ }
+
+ if (2 <= old.max_value) {
+ add(section, FORGE_COMMITTER, old.exclusive, rule(group));
+ } else if (2 <= inheritedMax(config, old)) {
+ add(section, FORGE_COMMITTER, old.exclusive, deny(group));
+ }
+
+ if (3 <= old.max_value) {
+ add(section, FORGE_SERVER, old.exclusive, rule(group));
+ } else if (3 <= inheritedMax(config, old)) {
+ add(section, FORGE_SERVER, old.exclusive, deny(group));
+ }
+
+ } else {
+ PermissionRule rule = rule(group);
+ rule.setRange(old.min_value, old.max_value);
+ if (old.min_value == 0 && old.max_value == 0) {
+ rule.setDeny(true);
+ }
+ add(section, LABEL + varNameOf(old.category), old.exclusive, rule);
+ }
+ }
+ }
+
+ private static Permission newChangePermission(ProjectConfig config,
+ String name) {
+ if (name.startsWith(AccessSection.REGEX_PREFIX)) {
+ name = AccessSection.REGEX_PREFIX
+ + "refs/for/"
+ + name.substring(AccessSection.REGEX_PREFIX.length());
+ } else {
+ name = "refs/for/" + name;
+ }
+ return config.getAccessSection(name, true).getPermission(PUSH, true);
+ }
+
+ private static Permission newMergePermission(ProjectConfig config,
+ String name) {
+ if (name.startsWith(AccessSection.REGEX_PREFIX)) {
+ name = AccessSection.REGEX_PREFIX
+ + "refs/for/"
+ + name.substring(AccessSection.REGEX_PREFIX.length());
+ } else {
+ name = "refs/for/" + name;
+ }
+ return config.getAccessSection(name, true).getPermission(PUSH_MERGE, true);
+ }
+
+ private static PermissionRule rule(GroupReference group) {
+ return new PermissionRule(group);
+ }
+
+ private static PermissionRule deny(GroupReference group) {
+ PermissionRule rule = rule(group);
+ rule.setDeny(true);
+ return rule;
+ }
+
+ private int inheritedMax(ProjectConfig config, OldRefRight old) {
+ int max = 0;
+
+ String ref = old.ref_pattern;
+ String category = old.category;
+ AccountGroup.UUID group = old.group.getUUID();
+
+ Project.NameKey project = config.getProject().getParent();
+ if (project == null) {
+ project = systemConfig.wildProjectName;
+ }
+ do {
+ List<OldRefRight> rights = rightsByProject.get(project);
+ if (rights != null) {
+ for (OldRefRight r : rights) {
+ if (r.ref_pattern.equals(ref) //
+ && r.group.getUUID().equals(group) //
+ && r.category.equals(category)) {
+ max = Math.max(max, r.max_value);
+ break;
+ }
+ }
+ }
+ project = parentsByProject.get(project);
+ } while (!project.equals(systemConfig.wildProjectName));
+
+ return max;
+ }
+
+ private String varNameOf(String id) {
+ ApprovalCategory category = categoryMap.get(new ApprovalCategory.Id(id));
+ if (category == null) {
+ category = new ApprovalCategory(new ApprovalCategory.Id(id), id);
+ }
+ return category.getLabelName();
+ }
+
+ private static void add(AccessSection section, String name,
+ boolean exclusive, PermissionRule rule) {
+ Permission p = section.getPermission(name, true);
+ if (exclusive) {
+ p.setExclusiveGroup(true);
+ }
+ p.add(rule);
+ }
+
+ private class OldRefRight {
+ final int min_value;
+ final int max_value;
+ final String ref_pattern;
+ final boolean exclusive;
+ final GroupReference group;
+ final String category;
+ final Project.NameKey project;
+
+ OldRefRight(ResultSet rs) throws SQLException {
+ min_value = rs.getInt("min_value");
+ max_value = rs.getInt("max_value");
+ project = new Project.NameKey(rs.getString("project_name"));
+
+ String r = rs.getString("ref_pattern");
+ exclusive = r.startsWith("-");
+ if (exclusive) {
+ r = r.substring(1);
+ }
+ ref_pattern = r;
+
+ category = rs.getString("category_id");
+ group = groupMap.get(new AccountGroup.Id(rs.getInt("group_id")));
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
index 6700393..ffed95a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.workflow;
import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.Permission;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.PatchSetApproval;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.project.RefControl;
import java.util.HashMap;
import java.util.Map;
@@ -29,7 +28,6 @@
private static Map<String, CategoryFunction> all =
new HashMap<String, CategoryFunction>();
static {
- all.put(SubmitFunction.NAME, new SubmitFunction());
all.put(MaxWithBlock.NAME, new MaxWithBlock());
all.put(MaxNoBlock.NAME, new MaxNoBlock());
all.put(NoOpFunction.NAME, new NoOpFunction());
@@ -44,22 +42,11 @@
* is not known to Gerrit and thus cannot be executed.
*/
public static CategoryFunction forCategory(final ApprovalCategory category) {
- final CategoryFunction r = forName(category.getFunctionName());
+ final CategoryFunction r = all.get(category.getFunctionName());
return r != null ? r : new NoOpFunction();
}
/**
- * Locate a function by name.
- *
- * @param functionName the function's unique name.
- * @return the function implementation; null if the function is not known to
- * Gerrit and thus cannot be executed.
- */
- public static CategoryFunction forName(final String functionName) {
- return all.get(functionName);
- }
-
- /**
* Normalize ChangeApprovals and set the valid flag for this category.
* <p>
* Implementors should invoke:
@@ -92,13 +79,8 @@
public boolean isValid(final CurrentUser user, final ApprovalType at,
final FunctionState state) {
- RefControl rc = state.controlFor(user);
- for (final RefRight pr : rc.getApplicableRights(at.getCategory().getId())) {
- if (user.getEffectiveGroups().contains(pr.getAccountGroupId())
- && (pr.getMinValue() < 0 || pr.getMaxValue() > 0)) {
- return true;
- }
- }
- return false;
+ return !state.controlFor(user) //
+ .getRange(Permission.forLabel(at.getCategory().getLabelName())) //
+ .isEmpty();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
index 36a52e2..2cb3e81 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
@@ -16,13 +16,13 @@
import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSetApproval;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ApprovalCategory.Id;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
@@ -139,28 +139,12 @@
* of them is used.
* <p>
*/
- private void applyRightFloor(final PatchSetApproval a) {
+ private void applyRightFloor(final ApprovalType at, final PatchSetApproval a) {
+ final ApprovalCategory category = at.getCategory();
+ final String permission = Permission.forLabel(category.getLabelName());
final IdentifiedUser user = userFactory.create(a.getAccountId());
- RefControl rc = controlFor(user);
-
- // Find the maximal range actually granted to the user.
- //
- short minAllowed = 0, maxAllowed = 0;
- for (final RefRight r : rc.getApplicableRights(a.getCategoryId())) {
- final AccountGroup.Id grp = r.getAccountGroupId();
- if (user.getEffectiveGroups().contains(grp)) {
- minAllowed = (short) Math.min(minAllowed, r.getMinValue());
- maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
- }
- }
-
- // Normalize the value into that range.
- //
- if (a.getValue() < minAllowed) {
- a.setValue(minAllowed);
- } else if (a.getValue() > maxAllowed) {
- a.setValue(maxAllowed);
- }
+ final PermissionRange range = controlFor(user).getRange(permission);
+ a.setValue((short) range.squash(a.getValue()));
}
RefControl controlFor(final CurrentUser user) {
@@ -172,7 +156,7 @@
/** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */
public void normalize(final ApprovalType at, final PatchSetApproval ca) {
applyTypeFloor(at, ca);
- applyRightFloor(ca);
+ applyRightFloor(at, ca);
}
private static Id id(final ApprovalType at) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java
deleted file mode 100644
index f0a00ff..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2008 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.workflow;
-
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.Change;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.project.RefControl;
-
-/**
- * Computes if the submit function can be used.
- * <p>
- * In order to be considered "approved" this function requires that all approval
- * categories with a position >= 0 (that is any whose
- * {@link ApprovalCategory#isAction()} method returns false) is valid and that
- * the change state be {@link Change.Status#NEW}.
- * <p>
- * This is mostly useful for actions, like {@link ApprovalCategory#SUBMIT}.
- */
-public class SubmitFunction extends CategoryFunction {
- public static String NAME = "Submit";
-
- @Override
- public void run(final ApprovalType at, final FunctionState state) {
- state.valid(at, valid(at, state));
- }
-
- @Override
- public boolean isValid(final CurrentUser user, final ApprovalType at,
- final FunctionState state) {
- if (valid(at, state)) {
- RefControl rc = state.controlFor(user);
- for (final RefRight pr : rc.getApplicableRights(at.getCategory().getId())) {
- if (user.getEffectiveGroups().contains(pr.getAccountGroupId())
- && pr.getMaxValue() > 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static boolean valid(final ApprovalType at, final FunctionState state) {
- if (state.getChange().getStatus() != Change.Status.NEW) {
- return false;
- }
- for (final ApprovalType t : state.getApprovalTypes()) {
- if (!state.isValid(t)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
new file mode 100644
index 0000000..e8a235f
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
@@ -0,0 +1,191 @@
+// Copyright (C) 2011 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.git;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
+ private final GroupReference developers = new GroupReference(
+ new AccountGroup.UUID("X"), "Developers");
+ private final GroupReference staff = new GroupReference(
+ new AccountGroup.UUID("Y"), "Staff");
+
+ private Repository db;
+ private TestRepository<Repository> util;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ db = createBareRepository();
+ util = new TestRepository<Repository>(db);
+ }
+
+ @Test
+ public void testReadConfig() throws Exception {
+ RevCommit rev = util.commit(util.tree( //
+ util.file("groups", util.blob(group(developers))), //
+ util.file("project.config", util.blob(""//
+ + "[access \"refs/heads/*\"]\n" //
+ + " exclusiveGroupPermissions = read submit create\n" //
+ + " submit = group Developers\n" //
+ + " push = group Developers\n" //
+ + " read = group Developers\n")) //
+ ));
+
+ ProjectConfig cfg = read(rev);
+ AccessSection section = cfg.getAccessSection("refs/heads/*");
+ assertNotNull("has refs/heads/*", section);
+ assertNull("no refs/*", cfg.getAccessSection("refs/*"));
+
+ Permission create = section.getPermission(Permission.CREATE);
+ Permission submit = section.getPermission(Permission.SUBMIT);
+ Permission read = section.getPermission(Permission.READ);
+ Permission push = section.getPermission(Permission.PUSH);
+
+ assertTrue(create.getExclusiveGroup());
+ assertTrue(submit.getExclusiveGroup());
+ assertTrue(read.getExclusiveGroup());
+ assertFalse(push.getExclusiveGroup());
+ }
+
+ @Test
+ public void testEditConfig() throws Exception {
+ RevCommit rev = util.commit(util.tree( //
+ util.file("groups", util.blob(group(developers))), //
+ util.file("project.config", util.blob(""//
+ + "[access \"refs/heads/*\"]\n" //
+ + " exclusiveGroupPermissions = read submit\n" //
+ + " submit = group Developers\n" //
+ + " upload = group Developers\n" //
+ + " read = group Developers\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ AccessSection section = cfg.getAccessSection("refs/heads/*");
+ Permission submit = section.getPermission(Permission.SUBMIT);
+ submit.add(new PermissionRule(cfg.resolve(staff)));
+ rev = commit(cfg);
+ assertEquals(""//
+ + "[access \"refs/heads/*\"]\n" //
+ + " exclusiveGroupPermissions = read submit\n" //
+ + " submit = group Developers\n" //
+ + "\tsubmit = group Staff\n" //
+ + " upload = group Developers\n" //
+ + " read = group Developers\n", text(rev, "project.config"));
+ }
+
+ @Test
+ public void testEditConfigMissingGroupTableEntry() throws Exception {
+ RevCommit rev = util.commit(util.tree( //
+ util.file("groups", util.blob(group(developers))), //
+ util.file("project.config", util.blob(""//
+ + "[access \"refs/heads/*\"]\n" //
+ + " exclusiveGroupPermissions = read submit\n" //
+ + " submit = group People Who Can Submit\n" //
+ + " upload = group Developers\n" //
+ + " read = group Developers\n")) //
+ ));
+ update(rev);
+
+ ProjectConfig cfg = read(rev);
+ AccessSection section = cfg.getAccessSection("refs/heads/*");
+ Permission submit = section.getPermission(Permission.SUBMIT);
+ submit.add(new PermissionRule(cfg.resolve(staff)));
+ rev = commit(cfg);
+ assertEquals(""//
+ + "[access \"refs/heads/*\"]\n" //
+ + " exclusiveGroupPermissions = read submit\n" //
+ + " submit = group People Who Can Submit\n" //
+ + "\tsubmit = group Staff\n" //
+ + " upload = group Developers\n" //
+ + " read = group Developers\n", text(rev, "project.config"));
+ }
+
+ private ProjectConfig read(RevCommit rev) throws IOException,
+ ConfigInvalidException {
+ ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
+ cfg.load(db, rev);
+ return cfg;
+ }
+
+ private RevCommit commit(ProjectConfig cfg) throws IOException,
+ MissingObjectException, IncorrectObjectTypeException {
+ MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), //
+ cfg.getProject().getNameKey(), //
+ db);
+ util.tick(5);
+ util.setAuthorAndCommitter(md.getCommitBuilder());
+ md.setMessage("Edit\n");
+ assertTrue("commit finished", cfg.commit(md));
+
+ Ref ref = db.getRef(GitRepositoryManager.REF_CONFIG);
+ return util.getRevWalk().parseCommit(ref.getObjectId());
+ }
+
+ private void update(RevCommit rev) throws Exception {
+ RefUpdate u = db.updateRef(GitRepositoryManager.REF_CONFIG);
+ u.disableRefLog();
+ u.setNewObjectId(rev);
+ switch (u.forceUpdate()) {
+ case FAST_FORWARD:
+ case FORCED:
+ case NEW:
+ case NO_CHANGE:
+ break;
+ default:
+ fail("Cannot update ref for test: " + u.getResult());
+ }
+ }
+
+ private String text(RevCommit rev, String path) throws Exception {
+ RevObject blob = util.get(rev.getTree(), path);
+ byte[] data = db.open(blob).getCachedBytes(Integer.MAX_VALUE);
+ return RawParseUtils.decode(data);
+ }
+
+ private static String group(GroupReference g) {
+ return g.getUUID().get() + "\t" + g.getName() + "\n";
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
index f819dac..6d56b4a 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
@@ -278,7 +278,7 @@
account.setFullName(name);
account.setPreferredEmail(email);
final AccountState s =
- new AccountState(account, Collections.<AccountGroup.Id> emptySet(),
+ new AccountState(account, Collections.<AccountGroup.UUID> emptySet(),
Collections.<AccountExternalId> emptySet());
return s;
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index cf5d264..ab5d75f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -14,23 +14,25 @@
package com.google.gerrit.server.project;
-import static com.google.gerrit.reviewdb.ApprovalCategory.OWN;
-import static com.google.gerrit.reviewdb.ApprovalCategory.READ;
-import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
+import static com.google.gerrit.common.data.Permission.OWNER;
+import static com.google.gerrit.common.data.Permission.PUSH;
+import static com.google.gerrit.common.data.Permission.READ;
+import static com.google.gerrit.common.data.Permission.SUBMIT;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.AccountProjectWatch;
-import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gerrit.reviewdb.RefRight.RefPattern;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
@@ -41,17 +43,17 @@
import org.eclipse.jgit.lib.Config;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
public class RefControlTest extends TestCase {
public void testOwnerProject() {
- grant(local, OWN, admin, "refs/*", 1);
+ grant(local, OWNER, admin, "refs/*");
ProjectControl uBlah = user(devs);
ProjectControl uAdmin = user(devs, admin);
@@ -61,8 +63,8 @@
}
public void testBranchDelegation1() {
- grant(local, OWN, admin, "refs/*", 1);
- grant(local, OWN, devs, "refs/heads/x/*", 1);
+ grant(local, OWNER, admin, "refs/*");
+ grant(local, OWNER, devs, "refs/heads/x/*");
ProjectControl uDev = user(devs);
assertFalse("not owner", uDev.isOwner());
@@ -77,9 +79,10 @@
}
public void testBranchDelegation2() {
- grant(local, OWN, admin, "refs/*", 1);
- grant(local, OWN, devs, "refs/heads/x/*", 1);
- grant(local, OWN, fixers, "-refs/heads/x/y/*", 1);
+ grant(local, OWNER, admin, "refs/*");
+ grant(local, OWNER, devs, "refs/heads/x/*");
+ grant(local, OWNER, fixers, "refs/heads/x/y/*");
+ doNotInherit(local, OWNER, "refs/heads/x/y/*");
ProjectControl uDev = user(devs);
assertFalse("not owner", uDev.isOwner());
@@ -104,8 +107,11 @@
}
public void testInheritRead_SingleBranchDeniesUpload() {
- grant(parent, READ, registered, "refs/*", 1, 2);
- grant(local, READ, registered, "-refs/heads/foobar", 1);
+ grant(parent, READ, registered, "refs/*");
+ grant(parent, PUSH, registered, "refs/for/refs/*");
+ grant(local, READ, registered, "refs/heads/foobar");
+ doNotInherit(local, READ, "refs/heads/foobar");
+ doNotInherit(local, PUSH, "refs/for/refs/heads/foobar");
ProjectControl u = user();
assertTrue("can upload", u.canPushToAtLeastOneRef());
@@ -118,8 +124,9 @@
}
public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
- grant(parent, READ, registered, "refs/*", 1, 2);
- grant(local, READ, registered, "refs/heads/foobar", 1);
+ grant(parent, READ, registered, "refs/*");
+ grant(parent, PUSH, registered, "refs/for/refs/*");
+ grant(local, READ, registered, "refs/heads/foobar");
ProjectControl u = user();
assertTrue("can upload", u.canPushToAtLeastOneRef());
@@ -132,16 +139,16 @@
}
public void testInheritRead_OverrideWithDeny() {
- grant(parent, READ, registered, "refs/*", 1);
- grant(local, READ, registered, "refs/*", 0);
+ grant(parent, READ, registered, "refs/*");
+ grant(local, READ, registered, "refs/*").setDeny(true);
ProjectControl u = user();
assertFalse("can't read", u.isVisible());
}
public void testInheritRead_AppendWithDenyOfRef() {
- grant(parent, READ, registered, "refs/*", 1);
- grant(local, READ, registered, "refs/heads/*", 0);
+ grant(parent, READ, registered, "refs/*");
+ grant(local, READ, registered, "refs/heads/*").setDeny(true);
ProjectControl u = user();
assertTrue("can read", u.isVisible());
@@ -151,9 +158,9 @@
}
public void testInheritRead_OverridesAndDeniesOfRef() {
- grant(parent, READ, registered, "refs/*", 1);
- grant(local, READ, registered, "refs/*", 0);
- grant(local, READ, registered, "refs/heads/*", -1, 1);
+ grant(parent, READ, registered, "refs/*");
+ grant(local, READ, registered, "refs/*").setDeny(true);
+ grant(local, READ, registered, "refs/heads/*");
ProjectControl u = user();
assertTrue("can read", u.isVisible());
@@ -163,9 +170,9 @@
}
public void testInheritSubmit_OverridesAndDeniesOfRef() {
- grant(parent, SUBMIT, registered, "refs/*", 1);
- grant(local, SUBMIT, registered, "refs/*", 0);
- grant(local, SUBMIT, registered, "refs/heads/*", -1, 1);
+ grant(parent, SUBMIT, registered, "refs/*");
+ grant(local, SUBMIT, registered, "refs/*").setDeny(true);
+ grant(local, SUBMIT, registered, "refs/heads/*");
ProjectControl u = user();
assertFalse("can't submit", u.controlForRef("refs/foobar").canSubmit());
@@ -174,8 +181,9 @@
}
public void testCannotUploadToAnyRef() {
- grant(parent, READ, registered, "refs/*", 1);
- grant(local, READ, devs, "refs/heads/*", 1, 2);
+ grant(parent, READ, registered, "refs/*");
+ grant(local, READ, devs, "refs/heads/*");
+ grant(local, PUSH, devs, "refs/for/refs/heads/*");
ProjectControl u = user();
assertFalse("cannot upload", u.canPushToAtLeastOneRef());
@@ -186,15 +194,14 @@
// -----------------------------------------------------------------------
- private final Project.NameKey local = new Project.NameKey("test");
- private final Project.NameKey parent = new Project.NameKey("parent");
- private final AccountGroup.Id admin = new AccountGroup.Id(1);
- private final AccountGroup.Id anonymous = new AccountGroup.Id(2);
- private final AccountGroup.Id registered = new AccountGroup.Id(3);
- private final AccountGroup.Id owners = new AccountGroup.Id(4);
+ private ProjectConfig local;
+ private ProjectConfig parent;
+ private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin");
+ private final AccountGroup.UUID anonymous = AccountGroup.ANONYMOUS_USERS;
+ private final AccountGroup.UUID registered = AccountGroup.REGISTERED_USERS;
- private final AccountGroup.Id devs = new AccountGroup.Id(5);
- private final AccountGroup.Id fixers = new AccountGroup.Id(6);
+ private final AccountGroup.UUID devs = new AccountGroup.UUID("test.devs");
+ private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
private final SystemConfig systemConfig;
private final AuthConfig authConfig;
@@ -202,11 +209,8 @@
public RefControlTest() {
systemConfig = SystemConfig.create();
- systemConfig.adminGroupId = admin;
- systemConfig.anonymousGroupId = anonymous;
- systemConfig.registeredGroupId = registered;
- systemConfig.ownerGroupId = owners;
- systemConfig.batchUsersGroupId = anonymous;
+ systemConfig.adminGroupUUID = admin;
+ systemConfig.batchUsersGroupUUID = anonymous;
try {
byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8");
systemConfig.registerEmailPrivateKey = Base64.encodeBase64String(bin);
@@ -230,14 +234,16 @@
anonymousUser = injector.getInstance(AnonymousUser.class);
}
- private List<RefRight> localRights;
- private List<RefRight> inheritedRights;
-
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
- localRights = new ArrayList<RefRight>();
- inheritedRights = new ArrayList<RefRight>();
+
+ parent = new ProjectConfig(new Project.NameKey("parent"));
+ parent.createInMemory();
+
+ local = new ProjectConfig(new Project.NameKey("local"));
+ local.createInMemory();
+ local.getProject().setParentName(parent.getProject().getName());
}
private static void assertOwner(String ref, ProjectControl u) {
@@ -248,63 +254,91 @@
assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner());
}
- private void grant(Project.NameKey project, ApprovalCategory.Id categoryId,
- AccountGroup.Id group, String ref, int maxValue) {
- grant(project, categoryId, group, ref, maxValue, maxValue);
+ private PermissionRule grant(ProjectConfig project, String permissionName,
+ AccountGroup.UUID group, String ref) {
+ PermissionRule rule = newRule(project, group);
+ project.getAccessSection(ref, true) //
+ .getPermission(permissionName, true) //
+ .add(rule);
+ return rule;
}
- private void grant(Project.NameKey project, ApprovalCategory.Id categoryId, AccountGroup.Id group,
- String ref, int minValue, int maxValue) {
- RefRight right =
- new RefRight(new RefRight.Key(project, new RefPattern(ref),
- categoryId, group));
- right.setMinValue((short) minValue);
- right.setMaxValue((short) maxValue);
-
- if (project == parent) {
- inheritedRights.add(right);
- } else if (project == local) {
- localRights.add(right);
- } else {
- fail("Unknown project key: " + project);
- }
+ private void doNotInherit(ProjectConfig project, String permissionName,
+ String ref) {
+ project.getAccessSection(ref, true) //
+ .getPermission(permissionName, true) //
+ .setExclusiveGroup(true);
}
- private ProjectControl user(AccountGroup.Id... memberOf) {
+ private PermissionRule newRule(ProjectConfig project, AccountGroup.UUID groupUUID) {
+ GroupReference group = new GroupReference(groupUUID, groupUUID.get());
+ group = project.resolve(group);
+
+ return new PermissionRule(group);
+ }
+
+ private ProjectControl user(AccountGroup.UUID... memberOf) {
RefControl.Factory refControlFactory = new RefControl.Factory() {
@Override
public RefControl create(final ProjectControl projectControl, final String ref) {
- return new RefControl(systemConfig, projectControl, ref);
+ return new RefControl(projectControl, ref);
}
};
- return new ProjectControl(Collections.<AccountGroup.Id> emptySet(),
- Collections.<AccountGroup.Id> emptySet(), refControlFactory,
+ return new ProjectControl(Collections.<AccountGroup.UUID> emptySet(),
+ Collections.<AccountGroup.UUID> emptySet(), refControlFactory,
new MockUser(memberOf), newProjectState());
}
private ProjectState newProjectState() {
- ProjectCache projectCache = null;
+ final Map<Project.NameKey, ProjectState> all =
+ new HashMap<Project.NameKey, ProjectState>();
+ final ProjectCache projectCache = new ProjectCache() {
+ @Override
+ public ProjectState get(Project.NameKey projectName) {
+ return all.get(projectName);
+ }
+
+ @Override
+ public void evict(Project p) {
+ }
+
+ @Override
+ public Iterable<Project.NameKey> all() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Iterable<Project.NameKey> byName(String prefix) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void onCreateProject(Project.NameKey newProjectName) {
+ }
+ };
+
+ GitRepositoryManager mgr = null;
Project.NameKey wildProject = new Project.NameKey("-- All Projects --");
ProjectControl.AssistedFactory projectControlFactory = null;
- ProjectState ps =
- new ProjectState(anonymousUser, projectCache, wildProject,
- projectControlFactory, new Project(parent), localRights);
- ps.setInheritedRights(inheritedRights);
- return ps;
+ all.put(local.getProject().getNameKey(), new ProjectState(anonymousUser,
+ projectCache, wildProject, projectControlFactory, mgr, local));
+ all.put(parent.getProject().getNameKey(), new ProjectState(anonymousUser,
+ projectCache, wildProject, projectControlFactory, mgr, parent));
+ return all.get(local.getProject().getNameKey());
}
private class MockUser extends CurrentUser {
- private final Set<AccountGroup.Id> groups;
+ private final Set<AccountGroup.UUID> groups;
- MockUser(AccountGroup.Id[] groupId) {
+ MockUser(AccountGroup.UUID[] groupId) {
super(AccessPath.UNKNOWN, RefControlTest.this.authConfig);
- groups = new HashSet<AccountGroup.Id>(Arrays.asList(groupId));
+ groups = new HashSet<AccountGroup.UUID>(Arrays.asList(groupId));
groups.add(registered);
groups.add(anonymous);
}
@Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
+ public Set<AccountGroup.UUID> getEffectiveGroups() {
return groups;
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
index ee9af13..1d70d4e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
@@ -17,12 +17,8 @@
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
-import com.google.gerrit.server.workflow.NoOpFunction;
-import com.google.gerrit.server.workflow.SubmitFunction;
import com.google.gerrit.testutil.InMemoryDatabase;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.jdbc.JdbcSchema;
@@ -151,24 +147,6 @@
}
}
- public void testCreateSchema_WildCardProject() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final SystemConfig cfg;
- final Project all;
-
- cfg = c.systemConfig().get(new SystemConfig.Key());
- all = c.projects().get(cfg.wildProjectName);
- assertNotNull(all);
- assertEquals("-- All Projects --", all.getName());
- assertFalse(all.isUseContributorAgreements());
- assertFalse(all.isUseSignedOffBy());
- assertFalse(all.isRequireChangeID());
- } finally {
- c.close();
- }
- }
-
public void testCreateSchema_ApprovalCategory_CodeReview()
throws OrmException {
final ReviewDb c = db.create().open();
@@ -182,7 +160,6 @@
assertEquals("R", cat.getAbbreviatedName());
assertEquals("MaxWithBlock", cat.getFunctionName());
assertTrue(cat.isCopyMinScore());
- assertFalse(cat.isAction());
assertTrue(0 <= cat.getPosition());
} finally {
c.close();
@@ -190,101 +167,6 @@
assertValueRange(codeReview, -2, -1, 0, 1, 2);
}
- public void testCreateSchema_ApprovalCategory_Read() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.READ);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.READ, cat.getId());
- assertEquals("Read Access", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.READ, -1, 1, 2, 3);
- }
-
- public void testCreateSchema_ApprovalCategory_Submit() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.SUBMIT);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.SUBMIT, cat.getId());
- assertEquals("Submit", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(SubmitFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.SUBMIT, 1);
- }
-
- public void testCreateSchema_ApprovalCategory_PushTag() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.PUSH_TAG);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.PUSH_TAG, cat.getId());
- assertEquals("Push Tag", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.PUSH_TAG, //
- ApprovalCategory.PUSH_TAG_SIGNED, //
- ApprovalCategory.PUSH_TAG_ANNOTATED);
- }
-
- public void testCreateSchema_ApprovalCategory_PushHead() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.PUSH_HEAD);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.PUSH_HEAD, cat.getId());
- assertEquals("Push Branch", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.PUSH_HEAD, //
- ApprovalCategory.PUSH_HEAD_UPDATE, //
- ApprovalCategory.PUSH_HEAD_CREATE, //
- ApprovalCategory.PUSH_HEAD_REPLACE);
- }
-
- public void testCreateSchema_ApprovalCategory_Owner() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.OWN);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.OWN, cat.getId());
- assertEquals("Owner", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.OWN, 1);
- }
-
private void assertValueRange(ApprovalCategory.Id cat, int... range)
throws OrmException {
final HashSet<ApprovalCategoryValue.Id> act =
@@ -314,57 +196,4 @@
fail("Category " + cat + " has additional values: " + act);
}
}
-
- public void testCreateSchema_DefaultAccess_AnonymousUsers()
- throws OrmException {
- db.create();
- final SystemConfig config = db.getSystemConfig();
- assertDefaultRight("refs/*", config.anonymousGroupId,
- ApprovalCategory.READ, 1, 1);
- }
-
- public void testCreateSchema_DefaultAccess_RegisteredUsers()
- throws OrmException {
- db.create();
- final SystemConfig config = db.getSystemConfig();
- assertDefaultRight("refs/*", config.registeredGroupId,
- ApprovalCategory.READ, 1, 2);
- assertDefaultRight("refs/heads/*", config.registeredGroupId, codeReview,
- -1, 1);
- }
-
- public void testCreateSchema_DefaultAccess_Administrators()
- throws OrmException {
- db.create();
- final SystemConfig config = db.getSystemConfig();
- assertDefaultRight("refs/*", config.adminGroupId, ApprovalCategory.READ, 1,
- 1);
- }
-
- private void assertDefaultRight(final String pattern,
- final AccountGroup.Id group, final ApprovalCategory.Id category, int min,
- int max) throws OrmException {
- final ReviewDb c = db.open();
- try {
- final SystemConfig cfg;
- final Project all;
- final RefRight right;
-
- cfg = c.systemConfig().get(new SystemConfig.Key());
- all = c.projects().get(cfg.wildProjectName);
- right =
- c.refRights().get(
- new RefRight.Key(all.getNameKey(), new RefRight.RefPattern(
- pattern), category, group));
-
- assertNotNull(right);
- assertEquals(all.getNameKey(), right.getProjectNameKey());
- assertEquals(group, right.getAccountGroupId());
- assertEquals(category, right.getApprovalCategoryId());
- assertEquals(min, right.getMinValue());
- assertEquals(max, right.getMaxValue());
- } finally {
- c.close();
- }
- }
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index a009f24..f2ba70e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -16,7 +16,12 @@
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.GerritPersonIdentProvider;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.testutil.InMemoryDatabase;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
@@ -27,6 +32,9 @@
import junit.framework.TestCase;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
@@ -58,6 +66,22 @@
bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).toInstance(db);
bind(SitePaths.class).toInstance(paths);
install(new SchemaVersion.Module());
+
+ Config cfg = new Config();
+ cfg.setString("gerrit", null, "basePath", "git");
+ cfg.setString("user", null, "name", "Gerrit Code Review");
+ cfg.setString("user", null, "email", "gerrit@localhost");
+
+ bind(Config.class) //
+ .annotatedWith(GerritServerConfig.class) //
+ .toInstance(cfg);
+
+ bind(PersonIdent.class) //
+ .annotatedWith(GerritPersonIdent.class) //
+ .toProvider(GerritPersonIdentProvider.class);
+
+ bind(GitRepositoryManager.class) //
+ .to(LocalDiskRepositoryManager.class);
}
}).getInstance(SchemaUpdater.class);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
index fe138c6..d95f17a4 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
@@ -17,7 +17,13 @@
import com.google.gerrit.reviewdb.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.GerritPersonIdentProvider;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SystemConfigProvider;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.schema.Current;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.schema.SchemaVersion;
@@ -25,13 +31,19 @@
import com.google.gwtorm.client.SchemaFactory;
import com.google.gwtorm.jdbc.Database;
import com.google.gwtorm.jdbc.SimpleDataSource;
+import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.inject.Provider;
import junit.framework.TestCase;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+
import java.io.File;
+import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
@@ -84,8 +96,33 @@
database = new Database<ReviewDb>(dataSource, ReviewDb.class);
schemaVersion =
- Guice.createInjector(new SchemaVersion.Module()).getBinding(
- Key.get(SchemaVersion.class, Current.class)).getProvider().get();
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ install(new SchemaVersion.Module());
+
+ bind(File.class) //
+ .annotatedWith(SitePath.class) //
+ .toInstance(new File("."));
+
+ Config cfg = new Config();
+ cfg.setString("gerrit", null, "basePath", "git");
+ cfg.setString("user", null, "name", "Gerrit Code Review");
+ cfg.setString("user", null, "email", "gerrit@localhost");
+
+ bind(Config.class) //
+ .annotatedWith(GerritServerConfig.class) //
+ .toInstance(cfg);
+
+ bind(PersonIdent.class) //
+ .annotatedWith(GerritPersonIdent.class) //
+ .toProvider(GerritPersonIdentProvider.class);
+
+ bind(GitRepositoryManager.class) //
+ .to(LocalDiskRepositoryManager.class);
+ }
+ }).getBinding(Key.get(SchemaVersion.class, Current.class))
+ .getProvider().get();
} catch (SQLException e) {
throw new OrmException(e);
}
@@ -106,7 +143,14 @@
created = true;
final ReviewDb c = open();
try {
- new SchemaCreator(new File("."), schemaVersion).create(c);
+ try {
+ new SchemaCreator(new File("."), schemaVersion, null,
+ new PersonIdent("name", "email@site")).create(c);
+ } catch (IOException e) {
+ throw new OrmException("Cannot create in-memory database", e);
+ } catch (ConfigInvalidException e) {
+ throw new OrmException("Cannot create in-memory database", e);
+ }
} finally {
c.close();
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
index 5b8edf0..61e8bfb 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
@@ -32,6 +32,7 @@
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.sshd.args4j.AccountGroupIdHandler;
+import com.google.gerrit.sshd.args4j.AccountGroupUUIDHandler;
import com.google.gerrit.sshd.args4j.AccountIdHandler;
import com.google.gerrit.sshd.args4j.PatchSetIdHandler;
import com.google.gerrit.sshd.args4j.ProjectControlHandler;
@@ -117,6 +118,7 @@
registerOptionHandler(Account.Id.class, AccountIdHandler.class);
registerOptionHandler(AccountGroup.Id.class, AccountGroupIdHandler.class);
+ registerOptionHandler(AccountGroup.UUID.class, AccountGroupUUIDHandler.class);
registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class);
registerOptionHandler(ProjectControl.class, ProjectControlHandler.class);
registerOptionHandler(SocketAddress.class, SocketAddressHandler.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java
new file mode 100644
index 0000000..90b4987
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2009 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.sshd.args4j;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+public class AccountGroupUUIDHandler extends OptionHandler<AccountGroup.UUID> {
+ private final GroupCache groupCache;
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ @Inject
+ public AccountGroupUUIDHandler(final GroupCache groupCache,
+ @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
+ @Assisted final Setter setter) {
+ super(parser, option, setter);
+ this.groupCache = groupCache;
+ }
+
+ @Override
+ public final int parseArguments(final Parameters params)
+ throws CmdLineException {
+ final String n = params.getParameter(0);
+ final AccountGroup group = groupCache.get(new AccountGroup.NameKey(n));
+ if (group == null) {
+ throw new CmdLineException(owner, "Group \"" + n + "\" does not exist");
+ }
+ setter.addValue(group.getGroupUUID());
+ return 1;
+ }
+
+ @Override
+ public final String getDefaultMetaVariable() {
+ return "GROUP";
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
index 07c573d..feb4891 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -15,22 +15,24 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.config.WildProjectName;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
-import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -44,10 +46,10 @@
private List<ProjectControl> children = new ArrayList<ProjectControl>();
@Inject
- private ReviewDb db;
+ private ProjectCache projectCache;
@Inject
- private ProjectCache projectCache;
+ private MetaDataUpdate.User metaDataUpdateFactory;
@Inject
@WildProjectName
@@ -64,7 +66,7 @@
});
}
- private void updateParents() throws OrmException, UnloggedFailure {
+ private void updateParents() throws Failure {
final StringBuilder err = new StringBuilder();
final Set<Project.NameKey> grandParents = new HashSet<Project.NameKey>();
Project.NameKey newParentKey;
@@ -112,22 +114,27 @@
continue;
}
- final Project child = db.projects().get(key);
- if (child == null) {
- // Race condition? Its in the cache, but not the database.
- //
- err.append("error: Project '" + name + "' not found\n");
- continue;
+ try {
+ MetaDataUpdate md = metaDataUpdateFactory.create(key);
+ try {
+ ProjectConfig config = ProjectConfig.read(md);
+ config.getProject().setParentName(newParentKey.get());
+ md.setMessage("Inherit access from " + newParentKey.get() + "\n");
+ if (!config.commit(md)) {
+ err.append("error: Could not update project " + name + "\n");
+ }
+ } finally {
+ md.close();
+ }
+ } catch (RepositoryNotFoundException notFound) {
+ err.append("error: Project " + name + " not found\n");
+ } catch (IOException e) {
+ throw new Failure(1, "Cannot update project " + name, e);
+ } catch (ConfigInvalidException e) {
+ throw new Failure(1, "Cannot update project " + name, e);
}
-
- child.setParent(newParentKey);
- db.projects().update(Collections.singleton(child));
}
- // Invalidate all projects in cache since inherited rights were changed.
- //
- projectCache.evictAll();
-
if (err.length() > 0) {
while (err.charAt(err.length() - 1) == '\n') {
err.setLength(err.length() - 1);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java
index 0be02fd..77dc8f1 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java
@@ -15,26 +15,30 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.CollectionsUtil;
+import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.Project.SubmitType;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.ProjectCreatorGroups;
import com.google.gerrit.server.config.ProjectOwnerGroups;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.sshd.BaseCommand;
-import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -42,7 +46,6 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RefUpdate.Result;
-import org.eclipse.jgit.lib.StoredConfig;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,7 +53,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -63,14 +65,11 @@
private String projectName;
@Option(name = "--owner", aliases = {"-o"}, usage = "owner(s) of project")
- private List<AccountGroup.Id> ownerIds;
+ private List<AccountGroup.UUID> ownerIds;
@Option(name = "--parent", aliases = {"-p"}, metaVar = "NAME", usage = "parent project")
private ProjectControl newParent;
- @Option(name = "--permissions-only", usage = "create project for use only as parent")
- private boolean permissionsOnly;
-
@Option(name = "--description", aliases = {"-d"}, metaVar = "DESC", usage = "description of project")
private String projectDescription = "";
@@ -98,18 +97,21 @@
private boolean createEmptyCommit;
@Inject
- private ReviewDb db;
-
- @Inject
private GitRepositoryManager repoManager;
@Inject
+ private ProjectCache projectCache;
+
+ @Inject
+ private GroupCache groupCache;
+
+ @Inject
@ProjectCreatorGroups
- private Set<AccountGroup.Id> projectCreatorGroups;
+ private Set<AccountGroup.UUID> projectCreatorGroups;
@Inject
@ProjectOwnerGroups
- private Set<AccountGroup.Id> projectOwnerGroups;
+ private Set<AccountGroup.UUID> projectOwnerGroups;
@Inject
private IdentifiedUser currentUser;
@@ -121,6 +123,9 @@
@GerritPersonIdent
private PersonIdent serverIdent;
+ @Inject
+ MetaDataUpdate.User metaDataUpdateFactory;
+
private Project.NameKey nameKey;
@Override
@@ -136,41 +141,28 @@
validateParameters();
nameKey = new Project.NameKey(projectName);
- if (!permissionsOnly) {
- final Repository repo = repoManager.createRepository(nameKey);
- try {
- repo.create(true);
+ final Repository repo = repoManager.createRepository(nameKey);
+ try {
+ RefUpdate u = repo.updateRef(Constants.HEAD);
+ u.disableRefLog();
+ u.link(branch);
- StoredConfig config = repo.getConfig();
- config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION,
- null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
- config.save();
-
- RefUpdate u = repo.updateRef(Constants.HEAD);
- u.disableRefLog();
- u.link(branch);
-
- repoManager.setProjectDescription(nameKey, projectDescription);
-
- createProject();
-
- rq.replicateNewProject(nameKey, branch);
-
- if (createEmptyCommit) {
- createEmptyCommit(repo, nameKey, branch);
- }
- } finally {
- repo.close();
- }
- } else {
createProject();
+ repoManager.setProjectDescription(nameKey, projectDescription);
+
+ if (createEmptyCommit) {
+ createEmptyCommit(repo, nameKey, branch);
+ }
+
+ rq.replicateNewProject(nameKey, branch);
+ } finally {
+ repo.close();
}
} catch (Exception e) {
p.print("Error when trying to create project: " + e.getMessage()
+ "\n");
p.flush();
}
-
}
});
}
@@ -181,9 +173,9 @@
try {
CommitBuilder cb = new CommitBuilder();
cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {}));
+ cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent());
cb.setCommitter(serverIdent);
- cb.setAuthor(cb.getCommitter());
- cb.setMessage("Initial empty repository");
+ cb.setMessage("Initial empty repository\n");
ObjectId id = oi.insert(cb);
oi.flush();
@@ -207,31 +199,41 @@
}
}
- private void createProject() throws OrmException {
- List<RefRight> access = new ArrayList<RefRight>();
- for (AccountGroup.Id ownerId : ownerIds) {
- final RefRight.Key prk =
- new RefRight.Key(nameKey, new RefRight.RefPattern(
- RefRight.ALL), ApprovalCategory.OWN, ownerId);
- final RefRight pr = new RefRight(prk);
- pr.setMaxValue((short) 1);
- pr.setMinValue((short) 1);
- access.add(pr);
- }
- db.refRights().insert(access);
+ private void createProject() throws IOException, ConfigInvalidException {
+ MetaDataUpdate md = metaDataUpdateFactory.create(nameKey);
+ try {
+ ProjectConfig config = ProjectConfig.read(md);
+ config.load(md);
- final Project newProject = new Project(nameKey);
- newProject.setDescription(projectDescription);
- newProject.setSubmitType(submitType);
- newProject.setUseContributorAgreements(contributorAgreements);
- newProject.setUseSignedOffBy(signedOffBy);
- newProject.setUseContentMerge(contentMerge);
- newProject.setRequireChangeID(requireChangeID);
- if (newParent != null) {
- newProject.setParent(newParent.getProject().getNameKey());
- }
+ Project newProject = config.getProject();
+ newProject.setDescription(projectDescription);
+ newProject.setSubmitType(submitType);
+ newProject.setUseContributorAgreements(contributorAgreements);
+ newProject.setUseSignedOffBy(signedOffBy);
+ newProject.setUseContentMerge(contentMerge);
+ newProject.setRequireChangeID(requireChangeID);
+ if (newParent != null) {
+ newProject.setParentName(newParent.getProject().getName());
+ }
- db.projects().insert(Collections.singleton(newProject));
+ if (!ownerIds.isEmpty()) {
+ AccessSection all = config.getAccessSection(AccessSection.ALL, true);
+ for (AccountGroup.UUID ownerId : ownerIds) {
+ AccountGroup accountGroup = groupCache.get(ownerId);
+ GroupReference group = config.resolve(accountGroup);
+ all.getPermission(Permission.OWNER, true).add(
+ new PermissionRule(group));
+ }
+ }
+
+ md.setMessage("Created project\n");
+ if (!config.commit(md)) {
+ throw new IOException("Cannot create " + projectName);
+ }
+ } finally {
+ md.close();
+ }
+ projectCache.onCreateProject(nameKey);
}
private void validateParameters() throws Failure {
@@ -246,9 +248,9 @@
if (ownerIds != null && !ownerIds.isEmpty()) {
ownerIds =
- new ArrayList<AccountGroup.Id>(new HashSet<AccountGroup.Id>(ownerIds));
+ new ArrayList<AccountGroup.UUID>(new HashSet<AccountGroup.UUID>(ownerIds));
} else {
- ownerIds = new ArrayList<AccountGroup.Id>(projectOwnerGroups);
+ ownerIds = new ArrayList<AccountGroup.UUID>(projectOwnerGroups);
}
while (branch.startsWith("/")) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
index bb04f7a..b70b743 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
@@ -15,15 +15,12 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
-import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.BaseCommand;
-import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
@@ -44,9 +41,6 @@
private static final String NOT_VISIBLE_PROJECT = "(x)";
@Inject
- private ReviewDb db;
-
- @Inject
private IdentifiedUser currentUser;
@Inject
@@ -55,10 +49,6 @@
@Inject
private GitRepositoryManager repoManager;
- @Inject
- @WildProjectName
- private Project.NameKey wildProject;
-
@Option(name = "--show-branch", aliases = {"-b"}, usage = "displays the sha of each project in the specified branch")
private String showBranch;
@@ -93,14 +83,8 @@
}
try {
- for (final Project p : db.projects().all()) {
- if (p.getNameKey().equals(wildProject)) {
- // This project "doesn't exist". At least not as a repository.
- //
- continue;
- }
-
- final ProjectState e = projectCache.get(p.getNameKey());
+ for (final Project.NameKey projectName : projectCache.all()) {
+ final ProjectState e = projectCache.get(projectName);
if (e == null) {
// If we can't get it from the cache, pretend its not present.
//
@@ -118,7 +102,7 @@
}
if (showBranch != null) {
- final Ref ref = getBranchRef(p.getNameKey());
+ final Ref ref = getBranchRef(projectName);
if (ref == null || ref.getObjectId() == null
|| !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
// No branch, or the user can't see this branch, so skip it.
@@ -130,9 +114,10 @@
stdout.print(' ');
}
- stdout.print(p.getName() + "\n");
+ stdout.print(projectName.get() + "\n");
} else {
- treeMap.put(p.getName(), new TreeNode(p, pctl.isVisible()));
+ treeMap.put(projectName.get(),
+ new TreeNode(pctl.getProject(), pctl.isVisible()));
}
}
@@ -144,7 +129,7 @@
for (final TreeNode key : treeMap.values()) {
final String parentName = key.getParentName();
if (parentName != null) {
- final TreeNode node = treeMap.get((String)parentName);
+ final TreeNode node = treeMap.get(parentName);
if (node != null) {
node.addChild(key);
} else {
@@ -161,8 +146,6 @@
printElement(stdout, fakeRoot, -1, false, sortedNodes.get(sortedNodes.size() - 1));
stdout.flush();
}
- } catch (OrmException e) {
- throw new Failure(1, "fatal: database error", e);
} finally {
stdout.flush();
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 47bae4f..0a6033b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -331,7 +331,7 @@
functionStateFactory.create(changeControl.getChange(), patchSetId,
Collections.<PatchSetApproval> emptyList());
psa.setValue(v);
- fs.normalize(approvalTypes.getApprovalType(psa.getCategoryId()), psa);
+ fs.normalize(approvalTypes.byId(psa.getCategoryId()), psa);
if (v != psa.getValue()) {
throw error(ao.name() + "=" + ao.value() + " not permitted");
}
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
new file mode 100644
index 0000000..3bbcc98
--- /dev/null
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2009 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.httpd;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.config.SitePath;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.io.File;
+import java.util.List;
+
+/** Provides {@link java.io.File} annotated with {@link SitePath}. */
+class SitePathFromSystemConfigProvider implements Provider<File> {
+ private final File path;
+
+ @Inject
+ SitePathFromSystemConfigProvider(SchemaFactory<ReviewDb> schemaFactory)
+ throws OrmException {
+ path = read(schemaFactory);
+ }
+
+ @Override
+ public File get() {
+ return path;
+ }
+
+ private static File read(SchemaFactory<ReviewDb> schemaFactory)
+ throws OrmException {
+ ReviewDb db = schemaFactory.open();
+ try {
+ List<SystemConfig> all = db.systemConfig().all().toList();
+ switch (all.size()) {
+ case 1:
+ return new File(all.get(0).sitePath);
+ case 0:
+ throw new OrmException("system_config table is empty");
+ default:
+ throw new OrmException("system_config must have exactly 1 row;"
+ + " found " + all.size() + " rows instead");
+ }
+ } finally {
+ db.close();
+ }
+ }
+}
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 19c16ca..f1ac945 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -25,9 +25,9 @@
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.MasterNodeStartup;
import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.config.SitePathFromSystemConfigProvider;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.DatabaseModule;
+import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.sshd.SshModule;
import com.google.gerrit.sshd.commands.MasterCommandModule;
import com.google.inject.AbstractModule;
@@ -166,6 +166,7 @@
});
modules.add(new GerritServerConfigModule());
}
+ modules.add(new SchemaModule());
modules.add(new AuthConfigModule());
return dbInjector.createChildInjector(modules);
}