D
P
0
← All articles Baca dalam Bahasa Indonesia

WordPress & PHP in Production

Event List Gone, Only the Search Box Left: Events Search & Filter Bar v2 Split the Shortcode in Two

· · 8 min read
Event List Gone, Only the Search Box Left: Events Search & Filter Bar v2 Split the Shortcode in Two

All that was left on the per-venue event pages was the search bar. The data-ecsa-target attribute in the markup was empty, and the event list that normally sat under the bar was gone from every venue page at once. The data itself was intact, and not a single event had been deleted.

I did not build this site. My work there is limited to revisions requested by a contact on the client side. It runs WordPress on the Hello Elementor theme through a child theme, its pages are built in Elementor, and its events use The Events Calendar (TEC) 6.17.3 together with two addons from Cool Plugins. One of those addons, Events Search & Filter Bar for The Events Calendar, which I will call ECSA from here on, had moved up to v2.0.0, released on 24 August 2026, and that version is a complete rewrite.

One shortcode in v1, a pair in v2

In v1, a single shortcode did two jobs at once: the search box and the event list beneath it, through the show-events="5" attribute. The v1 shortcode in the block below is what the widget on one of the venue pages held before I changed it. In v2, that work is split into a pair of shortcodes bound together by a target attribute, one for the bar and one for the results, with the same target value on both.

# v1: one shortcode, bar and list together
[events-calendar-search placeholder="Search Events" show-events="5" disable-past-events="false" layout="medium"]
 
# v2: bar and results separated, bound by the same target value
[events-calendar-search target="ecsa-x"]
[events-calendar-search-results target="ecsa-x"]

All six venue pages still carried the v1 shortcode, byte for byte identical, so on every one of them v2 rendered only the bar.

The global setting does not stand in for target

ECSA has a global setting under Events Addons → Search & Filter Bar. I switched results_mode there from none to inline, and the attribute did reach the page. The results still did not render, because nothing gave them a target pointing at a container.

I checked that in two ways, without cookies through curl and in a browser with JavaScript enabled. Those two cover different possibilities. Curl without cookies shows the HTML an anonymous visitor receives, while a browser with JavaScript enabled also runs whatever gets rendered later on the client. Both agreed, so the conclusion was simple: the shortcode on the pages had to be replaced.

The page list came from the sitemap, not the report

The report from the client side only named three affected pages. I got the correct list by crawling the whole sitemap and filtering for pages that contained ecsa-bar, and that came to six venue pages. That is the method I hold to if something like this happens again, not a list from the client. Filtering on markup does not depend on anyone remembering which page uses which plugin.

Guessing URLs from city names does not help on this site either. Two guessed URLs for two venues did not return 404. Both were 301 redirects to the event page of one other venue, so a wrong guess still landed on the wrong page with a 200 status and never looked like an error.

Swapping the widget content through Elementor's command API

The new widget content was uniform across all six pages, with only the target value unique to each venue. The shortcode widget on those six pages even had the same Elementor id, because the pages were duplicated from one another. The old custom CSS on that same widget was already dead too, since it targeted v1 markup.

I did not make the replacement by steering the editor panel. I used Elementor's own command API:

// simplified: container is the shortcode widget's container in the currently open document
const shortcode = `[events-calendar-search placeholder="Search Events" target="<TARGET>"]
[events-calendar-search-results target="<TARGET>"]`;
 
$e.run('document/elements/settings', { container, settings: { shortcode } });
$e.run('document/save/default');

The reason is that the panel uses this same path, so Elementor itself writes the JSON and a revision still gets created. I deliberately avoided the shortcut of assembling _elementor_data myself and sending it with a POST, because that risks breaking the whole page. All six pages were done on 27 and 28 August 2026, and I let the client's contact know the pages were fixed.

The list came back, and with it a different problem. As of 27 August 2026, five of twelve cards carried old dates, some as far back as 23 September 2023. My first guess, that the TEC view was right and the search plugin was misreading the dates, turned out to be wrong. What was actually happening is that the TEC index was out of sync with the stored dates. TEC's own built-in view was just as broken, so swapping the widget would not fix it either. That is a TEC problem separate from the shortcode, and I am not covering it here.

