Obviously the final Twig template is more complicated, which is the whole reason for switching to Twig. I needed to group events by their type (specifically those that were "raffles") so all raffles that occurred on the same day were listed in the same "event", but just with different times and locations. Twig lets you work with actual arrays and objects in the template, so you can manipulate the data in a more controlled fashion. The index.html.twig file is the closest translation of the template to Twig prior to adding the grouping. Twig or Blade just make it a lot easier to perform simple conditionals and set variables that can be reused without worrying about parse order issues.
Last active
April 23, 2025 13:18
-
-
Save litzinger/bdf490f600b3b4f8a46c98d6d3b60d97 to your computer and use it in GitHub Desktop.
An ExpressionEngine template converted to Twig.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends 'ee::layouts.main' %} | |
{% block contents %} | |
{% include 'ee::_partials.filters' %} | |
{% include 'ee::_partials.upcoming' %} | |
{% include 'ee::_partials.promo' %} | |
{% set now = date().timestamp %} | |
{% set groupedEvents = [] %} | |
{% for event in exp.calendar.events({ | |
channel: "events", | |
calendar_short_name: "events", | |
dynamic: "no", | |
status: "open", | |
show_recurrences: "next", | |
date_range_start: global.events_start_date, | |
date_range_end: global.events_end_date | |
}) %} | |
{% set entry = exp.channel.entries({entry_id: event.entry_id}).first %} | |
{% set categoryUrlTitles = entry.categories.pluck('cat_url_title') %} | |
{% set key = event.event_start_date_timestamp|split('.')|first|date('Y-m-d') %} | |
{% if 'raffle' in categoryUrlTitles %} | |
{% set key = key ~ '-raffle' %} | |
{% else %} | |
{% set key = key ~ '-' ~ event.entry_id %} | |
{% endif %} | |
{% set current = groupedEvents[key]|default([]) %} | |
{% set current = current|merge([event]) %} | |
{% set groupedEvents = groupedEvents|merge({ (key): current }) %} | |
{% endfor %} | |
<ul role="list" class="space-y-4"> | |
{% for key, eventCollection in groupedEvents %} | |
{% set event = eventCollection|first %} | |
{% set startDate = event.event_start_date_timestamp|split('.')|first %} | |
{% set endDate = event.event_end_date_timestamp|split('.')|first %} | |
{% set startsSoon = ((now > startDate - global.events_starting_soon and now < startDate) or now > startDate and now < endDate) %} | |
{% set titleColor = startsSoon ? 'bg-green-600' : 'bg-sky-600' %} | |
{% set timeColor = startsSoon ? 'bg-green-500' : 'bg-sky-400' %} | |
{# Get the full entry b/c Calendar does not have access to everything, especially in Twig #} | |
{% set entry = exp.channel.entries({entry_id: event.entry_id}).first %} | |
{% set categoryUrlTitles = entry.categories.pluck('cat_url_title') %} | |
{% set hasMultipleInstances = eventCollection|length > 1 %} | |
{% if event.location_address %} | |
{% set location = event.location_address ~ ' ' ~ event.location_address2 %} | |
{% set locationUrl = 'https://www.google.com/maps?q=' ~ location %} | |
{% elseif event.latitude and event.longitude %} | |
{% set location = event.latitude ~ '+' ~ event.longitude %} | |
{% set locationUrl = 'https://www.google.com/maps?z=12&t=m&q=loc:' ~ location %} | |
{% else %} | |
{% set location = '' %} | |
{% set locationUrl = '' %} | |
{% endif %} | |
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow-md {{ categoryUrlTitles|join(' ') }}" id="event_{{ event.entry_id }}"> | |
<div class="event-title -mt-px flex items-stretch text-white rounded-t-lg {{ titleColor }}"> | |
<h3 class="flex flex-col justify-center py-2 px-4 divide-y divide-white/25 {{ timeColor }} rounded-tl-lg"> | |
<span class="block text-center"> | |
{% if (event.event_duration_days > 1) %} | |
{{ startDate|date('D') }}—{{ endDate|date('D') }} | |
{% else %} | |
{{ startDate|date('D') }} | |
{% endif %} | |
</span> | |
<span class="block text-xl text-center"> | |
{% if (event.event_duration_days > 1) %} | |
{{ startDate|date('M j') }}—{{ endDate|date('j') }} | |
{% else %} | |
{{ startDate|date('M j') }}<span class="text-sm">{{ startDate|date('S') }}</span> | |
{% endif %} | |
</span> | |
</h3> | |
<h2 class="flex flex-1 items-center p-4 text-xl font-medium tracking-wide"> | |
{{ event.title }} | |
</h2> | |
</div> | |
<div class="event-time justify-between p-4 relative w-full"> | |
{% include 'ee::_partials.event_image' with { | |
'coverPhoto': entry.cover_photo.url, | |
'useCoverPhoto': event.use_photo, | |
'categoryUrlTitles': categoryUrlTitles | |
} %} | |
<div class="flex-1 divide-y divide-gray-200 z-10 relative"> | |
{% for eventInstance in eventCollection %} | |
{% if eventInstance.location_address %} | |
{% set location = eventInstance.location_address ~ ' ' ~ eventInstance.location_address2 %} | |
{% set locationUrl = 'https://www.google.com/maps?q=' ~ location %} | |
{% elseif eventInstance.latitude and eventInstance.longitude %} | |
{% set location = eventInstance.latitude ~ '+' ~ eventInstance.longitude %} | |
{% set locationUrl = 'https://www.google.com/maps?z=12&t=m&q=loc:' ~ location %} | |
{% else %} | |
{% set location = '' %} | |
{% set locationUrl = '' %} | |
{% endif %} | |
<div {% if hasMultipleInstances %}class="py-4"{% endif %}> | |
{% set startDate = eventInstance.event_start_date_timestamp|split('.')|first %} | |
{% set endDate = eventInstance.event_end_date_timestamp|split('.')|first %} | |
{% if loop.first %} | |
<div> | |
{% if now > startDate and now < endDate %} | |
<span class="inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset">Happening Now!</span> | |
{% endif %} | |
{% if now > startDate - global.events_starting_soon and now < startDate %} | |
<span class="inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset">Starts Soon</span> | |
{% endif %} | |
</div> | |
{% endif %} | |
<p class="mt-1 text-gray-500"> | |
{% if eventInstance.event_duratin_days >= 1 %} | |
All day <span class="text-gray-400">(view event page for details)</span> | |
{% else %} | |
{{ startDate|date('g:ia') }} | |
{% if eventInstance.event_end_date and eventInstance.show_end_time %} | |
—{{ endDate|date('g:ia') }} | |
{% endif %} | |
{% endif %} | |
</p> | |
{% if eventInstance.location_name or eventInstance.location_owner %} | |
<div class="mt-2"> | |
{% if locationUrl %} | |
<a class="js-map-address" href="{{ locationUrl }}" data-address="{{ location }}" target="_blank" rel="noopener noreferrer"> | |
{% endif %} | |
<b> | |
{% if eventInstance.location_name %} | |
{{ eventInstance.location_name|raw }} | |
{% elseif eventInstance.lcoation_owner %} | |
{{ eventInstance.location_owner|raw }} | |
{% endif %} | |
</b> | |
{% if locationUrl %} | |
</a> | |
{% endif %} | |
</div> | |
{% endif %} | |
</div> | |
{% endfor %} | |
</div> | |
</div> | |
{% if (event.description) %} | |
<div class="event-description p-6 text-sm leading-6 wysiwyg-content"> | |
{{ event.description|raw }} | |
</div> | |
{% endif %} | |
{% if hasMultipleInstances == false and (event.event_url or event.location_address) or (event.latitude and event.longitude) %} | |
<div class="event-actions -mt-px flex divide-x divide-gray-200 p-4"> | |
<div class="flex w-0 flex-1"> | |
<span class="relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-transparent text-sm font-semibold text-gray-900"> | |
{% if event.event_url %} | |
<a type="button" href="{{ event.event_url }}" class="external-link rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" target="_blank" rel="noopener noreferrer"> | |
View Event Page | |
</a> | |
{% elseif event.source_url %} | |
<a type="button" href="{{ event.source_url }}" class="external-link rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" target="_blank" rel="noopener noreferrer"> | |
View Event Page | |
</a> | |
{% endif %} | |
{% if locationUrl %} | |
<a class="js-map-address rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="{{ locationUrl }}" data-address="{{ location }}" target="_blank" rel="noopener noreferrer"> | |
Open Maps | |
</a> | |
{% endif %} | |
</span> | |
</div> | |
</div> | |
{% endif %} | |
</li> | |
{% endfor %} | |
</ul> | |
<div class="text-center p-6"> | |
<a class="rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="/{{ global.events_next_range_url_segments }}">View {{ global.events_next_range_label }}</a> | |
</div> | |
{% endblock %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{layout="layouts/main"} | |
{filters} | |
{upcoming} | |
{promo} | |
<ul role="list" class="space-y-4"> | |
{exp:calendar:events | |
channel="events" | |
calendar_short_name="events" | |
dynamic="no" | |
status="open" | |
show_recurrences="next" | |
date_range_start="{events:start_date}" | |
date_range_end="{events:end_date}" | |
} | |
{if no_results} | |
<li class="rounded-lg bg-white shadow-md p-6">No events found.</li> | |
{/if} | |
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow-md {calendar:categories}{calendar:category_url_title} {/calendar:categories}" id="event_{calendar:entry_id}"> | |
<div class="event-title -mt-px flex items-stretch text-white rounded-t-lg {if (current_time > calendar:event_start_date_timestamp - events:starting_soon && current_time < calendar:event_start_date_timestamp) || (current_time > calendar:event_start_date_timestamp && current_time < calendar:event_end_date_timestamp)}bg-green-600{if:else}bg-sky-600{/if}"> | |
<h3 class="flex flex-col justify-center py-2 px-4 divide-y divide-white/25 {if (current_time > calendar:event_start_date_timestamp - events:starting_soon && current_time < calendar:event_start_date_timestamp) || (current_time > calendar:event_start_date_timestamp && current_time < calendar:event_end_date_timestamp)}bg-green-500{if:else}bg-sky-400{/if} rounded-tl-lg"> | |
<span class="block text-center"> | |
{if calendar:event_duration_days > 1} | |
{calendar:event_start_date format="%D"}—{calendar:event_end_date format="%D"} | |
{if:else} | |
{calendar:event_start_date format="%D"} | |
{/if} | |
</span> | |
<span class="block text-xl text-center"> | |
{if calendar:event_duration_days > 1} | |
{calendar:event_start_date format="%M %j"}—{calendar:event_end_date format="%j"} | |
{if:else} | |
{calendar:event_start_date format="%M %j"}<span class="text-sm">{calendar:event_start_date format="%S"}</span> | |
{/if} | |
</span> | |
</h3> | |
<h2 class="flex flex-1 items-center p-4 text-xl font-medium tracking-wide">{calendar:title}</h2> | |
</div> | |
<div class="event-time flex flex-row w-full items-start justify-between p-4"> | |
<div class="flex-1"> | |
<div class=""> | |
{if current_time > calendar:event_start_date_timestamp && current_time < calendar:event_end_date_timestamp} | |
<span class="inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset">Happening Now!</span> | |
{/if} | |
{if current_time > calendar:event_start_date_timestamp - events:starting_soon && current_time < calendar:event_start_date_timestamp} | |
<span class="inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset">Starts Soon</span> | |
{/if} | |
</div> | |
<p class="mt-1 text-gray-500"> | |
{if calendar:event_duration_days >= 1} | |
All day <span class="text-gray-400">(view event page for details)</span> | |
{if:else} | |
{calendar:event_start_date format="%g:%i%a"}{if calendar:event_end_date && calendar:show_end_time}—{calendar:event_end_date format="%g:%i%a"} {/if} | |
{/if} | |
</p> | |
{if calendar:location_name || calendar:location_owner} | |
<div class="mt-2"> | |
<b> | |
{if calendar:location_name} | |
{calendar:location_name} | |
{if:elseif calendar:location_owner} | |
{calendar:location_owner} | |
{/if} | |
</b> | |
</div> | |
{/if} | |
</div> | |
{event_image} | |
</div> | |
{if calendar:description} | |
<div class="event-description p-6 text-sm leading-6 wysiwyg-content"> | |
{calendar:description} | |
</div> | |
{/if} | |
{if calendar:event_url || calendar:location_address || (calendar:latitude && calendar:longitude)} | |
<div class="event-actions -mt-px flex divide-x divide-gray-200 p-4"> | |
<div class="flex w-0 flex-1"> | |
<span class="relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-transparent text-sm font-semibold text-gray-900"> | |
{if calendar:event_url} | |
<a type="button" href="{calendar:event_url}" class="external-link rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" target="_blank" rel="noopener noreferrer"> | |
View Event Page | |
</a> | |
{if:elseif calendar:source_url} | |
<a type="button" href="{calendar:source_url}" class="external-link rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" target="_blank" rel="noopener noreferrer"> | |
View Event Page | |
</a> | |
{/if} | |
{if calendar:location_address} | |
<a class="js-map-address rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="https://www.google.com/maps?q={calendar:location_address}{calendar:location_address_2}" data-address="{calendar:location_address}" target="_blank" rel="noopener noreferrer"> | |
View Location | |
</a> | |
{if:elseif calendar:latitude && calendar:longitude} | |
<a class="js-map-address rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="https://www.google.com/maps?z=12&t=m&q=loc:{calendar:latitude}+{calendar:longitude}" data-address="loc:{calendar:latitude}+{calendar:longitude}" target="_blank" rel="noopener noreferrer"> | |
View Location | |
</a> | |
{/if} | |
</span> | |
</div> | |
</div> | |
{/if} | |
</li> | |
{/exp:calendar:events} | |
</ul> | |
<div class="text-center p-6"> | |
<a class="rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="{site_url}{events:next_range_url_segments}">View {events:next_range_label}</a> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends 'ee::layouts.main' %} | |
{% block contents %} | |
{% include 'ee::_partials.filters' %} | |
{% include 'ee::_partials.upcoming' %} | |
{% include 'ee::_partials.promo' %} | |
{% set now = date().timestamp %} | |
<ul role="list" class="space-y-4"> | |
{% for event in exp.calendar.events({ | |
channel: "events", | |
calendar_short_name: "events", | |
dynamic: "no", | |
status: "open", | |
show_recurrences: "next", | |
date_range_start: global.events_start_date, | |
date_range_end: global.events_end_date | |
}) %} | |
{% set startDate = event.event_start_date_timestamp|split('.')|first %} | |
{% set endDate = event.event_end_date_timestamp|split('.')|first %} | |
{% set startsSoon = ((now > startDate - global.events_starting_soon and now < startDate) or now > startDate and now < endDate) %} | |
{% set titleColor = startsSoon ? 'bg-green-600' : 'bg-sky-600' %} | |
{% set timeColor = startsSoon ? 'bg-green-500' : 'bg-sky-400' %} | |
{# Get the full entry b/c Calendar does not have access to everything, especially in Twig #} | |
{% set entry = exp.channel.entries({entry_id: event.entry_id}).first %} | |
{% set categoryUrlTitles = entry.categories.pluck('cat_url_title') %} | |
<li class="col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow-md {{ categoryUrlTitles|join(' ') }}" id="event_{{ event.entry_id }}"> | |
<div class="event-title -mt-px flex items-stretch text-white rounded-t-lg {{ titleColor }}"> | |
<h3 class="flex flex-col justify-center py-2 px-4 divide-y divide-white/25 {{ timeColor }} rounded-tl-lg"> | |
<span class="block text-center"> | |
{% if (event.event_duration_days > 1) %} | |
{{ startDate|date('D') }}—{{ endDate|date('D') }} | |
{% else %} | |
{{ startDate|date('D') }} | |
{% endif %} | |
</span> | |
<span class="block text-xl text-center"> | |
{% if (event.event_duration_days > 1) %} | |
{{ startDate|date('M j') }}—{{ endDate|date('j') }} | |
{% else %} | |
{{ startDate|date('M j') }}<span class="text-sm">{{ startDate|date('S') }}</span> | |
{% endif %} | |
</span> | |
</h3> | |
<h2 class="flex flex-1 items-center p-4 text-xl font-medium tracking-wide">{{ event.title }}</h2> | |
</div> | |
<div class="event-time flex flex-row w-full items-start justify-between p-4"> | |
<div class="flex-1"> | |
<div class=""> | |
{% if now > startDate and now < endDate %} | |
<span class="inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset">Happening Now!</span> | |
{% endif %} | |
{% if now > startDate - global.events_starting_soon and now < startDate %} | |
<span class="inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset">Starts Soon</span> | |
{% endif %} | |
</div> | |
<p class="mt-1 text-gray-500"> | |
{% if event.event_duratin_days >= 1 %} | |
All day <span class="text-gray-400">(view event page for details)</span> | |
{% else %} | |
{{ startDate|date('g:ia') }} | |
{% if event.event_end_date and event.show_end_time %} | |
—{{ endDate|date('g:ia') }} | |
{% endif %} | |
{% endif %} | |
</p> | |
{% if event.location_name or event.location_owner %} | |
<div class="mt-2"> | |
<b> | |
{% if event.location_name %} | |
{{ event.location_name|raw }} | |
{% elseif event.lcoation_owner %} | |
{{ event.location_owner|raw }} | |
{% endif %} | |
</b> | |
</div> | |
{% endif %} | |
</div> | |
{% include 'ee::_partials.event_image' with { | |
'coverPhoto': entry.cover_photo.url, | |
'useCoverPhoto': event.use_photo, | |
'categoryUrlTitles': categoryUrlTitles | |
} %} | |
</div> | |
{% if (event.description) %} | |
<div class="event-description p-6 text-sm leading-6 wysiwyg-content"> | |
{{ event.description|raw }} | |
</div> | |
{% endif %} | |
{% if event.event_url or event.location_address or (event.latitude and event.longitude) %} | |
<div class="event-actions -mt-px flex divide-x divide-gray-200 p-4"> | |
<div class="flex w-0 flex-1"> | |
<span class="relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-transparent text-sm font-semibold text-gray-900"> | |
{% if event.event_url %} | |
<a type="button" href="{{ event.event_url }}" class="external-link rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" target="_blank" rel="noopener noreferrer"> | |
View Event Page | |
</a> | |
{% elseif event.source_url %} | |
<a type="button" href="{{ event.source_url }}" class="external-link rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" target="_blank" rel="noopener noreferrer"> | |
View Event Page | |
</a> | |
{% endif %} | |
{# there is actually an error here, it still had EE tags for the location variables which was fixed in the final version above #} | |
{% if event.location_address %} | |
<a class="js-map-address rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="https://www.google.com/maps?q={calendar:location_address}{calendar:location_address_2}" data-address="{calendar:location_address}" target="_blank" rel="noopener noreferrer"> | |
View Location | |
</a> | |
{% elseif event.latitude and event.longitude %} | |
<a class="js-map-address rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="https://www.google.com/maps?z=12&t=m&q=loc:{calendar:latitude}+{calendar:longitude}" data-address="loc:{calendar:latitude}+{calendar:longitude}" target="_blank" rel="noopener noreferrer"> | |
View Location | |
</a> | |
{% endif %} | |
</span> | |
</div> | |
</div> | |
{% endif %} | |
</li> | |
{% endfor %} | |
</ul> | |
<div class="text-center p-6"> | |
<a class="rounded-md bg-gray-200 px-3 py-2 text-sm font-semibold text-gray-900 ring-1 shadow-xs ring-gray-300 ring-inset hover:bg-gray-50" href="/{{ global.events_next_range_url_segments }}">View {{ global.events_next_range_label }}</a> | |
</div> | |
{% endblock %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment