Allow users to register new contributor agreements in Gerrit

The HTML text shown in the body of an agreement is read from a
static HTML file in the site path, under "static/", but may also
come from any other location on the Internet.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2008-12-30 17:26:31 -08:00
parent af3e0d6bc6
commit 363e7c2984
22 changed files with 802 additions and 24 deletions

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.client;
import com.google.gerrit.client.account.AccountSettings;
import com.google.gerrit.client.account.NewAgreementScreen;
import com.google.gerrit.client.admin.AccountGroupScreen;
import com.google.gerrit.client.admin.GroupListScreen;
import com.google.gerrit.client.changes.AccountDashboardScreen;
@@ -39,6 +40,7 @@ public class Link implements HistoryListener {
public static final String SETTINGS_WEBIDENT = "settings,web-identities";
public static final String SETTINGS_AGREEMENTS = "settings,agreements";
public static final String SETTINGS_CONTACT = "settings,contact";
public static final String SETTINGS_NEW_AGREEMENT = "settings,new-agreement";
public static final String MINE = "mine";
public static final String MINE_UNCLAIMED = "mine,unclaimed";
@@ -108,6 +110,9 @@ public class Link implements HistoryListener {
}
if (SETTINGS.equals(token) || token.startsWith("settings,")) {
if (SETTINGS_NEW_AGREEMENT.equals(token)) {
return new NewAgreementScreen();
}
return new AccountSettings(token);
}

View File

@@ -58,4 +58,23 @@ public interface AccountConstants extends Constants {
String contactFieldPhone();
String contactFieldFax();
String buttonSaveContact();
String newAgreement();
String agreementStatus();
String agreementName();
String agreementDescription();
String agreementAccepted();
String agreementStatus_EXPIRED();
String agreementStatus_NEW();
String agreementStatus_REJECTED();
String agreementStatus_VERIFIED();
String newAgreementSelectTypeHeading();
String newAgreementNoneAvailable();
String newAgreementReviewLegalHeading();
String newAgreementReviewContactHeading();
String newAgreementCompleteHeading();
String newAgreementIAGREE();
String newAgreementAlreadySubmitted();
String buttonSubmitNewAgreement();
}

View File

@@ -39,3 +39,22 @@ contactFieldCountry = Country
contactFieldPhone = Phone Number
contactFieldFax = Fax Number
buttonSaveContact = Save
newAgreement = New Contributor Agreement
agreementStatus = Status
agreementName = Name
agreementStatus_EXPIRED = Expired
agreementStatus_NEW = Pending
agreementStatus_REJECTED = Rejected
agreementStatus_VERIFIED = Verified
agreementDescription = Description
agreementAccepted = Accepted
newAgreementSelectTypeHeading = Select an agreement type:
newAgreementNoneAvailable = No contributor agreements are configured.
newAgreementReviewLegalHeading = Review the agreement:
newAgreementReviewContactHeading = Review your contact information:
newAgreementCompleteHeading = Complete the agreement:
newAgreementIAGREE = I AGREE
newAgreementAlreadySubmitted = Agreement already submitted.
buttonSubmitNewAgreement = Submit Agreement

View File

@@ -18,4 +18,5 @@ import com.google.gwt.i18n.client.Messages;
public interface AccountMessages extends Messages {
String lines(short cnt);
String enterIAGREE(String iagree);
}

View File

@@ -1 +1,3 @@
lines = {0} lines
enterIAGREE = (enter {0} in the box to the left)

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.client.account;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.rpc.SignInRequired;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.RemoteJsonService;
@@ -41,4 +42,11 @@ public interface AccountService extends RemoteJsonService {
@SignInRequired
void deleteProjectWatches(Set<AccountProjectWatch.Key> keys,
AsyncCallback<VoidResult> callback);
@SignInRequired
void myAgreements(AsyncCallback<AgreementInfo> callback);
@SignInRequired
void enterAgreement(ContributorAgreement.Id id,
AsyncCallback<VoidResult> callback);
}

View File

@@ -15,7 +15,9 @@
package com.google.gerrit.client.account;
import com.google.gerrit.client.reviewdb.Account;
import com.google.gerrit.client.reviewdb.AccountAgreement;
import com.google.gerrit.client.reviewdb.AccountProjectWatch;
import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.rpc.BaseServiceImplementation;
@@ -128,4 +130,27 @@ public class AccountServiceImpl extends BaseServiceImplementation implements
}
});
}
public void myAgreements(final AsyncCallback<AgreementInfo> callback) {
run(callback, new Action<AgreementInfo>() {
public AgreementInfo run(final ReviewDb db) throws OrmException {
final AgreementInfo i = new AgreementInfo();
i.load(RpcUtil.getAccountId(), db);
return i;
}
});
}
public void enterAgreement(final ContributorAgreement.Id id,
final AsyncCallback<VoidResult> callback) {
run(callback, new Action<VoidResult>() {
public VoidResult run(final ReviewDb db) throws OrmException {
final AccountAgreement a =
new AccountAgreement(new AccountAgreement.Key(RpcUtil
.getAccountId(), id));
db.accountAgreements().insert(Collections.singleton(a));
return VoidResult.INSTANCE;
}
});
}
}

