Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature for muting email and/or in-app notifications #8530

Merged
merged 54 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4878379
New feature for muting email and/or in-app notifications by users for…
ErykKul Mar 24, 2022
99b47e0
added tests to AuthenticatedUserTest.java
ErykKul Mar 29, 2022
2341435
muting properties are now better documented and moved to more logical…
ErykKul Mar 29, 2022
9b79afb
added support for muting notification types for the installation wher…
ErykKul Mar 29, 2022
abe1af2
implementation of in review requested changes
ErykKul Mar 30, 2022
2aabda1
bugfix: user notification bean did not use settings wrapper
ErykKul Mar 30, 2022
18f7b5b
nullpointer fix
ErykKul Mar 30, 2022
437df33
fixed the remaining bugs after refactoring
ErykKul Mar 30, 2022
ef12352
added requested changes
ErykKul Apr 1, 2022
b2f684a
alternative UI for muting notifications
ErykKul Apr 1, 2022
3bc644f
removed unused import
ErykKul Apr 19, 2022
70e01d9
resolved merge conflict
ErykKul Apr 19, 2022
4fd7e53
renames of the notification variables in AuthenticatedUser
ErykKul Apr 19, 2022
8d2e3ce
resolved merge conflict
ErykKul Apr 19, 2022
8f2afd7
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul Apr 22, 2022
782c5a2
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul Apr 22, 2022
cf13a0b
muting options are now collapsible and the logic is inverterd: you se…
ErykKul Apr 25, 2022
e5e80d8
bugfix in inverted muting logic
ErykKul Apr 25, 2022
1607359
when saving notification settings the settings panel collapses
ErykKul Apr 26, 2022
32cb770
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul Apr 26, 2022
f2b26fe
when saving notification settings the settings panel collapses; added…
ErykKul Apr 26, 2022
a83cfff
fixed inconsistencies when switching tabs and improved accessibility
ErykKul Apr 27, 2022
9c2d8ec
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul May 5, 2022
efb9951
always muted notifications are no longer shown in notification muttin…
ErykKul May 5, 2022
6b4bd0f
bumped flyway script filename according to the current release
ErykKul May 5, 2022
fde4989
bumped flyway script filename according to the current release
ErykKul May 5, 2022
2a9d840
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul May 6, 2022
4818f79
added documentation for muting notifications feature
ErykKul May 5, 2022
450a965
suggested doc changes for #7492
pdurbin May 6, 2022
45f2761
Merge pull request #1 from pdurbin/7492-docs
ErykKul May 9, 2022
c8c74c6
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul May 10, 2022
64f631c
updated notifications documentation for users
ErykKul May 10, 2022
670c00c
minor tweaks to user guide.
pdurbin May 10, 2022
d098827
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul May 10, 2022
51521ca
Merge branch 'develop' into 7492_muting_notifications #7492
pdurbin May 16, 2022
6427be1
test a few more methods #7492
pdurbin May 16, 2022
70d54fc
add comment to keep notification type in sync with guides #7492
pdurbin May 16, 2022
01db132
doc tweaks #7492
pdurbin May 16, 2022
bd87464
Merge branch 'develop' into 7492_muting_notifications #7492
pdurbin May 16, 2022
297b90c
Update src/main/java/propertyFiles/Bundle.properties
ErykKul May 17, 2022
40d0208
changes after review
ErykKul May 17, 2022
ec7aa85
Merge branch '7492_muting_notifications' of https://github.com/ErykKu…
ErykKul May 17, 2022
8cc29d9
notifications api extension
ErykKul May 17, 2022
ea342ab
rename SQL scripts, clarify docs #7492 #8533 #8590 #8592
pdurbin May 18, 2022
2b8cb0d
fix formatting in docs, more links #7492
pdurbin May 18, 2022
230c22b
add timestamp to notification JSON #7492 #8487
pdurbin May 18, 2022
04ecb67
add more API tests for notifications #7492
pdurbin May 18, 2022
d861089
Merge branch 'develop' into 7492_muting_notifications #7492
pdurbin May 18, 2022
76c49c2
mention notification API updates in release note #7492 #8487
pdurbin May 19, 2022
3feb296
Merge branch 'IQSS:develop' into 7492_muting_notifications
ErykKul May 20, 2022
9469fad
change exception
qqmyers May 20, 2022
986518f
mention API in User Guide section on notifications #7492
pdurbin May 23, 2022
ca87b28
fix copy/paste issue (in-app vs email) #7492
pdurbin May 23, 2022
aa38724
better error handling when the notification type does not exist
ErykKul May 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/release-notes/7492_muting_notifications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This release has a new feature that allows users to control which notifications they receive. How to enable this feature and which options can be set by the admins is described in the [Letting users manage receiving notifications section of the Admin Guide](https://guides.dataverse.org/en/latest/admin/user-administration.html#letting-users-manage-receiving-notifications).

## New DB Settings

- :ShowMuteOptions
- :AlwaysMuted
- :NeverMuted
47 changes: 47 additions & 0 deletions doc/sphinx-guides/source/admin/user-administration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,50 @@ Using the API token 7ae33670-be21-491d-a244-008149856437 as an example:
``delete from apitoken where tokenstring = '7ae33670-be21-491d-a244-008149856437';``

You should expect the output ``DELETE 1`` after issuing the command above.

.. _mute-notifications:

Letting Users Manage Notifications
-----------------------------------

See :ref:`account-notifications` in the User Guide for how notifications are described to end users.

You can let users manage which notification types they wish to receive by setting :ref:`:ShowMuteOptions` to "true":

``curl -X PUT -d 'true' http://localhost:8080/api/admin/settings/:ShowMuteOptions``

This enables additional settings for each user in the notifications tab of their account page. The users can select which in-app notifications and/or e-mails they wish to receive out of the following list:

* ``APIGENERATED`` API token is generated
* ``ASSIGNROLE`` Role is assigned
* ``CHECKSUMFAIL`` Checksum validation failed
* ``CHECKSUMIMPORT`` Dataset had file checksums added via a batch job
* ``CONFIRMEMAIL`` Email Verification
* ``CREATEACC`` Account is created
* ``CREATEDS`` Your dataset is created
* ``CREATEDV`` Dataverse collection is created
* ``DATASETCREATED`` Dataset was created by user
* ``FILESYSTEMIMPORT`` Dataset has been successfully uploaded and verified
* ``GRANTFILEACCESS`` Access to file is granted
* ``INGESTCOMPLETEDWITHERRORS`` Ingest completed with errors
* ``INGESTCOMPLETED`` Ingest is completed
* ``PUBLISHEDDS`` Dataset is published
* ``PUBLISHFAILED_PIDREG`` Publish has failed
* ``REJECTFILEACCESS`` Access to file is rejected
* ``REQUESTFILEACCESS`` Access to file is requested
* ``RETURNEDDS`` Returned from review
* ``REVOKEROLE`` Role is revoked
* ``STATUSUPDATED`` Status of dataset has been updated
* ``SUBMITTEDDS`` Submitted for review
* ``WORKFLOW_FAILURE`` External workflow run has failed
* ``WORKFLOW_SUCCESS`` External workflow run has succeeded

After enabling this feature, all notifications are enabled by default, until this is changed by the user.

You can shorten this list by configuring some notification types (e.g., ``ASSIGNROLE`` and ``REVOKEROLE``) to be always muted for everyone and not manageable by users (not visible in the user interface) with the :ref:`:AlwaysMuted` setting:

``curl -X PUT -d 'ASSIGNROLE,REVOKEROLE' http://localhost:8080/api/admin/settings/:AlwaysMuted``

Finally, you can set some notifications (e.g., ``REQUESTFILEACCESS``, ``GRANTFILEACCESS`` and ``REJECTFILEACCESS``) as always enabled for everyone and not manageable by users (grayed out in the user interface) with the :ref:`:NeverMuted` setting:

``curl -X PUT -d 'REQUESTFILEACCESS,GRANTFILEACCESS,REJECTFILEACCESS' http://localhost:8080/api/admin/settings/:NeverMuted``
83 changes: 80 additions & 3 deletions doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2836,13 +2836,90 @@ Show Info About Single Metadata Block
Notifications
-------------

See :ref:`account-notifications` in the User Guide for an overview. For a list of all the notification types mentioned below (e.g. ASSIGNROLE), see :ref:`mute-notifications` in the Admin Guide.

Get All Notifications by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can get a dump of their notifications by passing in their API token::
Each user can get a dump of their notifications by passing in their API token:

.. code-block:: bash

curl -H "X-Dataverse-key:$API_TOKEN" $SERVER_URL/api/notifications/all

Delete Notification by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can delete notifications by passing in their API token and specifying notification ID (e.g., 555):

.. code-block:: bash

export NOTIFICATION_ID=555

curl -H X-Dataverse-key:$API_TOKEN -X DELETE "$SERVER_URL/api/notifications/$NOTIFICATION_ID"

Get All Muted In-app Notifications by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can get a list of their muted in-app notification types by passing in their API token:

.. code-block:: bash

curl -H X-Dataverse-key:$API_TOKEN -X GET "$SERVER_URL/api/notifications/mutedNotifications"

Mute In-app Notification by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can mute in-app notifications by passing in their API token and specifying notification type to be muted (e.g., ASSIGNROLE):

.. code-block:: bash

export NOTIFICATION_TYPE=ASSIGNROLE

curl -H X-Dataverse-key:$API_TOKEN -X PUT "$SERVER_URL/api/notifications/mutedNotifications/$NOTIFICATION_TYPE"

Unmute In-app Notification by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can unmute in-app notifications by passing in their API token and specifying notification type to be unmuted (e.g., ASSIGNROLE):

.. code-block:: bash

export NOTIFICATION_TYPE=ASSIGNROLE

curl -H X-Dataverse-key:$API_TOKEN -X DELETE "$SERVER_URL/api/notifications/mutedNotifications/$NOTIFICATION_TYPE"

Get All Muted In-app Notifications by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can get a list of their muted email notification types by passing in their API token:

.. code-block:: bash

curl -H X-Dataverse-key:$API_TOKEN -X GET "$SERVER_URL/api/notifications/mutedEmails"

Mute In-app Notification by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can mute email notifications by passing in their API token and specifying notification type to be muted (e.g., ASSIGNROLE):

.. code-block:: bash

export NOTIFICATION_TYPE=ASSIGNROLE

curl -H X-Dataverse-key:$API_TOKEN -X PUT "$SERVER_URL/api/notifications/mutedEmails/$NOTIFICATION_TYPE"

Unmute Email Notification by User
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Each user can unmute email notifications by passing in their API token and specifying notification type to be unmuted (e.g., ASSIGNROLE):

.. code-block:: bash

export NOTIFICATION_TYPE=ASSIGNROLE

curl -H X-Dataverse-key:$API_TOKEN -X DELETE "$SERVER_URL/api/notifications/mutedEmails/$NOTIFICATION_TYPE"

curl -H "X-Dataverse-key:$API_TOKEN" $SERVER_URL/api/notifications/all

.. _User Information:

User Information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ How to Create a SQL Upgrade Script

We assume you have already read the :doc:`version-control` section and have been keeping your feature branch up to date with the "develop" branch.

Create a new file called something like ``V4.11.0.1__5565-sanitize-directory-labels.sql`` in the ``src/main/resources/db/migration`` directory. Use a version like "4.11.0.1" in the example above where the previously released version was 4.11, ensuring that the version number is unique. Note that this is not the version that you expect the code changes to be included in (4.12 in this example). For the "description" you should the name of your branch, which should include the GitHub issue you are working on, as in the example above. To read more about Flyway file naming conventions, see https://flywaydb.org/documentation/migrations#naming
Create a new file called something like ``V4.11.0.1__5565-sanitize-directory-labels.sql`` in the ``src/main/resources/db/migration`` directory. Use a version like "4.11.0.1" in the example above where the previously released version was 4.11, ensuring that the version number is unique. Note that this is not the version that you expect the code changes to be included in (4.12 in this example). When the previously released version is a patch version (e.g. 5.10.1), use "5.10.1.1" for the first SQL script version (rather than "5.10.1.0.1"). For the "description" you should the name of your branch, which should include the GitHub issue you are working on, as in the example above. To read more about Flyway file naming conventions, see https://flywaydb.org/documentation/migrations#naming

The SQL migration script you wrote will be part of the war file and executed when the war file is deployed. To see a history of Flyway database migrations that have been applied, look at the ``flyway_schema_history`` table.

Expand Down
33 changes: 30 additions & 3 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,10 @@ The image below indicates that the page layout consists of three main blocks: a

|dvPageBlocks|

.. To edit, use dvBrandingCustBlocks.drawio with https://app.diagrams.net
.. |dvPageBlocks| image:: ./img/dvBrandingCustBlocks.png
:class: img-responsive

Installation Name/Brand Name
++++++++++++++++++++++++++++

Expand Down Expand Up @@ -2760,6 +2764,29 @@ To remove the override and go back to the default list:

``curl -X PUT -d '' http://localhost:8080/api/admin/settings/:FileCategories``

.. To edit, use dvBrandingCustBlocks.drawio with https://app.diagrams.net
.. |dvPageBlocks| image:: ./img/dvBrandingCustBlocks.png
:class: img-responsive
.. _:ShowMuteOptions:

:ShowMuteOptions
++++++++++++++++

Allows users to mute notifications by showing additional configuration options in the Notifications tab of the account page (see :ref:`account-notifications` in the User Guide). By default, this setting is "false" and users cannot mute any notifications (this feature is not shown in the user interface).

For configuration details, see :ref:`mute-notifications`.

.. _:AlwaysMuted:

:AlwaysMuted
++++++++++++

Overrides the default empty list of always muted notifications. Always muted notifications cannot be unmuted by the users. Always muted notifications are not shown in the notification settings for the users.

For configuration details, see :ref:`mute-notifications`.

.. _:NeverMuted:

:NeverMuted
+++++++++++

Overrides the default empty list of never muted notifications. Never muted notifications cannot be muted by the users. Always muted notifications are grayed out and are not adjustable by the user.

For configuration details, see :ref:`mute-notifications`.
14 changes: 10 additions & 4 deletions doc/sphinx-guides/source/user/account.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,25 @@ The My Data section of your account page displays a listing of all the Dataverse

You can use the Add Data button to create a new Dataverse collection or dataset. By default, the new Dataverse collection or dataset will be created in the root Dataverse collection, but from the create form you can use the Host Dataverse collection dropdown menu to choose a different Dataverse collection, for which you have the proper access privileges. However, you will not be able to change this selection after you create your Dataverse collection or dataset.

.. _account-notifications:

Notifications
-------------

Notifications appear in the notifications tab on your account page and are also displayed as a number next to your account name.
Notifications appear in the notifications tab on your account page and are also displayed as a number next to your account name. You also receive notifications via email.

If your admin has enabled the option to change the notification settings, you will find an overview of the notification and email settings in the notifications tab. There, you can select which notifications and/or emails you wish to receive. If certain notification or email options are greyed out, you can’t change the setting for this notification because the admin has set these as never to be muted by the user. You control the in-app and the email notifications separately in the two lists.
ErykKul marked this conversation as resolved.
Show resolved Hide resolved

You will receive a notification when:
You will typically receive a notification or email when:

- You've created your account.
- You've created a Dataverse collection or added a dataset.
- Another Dataverse installation user has requested access to restricted files in a dataset that you published. (If you submitted your dataset for review and it was published by a curator, the curators of the Dataverse collection that contains your dataset will get a notification about requests to access your restricted files.)
- Another Dataverse installation user has requested access to restricted files in a dataset that you published. (If you submitted your dataset for review, and it was published by a curator, the curators of the Dataverse collection that contains your dataset will get a notification about requests to access your restricted files.)
- A file in one of your datasets has finished the ingest process.

Notifications will only be emailed one time even if you haven't read the notification on the Dataverse installation.
There are other notification types that you can receive, e.g., notification on granted roles, API key generation, etc. These types of notifications are less common and are not described here. Some other notifications are limited to specific roles. For example, if the installation has a curation workflow, reviewers get notified when a new dataset is submitted for review.

Notifications will only be emailed once, even if you haven't read the in-app notification.

API Token
---------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ public String getMessageTextBasedOnNotification(UserNotification userNotificatio
return "";
}

private Object getObjectOfNotification (UserNotification userNotification){
public Object getObjectOfNotification (UserNotification userNotification){
switch (userNotification.getType()) {
case ASSIGNROLE:
case REVOKEROLE:
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/SettingsWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import edu.harvard.iq.dataverse.util.MailUtil;
import edu.harvard.iq.dataverse.util.StringUtil;
import edu.harvard.iq.dataverse.util.SystemConfig;
import edu.harvard.iq.dataverse.UserNotification.Type;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
Expand All @@ -23,6 +24,7 @@
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.Set;

import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
Expand Down Expand Up @@ -102,6 +104,10 @@ public class SettingsWrapper implements java.io.Serializable {

private Boolean customLicenseAllowed = null;

private Set<Type> alwaysMuted = null;

private Set<Type> neverMuted = null;

public String get(String settingKey) {
if (settingsMap == null) {
initSettingsMap();
Expand Down Expand Up @@ -176,6 +182,40 @@ private void initSettingsMap() {
}
}

private void initAlwaysMuted() {
alwaysMuted = UserNotification.Type.tokenizeToSet(getValueForKey(Key.AlwaysMuted));
}

private void initNeverMuted() {
neverMuted = UserNotification.Type.tokenizeToSet(getValueForKey(Key.NeverMuted));
}

public Set<Type> getAlwaysMutedSet() {
if (alwaysMuted == null) {
initAlwaysMuted();
}
return alwaysMuted;
}

public Set<Type> getNeverMutedSet() {
if (neverMuted == null) {
initNeverMuted();
}
return neverMuted;
}

public boolean isAlwaysMuted(Type type) {
return getAlwaysMutedSet().contains(type);
}

public boolean isNeverMuted(Type type) {
return getNeverMutedSet().contains(type);
}

public boolean isShowMuteOptions() {
return isTrueForKey(Key.ShowMuteOptions, false);
}


public String getGuidesBaseUrl() {
if (guidesBaseUrl == null) {
Expand Down
38 changes: 37 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/UserNotification.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package edu.harvard.iq.dataverse;

import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.util.BundleUtil;
import edu.harvard.iq.dataverse.util.DateUtil;

import java.io.Serializable;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Collections;
import java.util.HashSet;
import java.util.stream.Collectors;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
Expand All @@ -26,11 +32,37 @@
@Table(indexes = {@Index(columnList="user_id")})

public class UserNotification implements Serializable {
// Keep in sync with list at admin/user-administration.rst
public enum Type {
ASSIGNROLE, REVOKEROLE, CREATEDV, CREATEDS, CREATEACC, SUBMITTEDDS, RETURNEDDS,
PUBLISHEDDS, REQUESTFILEACCESS, GRANTFILEACCESS, REJECTFILEACCESS, FILESYSTEMIMPORT,
CHECKSUMIMPORT, CHECKSUMFAIL, CONFIRMEMAIL, APIGENERATED, INGESTCOMPLETED, INGESTCOMPLETEDWITHERRORS,
PUBLISHFAILED_PIDREG, WORKFLOW_SUCCESS, WORKFLOW_FAILURE, STATUSUPDATED, DATASETCREATED
PUBLISHFAILED_PIDREG, WORKFLOW_SUCCESS, WORKFLOW_FAILURE, STATUSUPDATED, DATASETCREATED;

public String getDescription() {
return BundleUtil.getStringFromBundle("notification.typeDescription." + this.name());
}

public boolean hasDescription() {
final String description = getDescription();
return description != null && !description.isEmpty();
}

public static Set<Type> tokenizeToSet(String tokens) {
if (tokens == null || tokens.isEmpty()) {
return new HashSet<>();
}
return Collections.list(new StringTokenizer(tokens, ",")).stream()
.map(token -> Type.valueOf(((String) token).trim()))
.collect(Collectors.toSet());
}

public static String toStringValue(Set<Type> typesSet) {
if (typesSet == null || typesSet.isEmpty()) {
return null;
}
return String.join(",", typesSet.stream().map(x -> x.name()).collect(Collectors.toList()));
}
};

private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -93,6 +125,10 @@ public String getSendDate() {
return new SimpleDateFormat("MMMM d, yyyy h:mm a z").format(sendDate);
}

public Timestamp getSendDateTimestamp() {
return sendDate;
}

public void setSendDate(Timestamp sendDate) {
this.sendDate = sendDate;
}
Expand Down
Loading