Skip to content

Unify Reply-To handling across tickets and talks components#2930

Open
MukundC25 wants to merge 1 commit intofossasia:devfrom
MukundC25:fix/unify-reply-to-handling
Open

Unify Reply-To handling across tickets and talks components#2930
MukundC25 wants to merge 1 commit intofossasia:devfrom
MukundC25:fix/unify-reply-to-handling

Conversation

@MukundC25
Copy link
Copy Markdown
Member

@MukundC25 MukundC25 commented Mar 22, 2026

Updates - (Old PR) #1781
Fixes #1564
Follow-up PR for #1611 #1781 (review)

The Event Creation Unification PR added Event.email as the single "Organizer email address" set during event creation. However, Reply-To logic remained inconsistent as described earlier in #1611 (comment):

  • Different logic in tickets vs talks
  • event.settings.contact_mail could silently override Event.email
  • Bulk emails hardcoded contact_mail
  • No centralized precedence

This caused Event.email (shown in UI as canonical) to be silently overridden by contact_mail in certain scenarios.

Changes

Unified Reply-To logic via get_reply_to_address() helper with SMTP context awareness:

  1. Explicit override (manual emails)
  2. Template reply_to (talk-specific)
  3. event.mail_settings['reply_to'] (explicit Reply-To configuration)
  4. Event.email (canonical) - used only with default system sender
  5. None (system default)

Key Features:

  • SMTP Context Awareness: Event.email is only used as Reply-To with default system sender
  • Custom SMTP Support: When custom SMTP is configured, no automatic Reply-To header is set (organizers can explicitly set one)
  • Event.email Now Optional: Made Event.email truly optional with proper database migration - organizers can leave it empty to use platform default
  • Placeholder Elimination: Removed legacy placeholder handling from runtime logic
  • Unified Precedence: Single source of truth across tickets, talks, and bulk emails
  • Clear Help Texts: Updated all help texts to accurately describe Reply-To behavior with platform vs custom SMTP

Removes event.settings.contact_mail from all Reply-To resolution paths.

Modified files:

  • [app/eventyay/common/mail.py] - Added unified helper with SMTP context, removed contact_mail, added type consistency
  • [app/eventyay/base/services/mail.py] - Use helper for ticket emails with sender context
  • [app/eventyay/base/models/mail.py] - Use helper for talk emails with sender context
  • [app/eventyay/plugins/sendmail/views.py] - Use helper for bulk emails, removed duplicate class definition
  • [app/eventyay/base/models/event.py] - Made Event.email optional (blank=True, null=True)
  • [app/eventyay/base/configurations/default_setting.py] - Updated SMTP sender help text
  • [app/eventyay/orga/forms/event.py] - Updated Reply-To and sender help texts
  • [app/eventyay/control/forms/event.py] - Made email optional in forms, updated help texts
  • [app/eventyay/event/forms.py] - Made email optional in wizard, updated help texts
  • [app/eventyay/eventyay_common/forms/event.py] - Made email optional, updated help texts
  • [app/eventyay/plugins/sendmail/forms.py] - Updated email editor Reply-To help text

Removed:

  • event.settings.contact_mail from all Reply-To paths
  • Organizer-level inheritance for Reply-To
  • Unused use_custom_smtp parameter from helper function
  • Duplicate ComposeTeamsMail class definition
  • Legacy placeholder handling (LEGACY_DEFAULT_EMAIL)
  • Default placeholder value org@mail.com from Event.email model field
  • Validation that rejected empty email addresses

Impact

  • Event.email is now consistently used across tickets, talks, and bulk emails when using default SMTP
  • No invisible overrides from organizer-level settings
  • SMTP context-aware Reply-To behavior
  • Event.email is truly optional - organizers can leave it empty to use platform default email
  • Help texts clearly explain when Event.email is used as Reply-To vs when custom SMTP sender handles replies
  • Existing tests (e.g., test_mail_send_ignored_sender_but_custom_reply_to) continue to pass
  • Placeholder logic removed, SMTP context added, email field made optional

Summary by Sourcery

Unify Reply-To handling across tickets, talks, and bulk emails and make the event organizer email truly optional with updated help texts and migration.

New Features:

  • Introduce a centralized get_reply_to_address helper to resolve Reply-To headers with consistent precedence across all email flows.

Bug Fixes:

  • Prevent organizer-level contact_mail from silently overriding the canonical event email in Reply-To resolution and avoid sending internal submission emails when no organizer email is configured.

Enhancements:

  • Apply the unified Reply-To resolution to ticket emails, talk template emails, and bulk sendmail views with SMTP-context awareness.
  • Make the Event.email field optional in the data model and forms, removing legacy placeholder behavior and allowing events to rely on the platform default address.
  • Update organizer- and email-related help texts across models, forms, and settings to clearly describe Reply-To behavior for platform vs custom SMTP senders.
  • Adjust mail templates and queued mail handling so that Reply-To defaults to the organizer email only when using the platform sender and can be overridden per email.

