Add a change deleted event/listener
The new event is emitted on deletion of a draft change, and deletion of a regular change. Plugins may implement the ChangeDeletedListener to perform specific actions on change deletion. The event is included in the output of the stream-events ssh command. Bug: Issue 9711 Change-Id: I37424eafa73703f51072ff8ae6c2546a10ed5c7d
This commit is contained in:
@@ -90,6 +90,16 @@ reason:: Reason for abandoning the change.
|
|||||||
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
|
eventCreatedOn:: Time in seconds since the UNIX epoch when this event was
|
||||||
created.
|
created.
|
||||||
|
|
||||||
|
== Change Deleted
|
||||||
|
|
||||||
|
Sent when a change has been deleted.
|
||||||
|
|
||||||
|
type:: "change-deleted"
|
||||||
|
|
||||||
|
change:: link:json.html#change[change attribute]
|
||||||
|
|
||||||
|
deleter:: link:json.html#account[account attribute]
|
||||||
|
|
||||||
=== Change Merged
|
=== Change Merged
|
||||||
|
|
||||||
Sent when a change has been merged into the git repository.
|
Sent when a change has been merged into the git repository.
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.google.gerrit.extensions.registration.RegistrationHandle;
|
|||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.data.RefUpdateAttribute;
|
import com.google.gerrit.server.data.RefUpdateAttribute;
|
||||||
|
import com.google.gerrit.server.events.ChangeDeletedEvent;
|
||||||
import com.google.gerrit.server.events.ChangeMergedEvent;
|
import com.google.gerrit.server.events.ChangeMergedEvent;
|
||||||
import com.google.gerrit.server.events.Event;
|
import com.google.gerrit.server.events.Event;
|
||||||
import com.google.gerrit.server.events.RefEvent;
|
import com.google.gerrit.server.events.RefEvent;
|
||||||
@@ -69,6 +70,8 @@ public class EventRecorder {
|
|||||||
public void onEvent(Event e) {
|
public void onEvent(Event e) {
|
||||||
if (e instanceof ReviewerDeletedEvent) {
|
if (e instanceof ReviewerDeletedEvent) {
|
||||||
recordedEvents.put(ReviewerDeletedEvent.TYPE, (ReviewerDeletedEvent) e);
|
recordedEvents.put(ReviewerDeletedEvent.TYPE, (ReviewerDeletedEvent) e);
|
||||||
|
} else if (e instanceof ChangeDeletedEvent) {
|
||||||
|
recordedEvents.put(ChangeDeletedEvent.TYPE, (ChangeDeletedEvent) e);
|
||||||
} else if (e instanceof RefEvent) {
|
} else if (e instanceof RefEvent) {
|
||||||
RefEvent event = (RefEvent) e;
|
RefEvent event = (RefEvent) e;
|
||||||
String key =
|
String key =
|
||||||
@@ -138,6 +141,21 @@ public class EventRecorder {
|
|||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ImmutableList<ChangeDeletedEvent> getChangeDeletedEvents(int expectedSize) {
|
||||||
|
String key = ChangeDeletedEvent.TYPE;
|
||||||
|
if (expectedSize == 0) {
|
||||||
|
assertThat(recordedEvents).doesNotContainKey(key);
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
assertThat(recordedEvents).containsKey(key);
|
||||||
|
ImmutableList<ChangeDeletedEvent> events =
|
||||||
|
FluentIterable.from(recordedEvents.get(key))
|
||||||
|
.transform(ChangeDeletedEvent.class::cast)
|
||||||
|
.toList();
|
||||||
|
assertThat(events).hasSize(expectedSize);
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
public void assertNoRefUpdatedEvents(String project, String branch) throws Exception {
|
public void assertNoRefUpdatedEvents(String project, String branch) throws Exception {
|
||||||
getRefUpdatedEvents(project, branch, 0);
|
getRefUpdatedEvents(project, branch, 0);
|
||||||
}
|
}
|
||||||
@@ -197,6 +215,18 @@ public class EventRecorder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void assertChangeDeletedEvents(String... expected) {
|
||||||
|
ImmutableList<ChangeDeletedEvent> events = getChangeDeletedEvents(expected.length / 2);
|
||||||
|
int i = 0;
|
||||||
|
for (ChangeDeletedEvent event : events) {
|
||||||
|
String id = event.change.get().id;
|
||||||
|
assertThat(id).isEqualTo(expected[i]);
|
||||||
|
String reviewer = event.deleter.get().email;
|
||||||
|
assertThat(reviewer).isEqualTo(expected[i + 1]);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
eventListenerRegistration.remove();
|
eventListenerRegistration.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -437,10 +437,13 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
@Test
|
@Test
|
||||||
public void deleteDraftChange() throws Exception {
|
public void deleteDraftChange() throws Exception {
|
||||||
PushOneCommit.Result r = createChange("refs/drafts/master");
|
PushOneCommit.Result r = createChange("refs/drafts/master");
|
||||||
assertThat(query(r.getChangeId())).hasSize(1);
|
String changeId = r.getChangeId();
|
||||||
assertThat(info(r.getChangeId()).status).isEqualTo(ChangeStatus.DRAFT);
|
assertThat(query(changeId)).hasSize(1);
|
||||||
gApi.changes().id(r.getChangeId()).delete();
|
assertThat(info(changeId).status).isEqualTo(ChangeStatus.DRAFT);
|
||||||
assertThat(query(r.getChangeId())).isEmpty();
|
gApi.changes().id(changeId).delete();
|
||||||
|
assertThat(query(changeId)).isEmpty();
|
||||||
|
|
||||||
|
eventRecorder.assertChangeDeletedEvents(changeId, admin.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -521,6 +524,8 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
gApi.changes().id(changeId).delete();
|
gApi.changes().id(changeId).delete();
|
||||||
|
|
||||||
assertThat(query(changeId)).isEmpty();
|
assertThat(query(changeId)).isEmpty();
|
||||||
|
|
||||||
|
eventRecorder.assertChangeDeletedEvents(changeId, deleteAs.email);
|
||||||
} finally {
|
} finally {
|
||||||
removePermission(Permission.DELETE_OWN_CHANGES, project, "refs/*");
|
removePermission(Permission.DELETE_OWN_CHANGES, project, "refs/*");
|
||||||
removePermission(Permission.DELETE_CHANGES, project, "refs/*");
|
removePermission(Permission.DELETE_CHANGES, project, "refs/*");
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (C) 2018 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.extensions.events;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.annotations.ExtensionPoint;
|
||||||
|
|
||||||
|
/** Notified whenever a Change is deleted. */
|
||||||
|
@ExtensionPoint
|
||||||
|
public interface ChangeDeletedListener {
|
||||||
|
interface Event extends ChangeEvent {}
|
||||||
|
|
||||||
|
void onChangeDeleted(Event event);
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import com.google.gerrit.reviewdb.server.ReviewDbUtil;
|
|||||||
import com.google.gerrit.server.PatchSetUtil;
|
import com.google.gerrit.server.PatchSetUtil;
|
||||||
import com.google.gerrit.server.StarredChangesUtil;
|
import com.google.gerrit.server.StarredChangesUtil;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
|
import com.google.gerrit.server.extensions.events.ChangeDeleted;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.update.BatchUpdateOp;
|
import com.google.gerrit.server.update.BatchUpdateOp;
|
||||||
import com.google.gerrit.server.update.BatchUpdateReviewDb;
|
import com.google.gerrit.server.update.BatchUpdateReviewDb;
|
||||||
@@ -66,6 +67,7 @@ class DeleteChangeOp implements BatchUpdateOp {
|
|||||||
private final StarredChangesUtil starredChangesUtil;
|
private final StarredChangesUtil starredChangesUtil;
|
||||||
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore;
|
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore;
|
||||||
private final boolean allowDrafts;
|
private final boolean allowDrafts;
|
||||||
|
private final ChangeDeleted changeDeleted;
|
||||||
|
|
||||||
private Change.Id id;
|
private Change.Id id;
|
||||||
|
|
||||||
@@ -74,11 +76,13 @@ class DeleteChangeOp implements BatchUpdateOp {
|
|||||||
PatchSetUtil psUtil,
|
PatchSetUtil psUtil,
|
||||||
StarredChangesUtil starredChangesUtil,
|
StarredChangesUtil starredChangesUtil,
|
||||||
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore,
|
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore,
|
||||||
@GerritServerConfig Config cfg) {
|
@GerritServerConfig Config cfg,
|
||||||
|
ChangeDeleted changeDeleted) {
|
||||||
this.psUtil = psUtil;
|
this.psUtil = psUtil;
|
||||||
this.starredChangesUtil = starredChangesUtil;
|
this.starredChangesUtil = starredChangesUtil;
|
||||||
this.accountPatchReviewStore = accountPatchReviewStore;
|
this.accountPatchReviewStore = accountPatchReviewStore;
|
||||||
this.allowDrafts = allowDrafts(cfg);
|
this.allowDrafts = allowDrafts(cfg);
|
||||||
|
this.changeDeleted = changeDeleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -98,6 +102,7 @@ class DeleteChangeOp implements BatchUpdateOp {
|
|||||||
deleteChangeElementsFromDb(ctx, id);
|
deleteChangeElementsFromDb(ctx, id);
|
||||||
|
|
||||||
ctx.deleteChange();
|
ctx.deleteChange();
|
||||||
|
changeDeleted.fire(ctx.getChange(), ctx.getAccount(), ctx.getWhen());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import com.google.gerrit.extensions.events.AccountIndexedListener;
|
|||||||
import com.google.gerrit.extensions.events.AgreementSignupListener;
|
import com.google.gerrit.extensions.events.AgreementSignupListener;
|
||||||
import com.google.gerrit.extensions.events.AssigneeChangedListener;
|
import com.google.gerrit.extensions.events.AssigneeChangedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
|
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
|
||||||
|
import com.google.gerrit.extensions.events.ChangeDeletedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeIndexedListener;
|
import com.google.gerrit.extensions.events.ChangeIndexedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeMergedListener;
|
import com.google.gerrit.extensions.events.ChangeMergedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeRestoredListener;
|
import com.google.gerrit.extensions.events.ChangeRestoredListener;
|
||||||
@@ -310,6 +311,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
|
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
|
||||||
DynamicSet.setOf(binder(), AssigneeChangedListener.class);
|
DynamicSet.setOf(binder(), AssigneeChangedListener.class);
|
||||||
DynamicSet.setOf(binder(), ChangeAbandonedListener.class);
|
DynamicSet.setOf(binder(), ChangeAbandonedListener.class);
|
||||||
|
DynamicSet.setOf(binder(), ChangeDeletedListener.class);
|
||||||
DynamicSet.setOf(binder(), CommentAddedListener.class);
|
DynamicSet.setOf(binder(), CommentAddedListener.class);
|
||||||
DynamicSet.setOf(binder(), DraftPublishedListener.class);
|
DynamicSet.setOf(binder(), DraftPublishedListener.class);
|
||||||
DynamicSet.setOf(binder(), HashtagsEditedListener.class);
|
DynamicSet.setOf(binder(), HashtagsEditedListener.class);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2018 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;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gerrit.server.data.AccountAttribute;
|
||||||
|
|
||||||
|
public class ChangeDeletedEvent extends ChangeEvent {
|
||||||
|
public static final String TYPE = "change-deleted";
|
||||||
|
public Supplier<AccountAttribute> deleter;
|
||||||
|
|
||||||
|
public ChangeDeletedEvent(Change change) {
|
||||||
|
super(TYPE, change);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ public class EventTypes {
|
|||||||
static {
|
static {
|
||||||
register(AssigneeChangedEvent.TYPE, AssigneeChangedEvent.class);
|
register(AssigneeChangedEvent.TYPE, AssigneeChangedEvent.class);
|
||||||
register(ChangeAbandonedEvent.TYPE, ChangeAbandonedEvent.class);
|
register(ChangeAbandonedEvent.TYPE, ChangeAbandonedEvent.class);
|
||||||
|
register(ChangeDeletedEvent.TYPE, ChangeDeletedEvent.class);
|
||||||
register(ChangeMergedEvent.TYPE, ChangeMergedEvent.class);
|
register(ChangeMergedEvent.TYPE, ChangeMergedEvent.class);
|
||||||
register(ChangeRestoredEvent.TYPE, ChangeRestoredEvent.class);
|
register(ChangeRestoredEvent.TYPE, ChangeRestoredEvent.class);
|
||||||
register(CommentAddedEvent.TYPE, CommentAddedEvent.class);
|
register(CommentAddedEvent.TYPE, CommentAddedEvent.class);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.google.gerrit.extensions.common.ChangeInfo;
|
|||||||
import com.google.gerrit.extensions.common.RevisionInfo;
|
import com.google.gerrit.extensions.common.RevisionInfo;
|
||||||
import com.google.gerrit.extensions.events.AssigneeChangedListener;
|
import com.google.gerrit.extensions.events.AssigneeChangedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
|
import com.google.gerrit.extensions.events.ChangeAbandonedListener;
|
||||||
|
import com.google.gerrit.extensions.events.ChangeDeletedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeMergedListener;
|
import com.google.gerrit.extensions.events.ChangeMergedListener;
|
||||||
import com.google.gerrit.extensions.events.ChangeRestoredListener;
|
import com.google.gerrit.extensions.events.ChangeRestoredListener;
|
||||||
import com.google.gerrit.extensions.events.CommentAddedListener;
|
import com.google.gerrit.extensions.events.CommentAddedListener;
|
||||||
@@ -75,6 +76,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class StreamEventsApiListener
|
public class StreamEventsApiListener
|
||||||
implements AssigneeChangedListener,
|
implements AssigneeChangedListener,
|
||||||
ChangeAbandonedListener,
|
ChangeAbandonedListener,
|
||||||
|
ChangeDeletedListener,
|
||||||
ChangeMergedListener,
|
ChangeMergedListener,
|
||||||
ChangeRestoredListener,
|
ChangeRestoredListener,
|
||||||
CommentAddedListener,
|
CommentAddedListener,
|
||||||
@@ -94,6 +96,7 @@ public class StreamEventsApiListener
|
|||||||
protected void configure() {
|
protected void configure() {
|
||||||
DynamicSet.bind(binder(), AssigneeChangedListener.class).to(StreamEventsApiListener.class);
|
DynamicSet.bind(binder(), AssigneeChangedListener.class).to(StreamEventsApiListener.class);
|
||||||
DynamicSet.bind(binder(), ChangeAbandonedListener.class).to(StreamEventsApiListener.class);
|
DynamicSet.bind(binder(), ChangeAbandonedListener.class).to(StreamEventsApiListener.class);
|
||||||
|
DynamicSet.bind(binder(), ChangeDeletedListener.class).to(StreamEventsApiListener.class);
|
||||||
DynamicSet.bind(binder(), ChangeMergedListener.class).to(StreamEventsApiListener.class);
|
DynamicSet.bind(binder(), ChangeMergedListener.class).to(StreamEventsApiListener.class);
|
||||||
DynamicSet.bind(binder(), ChangeRestoredListener.class).to(StreamEventsApiListener.class);
|
DynamicSet.bind(binder(), ChangeRestoredListener.class).to(StreamEventsApiListener.class);
|
||||||
DynamicSet.bind(binder(), CommentAddedListener.class).to(StreamEventsApiListener.class);
|
DynamicSet.bind(binder(), CommentAddedListener.class).to(StreamEventsApiListener.class);
|
||||||
@@ -497,4 +500,20 @@ public class StreamEventsApiListener
|
|||||||
log.error("Failed to dispatch event", e);
|
log.error("Failed to dispatch event", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChangeDeleted(ChangeDeletedListener.Event ev) {
|
||||||
|
try {
|
||||||
|
ChangeNotes notes = getNotes(ev.getChange());
|
||||||
|
Change change = notes.getChange();
|
||||||
|
ChangeDeletedEvent event = new ChangeDeletedEvent(change);
|
||||||
|
|
||||||
|
event.change = changeAttributeSupplier(change);
|
||||||
|
event.deleter = accountAttributeSupplier(ev.getWho());
|
||||||
|
|
||||||
|
dispatcher.get().postEvent(change, event);
|
||||||
|
} catch (OrmException e) {
|
||||||
|
log.error("Failed to dispatch event", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (C) 2018 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.extensions.events;
|
||||||
|
|
||||||
|
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||||
|
import com.google.gerrit.extensions.common.AccountInfo;
|
||||||
|
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||||
|
import com.google.gerrit.extensions.events.ChangeDeletedListener;
|
||||||
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class ChangeDeleted {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ChangeDeleted.class);
|
||||||
|
|
||||||
|
private final DynamicSet<ChangeDeletedListener> listeners;
|
||||||
|
private final EventUtil util;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ChangeDeleted(DynamicSet<ChangeDeletedListener> listeners, EventUtil util) {
|
||||||
|
this.listeners = listeners;
|
||||||
|
this.util = util;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fire(Change change, Account deleter, Timestamp when) {
|
||||||
|
if (!listeners.iterator().hasNext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Event event = new Event(util.changeInfo(change), util.accountInfo(deleter), when);
|
||||||
|
for (ChangeDeletedListener l : listeners) {
|
||||||
|
try {
|
||||||
|
l.onChangeDeleted(event);
|
||||||
|
} catch (Exception e) {
|
||||||
|
util.logEventListenerError(this, l, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (OrmException e) {
|
||||||
|
log.error("Couldn't fire event", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Event extends AbstractChangeEvent implements ChangeDeletedListener.Event {
|
||||||
|
Event(ChangeInfo change, AccountInfo deleter, Timestamp when) {
|
||||||
|
super(change, deleter, when, NotifyHandling.ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule plugins/hooks updated: 08c4186511...8b71877346
Reference in New Issue
Block a user