<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeffrey Inman</title>
    <description>The latest articles on DEV Community by Jeffrey Inman (@jeffreyinman).</description>
    <link>https://dev.to/jeffreyinman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4144909%2F4f6145a2-c381-4ce2-be84-942be5068a32.png</url>
      <title>DEV Community: Jeffrey Inman</title>
      <link>https://dev.to/jeffreyinman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeffreyinman"/>
    <language>en</language>
    <item>
      <title>Show a Google Calendar on WordPress without the iframe</title>
      <dc:creator>Jeffrey Inman</dc:creator>
      <pubDate>Sun, 27 Sep 2026 22:01:58 +0000</pubDate>
      <link>https://dev.to/jeffreyinman/show-a-google-calendar-on-wordpress-without-the-iframe-5c3p</link>
      <guid>https://dev.to/jeffreyinman/show-a-google-calendar-on-wordpress-without-the-iframe-5c3p</guid>
      <description>&lt;p&gt;A lot of club, church and studio websites show their events with Google Calendar's embed code. It works, and it's free, but it's also why the events page looks like a different website. Here's what the embed gives you, the other way to put a Google Calendar on a WordPress site, and what to check before you trust a plugin with it.&lt;/p&gt;

&lt;p&gt;Disclosure: I build Beacon Events, a WordPress events plugin that syncs calendar feeds. The checklist below applies to whatever you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the embed gives you
&lt;/h2&gt;

&lt;p&gt;Google's embed code is an iframe with a fixed width and height (its customize page asks for at least 500 by 500). Inside it, everything is Google's page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't fit.&lt;/strong&gt; A fixed-size box needs extra CSS before it behaves on a phone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It doesn't match.&lt;/strong&gt; You can pick colours and hide a few tabs, but not your theme's fonts or layout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your site gets nothing for search.&lt;/strong&gt; The events live on Google's page, so your site has no page per event and no Event markup for search results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The other way: sync the feed
&lt;/h2&gt;

&lt;p&gt;Every Google Calendar has an iCal (ICS) address. A plugin can read that address on a schedule and turn each event into a real post on your site. You keep editing in Google Calendar, as you do now, and the website follows.&lt;/p&gt;

&lt;p&gt;To get the address: open Google Calendar on a computer, go to Settings, click your calendar on the left, and click "Integrate calendar". The "Public address in iCal format" only works if the calendar is shared publicly ("Make available to public" under access permissions). If you'd rather not make it public, the "Secret address in iCal format" works too. Treat it like a password: anyone who has it can read the whole calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check in a plugin that syncs
&lt;/h2&gt;

&lt;p&gt;Google's feed has a few habits that trip up sync code. Try these with a test calendar before you point a plugin at the real one.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A repeating event stays one event.&lt;/strong&gt; A weekly service should arrive as one event with a rule, not 52 separate posts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One changed date doesn't break the series.&lt;/strong&gt; When you move or cancel a single date of a repeating event, Google sends that date as a separate entry with the same ID as the series. Sync code that matches on the ID alone overwrites the whole series with that one date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old series still show upcoming dates.&lt;/strong&gt; A weekly event that started years ago should still show next week. Some plugins only work out dates for a fixed period from the first one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skipped dates all stay skipped.&lt;/strong&gt; Google writes one line per skipped date. Code that keeps only the last one brings the others back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evening events land on the right day.&lt;/strong&gt; Some feeds write times in UTC, where 7:30 pm in Chicago is already Wednesday. The weekday has to be moved back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deleted events are handled in a way you can live with.&lt;/strong&gt; Removed from the site, hidden, or kept: know which before it happens to a real event.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You know how often it syncs.&lt;/strong&gt; WordPress's scheduled tasks run when someone visits the site, so an "hourly" sync on a quiet site can run late unless the host runs a real cron job.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One more thing that's true of any sync: edits you make on the WordPress side get overwritten on the next run, because the feed is the source. Change events in Google Calendar, not in WordPress.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to try Beacon Events
&lt;/h2&gt;

