All skills
michtio avatar

/craft-plugins

@44a2800

Index and router for plugin-specific Craft CMS 5 guidance — configuration, Twig API, PHP API, migrations, deployment, and pitfalls for the plugins this pack documents. Triggers whenever a task names one of these plugins in ANY context (build, configure, style, render, query, import, migrate, deploy, cache, debug): Formie (forms, submissions, File Upload, form in a migration, notifications, translations), SEOmatic (meta, sitemaps, JSON-LD, SEO field), Blitz (static/page caching, purge), Feed Me (XML/JSON/CSV import), Imager-X (transforms, srcset, quick syntax, named transforms, Power Pack, pppicture, ppimg), ImageOptimize (OptimizedImages), CKEditor (rich text, nested entries), Sprig (reactive, htmx), Element API (JSON endpoints), Retour (redirects, 404s), Navigation (nav menus), Hyper (link field), Colour Swatches, Password Policy (HIBP), Typogrify, Cache Igniter, Knock Knock (staging password), Elements Panel (N+1 debug), Sherlock (security scan), Amazon SES (SES/SNS bounce), Embedded Assets (oEmbed), Timeloop (recurring dates), Vite (craft.vite.*, asset bundling), Warp (passwordless login, magic link, one-time code/OTP, passkeys, WebAuthn, craft.warp, member sessions). Also load for passwordless or magic-link auth with NO plugin named. Always load when a task names one of these plugins — read references/<plugin>.md first. Do NOT trigger for Craft core with no plugin named (craftcms), template architecture (craft-site), or content modeling (craft-content-modeling).

Use this Skill: https://skilld.dev/gh/michtio/craftcms-claude-skills/craft-plugins

This session only. Nothing lands on disk.

referencestimeloop.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Timeloop

Repeating date field by CraftPulse. Creates recurring dates (daily, weekly, monthly, yearly) without complex inputs — editors pick a frequency, cycle, and optional end date. Exposes a Twig variable API, a recurringDates() function for cross-entry queries, and full GraphQL support. CraftPulse's own plugin.

craftpulse/craft-timeloop — Free (internal)

Documentation

Source lives at ~/dev/craft-plugins/v5/craft-timeloop/src/. When in doubt, read the source directly.

Common Pitfalls

  • Not setting an end date — without loopEndDate, the plugin defaults to start + 20 years. That's 7,300+ daily entries. Always set a sane end date or use limit in getDates().
  • Expecting getUpcoming() to return an array — it returns a single DateTime|null (the next occurrence). For multiple upcoming dates, use getDates() with a limit.
  • Monthly frequency without timestring — when using monthly recurrence, set the ordinal + day (e.g., "second Tuesday") via the field UI. Without it, the plugin uses raw month arithmetic with end-of-month correction, which may not be what editors expect.
  • Using recurringDates() on large element queries — the Twig function iterates every element and computes loops per entry. Pre-filter with element criteria to avoid performance hits.
  • Forgetting .eagerly() on relation fields that carry Timeloop fields — the Timeloop field normalizes to a TimeloopModel, not a relation, so N+1 isn't the issue. But if the Timeloop field lives on a related entry type, eager-load the relation itself.
  • loopPeriod stored as JSON text — the field stores its value as a JSON string in a TEXT column. Custom queries against the raw DB value need JSON_EXTRACT or similar.

Field Configuration

The Timeloop field has one setting:

Setting Type Description
showTime boolean Enable time selection on start/end dates

Data Model

The field normalizes to a TimeloopModel with these properties:

Property Type Description
loopStartDate DateTime|null Recurrence start
loopEndDate DateTime|null Recurrence end (null = +20 years)
loopStartTime DateTime|null Start time (when showTime enabled)
loopEndTime DateTime|null End time (when showTime enabled)
loopPeriod array|null { frequency, cycle, days[], timestring }
loopReminderValue int|null Reminder offset value
loopReminderPeriod string|null Reminder offset unit (days, weeks, etc.)

Period Frequencies

Frequency code Meaning Cycle example
P1D Daily Every cycle days
P1W Weekly Every cycle weeks (with optional days[] selection)
P1M Monthly Every cycle months (with optional ordinal + day timestring)
P1Y Yearly Every cycle years

