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

feat: allow discussion notification customization #3072

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Add api endpoint /me/org_topics/ [#3070](https://github.com/opendatateam/udata/pull/3070)
- Expose dataservices in RDF catalog [#3058](https://github.com/opendatateam/udata/pull/3058)
- CORS: always returns 204 on OPTIONS request [#3046](https://github.com/opendatateam/udata/pull/3046)
- Allow discussion notification customization [#3072](https://github.com/opendatateam/udata/pull/3072)

## 9.0.0 (2024-06-07)

Expand Down
87 changes: 74 additions & 13 deletions udata/core/discussions/tasks.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import fnmatch

from urllib.parse import urlparse

from flask import current_app

from udata import mail
from udata.i18n import lazy_gettext as _
from udata.core.dataset.models import Dataset
from udata.core.reuse.models import Reuse
from udata.core.post.models import Post
from udata.core.reuse.models import Reuse
from udata.core.topic.models import Topic
from udata.tasks import connect, get_logger

from .models import Discussion, Message
from .models import Discussion
from .signals import (
on_new_discussion, on_new_discussion_comment, on_discussion_closed
)
Expand All @@ -22,15 +29,48 @@ def owner_recipients(discussion):
return []


def get_external_url(discussion):
url = None
if (meta_url := discussion.extras.get('notification', {}).get('external_url')):
meta_url_parsed = urlparse(meta_url)
if any(
fnmatch.fnmatch(meta_url_parsed.netloc, pattern)
for pattern in current_app.config['DISCUSSION_ALLOWED_EXTERNAL_DOMAINS']
):
url = f'{meta_url}#discussion-{discussion.id}'
if not url:
url = getattr(discussion, 'external_url', None)
return url


def get_subject_type(discussion):
if (meta_name := discussion.extras.get('notification', {}).get('model_name')):
if meta_name in current_app.config['DISCUSSION_ALTERNATE_MODEL_NAMES']:
return meta_name
return discussion.subject.verbose_name


@connect(on_new_discussion, by_id=True)
def notify_new_discussion(discussion_id):
discussion = Discussion.objects.get(pk=discussion_id)
if isinstance(discussion.subject, (Dataset, Reuse, Post)):
if isinstance(discussion.subject, (Dataset, Reuse, Post, Topic)):
recipients = owner_recipients(discussion)
subject_type = get_subject_type(discussion)
subject = _('Your %(type)s have a new discussion',
type=discussion.subject.verbose_name)
mail.send(subject, recipients, 'new_discussion',
discussion=discussion, message=discussion.discussion[0])
type=subject_type)
external_url = get_external_url(discussion)
if external_url:
mail.send(
subject,
recipients,
'new_discussion',
discussion=discussion,
message=discussion.discussion[0],
external_url=external_url,
subject_type=subject_type
)
else:
log.warning(f'No external url could be computed for discussion {discussion.id}')
else:
log.warning('Unrecognized discussion subject type %s',
type(discussion.subject))
Expand All @@ -40,15 +80,25 @@ def notify_new_discussion(discussion_id):
def notify_new_discussion_comment(discussion_id, message=None):
discussion = Discussion.objects.get(pk=discussion_id)
message = discussion.discussion[message]
if isinstance(discussion.subject, (Dataset, Reuse, Post)):
if isinstance(discussion.subject, (Dataset, Reuse, Post, Topic)):
recipients = owner_recipients(discussion) + [
m.posted_by for m in discussion.discussion]
recipients = list({u.id: u for u in recipients if u != message.posted_by}.values())
subject = _('%(user)s commented your discussion',
user=message.posted_by.fullname)

mail.send(subject, recipients, 'new_discussion_comment',
discussion=discussion, message=message)
external_url = get_external_url(discussion)
if external_url:
mail.send(
subject,
recipients,
'new_discussion_comment',
discussion=discussion,
message=message,
external_url=external_url,
subject_type=get_subject_type(discussion)
)
else:
log.warning(f'No external url could be computed for discussion {discussion.id}')
else:
log.warning('Unrecognized discussion subject type %s',
type(discussion.subject))
Expand All @@ -58,13 +108,24 @@ def notify_new_discussion_comment(discussion_id, message=None):
def notify_discussion_closed(discussion_id, message=None):
discussion = Discussion.objects.get(pk=discussion_id)
message = discussion.discussion[message]
if isinstance(discussion.subject, (Dataset, Reuse, Post)):
if isinstance(discussion.subject, (Dataset, Reuse, Post, Topic)):
recipients = owner_recipients(discussion) + [
m.posted_by for m in discussion.discussion]
recipients = list({u.id: u for u in recipients if u != message.posted_by}.values())
subject = _('A discussion has been closed')
mail.send(subject, recipients, 'discussion_closed',
discussion=discussion, message=message)
external_url = get_external_url(discussion)
if external_url:
mail.send(
subject,
recipients,
'discussion_closed',
discussion=discussion,
message=message,
external_url=external_url,
subject_type=get_subject_type(discussion),
)
else:
log.warning(f'No external url could be computed for discussion {discussion.id}')
else:
log.warning('Unrecognized discussion subject type %s',
type(discussion.subject))
3 changes: 3 additions & 0 deletions udata/core/topic/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from flask import url_for
from mongoengine.signals import pre_save
from udata.i18n import lazy_gettext as _
from udata.models import db, SpatialCoverage
from udata.search import reindex
from udata.tasks import as_task_param
Expand Down Expand Up @@ -40,6 +41,8 @@ class Topic(db.Document, Owned, db.Datetimed):
'queryset_class': OwnedQuerySet,
}

verbose_name = _('topic')

def __str__(self):
return self.name

Expand Down
9 changes: 7 additions & 2 deletions udata/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class Defaults(object):
# A list of tuples, each tuple describing a group with its title and
# a list of licenses associated. Translations are not supported.
# Example:
# LICENSE_GROUPS = [
# LICENSE_GROUPS = [
# ("Autorités administratives", [
# {"value": "lov2", "recommended": True, "description": "Recommandée", "code": "etalab-2.0"},
# {"value": "notspecified", "description": "Le Code des relations entre le public et l’administration ne s’applique pas"}]),
Expand Down Expand Up @@ -264,7 +264,7 @@ class Defaults(object):

HARVEST_VALIDATION_CONTACT_FORM = None

HARVEST_MAX_CATALOG_SIZE_IN_MONGO = None # Defaults to the size of a MongoDB document
HARVEST_MAX_CATALOG_SIZE_IN_MONGO = None # Defaults to the size of a MongoDB document
HARVEST_GRAPHS_S3_BUCKET = None # If the catalog is bigger than `HARVEST_MAX_CATALOG_SIZE_IN_MONGO` store the graph inside S3 instead of MongoDB
HARVEST_GRAPHS_S3_FILENAME_PREFIX = '' # Useful to store the graphs inside a subfolder of the bucket. For example by setting `HARVEST_GRAPHS_S3_FILENAME_PREFIX = 'graphs/'`

Expand Down Expand Up @@ -473,6 +473,11 @@ class Defaults(object):
###########################################################################
MATTERMOST_WEBHOOK = None

# Discussion settings
###########################################################################
DISCUSSION_ALLOWED_EXTERNAL_DOMAINS = ['*.data.gouv.fr']
DISCUSSION_ALTERNATE_MODEL_NAMES = []


class Testing(object):
'''Sane values for testing. Should be applied as override'''
Expand Down
6 changes: 3 additions & 3 deletions udata/templates/mail/discussion_closed.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% block body %}
<p style="margin: 0;padding: 0;">{{
_('%(user)s closed an discussion on your %(type)s %(subject)s',
type=discussion.subject.verbose_name,
type=subject_type,
user=(
'<a href="'|safe
+ url_for('api.user', user=message.posted_by, _external=True)
Expand All @@ -14,7 +14,7 @@
),
subject=(
'<a href="'|safe
+ discussion.external_url
+ external_url
+ '">'|safe
+ discussion.subject|string
+ '</a>'|safe
Expand All @@ -38,7 +38,7 @@
<td align="center">
{{ mail_button(
_('See the discussion'),
discussion.external_url
external_url
) }}
</td>
</tr>
Expand Down
4 changes: 2 additions & 2 deletions udata/templates/mail/discussion_closed.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block body %}
{{ _('%(user)s closed an discussion on your %(type)s %(subject)s',
type=discussion.subject.verbose_name,
type=subject_type,
user=message.posted_by.fullname,
subject=discussion.subject|string
) }}.
Expand All @@ -12,5 +12,5 @@


{{ _('You can see the discussion on this page:') }}
{{ discussion.external_url }}
{{ external_url }}
{% endblock %}
6 changes: 3 additions & 3 deletions udata/templates/mail/new_discussion.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% block body %}
<p style="margin: 0;padding: 0;">{{
_('%(user)s submitted a new discussion on your %(type)s %(subject)s',
type=discussion.subject.verbose_name,
type=subject_type,
user=(
'<a href="'|safe
+ url_for('api.user', user=discussion.user, _external=True)
Expand All @@ -14,7 +14,7 @@
),
subject=(
'<a href="'|safe
+ discussion.external_url
+ external_url
+ '">'|safe
+ discussion.subject|string
+ '</a>'|safe
Expand All @@ -36,7 +36,7 @@
<td align="center">
{{ mail_button(
_('See the discussion'),
discussion.external_url
external_url
) }}
</td>
</tr>
Expand Down
4 changes: 2 additions & 2 deletions udata/templates/mail/new_discussion.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block body %}
{{ _('%(user)s submitted a new discussion on your %(type)s %(subject)s',
type=discussion.subject.verbose_name,
type=subject_type,
user=discussion.user.fullname,
subject=discussion.subject|string
) }}.
Expand All @@ -11,5 +11,5 @@
{{ _('Title') }}: {{ discussion.title }}

{{ _('You can see the discussion on this page:') }}
{{ discussion.subject.external_url }}
{{ external_url }}
{% endblock %}
6 changes: 3 additions & 3 deletions udata/templates/mail/new_discussion_comment.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% block body %}
<p style="margin: 0;padding: 0;">{{
_('%(user)s commented an discussion on your %(type)s %(subject)s',
type=discussion.subject.verbose_name,
type=subject_type,
user=(
'<a href="'|safe
+ url_for('api.user', user=message.posted_by, _external=True)
Expand All @@ -14,7 +14,7 @@
),
subject=(
'<a href="'|safe
+ discussion.external_url
+ external_url
+ '">'|safe
+ discussion.subject|string
+ '</a>'|safe
Expand All @@ -37,7 +37,7 @@
<td align="center">
{{ mail_button(
_('See the discussion'),
discussion.external_url
external_url
) }}
</td>
</tr>
Expand Down
4 changes: 2 additions & 2 deletions udata/templates/mail/new_discussion_comment.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% block body %}
{{ _('%(user)s commented an discussion on your %(type)s %(subject)s',
type=discussion.subject.verbose_name,
type=subject_type,
user=message.posted_by.fullname,
subject=discussion.subject|string
) }}.
Expand All @@ -12,5 +12,5 @@


{{ _('You can see the discussion on this page:') }}
{{ discussion.external_url }}
{{ external_url }}
{% endblock %}
Loading