Jul 31 - Update to ensure customers don't get notification for internal comments
This commit is contained in:
@@ -8,7 +8,8 @@
|
|||||||
"Bash(python docs/convert_manual.py)",
|
"Bash(python docs/convert_manual.py)",
|
||||||
"Bash(grep -n -A3 dateOnlyFormatter __TRACKED_VAR__/Sync/SyncManager.swift)",
|
"Bash(grep -n -A3 dateOnlyFormatter __TRACKED_VAR__/Sync/SyncManager.swift)",
|
||||||
"Bash(python -c \"import ast; ast.parse\\(open\\('app/utils/pdf_export.py'\\).read\\(\\)\\); print\\('OK'\\)\")",
|
"Bash(python -c \"import ast; ast.parse\\(open\\('app/utils/pdf_export.py'\\).read\\(\\)\\); print\\('OK'\\)\")",
|
||||||
"Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/utils/pdf_export.py',encoding='utf-8'\\).read\\(\\)\\); print\\('OK'\\)\")"
|
"Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/utils/pdf_export.py',encoding='utf-8'\\).read\\(\\)\\); print\\('OK'\\)\")",
|
||||||
|
"Bash(python -c \"import ast,io; ast.parse\\(io.open\\('app/routes/issues.py',encoding='utf-8'\\).read\\(\\)\\); print\\('OK'\\)\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-8
@@ -30,17 +30,31 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# ── Shared helper ─────────────────────────────────────────────────────────────
|
# ── Shared helper ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _notify_followers(issue, title, body, exclude_user_ids=None):
|
def _notify_followers(issue, title, body, exclude_user_ids=None,
|
||||||
"""Dispatch a notification to every follower of the given issue."""
|
customer_body=None, skip_customers=False):
|
||||||
|
"""Dispatch a notification to every follower of the given issue.
|
||||||
|
|
||||||
|
Customer followers are handled separately so a staff-only (internal) comment
|
||||||
|
never leaks to them: pass ``skip_customers=True`` to omit customer followers
|
||||||
|
entirely, or ``customer_body`` to send them a customer-safe message in place
|
||||||
|
of ``body``. Non-customer followers always receive ``body``.
|
||||||
|
"""
|
||||||
exclude = set(exclude_user_ids or [])
|
exclude = set(exclude_user_ids or [])
|
||||||
issue_link = url_for('issues.view', issue_id=issue.id)
|
issue_link = url_for('issues.view', issue_id=issue.id)
|
||||||
for follower in issue.followers.all():
|
for follower in issue.followers.all():
|
||||||
if follower.user_id in exclude:
|
if follower.user_id in exclude:
|
||||||
continue
|
continue
|
||||||
|
is_customer = bool(follower.user) and follower.user.role == 'customer'
|
||||||
|
if is_customer:
|
||||||
|
if skip_customers:
|
||||||
|
continue
|
||||||
|
f_body = customer_body if customer_body is not None else body
|
||||||
|
else:
|
||||||
|
f_body = body
|
||||||
notify(
|
notify(
|
||||||
recipient = follower.user,
|
recipient = follower.user,
|
||||||
title = title,
|
title = title,
|
||||||
body = body,
|
body = f_body,
|
||||||
link = issue_link,
|
link = issue_link,
|
||||||
issue_id = issue.id,
|
issue_id = issue.id,
|
||||||
event_type = EVENT_ISSUE_FOLLOW,
|
event_type = EVENT_ISSUE_FOLLOW,
|
||||||
@@ -585,34 +599,58 @@ def view(issue_id):
|
|||||||
if old_assigned_to:
|
if old_assigned_to:
|
||||||
exclude_ids.add(old_assigned_to)
|
exclude_ids.add(old_assigned_to)
|
||||||
|
|
||||||
|
# Whether the comment just added is visible to customers. Internal
|
||||||
|
# (staff-only) comments must NEVER reach customer accounts — they only
|
||||||
|
# ever hear about comments explicitly shared with them. Only staff reach
|
||||||
|
# this branch (customers POST via the earlier customer-only path), so the
|
||||||
|
# checkbox governs. `changes` drives staff-facing notifications;
|
||||||
|
# `customer_changes` drives every customer-facing dispatch.
|
||||||
|
comment_customer_visible = bool(comment_body) and ('is_customer_visible' in request.form)
|
||||||
|
|
||||||
changes = []
|
changes = []
|
||||||
|
customer_changes = []
|
||||||
if old_status != issue.status:
|
if old_status != issue.status:
|
||||||
changes.append(
|
_c = (
|
||||||
f'status changed from "{old_status.replace("_"," ").title()}" '
|
f'status changed from "{old_status.replace("_"," ").title()}" '
|
||||||
f'to "{issue.status.replace("_"," ").title()}"'
|
f'to "{issue.status.replace("_"," ").title()}"'
|
||||||
)
|
)
|
||||||
|
changes.append(_c)
|
||||||
|
customer_changes.append(_c)
|
||||||
if old_assigned_to != new_assigned_to:
|
if old_assigned_to != new_assigned_to:
|
||||||
_new_assignee_obj = db.session.get(User, new_assigned_to) if new_assigned_to else None
|
_new_assignee_obj = db.session.get(User, new_assigned_to) if new_assigned_to else None
|
||||||
new_name = _new_assignee_obj.username if _new_assignee_obj else 'Unassigned'
|
new_name = _new_assignee_obj.username if _new_assignee_obj else 'Unassigned'
|
||||||
changes.append(f'reassigned to {new_name}')
|
_c = f'reassigned to {new_name}'
|
||||||
|
changes.append(_c)
|
||||||
|
customer_changes.append(_c)
|
||||||
if comment_body:
|
if comment_body:
|
||||||
changes.append(f'new comment added by {current_user.username}')
|
changes.append(f'new comment added by {current_user.username}')
|
||||||
|
if comment_customer_visible:
|
||||||
|
customer_changes.append(f'new comment added by {current_user.username}')
|
||||||
|
|
||||||
if changes:
|
if changes:
|
||||||
|
_loc = issue.area.name if issue.area else issue.resolved_facility.name if issue.resolved_facility else '—'
|
||||||
|
customer_relevant = bool(customer_changes)
|
||||||
_notify_followers(
|
_notify_followers(
|
||||||
issue = issue,
|
issue = issue,
|
||||||
title = f'Issue #{issue.id} Updated',
|
title = f'Issue #{issue.id} Updated',
|
||||||
body = (
|
body = (
|
||||||
f'Issue #{issue.id} in {issue.area.name if issue.area else issue.resolved_facility.name if issue.resolved_facility else '—'} was updated by '
|
f'Issue #{issue.id} in {_loc} was updated by '
|
||||||
f'{current_user.username}: {"; ".join(changes)}.'
|
f'{current_user.username}: {"; ".join(changes)}.'
|
||||||
),
|
),
|
||||||
|
customer_body = (
|
||||||
|
f'Issue #{issue.id} in {_loc} was updated by '
|
||||||
|
f'{current_user.username}: {"; ".join(customer_changes)}.'
|
||||||
|
) if customer_relevant else None,
|
||||||
|
skip_customers = not customer_relevant,
|
||||||
exclude_user_ids = exclude_ids,
|
exclude_user_ids = exclude_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── Notify via matrix (issue_updated_customer) ───────────────
|
# ── Notify via matrix (issue_updated_customer) ───────────────
|
||||||
|
# Gated on `customer_changes`: an update whose ONLY change is an internal
|
||||||
|
# comment leaves this empty, so no customer notification fires.
|
||||||
facility_id = issue.resolved_facility.id if issue.resolved_facility else None
|
facility_id = issue.resolved_facility.id if issue.resolved_facility else None
|
||||||
if facility_id and changes:
|
if facility_id and customer_changes:
|
||||||
changes_summary = '; '.join(changes)
|
changes_summary = '; '.join(customer_changes)
|
||||||
notify_by_matrix(
|
notify_by_matrix(
|
||||||
event_type = 'issue_updated_customer',
|
event_type = 'issue_updated_customer',
|
||||||
title = f'Issue #{issue.id} Updated at {issue.resolved_facility.name if issue.resolved_facility else '—'}',
|
title = f'Issue #{issue.id} Updated at {issue.resolved_facility.name if issue.resolved_facility else '—'}',
|
||||||
|
|||||||
Reference in New Issue
Block a user