Twig API

Variable: craft.timeloop

Access via the craft.timeloop variable on any element's Timeloop field value:

{% set timeloopData = entry.myTimeloopField %}

{# Next upcoming occurrence — returns DateTime|null #}
{% set next = craft.timeloop.getUpcoming(timeloopData) %}
{% if next %}
    <time datetime="{{ next|date('c') }}">{{ next|date('d M Y') }}</time>
{% endif %}

{# Next 5 upcoming dates — returns DateTime[] #}
{% set dates = craft.timeloop.getDates(timeloopData, 5) %}
{% for date in dates %}
    <li>{{ date|date('d M Y') }}</li>
{% endfor %}

{# All dates (including past) — set futureDates to false #}
{% set allDates = craft.timeloop.getDates(timeloopData, 10, false) %}

{# Reminder date for next occurrence — returns DateTime|null #}
{% set reminder = craft.timeloop.getReminder(timeloopData) %}

{# Period model — returns PeriodModel|null #}
{% set period = craft.timeloop.period(timeloopData) %}

Variable Methods

Method Returns Description
getUpcoming(data) DateTime|null Next single upcoming occurrence
getDates(data, limit?, futureDates?) DateTime[] Up to limit dates (default 0 = no limit). futureDates default true. (The 100 default applies only to the GraphQL dates(limit:) arg.)
getReminder(data) DateTime|null Reminder date based on configured offset before next occurrence
period(data) PeriodModel|null Raw period configuration model

Model Methods (on the field value directly)

The TimeloopModel also exposes methods directly:

{% set tl = entry.myTimeloopField %}

{{ tl.getUpcoming()|date('d M Y') }}
{{ tl.getNextUpcoming()|date('d M Y') }}
{{ tl.getDates(5)|length }} upcoming dates
{{ tl.getReminder()|date('d M Y') }}
{{ tl.loopStartTime }}
{{ tl.loopEndTime }}
Method Returns Description
getUpcoming() DateTime|null First upcoming date
getNextUpcoming() DateTime|null Second upcoming date
getDates(limit?, futureDates?) DateTime[]|null Computed occurrence list (limit default 0 = no limit)
getReminder() DateTime|null Next reminder date
getPeriod() PeriodModel|null Period configuration
getLoopStartTime() string|null Formatted H:i
getLoopEndTime() string|null Formatted H:i

Function: recurringDates()

Cross-entry date query — finds recurring dates across multiple entries within a date range:

{% set events = craft.entries.section('events') %}
{% set dates = recurringDates(events, 'myTimeloopField', '2025-04-01', '2025-06-30') %}

{% for item in dates %}
    <h3>{{ item.entryTitle }} (ID: {{ item.entryId }})</h3>
    {% for date in item.dates %}
        <li>{{ date|date('d M Y') }}</li>
    {% endfor %}
{% endfor %}

Returns an array of objects: { entryId, entryTitle, dates[] }.

Performance note: This iterates every entry in the query and computes loops. Always scope the element query tightly.

GraphQL

{
  entries(section: "events") {
    ... on events_default_Entry {
      myTimeloopField {
        loopPeriod {
          frequency
          cycle
          days
          timestring {
            ordinal
            day
          }
        }
        loopStartDate
        loopStartTime
        loopEndDate
        loopEndTime
        loopReminder
        getUpcoming
        getReminder
        getDates(limit: 5, futureDates: true)
      }
    }
  }
}

Pair With

  • SEOMatic — use getUpcoming() to populate event structured data dates
  • Element API — expose recurring dates as JSON endpoints for calendar integrations
  • Formie — event registration forms linked to entries with Timeloop fields

Source: SKILL.md on GitHub

No alerts16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The 'craft-plugins' skill is a technical reference and router for Craft CMS 5 plugins. It provides guidance on configuration, Twig and PHP APIs, and best practices for popular extensions in the Craft ecosystem. The skill consists of documentation files that describe legitimate plugin behaviors and does not contain any malicious code, obfuscation, or security vulnerabilities.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

Signed by skilld at 44a2800. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated last month

README badge

README badge for michtio/craftcms-claude-skills/craft-plugins