Upcoming Calendar Events
WordPress plugin: display upcoming Google Calendar events via shortcode + AJAX, with transient caching
by Shivam · github.com/shivamtechie/xtendify-assignment · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/shivamtechie/xtendify-assignment/archive/refs/heads/main.zipReadme
Upcoming Calendar Events
A small WordPress plugin that displays upcoming events from a Google Calendar
on the front-end of your site via the [upcoming_events] shortcode. Events are
fetched through the Google Calendar API, cached with WordPress transients, and
rendered on the page with AJAX.
Features
- Settings page under Settings → Calendar Events (Google API key, cache duration, default event count) with a Save and validate button that performs a live test call against the API key.
[upcoming_events]shortcode with attributes for count, look-ahead window, descriptions and the target calendar.- Transient caching to stay well under Google's rate limits. Cache is automatically invalidated when settings are saved, and can be cleared manually with the Refresh cache button.
- AJAX rendering so page/full-page caches never serve stale event markup.
- Graceful failure: network errors, malformed responses, an empty calendar or a missing API key never break the page.
- OOP, namespaced, PSR-4 autoloaded code; sanitization on input, escaping on output, nonces on every form, capability checks on every admin action.
Requirements
- WordPress 5.8+
- PHP 7.4+
- A Google API key with the Google Calendar API enabled
- A public Google Calendar (or one shared publicly) and its Calendar ID
Installation
Option A — download / clone
- Copy the
upcoming-calendar-eventsfolder intowp-content/plugins/. - (Optional, recommended) From the plugin folder run
composer installto generate the optimized autoloader. If you skip this, the plugin still works — it falls back to a built-in PSR-4 autoloader. - Activate Upcoming Calendar Events in Plugins.
Option B — zip upload
Zip the upcoming-calendar-events folder and upload it via
Plugins → Add New → Upload Plugin.
Configuration
- Go to Settings → Calendar Events.
- Enter your Google API key, set the cache duration (default 60 min) and default number of events (default 5).
- Click Save and validate. A green notice confirms the key works; a red notice shows the exact error reported by Google.
How to obtain a Google API key
- Go to the Google Cloud Console.
- Create a project (or select an existing one).
- Navigate to APIs & Services → Library, search for Google Calendar API, and click Enable.
- Go to APIs & Services → Credentials → Create Credentials → API key.
- Copy the key. For security, click Restrict key and limit it to the Google Calendar API (and optionally to your server's IP / HTTP referrers).
This plugin uses an API key, not OAuth, so it can only read public calendars. That is sufficient for displaying public event listings.
How to find your Calendar ID
- Open Google Calendar on the web.
- Hover the calendar in the left sidebar → ⋮ → Settings and sharing.
- Under Access permissions for events, tick Make available to public.
- Scroll to Integrate calendar and copy the Calendar ID — it looks
like
something@group.calendar.google.com(your personal calendar's ID is your Gmail address).
Usage
Add the shortcode to any post or page:
[upcoming_events count="10" days_ahead="30" show_description="true" google_calendar_id="something@group.calendar.google.com"]
Shortcode attributes
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
google_calendar_id |
Yes | string | — | The Google Calendar ID to read from. |
days_ahead |
Yes | numeric | — | How many days into the future to look. |
count |
No | numeric | settings value (5) | Maximum number of events to show. |
show_description |
No | boolean | false |
Whether to display each event's description. |
If a mandatory attribute is missing, editors (users who can edit posts) see a helpful inline message while ordinary visitors see nothing — the page is never broken.
Architecture decisions
The plugin is intentionally split into single-responsibility classes under the
Shivam\UpcomingCalendarEvents namespace (PSR-4, autoloaded by Composer with a
hand-written fallback so it runs without a build step):
upcoming-calendar-events.php Bootstrap: constants, autoloader, activation hooks
src/Plugin.php Singleton orchestrator; wires services + asset registration
src/Admin/SettingsPage.php Options screen, save/validate + cache-refresh handlers
src/Api/GoogleCalendarClient.php Thin Google Calendar v3 client; normalises responses
src/Service/EventService.php Cache-aware façade used by both shortcode and AJAX
src/Cache/CacheManager.php Transient wrapper with a key index for reliable flushing
src/Frontend/Shortcode.php Registers [upcoming_events]; outputs the AJAX container
src/Frontend/Renderer.php Turns normalised events into escaped, semantic HTML
src/Ajax/AjaxHandler.php admin-ajax endpoint; re-validates input, returns HTML
assets/js/frontend.js Vanilla-JS loader (no jQuery)
assets/css/frontend.css Minimal default styling
Key choices and the reasoning behind them:
- AJAX rendering instead of server-side output. The brief asks for AJAX, and it has a real benefit: full-page caches (and CDNs) would otherwise freeze the event list into the cached HTML. Rendering client-side keeps the markup fresh while the transient cache still protects the Google API from per-view hits.
- A façade (
EventService) shared by the shortcode and AJAX handler. Cache lookup + API fallback live in exactly one place, so both entry points behave identically. - A tracked key index in
CacheManager. Deleting transients by SQLLIKE '_transient_uce_%'breaks on persistent object caches (Redis/Memcached). Keeping an index of our keys lets "flush all" work on every backend. - Errors are never cached. A failed API call returns a
WP_Errorand is not stored, so a transient outage doesn't lock in a broken state for the cache duration. The detail is logged (whenWP_DEBUG), and visitors get a generic message. - Defence in depth on input. The shortcode validates attributes, and the
AJAX handler re-sanitizes and re-validates everything from
$_POST— the client is never trusted. Output is escaped at the point of printing. admin-post.phpover the Settings API. The page needs custom side effects (live key validation, cache flush, two separate buttons), which are clearer to express with explicit nonce-protected handlers.
Coding standards & debugging
- Sanitization on all input, escaping on all output, nonces on every form,
manage_optionscapability checks on admin actions. - Activates cleanly with
WP_DEBUG = true— no notices or warnings.
Possible improvements (given more time)
- Gutenberg block wrapping the shortcode with live preview controls.
- Merge multiple calendars into one chronologically-sorted list.
- Group events by day with date headings.
- AJAX "Load more" pagination using the Calendar API
pageToken. - PHPUnit / WP test suite and PHPCS (
WordPress-Extra) wired into CI. - Stale-while-revalidate caching — serve the old cache instantly and refresh in the background via WP-Cron.
AI assistance declaration
See AI-DECLARATION.md.