55 */
66package edu .harvard .iq .dataverse .engine .command .impl ;
77
8- import edu .harvard .iq .dataverse .Dataset ;
9- import edu .harvard .iq .dataverse .DatasetLinkingDataverse ;
10- import edu .harvard .iq .dataverse .DatasetLock ;
11- import edu .harvard .iq .dataverse .Dataverse ;
12- import edu .harvard .iq .dataverse .Guestbook ;
8+ import edu .harvard .iq .dataverse .*;
139import edu .harvard .iq .dataverse .authorization .Permission ;
1410import edu .harvard .iq .dataverse .authorization .users .AuthenticatedUser ;
11+ import edu .harvard .iq .dataverse .authorization .users .User ;
1512import edu .harvard .iq .dataverse .engine .command .AbstractVoidCommand ;
1613import edu .harvard .iq .dataverse .engine .command .CommandContext ;
1714import edu .harvard .iq .dataverse .engine .command .DataverseRequest ;
2118import edu .harvard .iq .dataverse .engine .command .exception .IllegalCommandException ;
2219import edu .harvard .iq .dataverse .engine .command .exception .PermissionException ;
2320import edu .harvard .iq .dataverse .engine .command .exception .UnforcedCommandException ;
21+ import edu .harvard .iq .dataverse .settings .SettingsServiceBean ;
2422import edu .harvard .iq .dataverse .util .BundleUtil ;
25- import java . util . ArrayList ;
26- import java .util . Arrays ;
27- import java .util . Collections ;
28- import java .util .List ;
23+
24+ import java .sql . Timestamp ;
25+ import java .time . Instant ;
26+ import java .util .* ;
2927import java .util .logging .Logger ;
3028
3129/**
3432 * @author skraffmi
3533 */
3634@ RequiredPermissionsMap ({
37- @ RequiredPermissions (dataverseName = "moved" , value = {Permission .PublishDataset })
38- , @ RequiredPermissions (dataverseName = "destination" , value = {Permission .AddDataset , Permission .PublishDataset })
35+ @ RequiredPermissions (dataverseName = "moved" , value = {Permission .PublishDataset }),
36+ @ RequiredPermissions (dataverseName = "destination" , value = {Permission .AddDataset , Permission .PublishDataset })
3937})
4038public class MoveDatasetCommand extends AbstractVoidCommand {
4139
@@ -44,8 +42,13 @@ public class MoveDatasetCommand extends AbstractVoidCommand {
4442 final Dataset moved ;
4543 final Dataverse destination ;
4644 final Boolean force ;
45+ final Boolean allowSelfNotification ;
46+ final Dataverse originalOwner ;
4747
4848 public MoveDatasetCommand (DataverseRequest aRequest , Dataset moved , Dataverse destination , Boolean force ) {
49+ this ( aRequest , moved , destination , force , null );
50+ }
51+ public MoveDatasetCommand (DataverseRequest aRequest , Dataset moved , Dataverse destination , Boolean force , Boolean allowSelfNotification ) {
4952 super (
5053 aRequest ,
5154 dv ("moved" , moved ),
@@ -54,6 +57,8 @@ public MoveDatasetCommand(DataverseRequest aRequest, Dataset moved, Dataverse de
5457 this .moved = moved ;
5558 this .destination = destination ;
5659 this .force = force ;
60+ this .allowSelfNotification = allowSelfNotification ;
61+ this .originalOwner = moved .getOwner ();
5762 }
5863
5964 @ Override
@@ -72,13 +77,13 @@ public void executeImpl(CommandContext ctxt) throws CommandException {
7277 if (moved .getOwner ().equals (destination )) {
7378 throw new IllegalCommandException (BundleUtil .getStringFromBundle ("dashboard.move.dataset.command.error.targetDataverseSameAsOriginalDataverse" ), this );
7479 }
75-
80+
7681 // if dataset is published make sure that its target is published
77-
82+
7883 if (moved .isReleased () && !destination .isReleased ()){
7984 throw new IllegalCommandException (BundleUtil .getStringFromBundle ("dashboard.move.dataset.command.error.targetDataverseUnpublishedDatasetPublished" , Arrays .asList (destination .getDisplayName ())), this );
8085 }
81-
86+
8287 //if the datasets guestbook is not contained in the new dataverse then remove it
8388 if (moved .getGuestbook () != null ) {
8489 Guestbook gb = moved .getGuestbook ();
@@ -97,15 +102,15 @@ public void executeImpl(CommandContext ctxt) throws CommandException {
97102 }
98103 }
99104 }
100-
105+
101106 // generate list of all possible parent dataverses to check against
102107 List <Dataverse > ownersToCheck = new ArrayList <>();
103108 ownersToCheck .add (destination );
104109 if (destination .getOwners () != null ) {
105110 ownersToCheck .addAll (destination .getOwners ());
106111 }
107-
108- // if the dataset is linked to the new dataverse or any of
112+
113+ // if the dataset is linked to the new dataverse or any of
109114 // its parent dataverses then remove the link
110115 List <DatasetLinkingDataverse > linkingDatasets = new ArrayList <>();
111116 if (moved .getDatasetLinkingDataverses () != null ) {
@@ -124,7 +129,7 @@ public void executeImpl(CommandContext ctxt) throws CommandException {
124129 }
125130 }
126131 }
127-
132+
128133 if (removeGuestbook || removeLinkDs ) {
129134 StringBuilder errorString = new StringBuilder ();
130135 if (removeGuestbook ) {
@@ -135,10 +140,10 @@ public void executeImpl(CommandContext ctxt) throws CommandException {
135140 }
136141 throw new UnforcedCommandException (errorString .toString (), this );
137142 }
138-
143+
139144 // 6575 if dataset is submitted for review and the default contributor
140145 // role includes dataset publish then remove the lock
141-
146+
142147 if (moved .isLockedFor (DatasetLock .Reason .InReview )
143148 && destination .getDefaultContributorRole ().permissions ().contains (Permission .PublishDataset )) {
144149 ctxt .datasets ().removeDatasetLocks (moved , DatasetLock .Reason .InReview );
@@ -153,4 +158,56 @@ public void executeImpl(CommandContext ctxt) throws CommandException {
153158
154159 }
155160
161+ @ Override
162+ public boolean onSuccess (CommandContext ctxt , Object r ) {
163+ sendNotification (moved , originalOwner , ctxt );
164+ return true ;
165+ }
166+
167+ /**
168+ * Sends notifications to those able to publish the dataset upon the successful move of a dataset.
169+ * <p>
170+ * This method checks if dataset move notifications are enabled. If so, it
171+ * notifies all users with {@code Permission.PublishDataset} on the original owning Dataverse.
172+ * The user who initiated the action can be included or excluded from this
173+ * notification based on the allowSelfNotification flag.
174+ *
175+ * @param dataset The moved {@code Dataset}.
176+ * @param originalOwner The original owning {@code Dataverse}.
177+ * @param ctxt The {@code CommandContext} providing access to application services.
178+ */
179+ protected void sendNotification (Dataset dataset , Dataverse originalOwner , CommandContext ctxt ) {
180+ // 1. Exit early if the SendNotificationOnDatasetMove setting is disabled.
181+ if (!ctxt .settings ().isTrueForKey (SettingsServiceBean .Key .SendNotificationOnDatasetMove , false )) {
182+ return ;
183+ }
184+
185+ // 2. Identify the user who initiated the action.
186+ final User user = getUser ();
187+ final AuthenticatedUser requestor = user .isAuthenticated () ? (AuthenticatedUser ) user : null ;
188+
189+ // 3. Get all users with publish permission on the dataset's original owner (dataverse) and notify them.
190+ Map <String , AuthenticatedUser > recipients = ctxt .permissions ().getDistinctUsersWithPermissionOn (Permission .PublishDataset , originalOwner );
191+ // make sure the requestor is in the recipient list in case they don't match the permission but only if allowSelfNotification is true
192+ if (requestor != null ) {
193+ if (Boolean .TRUE .equals (allowSelfNotification )) {
194+ recipients .put (requestor .getIdentifier (), requestor );
195+ } else {
196+ recipients .remove (requestor .getIdentifier ());
197+ }
198+ }
199+
200+ recipients .values ()
201+ .stream ()
202+ .forEach (recipient -> ctxt .notifications ().sendNotification (
203+ recipient ,
204+ Timestamp .from (Instant .now ()),
205+ UserNotification .Type .DATASETMOVED ,
206+ dataset .getId (),
207+ null ,
208+ requestor ,
209+ true
210+ ));
211+ }
156212}
213+
0 commit comments