&lt;p&gt;The calendar is free on wordpress.org. Paste the feed address under Events → Settings, optionally followed by a category, and it syncs hourly with a log of what was created, updated or removed each time. A repeating event stays one event, and a moved or cancelled date shows as moved or cancelled on the series. Events that disappear from the feed become drafts rather than being deleted. It shows month, list and day views as blocks or shortcodes in your theme's fonts, and every event gets its own page with Event markup for search.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_X5LTwl0Y3I" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/beacon-events/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/beacon-events/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your calendar does something odd in any plugin, describe it in the comments and I'll look at the feed.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Moving off Event Tickets Plus: what your WooCommerce ticket data looks like</title>
      <dc:creator>Jeffrey Inman</dc:creator>
      <pubDate>Sun, 27 Sep 2026 19:22:35 +0000</pubDate>
      <link>https://dev.to/jeffreyinman/moving-off-event-tickets-plus-what-your-woocommerce-ticket-data-looks-like-2akm</link>
      <guid>https://dev.to/jeffreyinman/moving-off-event-tickets-plus-what-your-woocommerce-ticket-data-looks-like-2akm</guid>
      <description>&lt;p&gt;Event Tickets Plus sells your event tickets through WooCommerce. If you're moving off it, whether because of the Liquid Web changes or because you're leaving The Events Calendar altogether, the tickets and attendees are the part that's easy to get wrong. Here's what that data looks like in your database and where a migration usually breaks.&lt;/p&gt;

&lt;p&gt;Disclosure: I build Beacon Events, a WordPress events plugin, and I wrote an importer for this data this week. The details below apply whatever you move to.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in your database
&lt;/h2&gt;

&lt;p&gt;The field names below come from the free Event Tickets plugin's own code, which maps every ticket provider it supports.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Each ticket is a WooCommerce product.&lt;/strong&gt; It carries &lt;code&gt;_tribe_wooticket_for_event&lt;/code&gt; with the event's post ID, and &lt;code&gt;_tribe_ticket_capacity&lt;/code&gt; with the ticket's capacity (-1 means unlimited). The price is the product's price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each attendee is a &lt;code&gt;tribe_wooticket&lt;/code&gt; post&lt;/strong&gt;, one per ticket sold, so an order for three tickets makes three attendees. It points back with &lt;code&gt;_tribe_wooticket_event&lt;/code&gt; (the event), &lt;code&gt;_tribe_wooticket_product&lt;/code&gt; (which ticket), &lt;code&gt;_tribe_wooticket_order&lt;/code&gt; (the WooCommerce order) and &lt;code&gt;_tribe_wooticket_order_item&lt;/code&gt; (the line in that order). &lt;code&gt;_paid_price&lt;/code&gt; holds what was paid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether someone actually paid lives in WooCommerce,&lt;/strong&gt; in the order's status, not in the attendee record.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where migrations go wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unpaid orders become attendees.&lt;/strong&gt; Orders that are pending or on hold still have attendee posts. Only completed and processing orders are paid, so leave the rest out, and drop refunded, cancelled and failed ones too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Newer stores keep orders out of &lt;code&gt;wp_posts&lt;/code&gt;.&lt;/strong&gt; With WooCommerce's High-Performance Order Storage (HPOS), orders live in their own &lt;code&gt;wc_orders&lt;/code&gt; table. A query that joins attendees to &lt;code&gt;shop_order&lt;/code&gt; posts finds nothing on those sites. Ask WooCommerce for the order (&lt;code&gt;wc_get_order()&lt;/code&gt;) instead, which works either way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The buyer isn't always the guest.&lt;/strong&gt; One person can buy tickets for three people. Use the name and email stored on each attendee first, and fall back to the order's billing details only when those are empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ticket types collapse into one price.&lt;/strong&gt; An event with early bird, general and VIP tickets is three products. If your new plugin only has one price per event, you lose that, and your existing attendees lose the record of what they bought.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The import sends emails.&lt;/strong&gt; A migration shouldn't send everyone a fresh confirmation. Check that the importer is silent before you run it on real data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A checklist that holds up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Back up the database, including the WooCommerce order tables.&lt;/li&gt;
&lt;li&gt;Count your paid attendees in WooCommerce before you start, so you have a number to compare against.&lt;/li&gt;
&lt;li&gt;Run the import with Event Tickets Plus still active, and check one event with several ticket types and one order that was refunded.&lt;/li&gt;
&lt;li&gt;Make sure capacity still counts the people you imported, so nobody can buy a seat that's already taken.&lt;/li&gt;
&lt;li&gt;Only then switch the old plugins off.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If you want to try Beacon Events
&lt;/h2&gt;