Documentation:

  • Revise user-facing help texts for organizer email, sender, and Reply-To settings to explain how replies are routed under different SMTP configurations.

Chores:

  • Add a database migration to drop the default placeholder organizer email and clean up existing placeholder values from stored events.

- Add helper method to build Reply-To address with display name
- Update mail sending functions to use unified Reply-To logic
- Make Event.email field optional (blank=True, null=True)
- Add migration to remove placeholder emails and update field
- Update forms to handle optional event email
- Ensure consistent Reply-To behavior across components
Copilot AI review requested due to automatic review settings March 22, 2026 13:40
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Mar 22, 2026

Reviewer's Guide

Unifies Reply-To resolution across tickets, talks, and bulk emails via a centralized helper that is SMTP‑context aware, and makes Event.email an optional organizer email with updated help texts and migration away from placeholder/legacy behavior.

Class diagram for updated email and bulk send structures

classDiagram
    class Event {
        +EmailField email
        +JSONField mail_settings
    }
    %% optional organizer email, blank/null allowed

    class MailTemplate {
        +EmailField reply_to
        +CharField subject
        +TextField text
        +TextField html
        +to_mail(user, event, locale, context, address)
    }

    class QueuedMail {
        +Event event
        +MailTemplate template
        +CharField to
        +CharField reply_to
        +CharField bcc
        +CharField subject
        +TextField text
        +TextField html
    }
    %% reply_to uses unified default behavior

    class BulkReplyToMixin {
        +_get_reply_to_for_bulk_email()
    }

    class SenderView {
        +form_valid(form)
    }

    class ComposeTeamsMail {
        +form_valid(form)
    }

    Event "1" --> "*" MailTemplate : owns_templates
    Event "1" --> "*" QueuedMail : has_mails
    MailTemplate "1" --> "*" QueuedMail : enqueues

    BulkReplyToMixin <|.. SenderView : mixin
    BulkReplyToMixin <|.. ComposeTeamsMail : mixin
Loading

File-Level Changes

Change Details Files
Centralized, SMTP-aware Reply-To resolution logic and use across all mail send paths.
  • Added get_reply_to_address helper that resolves Reply-To based on explicit override, template, event mail_settings['reply_to'], Event.email (only with default sender), or none.
  • Updated mail_send_task to use get_reply_to_address when no explicit reply_to is provided and normalized reply_to to a list.
  • Adjusted base mail service to compute sender_email_raw and use get_reply_to_address for both manual and auto emails instead of contact_mail-based logic.
  • Updated mail template to_mail method to determine sender from mail_settings/mail_from and use get_reply_to_address for template reply_to resolution.
  • Introduced BulkReplyToMixin in bulk sendmail views and wired SenderView and ComposeTeamsMail to use event’s resolved default Reply-To instead of contact_mail.
app/eventyay/common/mail.py
app/eventyay/base/services/mail.py
app/eventyay/base/models/mail.py
app/eventyay/plugins/sendmail/views.py
Made Event.email optional and removed legacy placeholder semantics, with corresponding behavioral and data migration changes.
  • Changed Event.email model field to be nullable/blank without a default placeholder and updated its help text to describe Reply-To behavior with platform sender vs default address.
  • Added Django migration that alters the Event.email field and replaces existing 'org@mail.com' placeholder values with NULL.
  • Guarded internal submission notification sending so it only occurs when Event.email is set to avoid relying on a missing address.
  • Removed form-level validation enforcing a non-empty organizer email and any checks against the default placeholder value.
app/eventyay/base/models/event.py
app/eventyay/base/migrations/0021_make_event_email_optional.py
app/eventyay/base/models/submission.py
app/eventyay/control/forms/event.py
app/eventyay/event/forms.py
Removed use of event.settings.contact_mail and clarified configuration/help texts around sender and Reply-To behavior.
  • Eliminated all uses of event.settings.contact_mail in mail sending and bulk email views in favor of unified Reply-To resolution.
  • Updated MailSettingsForm, system default mail_from configuration, queued mail form, and event-related forms to explain how Reply-To and sender interact, especially with custom SMTP.
  • Ensured all event creation/edit forms treat email as optional while keeping a consistent label and help text for organizer email across control, orga, and common event forms.
