Enable groups to manage contributor agreements
For a corporate style contributor agreement there are typically more than one account which is covered by that agreement, and the CLA is actually between the company and the project leaders, not the users who are listed out on that agreement. Permission as to who is an authorized user under that agreement should be managed by the company who made the agreement as employees enter the company, leave, or are shifted to different job functions. By creating a pair of groups in Gerrit, e.g. "Initech Users" and "Initech Admins", with the later being the owner of the former, the Initech personnel can manage their own authorized users list, without involving the Gerrit site administrators. Bug: GERRIT-17 Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -18,17 +18,22 @@ import com.google.gerrit.client.data.AccountInfoCache;
|
||||
import com.google.gerrit.client.data.AccountInfoCacheFactory;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountAgreement;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
|
||||
import com.google.gerrit.client.reviewdb.ContributorAgreement;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.rpc.Common;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AgreementInfo {
|
||||
protected AccountInfoCache accounts;
|
||||
protected List<AccountAgreement> accepted;
|
||||
protected List<AccountAgreement> userAccepted;
|
||||
protected List<AccountGroupAgreement> groupAccepted;
|
||||
protected Map<ContributorAgreement.Id, ContributorAgreement> agreements;
|
||||
|
||||
public AgreementInfo() {
|
||||
@@ -37,9 +42,23 @@ public class AgreementInfo {
|
||||
public void load(final Account.Id me, final ReviewDb db) throws OrmException {
|
||||
final AccountInfoCacheFactory acc = new AccountInfoCacheFactory(db);
|
||||
|
||||
accepted = db.accountAgreements().byAccount(me).toList();
|
||||
userAccepted = db.accountAgreements().byAccount(me).toList();
|
||||
groupAccepted = new ArrayList<AccountGroupAgreement>();
|
||||
for (final AccountGroup.Id groupId : Common.getGroupCache()
|
||||
.getEffectiveGroups(me)) {
|
||||
groupAccepted.addAll(db.accountGroupAgreements().byGroup(groupId)
|
||||
.toList());
|
||||
}
|
||||
|
||||
agreements = new HashMap<ContributorAgreement.Id, ContributorAgreement>();
|
||||
for (final AccountAgreement a : accepted) {
|
||||
for (final AccountAgreement a : userAccepted) {
|
||||
acc.want(a.getReviewedBy());
|
||||
if (!agreements.containsKey(a.getAgreementId())) {
|
||||
agreements.put(a.getAgreementId(), db.contributorAgreements().get(
|
||||
a.getAgreementId()));
|
||||
}
|
||||
}
|
||||
for (final AccountGroupAgreement a : groupAccepted) {
|
||||
acc.want(a.getReviewedBy());
|
||||
if (!agreements.containsKey(a.getAgreementId())) {
|
||||
agreements.put(a.getAgreementId(), db.contributorAgreements().get(
|
||||
|
||||
@@ -16,7 +16,9 @@ package com.google.gerrit.client.account;
|
||||
|
||||
import com.google.gerrit.client.FormatUtil;
|
||||
import com.google.gerrit.client.Link;
|
||||
import com.google.gerrit.client.reviewdb.AbstractAgreement;
|
||||
import com.google.gerrit.client.reviewdb.AccountAgreement;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
|
||||
import com.google.gerrit.client.reviewdb.ContributorAgreement;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.FancyFlexTable;
|
||||
@@ -51,7 +53,7 @@ class AgreementPanel extends Composite {
|
||||
});
|
||||
}
|
||||
|
||||
private class AgreementTable extends FancyFlexTable<AccountAgreement> {
|
||||
private class AgreementTable extends FancyFlexTable<AbstractAgreement> {
|
||||
AgreementTable() {
|
||||
table.setText(0, 1, Util.C.agreementStatus());
|
||||
table.setText(0, 2, Util.C.agreementName());
|
||||
@@ -68,12 +70,15 @@ class AgreementPanel extends Composite {
|
||||
while (1 < table.getRowCount())
|
||||
table.removeRow(table.getRowCount() - 1);
|
||||
|
||||
for (final AccountAgreement k : result.accepted) {
|
||||
for (final AccountAgreement k : result.userAccepted) {
|
||||
addOne(result, k);
|
||||
}
|
||||
for (final AccountGroupAgreement k : result.groupAccepted) {
|
||||
addOne(result, k);
|
||||
}
|
||||
}
|
||||
|
||||
void addOne(final AgreementInfo info, final AccountAgreement k) {
|
||||
void addOne(final AgreementInfo info, final AbstractAgreement k) {
|
||||
final int row = table.getRowCount();
|
||||
table.insertRow(row);
|
||||
applyDataRowStyle(row);
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.gerrit.client.ErrorDialog;
|
||||
import com.google.gerrit.client.Gerrit;
|
||||
import com.google.gerrit.client.Link;
|
||||
import com.google.gerrit.client.reviewdb.AccountAgreement;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
|
||||
import com.google.gerrit.client.reviewdb.ContributorAgreement;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
import com.google.gerrit.client.ui.AccountScreen;
|
||||
@@ -72,7 +73,10 @@ public class NewAgreementScreen extends AccountScreen {
|
||||
public void onSuccess(AgreementInfo result) {
|
||||
if (isAttached()) {
|
||||
mySigned = new HashSet<ContributorAgreement.Id>();
|
||||
for (AccountAgreement a : result.accepted) {
|
||||
for (AccountAgreement a : result.userAccepted) {
|
||||
mySigned.add(a.getAgreementId());
|
||||
}
|
||||
for (AccountGroupAgreement a : result.groupAccepted) {
|
||||
mySigned.add(a.getAgreementId());
|
||||
}
|
||||
postRPC();
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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.client.reviewdb;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/** Base for {@link AccountAgreement} or {@link AccountGroupAgreement}. */
|
||||
public interface AbstractAgreement {
|
||||
public static enum Status {
|
||||
NEW('n'),
|
||||
|
||||
VERIFIED('V'),
|
||||
|
||||
REJECTED('R');
|
||||
|
||||
private final char code;
|
||||
|
||||
private Status(final char c) {
|
||||
code = c;
|
||||
}
|
||||
|
||||
public char getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static Status forCode(final char c) {
|
||||
for (final Status s : Status.values()) {
|
||||
if (s.code == c) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ContributorAgreement.Id getAgreementId();
|
||||
|
||||
public Timestamp getAcceptedOn();
|
||||
|
||||
public Status getStatus();
|
||||
|
||||
public Timestamp getReviewedOn();
|
||||
|
||||
public Account.Id getReviewedBy();
|
||||
|
||||
public String getReviewComments();
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import com.google.gwtorm.client.CompoundKey;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/** Electronic acceptance of a {@link ContributorAgreement} by {@link Account} */
|
||||
public final class AccountAgreement {
|
||||
public final class AccountAgreement implements AbstractAgreement {
|
||||
public static class Key extends CompoundKey<Account.Id> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -51,36 +51,6 @@ public final class AccountAgreement {
|
||||
}
|
||||
}
|
||||
|
||||
protected static final char NEW_CODE = 'n';
|
||||
|
||||
public static enum Status {
|
||||
NEW(NEW_CODE),
|
||||
|
||||
VERIFIED('V'),
|
||||
|
||||
REJECTED('R');
|
||||
|
||||
private final char code;
|
||||
|
||||
private Status(final char c) {
|
||||
code = c;
|
||||
}
|
||||
|
||||
public char getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static Status forCode(final char c) {
|
||||
for (final Status s : Status.values()) {
|
||||
if (s.code == c) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Column(name = Column.NONE)
|
||||
protected Key key;
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.client.reviewdb;
|
||||
|
||||
import com.google.gwtorm.client.Column;
|
||||
import com.google.gwtorm.client.CompoundKey;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/**
|
||||
* Acceptance of a {@link ContributorAgreement} by an {@link AccountGroup}.
|
||||
*/
|
||||
public final class AccountGroupAgreement implements AbstractAgreement {
|
||||
public static class Key extends CompoundKey<AccountGroup.Id> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Column
|
||||
protected AccountGroup.Id groupId;
|
||||
|
||||
@Column
|
||||
protected ContributorAgreement.Id claId;
|
||||
|
||||
protected Key() {
|
||||
groupId = new AccountGroup.Id();
|
||||
claId = new ContributorAgreement.Id();
|
||||
}
|
||||
|
||||
public Key(final AccountGroup.Id group, final ContributorAgreement.Id cla) {
|
||||
this.groupId = group;
|
||||
this.claId = cla;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccountGroup.Id getParentKey() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {claId};
|
||||
}
|
||||
}
|
||||
|
||||
@Column(name = Column.NONE)
|
||||
protected Key key;
|
||||
|
||||
@Column
|
||||
protected Timestamp acceptedOn;
|
||||
|
||||
@Column
|
||||
protected char status;
|
||||
|
||||
@Column(notNull = false)
|
||||
protected Account.Id reviewedBy;
|
||||
|
||||
@Column(notNull = false)
|
||||
protected Timestamp reviewedOn;
|
||||
|
||||
@Column(notNull = false, length = Integer.MAX_VALUE)
|
||||
protected String reviewComments;
|
||||
|
||||
protected AccountGroupAgreement() {
|
||||
}
|
||||
|
||||
public AccountGroupAgreement(final AccountGroupAgreement.Key k) {
|
||||
key = k;
|
||||
acceptedOn = new Timestamp(System.currentTimeMillis());
|
||||
status = Status.NEW.getCode();
|
||||
}
|
||||
|
||||
public AccountGroupAgreement.Key getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public ContributorAgreement.Id getAgreementId() {
|
||||
return key.claId;
|
||||
}
|
||||
|
||||
public Timestamp getAcceptedOn() {
|
||||
return acceptedOn;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return Status.forCode(status);
|
||||
}
|
||||
|
||||
public Timestamp getReviewedOn() {
|
||||
return reviewedOn;
|
||||
}
|
||||
|
||||
public Account.Id getReviewedBy() {
|
||||
return reviewedBy;
|
||||
}
|
||||
|
||||
public String getReviewComments() {
|
||||
return reviewComments;
|
||||
}
|
||||
|
||||
public void setReviewComments(final String s) {
|
||||
reviewComments = s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.client.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 AccountGroupAgreementAccess extends
|
||||
Access<AccountGroupAgreement, AccountGroupAgreement.Key> {
|
||||
@PrimaryKey("key")
|
||||
AccountGroupAgreement get(AccountGroupAgreement.Key key) throws OrmException;
|
||||
|
||||
@Query("WHERE key.groupId = ? ORDER BY acceptedOn DESC")
|
||||
ResultSet<AccountGroupAgreement> byGroup(AccountGroup.Id id)
|
||||
throws OrmException;
|
||||
}
|
||||
@@ -72,6 +72,9 @@ public interface ReviewDb extends Schema {
|
||||
@Relation
|
||||
AccountGroupMemberAuditAccess accountGroupMembersAudit();
|
||||
|
||||
@Relation
|
||||
AccountGroupAgreementAccess accountGroupAgreements();
|
||||
|
||||
@Relation
|
||||
StarredChangeAccess starredChanges();
|
||||
|
||||
|
||||
@@ -24,9 +24,12 @@ import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_TAG_ANY;
|
||||
|
||||
import com.google.gerrit.client.Link;
|
||||
import com.google.gerrit.client.data.ApprovalType;
|
||||
import com.google.gerrit.client.reviewdb.AbstractAgreement;
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountAgreement;
|
||||
import com.google.gerrit.client.reviewdb.AccountExternalId;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
|
||||
import com.google.gerrit.client.reviewdb.ApprovalCategory;
|
||||
import com.google.gerrit.client.reviewdb.Branch;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
@@ -199,20 +202,37 @@ class Receive extends AbstractGitCommand {
|
||||
}
|
||||
|
||||
private void verifyActiveContributorAgreement() throws Failure {
|
||||
AccountAgreement bestAgreement = null;
|
||||
AbstractAgreement bestAgreement = null;
|
||||
ContributorAgreement bestCla = null;
|
||||
try {
|
||||
for (final AccountAgreement a : db.accountAgreements().byAccount(
|
||||
userAccount.getId()).toList()) {
|
||||
final ContributorAgreement cla =
|
||||
db.contributorAgreements().get(a.getAgreementId());
|
||||
if (cla == null) {
|
||||
continue;
|
||||
}
|
||||
OUTER: for (final AccountGroup.Id groupId : getGroups()) {
|
||||
for (final AccountGroupAgreement a : db.accountGroupAgreements()
|
||||
.byGroup(groupId)) {
|
||||
final ContributorAgreement cla =
|
||||
db.contributorAgreements().get(a.getAgreementId());
|
||||
if (cla == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bestAgreement = a;
|
||||
bestCla = cla;
|
||||
break;
|
||||
bestAgreement = a;
|
||||
bestCla = cla;
|
||||
break OUTER;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestAgreement == null) {
|
||||
for (final AccountAgreement a : db.accountAgreements().byAccount(
|
||||
userAccount.getId()).toList()) {
|
||||
final ContributorAgreement cla =
|
||||
db.contributorAgreements().get(a.getAgreementId());
|
||||
if (cla == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bestAgreement = a;
|
||||
bestCla = cla;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (OrmException e) {
|
||||
throw new Failure(1, "fatal: database error", e);
|
||||
|
||||
@@ -21,4 +21,15 @@ WHERE group_id = (SELECT anonymous_group_id FROM system_config);
|
||||
UPDATE account_groups SET automatic_membership = 'Y'
|
||||
WHERE group_id = (SELECT registered_group_id FROM system_config);
|
||||
|
||||
CREATE TABLE account_group_agreements
|
||||
(accepted_on TIMESTAMP NOT NULL
|
||||
,status CHAR(1) NOT NULL
|
||||
,reviewed_by INT
|
||||
,reviewed_on TIMESTAMP
|
||||
,review_comments TEXT
|
||||
,group_id INT NOT NULL
|
||||
,cla_id INT NOT NULL
|
||||
,PRIMARY KEY (group_id, cla_id)
|
||||
);
|
||||
|
||||
UPDATE schema_version SET version_nbr = 13;
|
||||
|
||||
@@ -22,6 +22,18 @@ WHERE group_id = (SELECT anonymous_group_id FROM system_config);
|
||||
UPDATE account_groups SET automatic_membership = 'Y'
|
||||
WHERE group_id = (SELECT registered_group_id FROM system_config);
|
||||
|
||||
CREATE TABLE account_group_agreements
|
||||
(accepted_on TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
,status CHAR(1) NOT NULL
|
||||
,reviewed_by INT
|
||||
,reviewed_on TIMESTAMP WITH TIME ZONE
|
||||
,review_comments TEXT
|
||||
,group_id INT NOT NULL
|
||||
,cla_id INT NOT NULL
|
||||
,PRIMARY KEY (group_id, cla_id)
|
||||
);
|
||||
|
||||
ALTER TABLE account_group_members_audit OWNER TO gerrit2;
|
||||
ALTER TABLE account_group_agreements OWNER TO gerrit2;
|
||||
|
||||
UPDATE schema_version SET version_nbr = 13;
|
||||
|
||||
Reference in New Issue
Block a user