&lt;p&gt;The calendar is free on wordpress.org. Its paid add-on, Beacon Events Pro, imports Event Tickets Plus data when you move from The Events Calendar. That covers each ticket as its own ticket type with its price and capacity, and every attendee of a completed or processing order under their own name, with the WooCommerce order linked. Unpaid orders are listed in the report, and no emails go out. New tickets then sell through your WooCommerce checkout or your own Stripe account with no per-ticket fee from us. It also has check-in at the door and a printable door list. It's $49 a year for one site, with a 14-day free trial that needs no card.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_X5LTwl0Y3I" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/beacon-events/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/beacon-events/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your Tickets Plus setup has something this doesn't cover, say so in the comments and I'll look at it.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>woocommerce</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Eventbrite fees vs selling tickets on your own WordPress site, with the actual numbers</title>
      <dc:creator>Jeffrey Inman</dc:creator>
      <pubDate>Sun, 27 Sep 2026 16:50:14 +0000</pubDate>
      <link>https://dev.to/jeffreyinman/eventbrite-fees-vs-selling-tickets-on-your-own-wordpress-site-with-the-actual-numbers-21hi</link>
      <guid>https://dev.to/jeffreyinman/eventbrite-fees-vs-selling-tickets-on-your-own-wordpress-site-with-the-actual-numbers-21hi</guid>
      <description>&lt;p&gt;If you sell tickets for small events (a class, a workshop, a club night, a fundraiser), the ticketing platform's cut adds up fast. Here are the actual numbers for Eventbrite against taking payments on your own WordPress site, and what you give up if you switch.&lt;/p&gt;

&lt;p&gt;Disclosure: I build Beacon Events, a WordPress calendar plugin whose paid add-on sells tickets. The arithmetic below holds whatever you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Eventbrite charges
&lt;/h2&gt;

&lt;p&gt;From Eventbrite's US pricing page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free events: no fees.&lt;/li&gt;
&lt;li&gt;Paid tickets: "3.7% + $1.79 service fee per ticket".&lt;/li&gt;
&lt;li&gt;On top of that: "2.9% payment processing fee per order".&lt;/li&gt;
&lt;li&gt;Buyers pay these fees by default, or you can absorb them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What selling on your own site costs
&lt;/h2&gt;

&lt;p&gt;If the payment goes straight through your own Stripe account, Stripe's US price is "2.9% + 30¢ per successful transaction for domestic cards", plus 1.5% for international cards. The ticketing software is a flat yearly fee or free.&lt;/p&gt;

&lt;p&gt;The 2.9% shows up on both sides, so the real difference is Eventbrite's service fee against Stripe's 30¢.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per ticket
&lt;/h2&gt;

&lt;p&gt;Service fee on Eventbrite against Stripe's fixed fee, one ticket per order:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ticket price&lt;/th&gt;
&lt;th&gt;Eventbrite service fee&lt;/th&gt;
&lt;th&gt;Stripe fixed fee&lt;/th&gt;
&lt;th&gt;Difference per ticket&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;td&gt;$2.16&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;$1.86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$20&lt;/td&gt;
&lt;td&gt;$2.53&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;$2.23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$50&lt;/td&gt;
&lt;td&gt;$3.64&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;$3.34&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When buyers pay the fees, the 2.9% processing is charged on the service fee too, so a $20 ticket comes to about $23.18 at checkout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Over a year
&lt;/h2&gt;

&lt;p&gt;A studio running 40 classes a year with 25 people at $20 sells 1,000 tickets. That's about $2,530 in Eventbrite service fees, against about $300 in Stripe's fixed fees. Whether your attendees pay that difference or you do, it's roughly $2,200 a year.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you give up
&lt;/h2&gt;

&lt;p&gt;Being fair to Eventbrite, it does a few things your own site won't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovery.&lt;/strong&gt; People browse Eventbrite for things to do. Your own site only sells to people who already found you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check-in and seating.&lt;/strong&gt; Its organizer app scans tickets at the door, and it handles reserved seating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refunds and payouts.&lt;/strong&gt; It handles refund requests and chargebacks and pays you out on its schedule. With your own Stripe account, that's between you and Stripe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If most of your buyers find you through Eventbrite, the fee may be worth it. If they come from your own site, your newsletter or your regulars, you're paying a marketplace fee for a marketplace you don't use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist before you switch
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open a Stripe account and send one real test payment to yourself.&lt;/li&gt;
&lt;li&gt;Decide your refund policy and write it on the event page.&lt;/li&gt;
&lt;li&gt;Make sure buyers get a confirmation email with a calendar file.&lt;/li&gt;
&lt;li&gt;Set a capacity for each date so you never oversell.&lt;/li&gt;
&lt;li&gt;Keep a printable attendee list for the door.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If you want to try Beacon Events
&lt;/h2&gt;