app/eventyay/base/services/mail.py
app/eventyay/plugins/sendmail/views.py
app/eventyay/orga/forms/event.py
app/eventyay/base/configurations/default_setting.py
app/eventyay/plugins/sendmail/forms.py
app/eventyay/control/forms/event.py
app/eventyay/event/forms.py
app/eventyay/eventyay_common/forms/event.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1564 Provide a single unified "Organizer email address" contact field that replaces both the ticketing quick start "Getting in touch" contact options and the Talk component "Organizer email address", and use it consistently as the canonical contact configuration. The PR focuses on unifying Reply-To resolution logic and removing event.settings.contact_mail from mail handling paths. It does not modify the ticketing quick start "Getting in touch" section or the Talk component settings UI, nor does it explicitly merge their configuration into a single canonical contact field at the UI/configuration level.
#1564 Expose the unified "Organizer email address" field on the "Create a new event" page below "Display settings" and enforce it as a required field so that events cannot be created without it. The PR adjusts the event creation and control forms to make the Event.email field optional (required=False) and updates its help text, which contradicts the requirement to make it mandatory for event creation. There is no change that makes the field required or blocks event creation when it is empty, nor any explicit placement change under "Display settings" as described in the issue.
#1564 Align the contact configuration in Common settings and related UI by renaming to "Organizer email address", synchronizing behavior with the new unified logic, and removing outdated helper text and icons related to the old "Getting in touch" configuration. The PR updates help texts around Reply-To, sender addresses, and makes Event.email optional, but it does not update Common settings to introduce or rename a contact field to "Organizer email address" or ensure its value is shared across event creation, ticketing, and talk components. It also does not remove any helper text or icons associated with the old "Getting in touch" section.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@MukundC25
Copy link
Copy Markdown
Member Author

MukundC25 commented Mar 22, 2026

As suggested #1781 (review) Organizer email no longer required - it's optional, removed the org@mail.com placeholder/value entirely, and updated most of the locations that were creating confusion regarding the Reply-To handling. All locations can be seen in the images. Videos show the emails sent/received with and without Reply-To for default and custom senders. Let me know if it requires any further changes or needs any modification. Also, check the migrations to see whether they're aligned with recent updates. The screencasts are from old pr but have the same flow still valid

Event Creation

Screen.Recording.2026-01-30.at.9.51.29.PM.mov

Tickets component

Screen.Recording.2026-01-30.at.10.06.01.PM.mov

Talks component

Screen.Recording.2026-01-30.at.10.23.07.PM.MP4

Updated help texts

Event Creation Common Settings
Event Creation Common Settings
Tickets email settings Tickets mail editor
Tickets email settings Tickets mail editor
Talks settings Talks email settings
Talks settings Talks email settings
Talks mail editor
Talks settings

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The get_reply_to_address docstring describes step 3 as “Custom SMTP reply_to” and mentions SMTP context, but the implementation always uses event.mail_settings['reply_to'] regardless of SMTP flags; consider either conditioning this on custom SMTP usage or updating the docstring to accurately match the behavior.
  • The sender resolution for bulk emails in BulkReplyToMixin uses event.settings.get('mail_from'), while other mail paths rely on event.mail_settings['smtp_use_custom']/mail_from; aligning these to a single source of truth (or shared helper) would reduce the risk of divergent Reply-To behavior across email types.
  • The new organizer email / Reply-To help text string is duplicated across several forms and model fields; extracting this into a shared constant or translation key would simplify future updates and avoid inconsistencies.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `get_reply_to_address` docstring describes step 3 as “Custom SMTP reply_to” and mentions SMTP context, but the implementation always uses `event.mail_settings['reply_to']` regardless of SMTP flags; consider either conditioning this on custom SMTP usage or updating the docstring to accurately match the behavior.
- The sender resolution for bulk emails in `BulkReplyToMixin` uses `event.settings.get('mail_from')`, while other mail paths rely on `event.mail_settings['smtp_use_custom']`/`mail_from`; aligning these to a single source of truth (or shared helper) would reduce the risk of divergent Reply-To behavior across email types.
- The new organizer email / Reply-To help text string is duplicated across several forms and model fields; extracting this into a shared constant or translation key would simplify future updates and avoid inconsistencies.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes Reply-To resolution across ticketing, talk (CFP) mails, and bulk email sending by introducing a shared get_reply_to_address() helper and updating the affected send paths to use it. It also makes Event.email optional (including a migration to remove the previous placeholder) and updates UI help texts to describe the new behavior.

Changes:

  • Added get_reply_to_address() in common/mail.py and migrated ticket/talk/bulk flows to use unified precedence.
  • Made Event.email optional (model + migration) and removed reliance on legacy placeholder/default handling.
  • Updated multiple forms/settings help texts to reflect platform-vs-custom-sender Reply-To behavior.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
