Implement 'Restore Change' feature.
Now it is possible to restore status of abandoned changes to 'NEW'. It helps in a situation when a change has been abandoned accidently. Bug: issue 312 Change-Id: Iba61dcf82a9b5ee5b78cd529b041f92a042a9611
This commit is contained in:
parent
91c1532130
commit
3200245774
@ -70,6 +70,16 @@ patchset:: link:json.html#patchset[patchset attribute]
|
|||||||
|
|
||||||
abandoner:: link:json.html#account[account attribute]
|
abandoner:: link:json.html#account[account attribute]
|
||||||
|
|
||||||
|
Change Restored
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
type:: "change-restored"
|
||||||
|
|
||||||
|
change:: link:json.html#change[change attribute]
|
||||||
|
|
||||||
|
patchset:: link:json.html#patchset[patchset attribute]
|
||||||
|
|
||||||
|
restorer:: link:json.html#account[account attribute]
|
||||||
|
|
||||||
Change Merged
|
Change Merged
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
type:: "change-merged"
|
type:: "change-merged"
|
||||||
|
@ -57,6 +57,15 @@ Called whenever a change has been abandoned.
|
|||||||
change-abandoned --change <change id> --change-url <change url> --project <project name> --branch <branch> --abandoner <abandoner> --reason <reason>
|
change-abandoned --change <change id> --change-url <change url> --project <project name> --branch <branch> --abandoner <abandoner> --reason <reason>
|
||||||
====
|
====
|
||||||
|
|
||||||
|
change-restored
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Called whenever a change has been restored.
|
||||||
|
|
||||||
|
====
|
||||||
|
change-restored --change <change id> --change-url <change url> --project <project name> --branch <branch> --restorer <restorer> --reason <reason>
|
||||||
|
====
|
||||||
|
|
||||||
|
|
||||||
Configuration Settings
|
Configuration Settings
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -30,6 +30,7 @@ public class ChangeDetail {
|
|||||||
protected AccountInfoCache accounts;
|
protected AccountInfoCache accounts;
|
||||||
protected boolean allowsAnonymous;
|
protected boolean allowsAnonymous;
|
||||||
protected boolean canAbandon;
|
protected boolean canAbandon;
|
||||||
|
protected boolean canRestore;
|
||||||
protected Change change;
|
protected Change change;
|
||||||
protected boolean starred;
|
protected boolean starred;
|
||||||
protected List<ChangeInfo> dependsOn;
|
protected List<ChangeInfo> dependsOn;
|
||||||
@ -69,6 +70,14 @@ public class ChangeDetail {
|
|||||||
canAbandon = a;
|
canAbandon = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canRestore() {
|
||||||
|
return canRestore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCanRestore(final boolean a) {
|
||||||
|
canRestore = a;
|
||||||
|
}
|
||||||
|
|
||||||
public Change getChange() {
|
public Change getChange() {
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
@ -29,4 +29,8 @@ public interface ChangeManageService extends RemoteJsonService {
|
|||||||
@SignInRequired
|
@SignInRequired
|
||||||
void abandonChange(PatchSet.Id patchSetId, String message,
|
void abandonChange(PatchSet.Id patchSetId, String message,
|
||||||
AsyncCallback<ChangeDetail> callback);
|
AsyncCallback<ChangeDetail> callback);
|
||||||
|
|
||||||
|
@SignInRequired
|
||||||
|
void restoreChange(PatchSet.Id patchSetId, String message,
|
||||||
|
AsyncCallback<ChangeDetail> callback);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,12 @@ public interface ChangeConstants extends Constants {
|
|||||||
String headingCoverMessage();
|
String headingCoverMessage();
|
||||||
String headingPatchComments();
|
String headingPatchComments();
|
||||||
|
|
||||||
|
String buttonRestoreChangeBegin();
|
||||||
|
String restoreChangeTitle();
|
||||||
|
String buttonRestoreChangeCancel();
|
||||||
|
String headingRestoreMessage();
|
||||||
|
String buttonRestoreChangeSend();
|
||||||
|
|
||||||
String pagedChangeListPrev();
|
String pagedChangeListPrev();
|
||||||
String pagedChangeListNext();
|
String pagedChangeListNext();
|
||||||
|
|
||||||
|
@ -81,6 +81,12 @@ buttonAbandonChangeCancel = Cancel
|
|||||||
headingAbandonMessage = Abandon Message:
|
headingAbandonMessage = Abandon Message:
|
||||||
abandonChangeTitle = Code Review - Abandon Change
|
abandonChangeTitle = Code Review - Abandon Change
|
||||||
|
|
||||||
|
buttonRestoreChangeBegin = Restore Change
|
||||||
|
restoreChangeTitle = Code Review - Restore Change
|
||||||
|
buttonRestoreChangeCancel = Cancel
|
||||||
|
headingRestoreMessage = Restore Message:
|
||||||
|
buttonRestoreChangeSend = Restore Change
|
||||||
|
|
||||||
buttonReview = Review
|
buttonReview = Review
|
||||||
buttonPublishCommentsSend = Publish Comments
|
buttonPublishCommentsSend = Publish Comments
|
||||||
buttonPublishSubmitSend = Publish and Submit
|
buttonPublishSubmitSend = Publish and Submit
|
||||||
|
@ -418,6 +418,26 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
|
|||||||
});
|
});
|
||||||
actionsPanel.add(b);
|
actionsPanel.add(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changeDetail.canRestore()) {
|
||||||
|
final Button b = new Button(Util.C.buttonRestoreChangeBegin());
|
||||||
|
b.addClickHandler(new ClickHandler() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final ClickEvent event) {
|
||||||
|
new RestoreChangeDialog(patchSet.getId(),
|
||||||
|
new AsyncCallback<ChangeDetail>() {
|
||||||
|
public void onSuccess(ChangeDetail result) {
|
||||||
|
changeScreen.display(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
b.setEnabled(true);
|
||||||
|
}
|
||||||
|
}).center();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
actionsPanel.add(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateDiffAllActions(final PatchSetDetail detail) {
|
private void populateDiffAllActions(final PatchSetDetail detail) {
|
||||||
|
@ -0,0 +1,133 @@
|
|||||||
|
// 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.changes;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
|
import com.google.gerrit.client.ui.SmallHeading;
|
||||||
|
import com.google.gerrit.common.data.ChangeDetail;
|
||||||
|
import com.google.gerrit.reviewdb.PatchSet;
|
||||||
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
|
import com.google.gwt.event.logical.shared.CloseEvent;
|
||||||
|
import com.google.gwt.event.logical.shared.CloseHandler;
|
||||||
|
import com.google.gwt.user.client.DOM;
|
||||||
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
|
import com.google.gwt.user.client.ui.Button;
|
||||||
|
import com.google.gwt.user.client.ui.FlowPanel;
|
||||||
|
import com.google.gwt.user.client.ui.PopupPanel;
|
||||||
|
import com.google.gwtexpui.globalkey.client.GlobalKey;
|
||||||
|
import com.google.gwtexpui.globalkey.client.NpTextArea;
|
||||||
|
import com.google.gwtexpui.user.client.AutoCenterDialogBox;
|
||||||
|
|
||||||
|
public class RestoreChangeDialog extends AutoCenterDialogBox implements CloseHandler<PopupPanel>{
|
||||||
|
private final FlowPanel panel;
|
||||||
|
private final NpTextArea message;
|
||||||
|
private final Button sendButton;
|
||||||
|
private final Button cancelButton;
|
||||||
|
private final PatchSet.Id psid;
|
||||||
|
private final AsyncCallback<ChangeDetail> callback;
|
||||||
|
|
||||||
|
private boolean buttonClicked = false;
|
||||||
|
|
||||||
|
public RestoreChangeDialog(final PatchSet.Id psi,
|
||||||
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
|
super(/* auto hide */false, /* modal */true);
|
||||||
|
setGlassEnabled(true);
|
||||||
|
|
||||||
|
psid = psi;
|
||||||
|
this.callback = callback;
|
||||||
|
addStyleName(Gerrit.RESOURCES.css().abandonChangeDialog());
|
||||||
|
setText(Util.C.restoreChangeTitle());
|
||||||
|
|
||||||
|
panel = new FlowPanel();
|
||||||
|
add(panel);
|
||||||
|
|
||||||
|
panel.add(new SmallHeading(Util.C.headingRestoreMessage()));
|
||||||
|
|
||||||
|
final FlowPanel mwrap = new FlowPanel();
|
||||||
|
mwrap.setStyleName(Gerrit.RESOURCES.css().abandonMessage());
|
||||||
|
panel.add(mwrap);
|
||||||
|
|
||||||
|
message = new NpTextArea();
|
||||||
|
message.setCharacterWidth(60);
|
||||||
|
message.setVisibleLines(10);
|
||||||
|
DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
|
||||||
|
mwrap.add(message);
|
||||||
|
|
||||||
|
final FlowPanel buttonPanel = new FlowPanel();
|
||||||
|
panel.add(buttonPanel);
|
||||||
|
|
||||||
|
sendButton = new Button(Util.C.buttonRestoreChangeSend());
|
||||||
|
sendButton.addClickHandler(new ClickHandler() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final ClickEvent event) {
|
||||||
|
sendButton.setEnabled(false);
|
||||||
|
Util.MANAGE_SVC.restoreChange(psid, message.getText().trim(),
|
||||||
|
new GerritCallback<ChangeDetail>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(ChangeDetail result) {
|
||||||
|
buttonClicked = true;
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onSuccess(result);
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
sendButton.setEnabled(true);
|
||||||
|
super.onFailure(caught);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
buttonPanel.add(sendButton);
|
||||||
|
|
||||||
|
cancelButton = new Button(Util.C.buttonRestoreChangeCancel());
|
||||||
|
DOM.setStyleAttribute(cancelButton.getElement(), "marginLeft", "300px");
|
||||||
|
cancelButton.addClickHandler(new ClickHandler() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final ClickEvent event) {
|
||||||
|
buttonClicked = true;
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(null);
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
buttonPanel.add(cancelButton);
|
||||||
|
|
||||||
|
addCloseHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void center() {
|
||||||
|
super.center();
|
||||||
|
GlobalKey.dialog(this);
|
||||||
|
message.setFocus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(CloseEvent<PopupPanel> event) {
|
||||||
|
if (!buttonClicked) {
|
||||||
|
// the dialog was closed without one of the buttons being pressed
|
||||||
|
// e.g. the user pressed ESC to close the dialog
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFailure(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -101,6 +101,7 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
|
|||||||
detail.setChange(change);
|
detail.setChange(change);
|
||||||
detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
|
detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
|
||||||
detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
|
detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
|
||||||
|
detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
|
||||||
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
|
detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
|
||||||
changeId));
|
changeId));
|
||||||
loadPatchSets();
|
loadPatchSets();
|
||||||
|
@ -23,12 +23,15 @@ import com.google.inject.Inject;
|
|||||||
class ChangeManageServiceImpl implements ChangeManageService {
|
class ChangeManageServiceImpl implements ChangeManageService {
|
||||||
private final SubmitAction.Factory submitAction;
|
private final SubmitAction.Factory submitAction;
|
||||||
private final AbandonChange.Factory abandonChangeFactory;
|
private final AbandonChange.Factory abandonChangeFactory;
|
||||||
|
private final RestoreChange.Factory restoreChangeFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
|
ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
|
||||||
final AbandonChange.Factory abandonChangeFactory) {
|
final AbandonChange.Factory abandonChangeFactory,
|
||||||
|
final RestoreChange.Factory restoreChangeFactory) {
|
||||||
this.submitAction = patchSetAction;
|
this.submitAction = patchSetAction;
|
||||||
this.abandonChangeFactory = abandonChangeFactory;
|
this.abandonChangeFactory = abandonChangeFactory;
|
||||||
|
this.restoreChangeFactory = restoreChangeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(final PatchSet.Id patchSetId,
|
public void submit(final PatchSet.Id patchSetId,
|
||||||
@ -40,4 +43,9 @@ class ChangeManageServiceImpl implements ChangeManageService {
|
|||||||
final AsyncCallback<ChangeDetail> callback) {
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
abandonChangeFactory.create(patchSetId, message).to(callback);
|
abandonChangeFactory.create(patchSetId, message).to(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void restoreChange(final PatchSet.Id patchSetId, final String message,
|
||||||
|
final AsyncCallback<ChangeDetail> callback) {
|
||||||
|
restoreChangeFactory.create(patchSetId, message).to(callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public class ChangeModule extends RpcServletModule {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
factory(AbandonChange.Factory.class);
|
factory(AbandonChange.Factory.class);
|
||||||
|
factory(RestoreChange.Factory.class);
|
||||||
factory(ChangeDetailFactory.Factory.class);
|
factory(ChangeDetailFactory.Factory.class);
|
||||||
factory(IncludedInDetailFactory.Factory.class);
|
factory(IncludedInDetailFactory.Factory.class);
|
||||||
factory(PatchSetDetailFactory.Factory.class);
|
factory(PatchSetDetailFactory.Factory.class);
|
||||||
|
@ -0,0 +1,132 @@
|
|||||||
|
// 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.rpc.changedetail;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.ChangeHookRunner;
|
||||||
|
import com.google.gerrit.common.data.ChangeDetail;
|
||||||
|
import com.google.gerrit.common.errors.NoSuchEntityException;
|
||||||
|
import com.google.gerrit.httpd.rpc.Handler;
|
||||||
|
import com.google.gerrit.reviewdb.*;
|
||||||
|
import com.google.gerrit.server.ChangeUtil;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.mail.AbandonedSender;
|
||||||
|
import com.google.gerrit.server.mail.EmailException;
|
||||||
|
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||||
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
|
import com.google.gwtorm.client.AtomicUpdate;
|
||||||
|
import com.google.gwtorm.client.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class RestoreChange extends Handler<ChangeDetail> {
|
||||||
|
interface Factory {
|
||||||
|
RestoreChange create(PatchSet.Id patchSetId, String message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ChangeControl.Factory changeControlFactory;
|
||||||
|
private final ReviewDb db;
|
||||||
|
private final IdentifiedUser currentUser;
|
||||||
|
private final AbandonedSender.Factory abandonedSenderFactory;
|
||||||
|
private final ChangeDetailFactory.Factory changeDetailFactory;
|
||||||
|
|
||||||
|
private final PatchSet.Id patchSetId;
|
||||||
|
@Nullable
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
private final ChangeHookRunner hooks;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RestoreChange(final ChangeControl.Factory changeControlFactory,
|
||||||
|
final ReviewDb db, final IdentifiedUser currentUser,
|
||||||
|
final AbandonedSender.Factory abandonedSenderFactory,
|
||||||
|
final ChangeDetailFactory.Factory changeDetailFactory,
|
||||||
|
@Assisted final PatchSet.Id patchSetId,
|
||||||
|
@Assisted @Nullable final String message, final ChangeHookRunner hooks) {
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
|
this.db = db;
|
||||||
|
this.currentUser = currentUser;
|
||||||
|
this.abandonedSenderFactory = abandonedSenderFactory;
|
||||||
|
this.changeDetailFactory = changeDetailFactory;
|
||||||
|
|
||||||
|
this.patchSetId = patchSetId;
|
||||||
|
this.message = message;
|
||||||
|
this.hooks = hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChangeDetail call() throws NoSuchChangeException, OrmException,
|
||||||
|
EmailException, NoSuchEntityException, PatchSetInfoNotAvailableException {
|
||||||
|
final Change.Id changeId = patchSetId.getParentKey();
|
||||||
|
final ChangeControl control = changeControlFactory.validateFor(changeId);
|
||||||
|
if (!control.canRestore()) {
|
||||||
|
throw new NoSuchChangeException(changeId);
|
||||||
|
}
|
||||||
|
final PatchSet patch = db.patchSets().get(patchSetId);
|
||||||
|
if (patch == null) {
|
||||||
|
throw new NoSuchChangeException(changeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ChangeMessage cmsg =
|
||||||
|
new ChangeMessage(new ChangeMessage.Key(changeId, ChangeUtil
|
||||||
|
.messageUUID(db)), currentUser.getAccountId());
|
||||||
|
final StringBuilder msgBuf =
|
||||||
|
new StringBuilder("Patch Set " + patchSetId.get() + ": Restored");
|
||||||
|
if (message != null && message.length() > 0) {
|
||||||
|
msgBuf.append("\n\n");
|
||||||
|
msgBuf.append(message);
|
||||||
|
}
|
||||||
|
cmsg.setMessage(msgBuf.toString());
|
||||||
|
|
||||||
|
Change change = db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() {
|
||||||
|
@Override
|
||||||
|
public Change update(Change change) {
|
||||||
|
if (change.getStatus() == Change.Status.ABANDONED
|
||||||
|
&& change.currentPatchSetId().equals(patchSetId)) {
|
||||||
|
change.setStatus(Change.Status.NEW);
|
||||||
|
ChangeUtil.updated(change);
|
||||||
|
return change;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (change != null) {
|
||||||
|
db.changeMessages().insert(Collections.singleton(cmsg));
|
||||||
|
|
||||||
|
final List<PatchSetApproval> approvals =
|
||||||
|
db.patchSetApprovals().byChange(changeId).toList();
|
||||||
|
for (PatchSetApproval a : approvals) {
|
||||||
|
a.cache(change);
|
||||||
|
}
|
||||||
|
db.patchSetApprovals().update(approvals);
|
||||||
|
|
||||||
|
// Email the reviewers
|
||||||
|
final AbandonedSender cm = abandonedSenderFactory.create(change);
|
||||||
|
cm.setFrom(currentUser.getAccountId());
|
||||||
|
cm.setChangeMessage(cmsg);
|
||||||
|
cm.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
hooks.doChangeRestoreHook(change, currentUser.getAccount(), message);
|
||||||
|
|
||||||
|
return changeDetailFactory.create(changeId).call();
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,7 @@ import com.google.gerrit.server.events.ApprovalAttribute;
|
|||||||
import com.google.gerrit.server.events.ChangeAbandonedEvent;
|
import com.google.gerrit.server.events.ChangeAbandonedEvent;
|
||||||
import com.google.gerrit.server.events.ChangeEvent;
|
import com.google.gerrit.server.events.ChangeEvent;
|
||||||
import com.google.gerrit.server.events.ChangeMergedEvent;
|
import com.google.gerrit.server.events.ChangeMergedEvent;
|
||||||
|
import com.google.gerrit.server.events.ChangeRestoreEvent;
|
||||||
import com.google.gerrit.server.events.CommentAddedEvent;
|
import com.google.gerrit.server.events.CommentAddedEvent;
|
||||||
import com.google.gerrit.server.events.EventFactory;
|
import com.google.gerrit.server.events.EventFactory;
|
||||||
import com.google.gerrit.server.events.PatchSetCreatedEvent;
|
import com.google.gerrit.server.events.PatchSetCreatedEvent;
|
||||||
@ -40,7 +41,6 @@ import com.google.gerrit.server.project.ProjectControl;
|
|||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -90,6 +90,9 @@ public class ChangeHookRunner {
|
|||||||
/** Filename of the change abandoned hook. */
|
/** Filename of the change abandoned hook. */
|
||||||
private final File changeAbandonedHook;
|
private final File changeAbandonedHook;
|
||||||
|
|
||||||
|
/** Filename of the change abandoned hook. */
|
||||||
|
private final File changeRestoredHook;
|
||||||
|
|
||||||
/** Repository Manager. */
|
/** Repository Manager. */
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
|
|
||||||
@ -134,6 +137,7 @@ public class ChangeHookRunner {
|
|||||||
commentAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "commentAddedHook", "comment-added")).getPath());
|
commentAddedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "commentAddedHook", "comment-added")).getPath());
|
||||||
changeMergedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeMergedHook", "change-merged")).getPath());
|
changeMergedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeMergedHook", "change-merged")).getPath());
|
||||||
changeAbandonedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeAbandonedHook", "change-abandoned")).getPath());
|
changeAbandonedHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeAbandonedHook", "change-abandoned")).getPath());
|
||||||
|
changeRestoredHook = sitePath.resolve(new File(hooksPath, getValue(config, "hooks", "changeRestoredHook", "change-restored")).getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addChangeListener(ChangeListener listener, IdentifiedUser user) {
|
public void addChangeListener(ChangeListener listener, IdentifiedUser user) {
|
||||||
@ -328,6 +332,40 @@ public class ChangeHookRunner {
|
|||||||
runHook(getRepo(change), args);
|
runHook(getRepo(change), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire the Change Restored Hook.
|
||||||
|
*
|
||||||
|
* @param change The change itself.
|
||||||
|
* @param account The gerrit user who restored the change.
|
||||||
|
* @param reason Reason for restoring the change.
|
||||||
|
*/
|
||||||
|
public void doChangeRestoreHook(final Change change, final Account account, final String reason) {
|
||||||
|
final ChangeRestoreEvent event = new ChangeRestoreEvent();
|
||||||
|
|
||||||
|
event.change = eventFactory.asChangeAttribute(change);
|
||||||
|
event.restorer = eventFactory.asAccountAttribute(account);
|
||||||
|
event.reason = reason;
|
||||||
|
fireEvent(change, event);
|
||||||
|
|
||||||
|
final List<String> args = new ArrayList<String>();
|
||||||
|
args.add(changeRestoredHook.getAbsolutePath());
|
||||||
|
|
||||||
|
args.add("--change");
|
||||||
|
args.add(event.change.id);
|
||||||
|
args.add("--change-url");
|
||||||
|
args.add(event.change.url);
|
||||||
|
args.add("--project");
|
||||||
|
args.add(event.change.project);
|
||||||
|
args.add("--branch");
|
||||||
|
args.add(event.change.branch);
|
||||||
|
args.add("--restorer");
|
||||||
|
args.add(getDisplayName(account));
|
||||||
|
args.add("--reason");
|
||||||
|
args.add(reason == null ? "" : reason);
|
||||||
|
|
||||||
|
runHook(getRepo(change), args);
|
||||||
|
}
|
||||||
|
|
||||||
private void fireEvent(final Change change, final ChangeEvent event) {
|
private void fireEvent(final Change change, final ChangeEvent event) {
|
||||||
for (ChangeListenerHolder holder : listeners.values()) {
|
for (ChangeListenerHolder holder : listeners.values()) {
|
||||||
if (isVisibleTo(change, holder.user)) {
|
if (isVisibleTo(change, holder.user)) {
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
// 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.events;
|
||||||
|
|
||||||
|
public class ChangeRestoreEvent extends ChangeEvent {
|
||||||
|
public final String type = "change-restored";
|
||||||
|
public ChangeAttribute change;
|
||||||
|
public PatchSetAttribute patchSet;
|
||||||
|
public AccountAttribute restorer;
|
||||||
|
public String reason;
|
||||||
|
}
|
@ -159,6 +159,11 @@ public class ChangeControl {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Can this user restore this change? */
|
||||||
|
public boolean canRestore() {
|
||||||
|
return canAbandon(); // Anyone who can abandon the change can restore it back
|
||||||
|
}
|
||||||
|
|
||||||
public short normalize(ApprovalCategory.Id category, short score) {
|
public short normalize(ApprovalCategory.Id category, short score) {
|
||||||
return getRefControl().normalize(category, score);
|
return getRefControl().normalize(category, score);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user