&lt;p&gt;The calendar is free on wordpress.org, including recurring events and Google Calendar sync. The paid add-on, Beacon Events Pro, is $49 a year for one site, with a 14-day free trial that needs no card. It takes RSVPs and sells tickets through your own Stripe account or your WooCommerce checkout, with no per-ticket fee from us. It includes a capacity per date, a waitlist, reminder emails and an attendee list you can export. Each event can have several ticket types, like early bird, general and VIP, with their own prices and capacities. There's no reserved seating and no scanning app yet, so it fits classes and small events better than festivals.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_X5LTwl0Y3I" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Demo site: &lt;a href="https://store-safety.duckdns.org/demo/" rel="noopener noreferrer"&gt;https://store-safety.duckdns.org/demo/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/beacon-events/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/beacon-events/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your numbers look different, say, a country with other fees, post them in the comments.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>stripe</category>
      <category>webdev</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>All-in-One Event Calendar is gone from wordpress.org: getting your events out</title>
      <dc:creator>Jeffrey Inman</dc:creator>
      <pubDate>Sat, 26 Sep 2026 23:59:46 +0000</pubDate>
      <link>https://dev.to/jeffreyinman/all-in-one-event-calendar-is-gone-from-wordpressorg-getting-your-events-out-5ae3</link>
      <guid>https://dev.to/jeffreyinman/all-in-one-event-calendar-is-gone-from-wordpressorg-getting-your-events-out-5ae3</guid>
      <description>&lt;p&gt;Timely closed All-in-One Event Calendar on wordpress.org in August 2025, at their own request. Sites that still run it get no more updates from there. The last release still bundles an iCal library that calls &lt;code&gt;create_function()&lt;/code&gt;, which PHP 8 removed, and on a fresh install version 3 gives you Timely's hosted calendar instead of the local one.&lt;/p&gt;

&lt;p&gt;Your events never left your database, though. Here's what's in there and what goes wrong when you move it, whichever calendar you pick next.&lt;/p&gt;

&lt;p&gt;Disclosure: I build Beacon Events, a free calendar plugin, and this week I wrote an importer for this data. Everything below applies to any migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in your database
&lt;/h2&gt;

