This article covers form templates and user management in Localist Events Liquid templates. The content builds on template fundamentals (Article 1), form tags (Article 5), and object references to provide practical guidance for developers implementing custom event submission forms, user dashboards, and messaging interfaces.
Overview
Forms and user management in Localist represent two critical surfaces where developers customize the user experience. Event submission forms allow both public users and administrators to create and edit events. User management templates render account settings, notifications, messages, and profile information.
This article documents:
- Template variables available on form pages (event submission, user dashboards)
- Object structures for custom fields, messages, conversations, and notifications
- Practical patterns for rendering forms with custom field support
- User-facing dashboard and settings templates
Event Submission Forms
Public Event Submission Form
The public event submission form (tmpl-event-edit) provides a template for users to create or edit events in the calendar. This template has access to the event being edited (if applicable) and all available form configuration data.
Template Variables
| Variable | Type | Description |
|---|---|---|
event |
Event | The event object being edited (may be empty for new submissions) |
filters |
Hash | Available filter options by filter category |
visible_sections |
Hash | Boolean values indicating which form sections are visible (keyed by section name) |
groups |
Array | Group options formatted as label/value pairs for selection dropdowns |
departments |
Array | Department options formatted as label/value pairs (requires Departments feature) |
selected_groups |
Array of Group/Department | Groups already associated with the event |
selected_departments |
Array of Group/Department | Departments already associated with the event |
selected_tags |
Hash | Tags grouped by context, each value contains an Array of Tag objects |
visibility_options |
Array | Event visibility options (Public, Unlisted, Restricted) |
experience_options |
Array | Available experience types for the event |
status_options |
Array | Event status options (live, canceled, postponed, sold_out) |
has_zoom_integration |
String | Whether the platform’s Zoom integration feature is enabled |
zoom_status |
String | Zoom UI state for the editor: "unauthorized" (no Zoom token), "no_meeting" (token but no meeting on the event), or "meeting" (stream/meeting linked) |
limited_edit |
Boolean | True when the event is not new and its first start date is today or in the past, so certain fields are locked down |
Example: Rendering Form Sections Conditionally
{% if visible_sections.basic_info %}
<fieldset>
<legend>Event Details</legend>
<input type="text" name="event[title]" value="{{ event.title }}" />
<textarea name="event[description]">{{ event.description }}</textarea>
</fieldset>
{% endif %}
{% if visible_sections.categories %}
<fieldset>
<legend>Categories & Tags</legend>
{% for context in selected_tags %}
{% for tag in context[1] %}
<label>
<input type="checkbox" name="event[tag_ids][]" value="{{ tag.id }}" checked />
{{ tag.name }}
</label>
{% endfor %}
{% endfor %}
</fieldset>
{% endif %}
Example: Rendering Status and Visibility Options
<label>Event Status</label>
<select name="event[status]">
{% for option in status_options %}
<option value="{{ option.value }}" {% if event.status == option.value %}selected{% endif %}>
{{ option.label }}
</option>
{% endfor %}
</select>
<label>Visibility</label>
<select name="event[visibility]">
{% for option in visibility_options %}
<option value="{{ option.value }}" {% if event.visibility == option.value %}selected{% endif %}>
{{ option.label }}
</option>
{% endfor %}
</select>
Example: Conditional Zoom Integration
{% if has_zoom_integration == 'true' %}
<fieldset>
<legend>Zoom Meeting</legend>
<p>Zoom integration status: {{ zoom_status }}</p>
<label>
<input type="checkbox" name="event[zoom_enabled]" {% if event.zoom_enabled %}checked{% endif %} />
Enable Zoom meeting for this event
</label>
</fieldset>
{% endif %}
Custom Field Objects
The Custom Field Object
The Custom Field object represents a field definition (not its value). Use this object when rendering form inputs for custom fields or displaying field metadata.
Note: To access the actual value of a custom field on an object (event, user, place, group), use the Custom Fields accessor, not the Custom Field object definition.
Custom Field Attributes
| Variable | Type | Description |
|---|---|---|
name |
String | Internal name of the field |
id |
Integer | Unique identifier for the field definition |
key |
String | Machine-readable key for accessing the field value (e.g., custom_fields.my_field_key) |
label |
String | Human-readable label displayed to users |
field_type |
String | Type of field (text, textarea, dropdown, checkbox, etc.) |
required |
Boolean | Whether the field must be filled in |
public_form |
Boolean | Whether the field appears on public event submission forms |
visible_to_public |
Boolean | Whether the field value is visible to non-owners |
position |
Integer | Display order of the field in the form |
html |
Boolean | Whether HTML content is allowed in the field value |
Example: Iterating Over Custom Field Definitions
{% for field in site.public_event_custom_fields %}
<div class="form-group">
<label for="custom_field_{{ field.key }}">
{{ field.label }}
{% if field.required %}<span class="required">*</span>{% endif %}
</label>
{% if field.field_type == 'text' %}
<input
type="text"
id="custom_field_{{ field.key }}"
name="event[custom_fields][{{ field.key }}]"
{% if field.required %}required{% endif %}
/>
{% elsif field.field_type == 'textarea' %}
<textarea
id="custom_field_{{ field.key }}"
name="event[custom_fields][{{ field.key }}]"
{% if field.required %}required{% endif %}
></textarea>
{% endif %}
</div>
{% endfor %}
The Custom Fields Collection
The Custom Fields object is a dynamic collection providing access to custom field values on any object (event, user, place, group).
Use: Access individual field values by key: custom_fields.my_field_key returns the value for that field.
Example: Displaying Custom Field Values
{% if event.custom_fields.department %}
<p>Department: {{ event.custom_fields.department }}</p>
{% endif %}
{% if event.custom_fields.room_capacity %}
<p>Room Capacity: {{ event.custom_fields.room_capacity }}</p>
{% endif %}
Example: Rendering Dynamic Form Fields Based on Type
{% for field in site.public_event_custom_fields %}
{% assign field_value = event.custom_fields[field.key] %}
<div class="form-field">
<label>{{ field.label }}</label>
{% case field.field_type %}
{% when 'text' %}
<input type="text" name="event[custom_fields][{{ field.key }}]" value="{{ field_value }}" />
{% when 'textarea' %}
<textarea name="event[custom_fields][{{ field.key }}]">{{ field_value }}</textarea>
{% when 'dropdown' %}
<select name="event[custom_fields][{{ field.key }}]">
<option value="">-- Select --</option>
{% for option in field.options %}
<option value="{{ option }}" {% if field_value == option %}selected{% endif %}>
{{ option }}
</option>
{% endfor %}
</select>
{% when 'checkbox' %}
<label>
<input type="checkbox" name="event[custom_fields][{{ field.key }}]" value="yes" {% if field_value %}checked{% endif %} />
{{ field.label }}
</label>
{% endcase %}
</div>
{% endfor %}
User Dashboard and Profile Templates
Edit Profile Template
The Edit Profile template (tmpl-settings-index) allows users to modify their account information.
Template Variables
| Variable | Type | Description |
|---|---|---|
user |
User | The currently logged-in user |
the_user |
User | Alias for the user object |
Notification Settings Template
The Notification Settings template (tmpl-settings-notifications) enables users to configure how they receive notifications.
Template Variables
| Variable | Type | Description |
|---|---|---|
user |
User | The currently logged-in user |
the_user |
User | Alias for the user object |
sms_notification_options |
Array | SMS notification frequency options formatted as label/value pairs |
sms_notification_setting |
String | The user’s current SMS notification preference |
Example: SMS Notification Settings
<fieldset>
<legend>SMS Notifications</legend>
{% for option in sms_notification_options %}
<label>
<input
type="radio"
name="user[sms_frequency]"
value="{{ option.value }}"
{% if sms_notification_setting == option.value %}checked{% endif %}
/>
{{ option.label }}
</label>
{% endfor %}
</fieldset>
Privacy Settings Template
The Privacy Settings template (tmpl-settings-privacy) controls visibility of the user’s profile and activities.
Template Variables
| Variable | Type | Description |
|---|---|---|
user |
User | The currently logged-in user |
the_user |
User | Alias for the user object |
privacy_options |
Array | Privacy configuration options (e.g., private, friends-only, public) |
Calendar Digests Template
The Calendar Digests template (tmpl-settings-digests) displays and manages the user’s email digest subscriptions.
Template Variables
| Variable | Type | Description |
|---|---|---|
digest_summaries |
Array of Digest | All calendar digest configurations the user has created |
Example: Rendering Digest List
<div class="digests-list">
{% for digest in digest_summaries %}
<div class="digest-item">
<h3>{{ digest.title }}</h3>
<p>Sent: {{ digest.send_day }}</p>
<p>Status: {% if digest.enabled %}Active{% else %}Inactive{% endif %}</p>
<p>Date Range: {{ digest.date_range }}</p>
<p>Event Limit: {{ digest.limit }}</p>
</div>
{% endfor %}
</div>
Manage Friends & Places Template
The Manage Friends & Places template (tmpl-settings-manage-friends) shows a user’s social connections.
Template Variables
| Variable | Type | Description |
|---|---|---|
the_user |
User | The currently logged-in user |
current_tab |
String | Active tab (e.g., “friends”, “places”) |
friends |
Array | UserFriend or business friendable objects (varies by tab) |
The Conversation Object
The Conversation object represents a message thread between two users.
Used as: conversation (when viewing a thread), recent_conversations (array of conversation items)
Participants
| Variable | Type | Description |
|---|---|---|
user |
User | The current user in the conversation |
with |
User | The other participant (alias: along_with) |
Status
| Variable | Type | Description |
|---|---|---|
last_message_at |
DateTime | Timestamp of the most recent message |
read_at |
DateTime | When the current user last read the conversation |
unread |
Boolean | Whether the conversation has unread messages |
URLs
| Variable | Type | Description |
|---|---|---|
url |
String | Link to the conversation thread |
Example: Displaying a Conversation List
<div class="conversations">
{% for conv in recent_conversations %}
<div class="conversation-item {% if conv.unread %}unread{% endif %}">
<a href="{{ conv.url }}">
<strong>{{ conv.with.name }}</strong>
<p>{{ conv.last_message_at | date: "%b %d, %Y" }}</p>
</a>
</div>
{% endfor %}
</div>
The Message Object
The Message object represents an individual message in a conversation thread.
Used as: messages (array of message objects in conversation context)
Properties
| Variable | Type | Description |
|---|---|---|
message |
String | The message content |
from |
User | The sender of the message |
user |
User | Alias for sender |
sender |
User | Alias for sender |
sent_at |
DateTime | Timestamp when the message was sent |
read_at |
DateTime | Timestamp when the message was read (if applicable) |
is_reply |
Boolean | Whether this message is a reply to another message |
Status
| Variable | Type | Description |
|---|---|---|
unread |
Boolean | Whether the message is unread by the recipient |
deleted |
Boolean | Whether the message has been deleted |
sent |
Boolean | Whether the message was successfully sent |
received |
Boolean | Whether the message was received by the recipient |
Example: Rendering a Message Thread
<div class="conversation-thread">
{% for msg in messages reversed %}
<div class="message {% if msg.unread %}unread{% endif %} {% if msg.from.id == user.id %}sent{% else %}received{% endif %}">
<div class="message-header">
<strong>{{ msg.from.name }}</strong>
<span class="timestamp">{{ msg.sent_at | date: "%b %d at %I:%M %p" }}</span>
</div>
<div class="message-body">
{{ msg.message }}
</div>
{% if msg.unread %}
<span class="badge">New</span>
{% endif %}
</div>
{% endfor %}
</div>
The Notification Object
The Notification object represents notifications and feed items in a user’s dashboard.
Used as: messages (array), feed_item (single in dashboard context)
Properties
| Variable | Type | Description |
|---|---|---|
message |
String | The notification text |
sender |
User | The user who triggered the notification |
sender_name |
String | Display name of the sender |
sent_at |
DateTime | When the notification was generated |
sent_date |
Date | Date the notification was sent |
url |
String | Link to the related object or action |
unread |
Boolean | Whether the notification has been read |
notification_type |
Integer | Type code for the notification (e.g., friend request, message, group invite) |
Example: Rendering a Notification Feed
<div class="notifications">
{% for notification in messages %}
<div class="notification {% if notification.unread %}unread{% endif %}">
<a href="{{ notification.url }}">
<div class="notification-icon">
<img src="{{ notification.sender.photo.square_200 }}" alt="" />
</div>
<div class="notification-content">
<p>{{ notification.message }}</p>
<time>{{ notification.sent_at | date: "%b %d" }}</time>
</div>
</a>
</div>
{% endfor %}
</div>
The Request Object
The Request object represents user requests (invitations, friend requests, group membership requests). It is a union type, meaning it can represent multiple types of requests. Only attributes common across all variants are listed.
Used as: requests (array of request objects)
Note: This is a union type representing Invitation, Friend Request, or Group Membership Request. Specific attributes for each variant may be available but are not guaranteed across all request types.
Common Attributes
| Variable | Type | Description |
|---|---|---|
type |
String | Request type (e.g., “invitation”, “friend_request”, “group_membership_request”) |
message |
String | The request message or reason |
sender |
User | The user who initiated the request |
sent_at |
DateTime | When the request was sent |
accept_url |
String | URL to accept the request |
ignore_url |
String | URL to ignore/decline the request |
delete_url |
String | URL to delete the request |
Example: Rendering a Requests List
<div class="requests">
<h2>Pending Requests</h2>
{% for request in requests %}
<div class="request-item">
<div class="request-info">
<img src="{{ request.sender.photo.square_200 }}" alt="" />
<div>
<strong>{{ request.sender.name }}</strong>
<p>{{ request.type | replace: "_", " " | capitalize }}</p>
{% if request.message %}
<p class="message">{{ request.message }}</p>
{% endif %}
<small>{{ request.sent_at | date: "%b %d" }}</small>
</div>
</div>
<div class="request-actions">
<a href="{{ request.accept_url }}" class="btn btn-primary">Accept</a>
<a href="{{ request.ignore_url }}" class="btn btn-secondary">Decline</a>
</div>
</div>
{% endfor %}
</div>
The Digest Object
The Digest object represents a calendar digest (email subscription) configuration.
Used as: digest (single), digest_summaries (array)
Settings
| Variable | Type | Description |
|---|---|---|
title |
String | Name of the digest |
send_day |
String | Day(s) the digest is sent (e.g., “Monday”, “Daily”) |
enabled |
Boolean | Whether the digest is currently active |
limit |
Integer | Maximum number of events to include in each digest |
date_range |
String | Time span covered (e.g., “1 week”, “2 weeks”) |
match |
String | Filter criteria or matching rules applied to the digest |
URLs
| Variable | Type | Description |
|---|---|---|
token_unsubscribe_link |
String | URL to unsubscribe from this digest |
token_destroy_link |
String | URL to delete this digest configuration |
Example: Rendering a Digest Configuration Form
<form method="post" action="/digest/update">
<fieldset>
<legend>Digest Settings: {{ digest.title }}</legend>
<label>
Digest Title
<input type="text" name="digest[title]" value="{{ digest.title }}" />
</label>
<label>
Send Day
<input type="text" name="digest[send_day]" value="{{ digest.send_day }}" />
</label>
<label>
Date Range
<input type="text" name="digest[date_range]" value="{{ digest.date_range }}" />
</label>
<label>
Event Limit
<input type="number" name="digest[limit]" value="{{ digest.limit }}" min="1" max="50" />
</label>
<label>
<input type="checkbox" name="digest[enabled]" {% if digest.enabled %}checked{% endif %} />
Enable this digest
</label>
<button type="submit">Save Digest</button>
<a href="{{ digest.token_destroy_link }}">Delete Digest</a>
</fieldset>
</form>
The Campaign Object
The Campaign object represents an email campaign or announcement.
Used as: campaign (single context)
Details
| Variable | Type | Description |
|---|---|---|
title |
String | Campaign name or title |
subject |
String | Email subject line |
from_name |
String | Display name for the email sender |
from_address |
String | Email address the campaign is sent from |
reply_to |
String | Email address for reply-to header |
publish_target |
String | Target audience or distribution list |
allow_unsubscribe |
Boolean | Whether recipients can unsubscribe |
URLs
| Variable | Type | Description |
|---|---|---|
subscribe_url |
String | URL for users to subscribe to the campaign |
unsubscribe_url |
String | URL for users to unsubscribe from the campaign |
Common Form Patterns
Building a Custom Event Submission Form
<form method="post" action="/events" class="event-form">
<fieldset>
<legend>Event Information</legend>
<div class="form-group">
<label for="title">Event Title *</label>
<input
type="text"
id="title"
name="event[title]"
value="{{ event.title }}"
required
/>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea
id="description"
name="event[description]"
>{{ event.description }}</textarea>
</div>
</fieldset>
{% if visible_sections.custom_fields %}
<fieldset>
<legend>Additional Details</legend>
{% for field in site.public_event_custom_fields %}
{% if field.public_form %}
<div class="form-group">
<label for="cf_{{ field.key }}">
{{ field.label }}
{% if field.required %}<span class="required">*</span>{% endif %}
</label>
{% if field.field_type == 'text' %}
<input
type="text"
id="cf_{{ field.key }}"
name="event[custom_fields][{{ field.key }}]"
value="{{ event.custom_fields[field.key] }}"
{% if field.required %}required{% endif %}
/>
{% elsif field.field_type == 'textarea' %}
<textarea
id="cf_{{ field.key }}"
name="event[custom_fields][{{ field.key }}]"
{% if field.required %}required{% endif %}
>{{ event.custom_fields[field.key] }}</textarea>
{% endif %}
</div>
{% endif %}
{% endfor %}
</fieldset>
{% endif %}
<fieldset>
<legend>Settings</legend>
<div class="form-group">
<label for="status">Status</label>
<select id="status" name="event[status]">
{% for option in status_options %}
<option
value="{{ option.value }}"
{% if event.status == option.value %}selected{% endif %}
>
{{ option.label }}
</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="visibility">Visibility</label>
<select id="visibility" name="event[visibility]">
{% for option in visibility_options %}
<option
value="{{ option.value }}"
{% if event.visibility == option.value %}selected{% endif %}
>
{{ option.label }}
</option>
{% endfor %}
</select>
</div>
</fieldset>
<button type="submit">Save Event</button>
</form>
Rendering Custom Fields Dynamically
{% assign all_fields = site.public_event_custom_fields %}
<div class="custom-fields-section">
{% for field in all_fields %}
{% assign value = event.custom_fields[field.key] %}
{% assign is_required = field.required %}
{% assign is_visible = field.public_form %}
{% if is_visible %}
<div class="field-wrapper field-type-{{ field.field_type }}" data-field-id="{{ field.id }}">
<label for="field_{{ field.key }}">
{{ field.label }}
{% if is_required %}<span class="field-required" title="Required">*</span>{% endif %}
</label>
{% case field.field_type %}
{% when 'text' %}
<input
type="text"
id="field_{{ field.key }}"
class="text-input"
name="event[custom_fields][{{ field.key }}]"
value="{{ value }}"
{% if is_required %}required aria-required="true"{% endif %}
/>
{% when 'textarea' %}
<textarea
id="field_{{ field.key }}"
class="textarea-input"
name="event[custom_fields][{{ field.key }}]"
{% if is_required %}required aria-required="true"{% endif %}
>{{ value }}</textarea>
{% when 'checkbox' %}
<label class="checkbox-label">
<input
type="checkbox"
name="event[custom_fields][{{ field.key }}]"
value="true"
{% if value %}checked{% endif %}
/>
{{ field.label }}
</label>
{% endcase %}
{% if field.html %}
<small>HTML allowed in this field</small>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
Handling Form Visibility States
<form class="event-form" data-limited-edit="{{ limited_edit }}">
{% if limited_edit %}
<div class="alert alert-info">
Some fields are read-only because this event has already been published.
</div>
{% endif %}
<fieldset {% if limited_edit %}disabled{% endif %}>
<legend>Basic Event Information</legend>
<input type="text" name="event[title]" value="{{ event.title }}" />
</fieldset>
{% if visible_sections.description %}
<fieldset>
<legend>Description</legend>
<textarea name="event[description]">{{ event.description }}</textarea>
</fieldset>
{% endif %}
{% if visible_sections.zoom %}
{% if has_zoom_integration == 'true' %}
<fieldset>
<legend>Zoom Integration</legend>
<p>Current status: <strong>{{ zoom_status }}</strong></p>
</fieldset>
{% endif %}
{% endif %}
</form>
Related Articles
This article is part of an 8-part series on Localist developer theming:
- Introduction to Localist Developer Theming
- Site Configuration and Global Objects
- Events, Instances, and Related Objects
- Places, Groups, Departments, and Channels
- Custom Liquid Tags Reference
- Custom Liquid Filters Reference
- Template Variable Reference
- Forms and User Management
More Information
For additional guidance on Liquid syntax, filters, and template tags, refer to the Liquid documentation and Article 5 (Form Tags and HTML Rendering).