EP Locations
EP Locations turns a PageMotor site into a multi-location operator’s home: a Location content type so you create branches like any other PageMotor content, full Schema.org LocalBusiness emission per branch, and a built-in store finder shortcode with map, postcode search and “Use my location” geolocation. GDPR consent gating wraps the public finder when EP GDPR is installed.
This page documents EP Locations 1.2.19.
Published by ElmsPark Studio.
What EP Locations does
Section titled “What EP Locations does”- Registers
Locationas a PageMotor content type, admin CRUD is auto-generated by PM, the plugin contributes per-location options - Per-location options: address, hours grid, geo coordinates, payment methods, profile URLs, per-branch Schema.org subtype override, areas served
- Schema.org Pattern B graph: parent
Organizationat#organization, each branch as its ownLocalBusiness(or chosen subtype) at<location_url>#localbusinesswithparentOrganizationback-reference - An
[ep-locations_finder]shortcode that drops a full store finder onto any page: list of branches, MapLibre map with markers, postcode / address search via OpenStreetMap Nominatim, “Use my location” geolocation - Gates the entire public finder behind EP GDPR analytics consent when EP GDPR is active (defence in depth: client-side UI hide + server-side endpoint refusal)
- Coordinates with EP SEO and EP Local Business via static
owns_organization_node()hooks - Six EU language translations of the entire admin UI plus the public finder strings
What EP Locations doesn’t do
Section titled “What EP Locations doesn’t do”- Single-location. Use EP Local Business if you only have one branch, it’s simpler, no content type, no store finder, same schema quality.
- Booking / reservations. EP Locations covers presence and discovery. EP Booking is the booking plugin.
- Per-branch staff / team pages. Branches are operational records, not editorial content. Use PageMotor’s standard content types for team pages and link to them from the branch description.
Requirements
Section titled “Requirements”- PageMotor 0.8.3b or later (uses the action/operation pattern that landed in 0.8.3b)
- EP Suite base class (auto-loaded with any EP plugin)
- EP GDPR (optional but strongly recommended). Gates the public finder behind analytics-cookie consent
- MapTiler Cloud account (free tier covers up to 100,000 map loads per month). Only needed if you use the store finder
Installation
Section titled “Installation”- Download the latest
ep-locations.zipfrom the EP Suite downloads page. - In PageMotor admin go to Plugins → Manage Plugins → Upload, select the zip, and activate.
- Go to EP Locations → Settings. Set the Master Switch on once you’ve created at least one Location and filled in the parent Organisation details.
- (Optional) Sign up at maptiler.com for a free Cloud key and paste it into the Finder section of settings to enable map rendering.
Adding locations
Section titled “Adding locations”Locations are a regular PageMotor content type. Go to Content → Add → Location. PageMotor handles the URL slug, title, body content (free text describing the branch, anything you’d put in a “Visit our Wexford branch” page). EP Locations contributes the structured fields below in a Content Options panel:
- Schema.org subtype. Override the site-wide default for this branch (e.g. one branch is a
Restaurant, another is aFastFoodRestaurant, the corporate office stays as plainLocalBusiness) - Address. Street, locality, region, postcode, country
- Geo. Latitude, longitude, with the Look up coordinates from address helper
- Telephone, email, branch-specific URL
- Hours grid. Seven-day, supports overnight intervals (split automatically on emission)
- Payment methods, currencies accepted, price range
- Areas served. One per line
- Profile URLs. Branch-specific Facebook, Instagram, etc.
- Status. Live / Draft (only Live branches appear in the finder and emit schema)
Settings
Section titled “Settings”Master Switch
Section titled “Master Switch”Single tickbox. Off by default. Once you’ve added at least one Live Location and filled in the Organisation section, tick this and save to start emitting Schema.org and to expose the finder shortcode.
Parent Organisation
Section titled “Parent Organisation”Brand-level identity used when EP SEO is not installed. If EP SEO IS installed, EP Locations defers to EP SEO’s Schema Name and Logo URL (single source of truth).
- Organisation name. The brand name (used in
parentOrganizationreferences on every branch) - Organisation description, logo URL
- Profile URLs. Brand-level Facebook, LinkedIn, etc.
Per-Location Defaults
Section titled “Per-Location Defaults”Default values applied to every Location unless overridden per-branch. Useful for chains where every branch shares the same payment methods, country, etc.
- Default LocalBusiness subtype. Used when a Location hasn’t chosen its own
- Default country (ISO 3166-1). Pre-fills new Locations and biases postcode geocoding
- Default price range, payment methods, currencies
Finder
Section titled “Finder”- MapTiler API key. Required for the map to render. Free tier from maptiler.com.
- Map style. Pick from MapTiler’s curated styles (Streets, Outdoor, Toner, etc.)
- Default zoom, default centre. Used when the finder loads with no search query
Integration
Section titled “Integration”- Coexistence mode. The same merged / additive choice as EP Local Business, governing how the parent Organization is shared with EP SEO.
- Preview JSON-LD. Renders the emitted
@graphfor a chosen Location against current settings, before you save. - Open in Schema.org validator. Deep-links to
validator.schema.orgwith your site’s origin pre-filled.
Shortcodes
Section titled “Shortcodes”Every EP shortcode is ep- prefixed so it can never collide with a core or sibling-plugin shortcode. PageMotor’s shortcode registry is first-wins and core initialises before user plugins, so a bare name silently loses to any same-named core shortcode.
Use the prefixed forms. [locations_finder] and [location_details] still work as deprecated back-compat aliases for existing page content, but new pages should use:
[ep-locations_finder]
Section titled “[ep-locations_finder]”Renders the store finder anywhere on your site, typically on a /locations/ parent page.
[ep-locations_finder]Takes no attributes. Everything comes from the plugin’s sitewide settings.
[ep-location_details]
Section titled “[ep-location_details]”Renders the per-location detail block: name, description, address, contact, opening hours table and map link.
[ep-location_details] renders the current location page[ep-location_details id="42"] renders a specific location elsewhereUseful for themes that ship no custom location template. Drop it into your default Page template’s content area and every location page renders the detail block automatically.
The store finder
Section titled “The store finder”The finder renders:
- A search input that accepts postcode, town, address, or “near me”
- A “Use my location” button that triggers HTML5 geolocation (browser permission prompt)
- A peek-map at the top on mobile, full-height map on desktop
- A list of branches sorted by distance from the search point, each with address, distance, telephone, opening hours summary, and a “Get directions” link
- Clickable map markers that scroll the corresponding list entry into view
List-first mobile UX with the map collapsible, works cleanly on phones, doesn’t hide the actual location data behind an interactive map you can’t scroll.
Schema.org Pattern B explained
Section titled “Schema.org Pattern B explained”EP Locations emits the same shape Yext emits for Wendy’s, T-Mobile, Best Buy, and other major chains:
- A single parent
Organizationat#organization(with the brand name, logo, sameAs links) - Every Live Location as its own
LocalBusiness(or chosen subtype) at<location_url>#localbusiness, with aparentOrganizationreference back to#organization - No
subOrganizationarray on the parent (doesn’t scale; Google infers the inverse from the per-page back-references) - The deprecated
branchOfis not emitted (Schema.org superseded it years ago)
This validates clean against Google’s Rich Results Test for both Organization and LocalBusiness intents.
GDPR consent gating
Section titled “GDPR consent gating”When EP GDPR is installed and active, the entire public store finder gates behind analytics-cookie consent. Two layers:
- Client-side: the finder UI hides; the visitor sees a friendly “we need your consent to load the map and search service” explainer and an Open cookie settings button that resets the consent cookie and reloads the page
- Server-side: the public AJAX endpoint backing the finder refuses with
gdpr_consent_requiredif the cookie says no. Defence in depth, a direct POST can’t bypass the client-side gate.
When EP GDPR is not installed, the finder loads normally with no consent gate. No code change needed for sites that don’t need consent gating.
The finder uses two third-party services that the consent gate names explicitly in its explainer text: MapTiler (map tiles, sees visitor IP) and OpenStreetMap Nominatim (postcode geocoding, sees visitor IP and search query).
Languages
Section titled “Languages”The admin UI is fully translated into German (de), Spanish (es), French (fr), Italian (it), Dutch (nl), and Portuguese (pt). The public finder strings (search placeholder, “Use my location”, “Get directions”, consent gate body) are also translated. Schema.org code identifiers stay English in every language.
Switch UI language from the EP Suite language dropdown in the admin nav.
Privacy
Section titled “Privacy”Three sources of outbound network traffic:
- MapTiler. Every map load fetches tiles from
api.maptiler.com. Visitor IP is logged by MapTiler. Free-tier accounts get up to 100,000 loads per month before paid scaling. - OpenStreetMap Nominatim. Every postcode search fires a single GET against
nominatim.openstreetmap.org. Visitor IP and search query are visible to OSM. Rate-limited at 1 request per 1.5 seconds per visitor. - Browser geolocation. When the visitor clicks “Use my location”, the browser asks for permission. If granted, the coordinates are used client-side only, they’re not posted to your server or to any third party.
When EP GDPR is active, none of these fire until the visitor has accepted analytics cookies.
Coexistence matrix
Section titled “Coexistence matrix”EP Locations sits at the top of the LocalBusiness coexistence chain. EP Local Business defers entirely whenever EP Locations is installed and active, and the defer is one-way: EP Local Business reads EP Locations, with no reciprocal handshake needed.
| Setup | Behaviour |
|---|---|
| EP Locations alone | Owns Organization plus per-location LocalBusiness nodes. |
| EP Locations + EP SEO (merged) | EP SEO yields Organisation. EP Locations emits the parent and the branches. |
| EP Locations + EP SEO (additive) | EP SEO emits Organisation unchanged. EP Locations adds per-location LocalBusiness nodes with parentOrganization back-references. |
| EP Locations + EP Local Business | EP Local Business defers entirely. EP Locations owns all LocalBusiness emission. |
| EP Locations + EP SEO + EP Local Business | EP Local Business defers. EP SEO yields per its merged/additive setting against EP Locations. |
Action / boundary methods (for AI agents and CLI scripts)
Section titled “Action / boundary methods (for AI agents and CLI scripts)”Five actions are exposed as public methods, callable directly without simulating a POST:
| Action | Returns | Auth |
|---|---|---|
geocode($query, $country_hint) | ['success', 'lat', 'lng', 'display_name'], or failure | Admin only via boundary |
finder($filters) | ['success', 'locations', 'center', 'count'] | Public, anonymous-safe |
inventory() | ['success', 'locations'] | Admin only via boundary |
preview_node($location_id) | ['success', 'json_ld'] | Admin only via boundary |
validate_hours($hours) | ['success', 'errors', 'schema'] | Admin only via boundary |
Each has a paired <name>_post() boundary that reads $_POST, performs the auth check where applicable, and echoes JSON. PageMotor’s framework CSRF handles request validation for authenticated POSTs automatically; the public finder is anonymous-safe by design.
Troubleshooting
Section titled “Troubleshooting”“I added a Location but it’s not appearing in the finder”
Section titled ““I added a Location but it’s not appearing in the finder””Check three things in order: (1) is the Location’s status set to Live? (2) does it have geo coordinates filled in? (the finder needs lat/lng to plot the marker); (3) is the EP Locations Master Switch on?
“The map is blank”
Section titled ““The map is blank””Most likely no MapTiler API key. Go to EP Locations → Settings → Finder and paste your MapTiler key. The map style dropdown will populate too.
“The consent gate is blocking my finder even though I’m an admin”
Section titled ““The consent gate is blocking my finder even though I’m an admin””Admins are not exempt from the consent gate. Click Open cookie settings in the gate, accept analytics, and the finder will load. Or temporarily deactivate EP GDPR to test the finder without the gate.
“Look up coordinates fills in 0, 0 (or wrong location)”
Section titled ““Look up coordinates fills in 0, 0 (or wrong location)””The address was too vague for Nominatim. Add more specificity (full street, town, country) and try again. Manual entry of lat/lng always wins, Nominatim is a one-shot helper.
“Two Organization nodes are emitting on every page”
Section titled ““Two Organization nodes are emitting on every page””EP SEO and EP Locations are both emitting an Organization. Switch the Coexistence mode under EP Locations → Settings → Integration to Merged, save, and reload.
“A branch I deleted is still showing up in Google search results”
Section titled ““A branch I deleted is still showing up in Google search results””Schema only controls what you tell search engines today. Google’s index updates on its own crawl cycle, typically 1–4 weeks. Be patient or request re-crawl through Google Search Console.
Feedback and corrections
Section titled “Feedback and corrections”For a quick question about this plugin, EP Support inside your admin is the fastest option. The chat widget sits on every EP plugin settings page and knows which one you’re on, with starter questions and links preloaded for that exact screen.
For anything bigger, a bug report, a feature request, or a “how do I…” that needs a real reply, open a ticket at help.elmspark.com. A real person, helped by AI, writes the reply. Usually within a few hours. Tickets don’t disappear into the void.
Changelog
Section titled “Changelog”1.2.20
Section titled “1.2.20”- Settings language menu. The language menu in this plugin’s settings now lists only the languages it is actually translated into, plus English, so you can no longer pick a language that changes nothing.
- Danish. Adds a Danish translation.
- Section status. Each settings section shows whether that feature is switched on. On a site that also runs an older EP plugin, the section shows its plain title instead.
- The Docs link on the Plugins screen now opens this plugin’s page on documentation.elmspark.com.
1.2.19
Section titled “1.2.19”- Fixes “Your session has expired. Please reload to ensure your security.” on PageMotor 0.11. The message appeared on this plugin’s admin screens even though you were signed in perfectly normally, and whatever you were doing failed to save.
- Nothing was wrong with your session. PageMotor 0.11 started handling part of the security check that this plugin was already handling itself, and the two together made every save look invalid. The plugin now checks whether PageMotor has already done it.
- Visitors who were not signed in were never affected, on any version.
- There is nothing to reconfigure, and nothing else changed.