&lt;p&gt;Each event is a post of type &lt;code&gt;ai1ec_event&lt;/code&gt;. The dates and the rest live in a table of their own, &lt;code&gt;wp_ai1ec_events&lt;/code&gt;, one row per event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; are Unix timestamps in UTC. &lt;code&gt;timezone_name&lt;/code&gt; says which zone the event was entered in, and &lt;code&gt;sys.default&lt;/code&gt; means the site's zone.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;recurrence_rules&lt;/code&gt; holds an iCalendar RRULE. Rules written by its editor look like &lt;code&gt;FREQ=WEEKLY;WKST=MO;BYday=TU,TH;&lt;/code&gt; with a lower-case "day", and end dates come out as &lt;code&gt;UNTIL=20261231T235959Z&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exception_dates&lt;/code&gt; is a comma-separated list of skipped dates. &lt;code&gt;exception_rules&lt;/code&gt; can hold a whole rule of excluded dates.&lt;/li&gt;
&lt;li&gt;Venue, address, city, contact, cost and ticket link are plain columns, except &lt;code&gt;cost&lt;/code&gt;, which is a serialized PHP array.&lt;/li&gt;
&lt;li&gt;Events pulled in from a calendar feed carry &lt;code&gt;ical_feed_url&lt;/code&gt; and the feed's own &lt;code&gt;ical_uid&lt;/code&gt;. Moved single dates get that UID with a prefix.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Categories and tags are the &lt;code&gt;events_categories&lt;/code&gt; and &lt;code&gt;events_tags&lt;/code&gt; taxonomies, and category colours sit in &lt;code&gt;wp_ai1ec_event_category_meta&lt;/code&gt;. Expanded dates are cached in &lt;code&gt;wp_ai1ec_event_instances&lt;/code&gt;, which you can ignore because it's rebuilt from the rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where migrations go wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The calendar page goes blank.&lt;/strong&gt; It drew the calendar into an empty page you picked in its settings, with no shortcode or block in the content. Once the plugin is off, that page shows nothing, so put your new calendar on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Times shift by hours.&lt;/strong&gt; The timestamps are UTC. Convert them in the event's own zone, not the server's, or an evening class starts at midnight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule end dates drift by a day.&lt;/strong&gt; &lt;code&gt;UNTIL&lt;/code&gt; is a moment in UTC, not a date. In a zone ahead of UTC, &lt;code&gt;20261115T235959Z&lt;/code&gt; is already November 16, so read it as the last date whose start still comes before that moment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feed events get duplicated.&lt;/strong&gt; If you import them and then subscribe to the same feed again, you get two copies unless the importer keeps the feed's UIDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exclusion rules disappear.&lt;/strong&gt; Few calendars understand EXRULE at all. "Every day except weekends" only survives if it becomes a weekly rule on the remaining days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old links 404.&lt;/strong&gt; Calendar views live at URLs like &lt;code&gt;/calendar/action~month/…&lt;/code&gt; and categories at &lt;code&gt;/events_categories/your-category/&lt;/code&gt;. Search engines and bookmarks still have those.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to go
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open Source Event Calendar&lt;/strong&gt; is a GPL fork of All-in-One Event Calendar by digitaldonkey and looks the most like it. Its readme says the database isn't fully compatible with All-in-One Event Calendar 2.3.4 and that moving existing events may take manual work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timely's hosted calendar&lt;/strong&gt; is their own service, embedded into your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beacon Events&lt;/strong&gt; is the one I build. It reads the tables above directly, with the old plugin active or already switched off.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A checklist that holds up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Back up the database.&lt;/li&gt;
&lt;li&gt;Install the new calendar and run its import while All-in-One Event Calendar is still active. Don't delete anything yet.&lt;/li&gt;
&lt;li&gt;Spot-check a recurring event, an all-day event and one that came from a feed.&lt;/li&gt;
&lt;li&gt;Deactivate the old plugin, put the calendar on the old calendar page, and click a few old links.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If you want to try Beacon Events
&lt;/h2&gt;

&lt;p&gt;It's free on wordpress.org. Events → Import / Export shows what it found and imports it in one click: rules and skipped dates, category colours, venues, contacts, images and feed UIDs. The old category, calendar-view and event links redirect once the old plugin is gone, and the report names the page that used to show the calendar. Recurring events are free, and so is Google Calendar or ICS sync.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_X5LTwl0Y3I" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Demo site: &lt;a href="https://store-safety.duckdns.org/demo/" rel="noopener noreferrer"&gt;https://store-safety.duckdns.org/demo/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/beacon-events/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/beacon-events/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your old calendar has something this doesn't cover, tell me in the comments.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>php</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Moving off The Events Calendar: what has to come across, and what breaks</title>
      <dc:creator>Jeffrey Inman</dc:creator>
      <pubDate>Sat, 26 Sep 2026 22:37:12 +0000</pubDate>
      <link>https://dev.to/jeffreyinman/moving-off-the-events-calendar-what-has-to-come-across-and-what-breaks-1g1h</link>
      <guid>https://dev.to/jeffreyinman/moving-off-the-events-calendar-what-has-to-come-across-and-what-breaks-1g1h</guid>
      <description>&lt;p&gt;The Events Calendar Pro now starts at $259 a year after the Liquid Web consolidation, and plenty of sites that only use it for a calendar with a few recurring events are looking at the renewal and wondering if it's worth it. I spent this week writing an importer for TEC data, so here's what that data looks like and where a migration goes wrong, whichever plugin you end up on.&lt;/p&gt;

&lt;p&gt;Disclosure: the importer is part of Beacon Events, a free calendar plugin I built. Everything below applies no matter what you switch to.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in your database
&lt;/h2&gt;

&lt;p&gt;Events are regular posts (&lt;code&gt;tribe_events&lt;/code&gt;) with the dates in post meta: &lt;code&gt;_EventStartDate&lt;/code&gt;, &lt;code&gt;_EventEndDate&lt;/code&gt;, &lt;code&gt;_EventTimezone&lt;/code&gt;, &lt;code&gt;_EventAllDay&lt;/code&gt;. Venues and organizers are their own post types (&lt;code&gt;tribe_venue&lt;/code&gt;, &lt;code&gt;tribe_organizer&lt;/code&gt;), linked from the event through &lt;code&gt;_EventVenueID&lt;/code&gt; and &lt;code&gt;_EventOrganizerID&lt;/code&gt;. Categories are the &lt;code&gt;tribe_events_cat&lt;/code&gt; taxonomy.&lt;/p&gt;

