nextcloud / nextcloud/server

[Bug]: Event format description is lost in emails

Open
#58,749 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

1. to develop 32-feedback bug feature: caldav
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

⚠️ This issue respects the following points: ⚠️
Bug description

When creating a calendar event with a formatted description and inviting attendees, the invitation email sent by Nextcloud displays the description as a single unformatted block of text. All formatting — including line breaks — is lost.
The description is stored correctly in the ICS data with proper \n characters (RFC 5545 compliant). The problem is in the HTML email generation: the raw plain text string is inserted directly into the HTML template without any conversion.
This is particularly relevant because widely used platforms like Microsoft 365 / Outlook Web display the invitation email instead of the calendar event description in the event detail view. While this is not a Nextcloud issue per se, it means that for a large number of attendees the invitation email is the only place where the description is visible — making correct formatting in the email essential.

Steps to reproduce

Create a calendar event with a multi-line description (using Enter/line breaks)
Add an external attendee (email address)
Save the event
Attendee receives the invitation email
The description in the email body is displayed as a single continuous block of text

Expected behavior

The description in the invitation email should reflect the formatting as entered by the user. At minimum, line breaks should be preserved. Ideally, the full rich text formatting composed in the Nextcloud Calendar editor should be rendered.

Nextcloud Server version

32

Operating system

Debian/Ubuntu

PHP engine version

PHP 8.3

Web server

Apache (supported)

Database engine version

PostgreSQL

Is this bug present after an update or on a fresh install?

Upgraded to a MAJOR version (ex. 31 to 32)

Are you using the Nextcloud Server Encryption module?

None

What user-backends are you using?
  • Default user-backend (database)
  • LDAP/ Active Directory
  • SSO - SAML
  • Other
Configuration report
{
    "system": {
        "one-click-instance": true,
        "one-click-instance.user-limit": 100,
        "memcache.local": "\\OC\\Memcache\\APCu",
        "apps_paths": [
            {
                "path": "\/var\/www\/html\/apps",
                "url": "\/apps",
                "writable": false
            },
            {
                "path": "\/var\/www\/html\/custom_apps",
                "url": "\/custom_apps",
                "writable": true
            }
        ],
        "check_data_directory_permissions": false,
        "memcache.distributed": "\\OC\\Memcache\\Redis",
        "memcache.locking": "\\OC\\Memcache\\Redis",
        "redis": {
            "host": "***REMOVED SENSITIVE VALUE***",
            "password": "***REMOVED SENSITIVE VALUE***",
            "port": 6379
        },
        "overwritehost": "***",
        "overwriteprotocol": "https",
        "passwordsalt": "***REMOVED SENSITIVE VALUE***",
        "secret": "***REMOVED SENSITIVE VALUE***",
        "trusted_domains": [
            "localhost",
            "***"
        ],
        "datadirectory": "***REMOVED SENSITIVE VALUE***",
        "dbtype": "pgsql",
        "version": "32.0.6.1",
        "overwrite.cli.url": "https:\/\/***",
        "dbname": "***REMOVED SENSITIVE VALUE***",
        "dbhost": "***REMOVED SENSITIVE VALUE***",
        "dbport": "",
        "dbtableprefix": "oc_",
        "dbuser": "***REMOVED SENSITIVE VALUE***",
        "dbpassword": "***REMOVED SENSITIVE VALUE***",
        "installed": true,
        "instanceid": "***REMOVED SENSITIVE VALUE***",
        "maintenance": false,
        "loglevel": 2,
        "log_type": "file",
        "logfile": "\/var\/www\/html\/data\/nextcloud.log",
        "log_rotate_size": 10485760,
        "log.condition": {
            "apps": [
                "admin_audit"
            ]
        },
        "preview_max_x": 2048,
        "preview_max_y": 2048,
        "jpeg_quality": 60,
        "enabledPreviewProviders": {
            "1": "OC\\Preview\\Image",
            "2": "OC\\Preview\\MarkDown",
            "3": "OC\\Preview\\MP3",
            "4": "OC\\Preview\\TXT",
            "5": "OC\\Preview\\OpenDocument",
            "6": "OC\\Preview\\Movie",
            "7": "OC\\Preview\\Krita",
            "0": "OC\\Preview\\Imaginary",
            "23": "OC\\Preview\\ImaginaryPDF"
        },
        "enable_previews": true,
        "upgrade.disable-web": true,
        "mail_smtpmode": "smtp",
        "trashbin_retention_obligation": "auto, 30",
        "versions_retention_obligation": "auto, 30",
        "activity_expire_days": 30,
        "simpleSignUpLink.shown": false,
        "share_folder": "\/Shared",
        "one-click-instance.link": "https:\/\/nextcloud.com\/all-in-one\/",
        "upgrade.cli-upgrade-link": "https:\/\/github.com\/nextcloud\/all-in-one\/discussions\/2726",
        "updatedirectory": "\/nc-updater",
        "maintenance_window_start": 100,
        "allow_local_remote_servers": true,
        "davstorage.request_timeout": 3600,
        "documentation_url.server_logs": "https:\/\/github.com\/nextcloud\/all-in-one\/discussions\/5425",
        "htaccess.RewriteBase": "\/",
        "dbpersistent": false,
        "auth.bruteforce.protection.enabled": true,
        "ratelimit.protection.enabled": true,
        "files_external_allow_create_new_local": false,
        "trusted_proxies": "***REMOVED SENSITIVE VALUE***",
        "preview_imaginary_url": "***REMOVED SENSITIVE VALUE***",
        "preview_imaginary_key": "***REMOVED SENSITIVE VALUE***",
        "mail_from_address": "***REMOVED SENSITIVE VALUE***",
        "mail_sendmailmode": "smtp",
        "mail_domain": "***REMOVED SENSITIVE VALUE***",
        "mail_smtphost": "***REMOVED SENSITIVE VALUE***",
        "mail_smtpport": "25",
        "same_site_cookies": "lax",
        "updatechecker": false,
        "DOMAIN": "***",
        "AIO_VERSION": "v12.8.0"
    }
}
List of activated Apps
Enabled:
  - activity: 5.0.0
  - admin_audit: 1.22.0
  - appointments: 2.6.3
  - bruteforcesettings: 5.0.0
  - calendar: 6.2.1
  - circles: 32.0.0
  - cloud_federation_api: 1.16.0
  - comments: 1.22.0
  - contacts: 8.3.4
  - contactsinteraction: 1.13.1
  - dashboard: 7.12.0
  - dav: 1.34.2
  - deck: 1.16.3
  - federatedfilesharing: 1.22.0
  - federation: 1.22.0
  - files: 2.4.0
  - files_antivirus: 6.1.0
  - files_downloadlimit: 5.0.0-dev.0
  - files_pdfviewer: 5.0.0
  - files_reminders: 1.5.0
  - files_sharing: 1.24.1
  - files_trashbin: 1.22.0
  - files_versions: 1.25.0
  - firstrunwizard: 5.0.0
  - logreader: 5.0.0
  - lookup_server_connector: 1.20.0
  - nextcloud-aio: 0.8.0
  - nextcloud_announcements: 4.0.0
  - notifications: 5.0.0
  - notify_push: 1.3.0
  - oauth2: 1.20.0
  - password_policy: 4.0.0
  - photos: 5.0.0
  - privacy: 4.0.0
  - profile: 1.1.0
  - provisioning_api: 1.22.0
  - recommendations: 5.0.0
  - related_resources: 3.0.0
  - serverinfo: 4.0.0
  - settings: 1.15.1
  - sharebymail: 1.22.0
  - support: 4.0.0
  - survey_client: 4.0.0
  - systemtags: 1.22.0
  - tasks: 0.17.1
  - text: 6.0.1
  - theming: 2.7.0
  - twofactor_backupcodes: 1.21.0
  - twofactor_totp: 14.0.0
  - updatenotification: 1.22.0
  - user_status: 1.12.0
  - viewer: 5.0.0
  - weather_status: 1.12.0
  - webhook_listeners: 1.3.0
  - workflowengine: 2.14.0