app/eventyay/common/mail.py Adds unified Reply-To resolver and wires it into the common mail sending task.
app/eventyay/base/services/mail.py Switches ticket/service mail header construction to use the shared Reply-To resolver.
app/eventyay/base/models/mail.py Uses shared Reply-To resolver when creating queued mails from talk templates; updates help text.
app/eventyay/plugins/sendmail/views.py Uses shared Reply-To resolver for bulk email queue creation and deduplicates logic via a mixin.
app/eventyay/base/models/submission.py Avoids sending internal submission notifications to a missing organizer email.
app/eventyay/base/models/event.py Makes organizer email optional and updates its help text.
app/eventyay/base/migrations/0021_make_event_email_optional.py Alters DB field and removes placeholder organizer emails from existing rows.
app/eventyay/base/configurations/default_setting.py Updates sender help text to explain reply behavior with custom senders.
app/eventyay/orga/forms/event.py Updates Reply-To / sender help texts in organizer mail settings form.
app/eventyay/control/forms/event.py Makes organizer email optional in control-side wizard/forms and updates help text.
app/eventyay/event/forms.py Makes organizer email optional in event creation wizard and removes strict validation.
app/eventyay/eventyay_common/forms/event.py Makes organizer email optional and updates help text.
app/eventyay/plugins/sendmail/forms.py Clarifies queued-mail Reply-To help text behavior.

Comment on lines +172 to +176
if override:
return override

if template and hasattr(template, 'reply_to') and template.reply_to:
return template.reply_to
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

override is treated as truthy (if override:). This makes it impossible to explicitly clear Reply-To by passing an empty string (e.g. bulk/manual emails setting reply_to to ''). Instead, the resolver will fall back to event defaults and re-add Reply-To. Consider treating “override provided” as override is not None and returning None/'' early so an explicit empty override suppresses Reply-To.

Copilot uses AI. Check for mistakes.
Comment on lines +181 to +182
# Only use Event.email when using default system sender
if sender_email == settings.MAIL_FROM and event.email:
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "use Event.email only with default system sender" check relies solely on sender_email == settings.MAIL_FROM. If an event uses custom SMTP but configures mail_from equal to MAIL_FROM, this will incorrectly use Event.email as Reply-To, contradicting the documented custom-SMTP behavior. Consider also checking event.mail_settings['smtp_use_custom'] (or backend type) before falling back to Event.email.

Suggested change
# Only use Event.email when using default system sender
if sender_email == settings.MAIL_FROM and event.email:
# Only use Event.email when using default system sender and no custom SMTP
use_default_sender = not event.mail_settings.get('smtp_use_custom')
if use_default_sender and sender_email == settings.MAIL_FROM and event.email:

Copilot uses AI. Check for mistakes.
Comment on lines +599 to 601
if self.event.mail_settings['mail_on_new_submission'] and self.event.email:
self.event.get_mail_template(MailTemplateRoles.NEW_SUBMISSION_INTERNAL).to_mail(
user=self.event.email,
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With Event.email now optional, this internal notification is silently skipped when mail_on_new_submission is enabled but event.email is empty. That likely breaks the expected behavior (“receive an email on new proposal”) for events that rely on mail_settings['reply_to'] or mail_from instead. Consider resolving a recipient fallback (e.g., event.email -> event.mail_settings['reply_to'] -> event.settings.mail_from) and/or raising a validation error when enabling the setting without a recipient address.

Suggested change
if self.event.mail_settings['mail_on_new_submission'] and self.event.email:
self.event.get_mail_template(MailTemplateRoles.NEW_SUBMISSION_INTERNAL).to_mail(
user=self.event.email,
recipient_email = self.event.email or self.event.mail_settings.get('reply_to') or self.event.settings.mail_from
if self.event.mail_settings['mail_on_new_submission'] and recipient_email:
self.event.get_mail_template(MailTemplateRoles.NEW_SUBMISSION_INTERNAL).to_mail(
user=recipient_email,

Copilot uses AI. Check for mistakes.
verbose_name=_('Organizer email address'),
help_text=_('Will be used as Reply-To in emails.'),
default='org@mail.com',
help_text=_("Enter an organizer email address for event-related emails. When the platform sender is used, this address will be used as the Reply-To. If left empty, emails will be sent using the platform's default email address."),
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This help text suggests that leaving the organizer email empty changes what address emails are sent from ("emails will be sent using the platform's default email address"), but this field only affects Reply-To resolution; the sender is controlled by mail_from/SMTP settings. Consider rewording to clarify that if empty, no automatic Reply-To is set and replies will go to the sender address (platform default if mail_from is not customized).

Suggested change
help_text=_("Enter an organizer email address for event-related emails. When the platform sender is used, this address will be used as the Reply-To. If left empty, emails will be sent using the platform's default email address."),
help_text=_(
"Enter an organizer email address for event-related emails. "
"If set, this address will be used as the Reply-To when the platform sender address is used. "
"If left empty, no Reply-To will be added automatically and replies will go to the sender address (platform default if not customized)."
),

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Unify contact settings and move them to Event Creation page

2 participants