&lt;p&gt;Recurring events are the part that bites. On TEC 6 with Pro, the rule is stored in TEC's custom &lt;code&gt;wp_tec_events&lt;/code&gt; table as an iCalendar rule set, not in post meta. Older TEC 5 sites kept a serialized &lt;code&gt;_EventRecurrence&lt;/code&gt; array, and some still have one generated child post per occurrence sitting next to the parent event.&lt;/p&gt;

&lt;p&gt;If you sell tickets with Event Tickets, those are post types too: &lt;code&gt;tribe_rsvp_tickets&lt;/code&gt; and &lt;code&gt;tec_tc_ticket&lt;/code&gt;, with attendees in &lt;code&gt;tribe_rsvp_attendees&lt;/code&gt; and &lt;code&gt;tec_tc_attendee&lt;/code&gt;. A paid attendee points to its order through &lt;code&gt;post_parent&lt;/code&gt;, and the order's status is the order post's status (&lt;code&gt;tec-tc-completed&lt;/code&gt;, &lt;code&gt;tec-tc-pending&lt;/code&gt; and so on).&lt;/p&gt;

&lt;h2&gt;
  
  
  Where migrations go wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A recurring series becomes hundreds of events.&lt;/strong&gt; If you move data through a generic export, check whether a weekly class arrives as one rule or as 52 separate posts. The second one is miserable to edit later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time zones stored as offsets.&lt;/strong&gt; If the site time zone was ever set to something like &lt;code&gt;UTC+2&lt;/code&gt; instead of a city, event times can end up stored that way. An offset knows nothing about daylight saving, so a 6pm class drifts to 7pm for half the year. Map offsets to a named zone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leftover shortcodes and blocks.&lt;/strong&gt; Event content often contains &lt;code&gt;[tribe_events]&lt;/code&gt; shortcodes and &lt;code&gt;tribe/*&lt;/code&gt; blocks. Once TEC is deactivated, the shortcodes print as raw text and the blocks render nothing. Strip them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old URLs.&lt;/strong&gt; Single events usually keep &lt;code&gt;/event/slug/&lt;/code&gt;, but category pages and calendar views live under &lt;code&gt;/events/category/...&lt;/code&gt; and &lt;code&gt;/events/month/&lt;/code&gt;. Those 404 after the switch unless something redirects them, and that's search traffic you worked for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unpaid orders counted as seats.&lt;/strong&gt; If you bring attendees along, leave out pending and failed orders. Those people never paid.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A checklist that holds up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Back up the database. Actually do it.&lt;/li&gt;
&lt;li&gt;Install the new plugin with TEC still active and run the import. Nothing in TEC should be deleted while you check.&lt;/li&gt;
&lt;li&gt;Spot-check one recurring series, one event in a different time zone, and one event with tickets.&lt;/li&gt;
&lt;li&gt;Deactivate TEC and click through a few old URLs, including a category page.&lt;/li&gt;
&lt;li&gt;Only then cancel the renewal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If you want to try Beacon Events
&lt;/h2&gt;

&lt;p&gt;It's free on wordpress.org. Recurring events are free, including cancelling or moving a single date of a series. Events → Import / Export reads the TEC data described above in one click (events, venues, organizers, categories, images, recurring rules), strips the TEC shortcodes and blocks, and redirects the old category and view URLs. It also imports from Events Manager and CSV. The paid add-on ($49 a year, with a 14-day free trial that needs no card) does RSVP and tickets and brings Event Tickets attendees across: RSVP, Tickets Commerce, and Event Tickets Plus orders paid through WooCommerce.&lt;/p&gt;

&lt;p&gt;Short video tour (1:39):&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_X5LTwl0Y3I" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Live demo on a stock Twenty Twenty-Five site: &lt;a href="https://store-safety.duckdns.org/demo/" rel="noopener noreferrer"&gt;https://store-safety.duckdns.org/demo/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/beacon-events/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/beacon-events/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your TEC data has something this doesn't cover, tell me in the comments.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>php</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