View File

@@ -25,10 +25,7 @@ import com.google.gerrit.client.ui.LazyTabChild;
import com.google.gerrit.client.ui.Screen;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SourcesTabEvents;
import com.google.gwt.user.client.ui.TabListener;
import com.google.gwt.user.client.ui.TabPanel;
@@ -44,9 +41,7 @@ public class AccountSettings extends AccountScreen {
private List<String> tabTokens;
private TabPanel tabs;
private PreferencePanel prefsPanel;
private Panel agreementsPanel;
public AccountSettings(final String tabToken) {
super(Util.C.accountSettingsHeading());
@@ -108,8 +103,6 @@ public class AccountSettings extends AccountScreen {
fmt.addStyleName(2, 0, "bottomheader");
prefsPanel = new PreferencePanel();
agreementsPanel = new FlowPanel();
agreementsPanel.add(new Label("Not Implemented"));
tabTokens = new ArrayList<String>();
tabs = new TabPanel();
@@ -143,7 +136,12 @@ public class AccountSettings extends AccountScreen {
}, Util.C.tabWebIdentities());
tabTokens.add(Link.SETTINGS_WEBIDENT);
tabs.add(agreementsPanel, Util.C.tabAgreements());
tabs.add(new LazyTabChild<AgreementPanel>() {
@Override
protected AgreementPanel create() {
return new AgreementPanel();
}
}, Util.C.tabAgreements());
tabTokens.add(Link.SETTINGS_AGREEMENTS);
tabs.addTabListener(new TabListener() {

View File

@@ -0,0 +1,52 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.account;
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.ContributorAgreement;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gwtorm.client.OrmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AgreementInfo {
protected AccountInfoCache accounts;
protected List<AccountAgreement> accepted;
protected Map<ContributorAgreement.Id, ContributorAgreement> agreements;
public 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();
agreements = new HashMap<ContributorAgreement.Id, ContributorAgreement>();
for (final AccountAgreement a : accepted) {
acc.want(a.getReviewedBy());
if (!agreements.containsKey(a.getAgreementId())) {
agreements.put(a.getAgreementId(), db.contributorAgreements().get(
a.getAgreementId()));
}
}
accounts = acc.create();
}
}

View File

@@ -0,0 +1,141 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.account;
import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.Link;
import com.google.gerrit.client.reviewdb.AccountAgreement;
import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.FancyFlexTable;
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.Hyperlink;
import com.google.gwt.user.client.ui.SourcesTableEvents;
import com.google.gwt.user.client.ui.TableListener;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
class AgreementPanel extends Composite {
private AgreementTable agreements;
AgreementPanel() {
final FlowPanel body = new FlowPanel();
agreements = new AgreementTable();
body.add(agreements);
body.add(new Hyperlink(Util.C.newAgreement(), Link.SETTINGS_NEW_AGREEMENT));
initWidget(body);
}
@Override
public void onLoad() {
super.onLoad();
Util.ACCOUNT_SVC.myAgreements(new GerritCallback<AgreementInfo>() {
public void onSuccess(final AgreementInfo result) {
agreements.display(result);
agreements.finishDisplay(true);
}
});
}
private class AgreementTable extends FancyFlexTable<AccountAgreement> {
AgreementTable() {
table.setText(0, 1, Util.C.agreementStatus());
table.setText(0, 2, Util.C.agreementName());
table.setText(0, 3, Util.C.agreementDescription());
table.setText(0, 4, Util.C.agreementAccepted());
table.addTableListener(new TableListener() {
public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
if (getRowItem(row) != null) {
movePointerTo(row);
}
}
});
final FlexCellFormatter fmt = table.getFlexCellFormatter();
for (int c = 1; c <= 4; c++) {
fmt.addStyleName(0, c, S_DATA_HEADER);
}
}
@Override
protected Object getRowItemKey(final AccountAgreement item) {
return item.getKey();
}
void display(final AgreementInfo result) {
while (1 < table.getRowCount())
table.removeRow(table.getRowCount() - 1);
for (final AccountAgreement k : result.accepted) {
addOne(result, k);
}
}
void addOne(final AgreementInfo info, final AccountAgreement k) {
final int row = table.getRowCount();
table.insertRow(row);
final ContributorAgreement cla = info.agreements.get(k.getAgreementId());
final String statusName;
if (cla == null || !cla.isActive()) {
statusName = Util.C.agreementStatus_EXPIRED();
} else {
switch (k.getStatus()) {
case NEW:
statusName = Util.C.agreementStatus_NEW();
break;
case REJECTED:
statusName = Util.C.agreementStatus_REJECTED();
break;
case VERIFIED:
statusName = Util.C.agreementStatus_VERIFIED();
break;
default:
statusName = k.getStatus().name();
}
}
table.setText(row, 1, statusName);
if (cla == null) {
table.setText(row, 2, "");
table.setText(row, 3, "");
} else {
final String url = cla.getAgreementUrl();
if (url != null && url.length() > 0) {
final Anchor a = new Anchor(cla.getShortName(), url);
a.setTarget("_blank");
table.setWidget(row, 2, a);
} else {
table.setText(row, 2, cla.getShortName());
}
table.setText(row, 3, cla.getShortDescription());
}
table.setHTML(row, 4, FormatUtil.mediumFormat(k.getAcceptedOn())
+ "<br>\n" + FormatUtil.mediumFormat(k.getReviewedOn()));
final FlexCellFormatter fmt = table.getFlexCellFormatter();
for (int c = 1; c <= 4; c++) {
fmt.addStyleName(row, c, S_DATA_CELL);
}
fmt.addStyleName(row, 4, "C_LAST_UPDATE");
setRowItem(row, k);
}
}
}

View File

@@ -55,6 +55,10 @@ class ContactPanel extends Composite {
private TextBox faxTxt;
private Button save;
ContactPanel() {
this(null);
}
ContactPanel(final AccountSettings parent) {
parentScreen = parent;
@@ -129,6 +133,10 @@ class ContactPanel extends Composite {
initWidget(body);
}
void hideSaveButton() {
save.setVisible(false);
}
@Override
public void onLoad() {
super.onLoad();
@@ -214,7 +222,7 @@ class ContactPanel extends Composite {
save.setEnabled(false);
}
private void doSave() {
void doSave() {
final String newName = nameTxt.getText();
final String newEmail;
if (emailPick.isEnabled() && emailPick.getSelectedIndex() >= 0) {
@@ -238,7 +246,9 @@ class ContactPanel extends Composite {
me.setPreferredEmail(newEmail);
me.setContactInformation(info);
Gerrit.refreshMenuBar();
parentScreen.display(me);
if (parentScreen != null) {
parentScreen.display(me);
}
}
});
}

View File

@@ -59,7 +59,7 @@ class ExternalIdPanel extends Composite {
}
});
d.setMode(SignInDialog.Mode.LINK_IDENTIY);
d.show();
d.center();
}
@Override

View File

@@ -0,0 +1,277 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.client.account;
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.ContributorAgreement;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.AccountScreen;
import com.google.gerrit.client.ui.TextSaveButtonListener;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwtjsonrpc.client.VoidResult;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class NewAgreementScreen extends AccountScreen {
private Set<ContributorAgreement.Id> mySigned;
private List<ContributorAgreement> available;
private ContributorAgreement current;
private VerticalPanel radios;
private Panel agreementGroup;
private HTML agreementHtml;
private Panel contactGroup;
private ContactPanel contactPanel;
private Panel finalGroup;
private TextBox yesIAgreeBox;
private Button submit;
public NewAgreementScreen() {
super(Util.C.newAgreement());
}
@Override
public void onLoad() {
if (radios == null) {
initUI();
}
mySigned = null;
available = null;
current = null;
agreementGroup.setVisible(false);
contactGroup.setVisible(false);
finalGroup.setVisible(false);
super.onLoad();
Util.ACCOUNT_SVC.myAgreements(new GerritCallback<AgreementInfo>() {
public void onSuccess(AgreementInfo result) {
if (isAttached()) {
mySigned = new HashSet<ContributorAgreement.Id>();
for (AccountAgreement a : result.accepted) {
mySigned.add(a.getAgreementId());
}
postRPC();
}
}
});
Gerrit.SYSTEM_SVC
.contributorAgreements(new GerritCallback<List<ContributorAgreement>>() {
public void onSuccess(final List<ContributorAgreement> result) {
if (isAttached()) {
available = result;
postRPC();
}
}
});
}
private void initUI() {
Label hdr;
final FlowPanel formBody = new FlowPanel();
radios = new VerticalPanel();
formBody.add(radios);
agreementGroup = new FlowPanel();
hdr = new Label(Util.C.newAgreementReviewLegalHeading());
hdr.setStyleName("gerrit-SmallHeading");
agreementGroup.add(hdr);
agreementHtml = new HTML();
agreementHtml.setStyleName("gerrit-ContributorAgreement-Legal");
agreementGroup.add(agreementHtml);
formBody.add(agreementGroup);
contactGroup = new FlowPanel();
hdr = new Label(Util.C.newAgreementReviewContactHeading());
hdr.setStyleName("gerrit-SmallHeading");
contactGroup.add(hdr);
formBody.add(contactGroup);
finalGroup = new VerticalPanel();
hdr = new Label(Util.C.newAgreementCompleteHeading());
hdr.setStyleName("gerrit-SmallHeading");
finalGroup.add(hdr);
final FlowPanel fp = new FlowPanel();
yesIAgreeBox = new TextBox();
yesIAgreeBox.setVisibleLength(Util.C.newAgreementIAGREE().length() + 8);
yesIAgreeBox.setMaxLength(Util.C.newAgreementIAGREE().length());
fp.add(yesIAgreeBox);
fp.add(new InlineLabel(Util.M.enterIAGREE(Util.C.newAgreementIAGREE())));
finalGroup.add(fp);
submit = new Button(Util.C.buttonSubmitNewAgreement());
submit.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
doSign();
}
});
finalGroup.add(submit);
formBody.add(finalGroup);
new TextSaveButtonListener(yesIAgreeBox, submit);
final FormPanel form = new FormPanel();
form.add(formBody);
add(form);
}
private void postRPC() {
if (mySigned != null && available != null) {
display();
}
}
private void display() {
current = null;
agreementGroup.setVisible(false);
contactGroup.setVisible(false);
finalGroup.setVisible(false);
radios.clear();
final Label hdr;
if (available.isEmpty()) {
hdr = new Label(Util.C.newAgreementNoneAvailable());
} else {
hdr = new Label(Util.C.newAgreementSelectTypeHeading());
}
hdr.setStyleName("gerrit-SmallHeading");
radios.add(hdr);
for (final ContributorAgreement cla : available) {
final RadioButton r = new RadioButton("cla_id", cla.getShortName());
r.addStyleName("gerrit-ContributorAgreement-Button");
radios.add(r);
if (mySigned.contains(cla.getId())) {
r.setEnabled(false);
final Label l = new Label(Util.C.newAgreementAlreadySubmitted());
l.setStyleName("gerrit-ContributorAgreement-AlreadySubmitted");
radios.add(l);
} else {
r.addClickListener(new ClickListener() {
public void onClick(final Widget sender) {
showCLA(cla);
}
});
}
if (cla.getShortDescription() != null
&& !cla.getShortDescription().equals("")) {
final Label l = new Label(cla.getShortDescription());
l.setStyleName("gerrit-ContributorAgreement-ShortDescription");
radios.add(l);
}
}
}
private void doSign() {
submit.setEnabled(false);
if (current == null
|| !Util.C.newAgreementIAGREE()
.equalsIgnoreCase(yesIAgreeBox.getText())) {
yesIAgreeBox.setText("");
yesIAgreeBox.setFocus(true);
return;
}
if (contactGroup.isVisible()) {
contactPanel.doSave();
}
Util.ACCOUNT_SVC.enterAgreement(current.getId(),
new GerritCallback<VoidResult>() {
public void onSuccess(final VoidResult result) {
History.newItem(Link.SETTINGS_AGREEMENTS);
}
@Override
public void onFailure(final Throwable caught) {
yesIAgreeBox.setText("");
super.onFailure(caught);
}
});
}
private void showCLA(final ContributorAgreement cla) {
current = cla;
String url = cla.getAgreementUrl();
if (url != null && url.length() > 0) {
agreementGroup.setVisible(true);
agreementHtml.setText(Gerrit.C.rpcStatusLoading());
if (!url.startsWith("http:") && !url.startsWith("https:")) {
url = GWT.getModuleBaseURL() + url;
}
final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
rb.setCallback(new RequestCallback() {
public void onError(Request request, Throwable exception) {
new ErrorDialog(exception).center();
}
public void onResponseReceived(Request request, Response response) {
final String ct = response.getHeader("Content-Type");
if (response.getStatusCode() == 200 && ct != null
&& (ct.equals("text/html") || ct.startsWith("text/html;"))) {
agreementHtml.setHTML(response.getText());
} else {
new ErrorDialog(response.getStatusText()).center();
}
}
});
try {
rb.send();
} catch (RequestException e) {
new ErrorDialog(e).show();
}
} else {
agreementGroup.setVisible(false);
}
if (contactPanel == null && cla.isRequireContactInformation()) {
contactPanel = new ContactPanel();
contactPanel.hideSaveButton();
contactGroup.add(contactPanel);
}
contactGroup.setVisible(cla.isRequireContactInformation());
finalGroup.setVisible(true);
yesIAgreeBox.setText("");
submit.setEnabled(false);
}
}