The plain URL was still stale while the query string URL was right

On 28 August 2026, after all six pages had been saved, one venue page was still serving old HTML on its plain URL, consistently across three requests in a row. The same URL with any query string at all served the correct version right away. The response headers of both were exactly the same, cf-cache-status: DYNAMIC with max-age=0.

From that I concluded the cache lived at the origin and that a Cloudflare purge would not help. That conclusion was wrong. As soon as Cloudflare was purged, the page was clean. On this site, cf-cache-status: DYNAMIC does not mean Cloudflare is not storing anything. Two steps that feel reasonable also did nothing for the stale page, namely re-saving it through Elementor and looking for a purge button in wp-admin.

Separating cache from a failed fix turned out to be simple. Compare the plain URL with the same URL carrying a ?cb=<number> suffix. If the query version is right and the plain one is wrong, that is cache, not failed work. The final check still has to happen on the plain URL, because a cache buster hides exactly this problem.

# simplified sketch: URL and MARKER are only examples
URL="https://example.com/venue/events/"
MARKER="a string that only exists in the new version of the page"
curl -s "$URL" | grep -c "$MARKER"                 # plain URL, what visitors receive
curl -s "$URL?cb=$(date +%s)" | grep -c "$MARKER"  # query string URL

The next complaint: the venue pages had never been filtered

On 31 August 2026 a new complaint came from the client side, passed along through the same contact. According to them, every venue page was showing events from other venues, and the request was to "revert the changes made".

The premise of that complaint was wrong, and it could be proven. The revision history of one venue page, all the way back to the oldest revision dated 26 May 2025, contained the same shortcode every time, with no venue or category attribute. In my investigation notes from 28 August, that same page was already recorded as showing events from four other venues, just as before anything broke, because the v1 shortcode never had a venue attribute and the six pages stored byte-identical strings. The only thing that changed was how much was visible. Under v1 the page showed 5 cards through show-events="5", while v2 shows 12 by default.

I read that revision history through REST. The revisions endpoint with context=edit returns meta._elementor_data, so the old widget content can be read in full:

GET /wp-json/wp/v2/pages/<id>/revisions/<rev>?context=edit

Where the venue pages ended up

The v2 shortcode pair did not turn out to be the final state. Free ECSA cannot filter by venue, because the official attribute list of its shortcode is closed: target, view, per-page, facets, search-fields, columns, card-size, card-fields, date-format, template, colors, control-size, and corner-radius. A display exactly like the old grid plus a venue filter is only possible by buying ECSA Pro. I chose the free route after looking at the preview, accepting the loss of the three-column grid.

Five venue pages then stopped using the ECSA shortcode. Their shortcode widget was replaced with the Elementor widget the-events-calendar-addon from the Events Widgets plugin (events-widgets-for-elementor-and-the-events-calendar), filtered per event category. The sixth page was left out, because according to the client side that venue had closed, and at their request the page was redirected to the homepage with a 302. The ECSA search box disappeared from the five pages as well, because the bar and its results lived in the widget that was replaced. I raised that consequence before doing the work and it was not disputed. It is consistent anyway, since that bar searches the whole site and conflicts with pages scoped to a single venue.

One trap in the new widget is worth writing down. ectbe_ev_category is a select2 multiple field, so its value has to be an array. Filled with a category ID as a string, such as '<ID>', the widget renders empty with no error. The value has to be ['<ID>']. Since the event category is now the only filter key for the venue pages, a new event that shows up on no venue page should have its category checked first.

Shortcodes already saved in pages do not migrate themselves

A plugin can split its shortcode in a major version, but the old shortcode already saved in page content stays exactly as it was. Here, all six pages still carried the v1 string after the update, and the global setting I changed did not stand in for the missing target. So after a major update to a plugin that lives in shortcodes, the thing to check is the pages that use it. That list is safest when pulled from markup across the whole sitemap rather than from anyone's memory, and the final check belongs on the plain URL.

If someone later says it used to look different, the revision history through REST can answer that with the widget content as it was actually saved at the time.