Disabled:
  - app_api: 32.0.0 (installed 5.0.2)
  - encryption: 2.20.0
  - files_external: 1.24.1
  - files_mindmap: 0.0.33 (installed 0.0.33)
  - notes: 4.13.0 (installed 4.13.0)
  - suspicious_login: 10.0.0
  - twofactor_nextcloud_notification: 6.0.0
  - user_ldap: 1.23.0
  - webhooks: 0.4.3 (installed 0.4.3)
Nextcloud Signing status
Technical information
=====================
The following list covers which files have failed the integrity check. Please read
the previous linked documentation to learn more about the errors and how to fix
them.

Results
=======
- files_mindmap
	- EXCEPTION
		- OC\IntegrityCheck\Exceptions\InvalidSignatureException
		- Certificate is not valid.

Raw output
==========
Array
(
    [files_mindmap] => Array
        (
            [EXCEPTION] => Array
                (
                    [class] => OC\IntegrityCheck\Exceptions\InvalidSignatureException
                    [message] => Certificate is not valid.
                )

        )

)
Nextcloud Logs
not relevant
Additional info

Actual behavior
All formatting is lost. The description is displayed as one continuous block of text without any line breaks or structure.
Screenshots
Screenshots attached showing the same event as seen by different clients:

Thunderbird (event organizer view): description correctly formatted as entered
Gmail (external attendee): description correctly formatted in the calendar event view
Outlook Web / Microsoft 365 (external attendee): the invitation email is displayed instead of the calendar event description — and in the email the description appears as a single unformatted block of text

Root cause
In apps/dav/lib/CalDAV/Schedule/IMipService.php, line 781:
php$data['meeting_description_html'] = $newDescription !== '' ? sprintf($strikethrough, $newDescription) : '';
The $newDescription variable contains raw \n characters from the ICS DESCRIPTION property, but no conversion to HTML is performed before insertion into the HTML template.
Proposed fix
Minimum fix: preserve line breaks
php$data['meeting_description_html'] = $newDescription !== '' ? sprintf($strikethrough, nl2br(htmlspecialchars($newDescription))) : '';
Discussion: full rich text rendering
Beyond preserving line breaks, it is worth discussing whether the full formatting should be rendered. The Nextcloud Calendar rich text editor allows users to compose structured descriptions with paragraphs, bullet points and links. This content is stored internally as HTML/rich text by the Calendar app, but reduced to plain text in the ICS DESCRIPTION property.
A more complete solution could:

Render the full rich text / HTML content stored internally by the Calendar app in the invitation email
Or at minimum convert \n to
so that the basic structure entered by the user is preserved for attendees

Image Image Image

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in apps/dav/lib/CalDAV/Schedule/IMipService.php around line 781, where meeting_description_html is assembled from $newDescription. Check the surrounding invitation-email path and verify that a multiline description preserves line breaks while remaining safely escaped; done means the generated email no longer collapses the description into one block.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.