View File

@@ -14,13 +14,20 @@
package com.google.gerrit.client.data;
import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.rpc.SignInRequired;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
import com.google.gwtjsonrpc.client.HostPageCache;
import com.google.gwtjsonrpc.client.RemoteJsonService;
import java.util.List;
public interface SystemInfoService extends RemoteJsonService {
@AllowCrossSiteRequest
@HostPageCache(name = "gerrit_gerritconfig_obj", once = true)
void loadGerritConfig(AsyncCallback<GerritConfig> callback);
@SignInRequired
void contributorAgreements(AsyncCallback<List<ContributorAgreement>> callback);
}

View File

@@ -106,6 +106,10 @@ public final class AccountAgreement {
status = Status.NEW.getCode();
}
public AccountAgreement.Key getKey() {
return key;
}
public ContributorAgreement.Id getAgreementId() {
return key.claId;
}

View File

@@ -68,9 +68,9 @@ public final class ContributorAgreement {
@Column(notNull = false)
protected String shortDescription;
/** Text of the agreement, in formatted HTML */
@Column(length = Integer.MAX_VALUE)
protected String agreementHtml;
/** Web address of the agreement documentation. */
@Column
protected String agreementUrl;
protected ContributorAgreement() {
}
@@ -127,11 +127,11 @@ public final class ContributorAgreement {
shortDescription = d;
}
public String getAgreementHtml() {
return agreementHtml;
public String getAgreementUrl() {
return agreementUrl;
}
public void setAgreementHtml(final String h) {
agreementHtml = h;
public void setAgreementUrl(final String h) {
agreementUrl = h;
}
}

View File

@@ -34,7 +34,7 @@ public class TextSaveButtonListener extends KeyboardListenerAdapter {
@Override
public void onKeyPress(final Widget sender, final char key, final int mod) {
if (mod == 0) {
if ((mod & (MODIFIER_CTRL | MODIFIER_ALT | MODIFIER_META)) == 0) {
switch (key) {
case KEY_UP:
case KEY_DOWN:

View File

@@ -515,5 +515,31 @@
margin-top: 10px;
padding: 5px 5px 5px 5px;
display: table;
border: 1px solid #B0BDCC;
border: 1px solid #b0bdcc;
}
.gerrit-ContributorAgreement-Button {
font-weight: bold;
}
.gerrit-ContributorAgreement-ShortDescription {
margin-left: 20px;
margin-right: 20px;
margin-bottom: 10px;
padding: 5px 5px 5px 5px;
border: 1px solid #b0bdcc;
}
.gerrit-ContributorAgreement-AlreadySubmitted {
margin-left: 20px;
margin-right: 20px;
padding: 5px 5px 5px 5px;
color: red;
}
.gerrit-ContributorAgreement-Legal {
margin-left: 20px;
margin-right: 20px;
padding: 5px 5px 5px 5px;
border: 1px solid #b0bdcc;
}

View File

@@ -0,0 +1,155 @@
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import org.spearce.jgit.util.NB;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Sends static content from the site 's <code>static/</code> subdirectory. */
public class StaticServlet extends HttpServlet {
private static final long MAX_AGE = 12 * 60 * 60 * 1000L/* milliseconds */;
private static final String CACHE_CTRL =
"public, max-age=" + (MAX_AGE / 1000L);
private static final HashMap<String, String> MIME_TYPES =
new HashMap<String, String>();
static {
MIME_TYPES.put("html", "text/html");
MIME_TYPES.put("htm", "text/html");
MIME_TYPES.put("js", "application/x-javascript");
MIME_TYPES.put("css", "text/css");
MIME_TYPES.put("rtf", "text/rtf");
MIME_TYPES.put("txt", "text/plain");
MIME_TYPES.put("text", "text/plain");
MIME_TYPES.put("pdf", "application/pdf");
MIME_TYPES.put("jpeg", "image/jpeg");
MIME_TYPES.put("jpg", "image/jpeg");
MIME_TYPES.put("gif", "image/gif");
MIME_TYPES.put("png", "image/png");
MIME_TYPES.put("tiff", "image/tiff");
MIME_TYPES.put("tif", "image/tiff");
MIME_TYPES.put("svg", "image/svg+xml");
}
private static String contentType(final String name) {
final int dot = name.lastIndexOf('.');
final String ext = 0 < dot ? name.substring(dot + 1) : "";
final String type = MIME_TYPES.get(ext);
return type != null ? type : "application/octet-stream";
}
private static byte[] readFile(final File p) throws IOException {
final FileInputStream in = new FileInputStream(p);
try {
final byte[] r = new byte[(int) in.getChannel().size()];
NB.readFully(in, r, 0, r.length);
return r;
} finally {
in.close();
}
}
private static byte[] compress(final byte[] raw) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final GZIPOutputStream gz = new GZIPOutputStream(out);
gz.write(raw);
gz.finish();
gz.flush();
return out.toByteArray();
}
private File staticBase;
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
final GerritServer srv;
try {
srv = GerritServer.getInstance();
} catch (OrmException e) {
throw new ServletException("Cannot load GerritServer", e);
} catch (XsrfException e) {
throw new ServletException("Cannot load GerritServer", e);
}
final File p = srv.getSitePath();
staticBase = p != null ? new File(p, "static") : null;
}
private File local(final HttpServletRequest req) {
final String name = req.getPathInfo();
if (name.startsWith("/") && name.length() > 1 && name.indexOf('/', 1) < 0) {
final File p = new File(staticBase, name.substring(1));
return p.isFile() ? p : null;
}
return null;
}
@Override
protected long getLastModified(final HttpServletRequest req) {
final File p = local(req);
return p != null ? p.lastModified() : -1;
}
@Override
protected void doGet(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException {
final File p = local(req);
if (p == null) {
rsp.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
final String type = contentType(p.getName());
final byte[] tosend;
if (!type.equals("application/x-javascript")
&& RPCServletUtils.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
tosend = compress(readFile(p));
} else {
tosend = readFile(p);
}
rsp.setHeader("Cache-Control", CACHE_CTRL);
rsp.setDateHeader("Expires", System.currentTimeMillis() + MAX_AGE);
rsp.setDateHeader("Last-Modified", p.lastModified());
rsp.setContentType(type);
rsp.setContentLength(tosend.length);
final OutputStream out = rsp.getOutputStream();
try {
out.write(tosend);
} finally {
out.close();
}
}
}

View File

@@ -16,7 +16,12 @@ package com.google.gerrit.server;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gerrit.client.data.SystemInfoService;
import com.google.gerrit.client.reviewdb.ContributorAgreement;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException;
import java.util.List;
public class SystemInfoServiceImpl implements SystemInfoService {
private final GerritServer server;
@@ -28,4 +33,18 @@ public class SystemInfoServiceImpl implements SystemInfoService {
public void loadGerritConfig(final AsyncCallback<GerritConfig> callback) {
callback.onSuccess(server.getGerritConfig());
}
public void contributorAgreements(
final AsyncCallback<List<ContributorAgreement>> callback) {
try {
final ReviewDb db = server.getDatabase().open();
try {
callback.onSuccess(db.contributorAgreements().active().toList());
} finally {
db.close();
}
} catch (OrmException e) {
callback.onFailure(e);
}
}
}