This document describes how an event's details page is rendered, how to replace it with a custom page, and how to list the files attached to an event (for example PDFs) on that page.
Every event is served at its permanent URL /events/<title-slug>-e<id> (for example /events/general-assembly-e2). The URL is the same regardless of which page renders it; replacing the page never changes the URL.
When an event is requested the platform:
PostTime in the future) or has expired (ExpirationTime passed). Admins can preview with ?preview=true.EventDetailsPageId.
Event.Details.View, which renders the partial view Event.Details) is used.The built-in Event.Details partial view renders title, description, date and time, location, link, attached files, ticket price and cover image. The attached files appear as a "Vedhæftede filer" row with one link per file in FileRelations, and the row is omitted when the event has none. There is no setting that turns it on or off. The only per-resource configuration the view reads is the cover image size (cover_image_width, cover_image_height, cover_image_stretchmode).
A page that renders Event.Details itself must pass an event loaded with enough depth to include FileRelations (see below); otherwise the row is not shown.
Use a custom details page when the markup or layout of the attachments must differ from the built-in row.
Optionally set EventBrowsePageId the same way to replace the event overview page.
The router puts the event id in the page's global parameters under the key event_id. It is available as a variable in the page script (the aliases eId and event_id are both accepted in URLs). Load the event with the query provider, using a depth that includes the file relations:
{%
var event = QueryProvider.Single type="Event" id=event_id depth=6 cache=true;
if (event == null) {
respond code=404;
}
%}
This is the same call the built-in view uses. Without a sufficient depth the FileRelations list is not populated.
Event.FileRelations is a list of EntityFile objects, one per file attached to the event in the admin ("Vedhæftede filer").
| Expression | Meaning |
|---|---|
event.FileRelations |
The attached files. Empty when nothing is attached. |
rel.FileId |
ID of the file. Use it to build the download URL. |
rel.File |
A FileItem describing the file. |
rel.File.FileContentType |
MIME type, for example application/pdf. |
rel.File.Metadata["title"] |
File name as shown in the file archive. |
rel.File.Metadata["description"] |
Description entered on the file (may be empty). |
rel.File.Metadata["extension"] |
File extension. |
rel.File.Metadata["size"] |
Size in bytes. |
rel.File.Metadata["last_modified"] |
Last modified time. |
rel.File.Metadata["alternative_text"] |
Alternative text (mainly for images). |
rel.File.FileGuid |
GUID of the file, usable in the guid file URL. |
Because the list is read when the page is rendered, files that are added to or removed from the event in the admin appear and disappear without changing the page. Event and page queries are cached, so a change can take a few minutes to show on the public site.
Use the Url tag with the File route:
{%Url File id=rel.FileId filename=rel.File.Metadata['title']%}
This produces /File/<id>/<filename>. Add download=true to force a download instead of opening the file in the browser:
{%Url File id=rel.FileId filename=rel.File.Metadata['title'] download=true%}
Use AbsoluteUrl instead of Url when a full URL including the domain is needed.
Place this in a custom details page where the files should appear. The built-in view already contains an equivalent row. It renders nothing when the event has no attachments.
{%
var event = QueryProvider.Single type="Event" id=event_id depth=6 cache=true;
if (event == null) {
respond code=404;
}
%}
<h1>{%binding event.Title%}</h1>
<div class="description">{%binding event.Description%}</div>
{%if event.FileRelations.Count%}
<div class="attached-files">
<h2>Documents</h2>
<ul>
{%foreach rel in event.FileRelations%}
<li>
<a href="{%Url File id=rel.FileId filename=rel.File.Metadata['title']%}" target="_blank" rel="noopener">
{%binding rel.File.Metadata["description"] || rel.File.Metadata["title"] || "Attached file"%}
</a>
<span class="meta">{%binding rel.File.Metadata["extension"]%}</span>
</li>
{%endforeach%}
</ul>
</div>
{%endif%}
The same pattern is used for attachments on blog posts (BlogPostDetails.aspx in the Blogs module).
Whether a visitor without login can open an attached file is decided by the file itself, not by the event. Attaching a file to a published event does not make it public, and hiding the link does not protect it: anyone who has the URL gets the same answer as any other visitor.
For each request the file service checks, in this order:
IsPublic on the file ("Offentlig" in the file archive). When it is set, true allows everyone and false restricts the file.IsPublic on the folder. When the file has no value of its own, the nearest ancestor folder with a value decides.IsGuidRequiredForLookup on the file means the file can only be opened with the guid URL form (/File/<guid>/<filename>), not the numeric id URL.A visitor who is not allowed to open a restricted file is redirected to the protected file route, which requires login. A logged-in user who is not allowed gets 403.
To make one attached file open for anonymous visitors without making other files public:
IsPublic) to true on that file only, and leave the folder untouched. A file-level value overrides the folder.To check a file, open its URL in a private browser window. Responses to the file URL are cached for ten minutes, so a changed setting can take a short while to apply.
The Platform → Ressourcer list (platform resources such as Event.Details and Event.Details.View) requires the Unrestricted access level, which is the super-user level. A 403 there is expected for other users. The resources are embedded in the modules and cannot be created or deleted there; only their configuration and metadata can be edited. The custom page approach above does not need access to them.