=== Open Door Directory ===
Contributors: rippkedesign
Tags: directory, resources, map, nonprofit, community
Requires at least: 6.3
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.1.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Publish a searchable, filterable directory of local resources (food, shelter, health, legal aid and more) with an interactive map.

== Description ==

Open Door Directory helps nonprofits, libraries, churches, United Ways, and similar organizations publish a directory of local resources that people can actually use.

Staff add resources in wp-admin. Then a single shortcode on any page shows a filterable list, an interactive map, or both.

= Features =

* **Resources** with contact details, address, map location, weekly hours, an hours note for irregular schedules, a logo, and an "Info verified" date.
* **Hierarchical categories** (for example Food, then Food Pantries and Hot Meals). Ten starter categories are created on activation.
* **Find coordinates** from an address with one click, then fine tune by dragging the pin on a preview map.
* **Hours grid** with a "Copy Monday to weekdays" helper. Overnight and 24 hour schedules are supported.
* **Holiday closures and special hours** for specific dates, with an optional note such as "Thanksgiving". They override the weekly hours, show as a heads-up on cards during the week before, and appear in an "Upcoming schedule changes" list on the resource page. Past dates clean themselves up.
* **Open places first.** The directory lists resources open now first, then those opening later today, then closed ones, so visitors see help they can reach right away.
* **Plain-language status** on every listing, such as "Open until 5:00 pm", "Opens at 9:00 am", or "Closed until Monday at 9:00 am".
* **Before you go details**: who the service is for, what to bring, languages spoken, and service features such as Free, Walk-ins welcome, and No ID required. Visitors can filter by feature and by language.
* **Print and share.** "Print list" turns the filtered results into a clean two-column handout for caseworkers, libraries, and front desks. Each resource has a Share button that opens the phone's share sheet or copies the link.
* **Safety tools** for organizations that list sensitive services: an optional "Leave this site" quick exit button (visitors can also press Escape twice) and an optional crisis banner (911 and 988 by default).
* **Confidential locations.** Mark a resource such as a domestic violence shelter as confidential and its street address, ZIP code, map pin, and directions are hidden on the directory, the resource page, search engine data, and the public API. Only the city and phone number are shown. Staff still see the full address in wp-admin.
* **Search engine friendly.** Each resource page includes schema.org structured data with its address, phone, hours, and holiday hours, so search results can show accurate information.
* **Connect AI assistants.** Claude, ChatGPT, and other assistants that support MCP can search your published resources and answer questions such as "Which food pantries are open right now?" with live hours. It is read-only, hides confidential addresses, and can be turned off.
* **Languages** as checkboxes for 18 common languages plus Other, and an "Interpreter available on request" feature.
* **Spanish** translation of everything visitors see, built in.
* **Filter bar** with keyword search, a category dropdown, and an "Open now" toggle calculated in your site's timezone.
* **Map view** powered by Leaflet, bundled with the plugin. Filtering updates the list and the map together, and clicking a card focuses its marker.
* **Single resource pages** automatically show a details block with contact info, address, hours table, directions, and a map.
* **Appearance settings** for button color and shape, card shape and shadow, text, link, and background colors, and font, with a live preview and a built-in color contrast check.
* **Works with any theme**, including block themes, Divi, Elementor, and Bootstrap based themes. Styles are scoped to the directory and do not rely on theme CSS.
* **Accessible**: labeled filters, keyboard support, a live region that announces result counts, and color contrast designed to meet WCAG 2.1 AA. The list is the primary interface, and the map is an enhancement.
* **Works without JavaScript**: the full list renders as plain HTML.
* **No tracking.** The only external requests are the map tiles and the admin "Find coordinates" button. Get directions links open Google Maps only when clicked.
* **Developer friendly**: a PHP data API, a read-only REST endpoint, and filters and actions for extending the plugin.

= Shortcode =

`[opendoor_directory]`

The easiest way to set it up is Resources > Settings > Add to a page: choose the layout, categories, and tools, and copy the ready-made shortcode.

Options:

* `view`: `both` (default), `list`, or `map`
* `category`: comma separated category slugs to limit the directory, for example `category="food,health"`. Child categories are included.
* `filters`: `yes` (default) or `no` to hide the filter bar
* `columns`: `1` (default), `2`, or `3` for the list layout. Columns collapse automatically on narrow screens.
* `map_height`: any CSS height, default `450px`
* `sort`: `open` (default, open now first) or `name` (A to Z)
* `print`: `yes` (default) or `no` to hide the Print list button
* `quick_exit`: `yes` (default) or `no` to hide the quick exit button on this page, when it is turned on in Settings
* `banner`: `yes` (default) or `no` to hide the crisis banner in this directory, when it is turned on in Settings
* `heading`: `h3` (default), `h2`, or `h4` for resource names on cards, to fit the headings around the directory on your page

Example: `[opendoor_directory view="list" columns="2" category="food"]`

You can place several directories on one page. Each works independently.

= For developers =

* `opendoor_get_resources( array $args )` returns normalized resource arrays, including a `status` (state, rank, and label). Arguments: `category`, `search`, `open_now`, `limit`, `orderby` (`name` or `open`), `features` (for example `free,walk_in`), `language`.
* `opendoor_get_resource( int $post_id )` returns one normalized resource.
* `opendoor_is_open_now( int $post_id, ?DateTimeInterface $at = null )` checks open status in the site timezone.
* REST: `GET /wp-json/opendoor/v1/resources` with the same arguments.
* Abilities (WordPress 6.9+): `opendoor/search-resources`, `opendoor/get-resource`, and `opendoor/list-categories`, marked public and read-only.
* MCP: `POST /wp-json/opendoor/v1/mcp` (Streamable HTTP, stateless, protocol versions 2025-03-26 through 2026-07-28) with the tools `search_resources`, `get_resource`, and `list_categories`.
* Filters: `opendoor_resource_fields`, `opendoor_query_args`, `opendoor_card_html`, `opendoor_directions_url`, `opendoor_max_resources`, `opendoor_settings_fields`, `opendoor_settings_sections`, `opendoor_settings_tabs`, `opendoor_settings_section_tab`, `opendoor_appearance_css`, `opendoor_service_features`, `opendoor_share_text`, `opendoor_schema_type`, `opendoor_schema_data`, `opendoor_schema_page_data`, `opendoor_post_type_args`, `opendoor_taxonomy_args`, `opendoor_template_path`, `opendoor_language_options`, `opendoor_ai_enabled`, `opendoor_ai_tools`, `opendoor_ai_resource`, `opendoor_ai_reminders`, `opendoor_mcp_instructions`, `opendoor_mcp_rate_limit`, `opendoor_mcp_client_ip`, `opendoor_mcp_allow_origin`.
* Actions: `opendoor_loaded`, `opendoor_meta_box_after`, `opendoor_settings_after`.
* JavaScript: each directory fires a bubbling `opendoor:filter` event when a visitor changes the filters, with `detail` containing `search`, `category`, `categoryLabel`, `language`, `features`, `openNow`, and `count`.
* Templates: copy `card.php`, `popup.php`, or `single-details.php` from the plugin's `templates` folder into `your-theme/open-door-directory/` to customize the markup.
* Colors: override CSS custom properties such as `--opendoor-accent` on `.opendoor-directory` and `.opendoor-details`.
* WP-CLI: `wp opendoor seed [--count=20]` creates sample resources around Ames, Iowa for testing, and `wp opendoor reset` removes them.

= Credits =

Developed by Matter & Craft (https://matterandcraft.com). Based on the original Missionist code by Bennett Stone of Code & Pixel (https://codeandpixel.com).

== Source code and libraries ==

The plugin's own JavaScript and CSS are unminified and readable in the plugin folder.

The plugin bundles Leaflet 1.9.4 (BSD-2-Clause license), unmodified, in `assets/vendor/leaflet/`. The minified file is the official release build; its human-readable source is at https://github.com/Leaflet/Leaflet/tree/v1.9.4 and the release is listed at https://leafletjs.com/download.html.

== External services ==

This plugin connects to two external services. Nothing else is sent anywhere, and there is no tracking.

= Map tiles (OpenStreetMap by default) =

Maps on your site load image tiles from a tile server. By default this is the OpenStreetMap tile server (tile.openstreetmap.org). When a visitor views a page with a map, their browser requests tiles directly from the tile server. That request includes the visitor's IP address and browser details, along with the map area being viewed.

You can change the tile server in Resources > Settings. OpenStreetMap's tile servers are run by volunteers and are not meant for heavy production use. Busy sites should switch to a commercial or self-hosted tile provider.

* OpenStreetMap Tile Usage Policy: https://operations.osmfoundation.org/policies/tiles/
* OpenStreetMap Foundation Privacy Policy: https://osmfoundation.org/wiki/Privacy_Policy

= Geocoding (OpenStreetMap Nominatim) =

When a site editor clicks "Find coordinates" while editing a resource, your server sends the resource's address to the Nominatim service (nominatim.openstreetmap.org) and receives coordinates. This happens only when the button is clicked, never for site visitors. Results are cached for a week, and requests are limited to one per second, per Nominatim's usage policy.

* Nominatim Usage Policy: https://operations.osmfoundation.org/policies/nominatim/
* OpenStreetMap Foundation Privacy Policy: https://osmfoundation.org/wiki/Privacy_Policy

== Installation ==

1. Upload the `open-door-directory` folder to `/wp-content/plugins/`, or install it from the Plugins screen.
2. Activate the plugin.
3. Go to Resources > Add Resource and add your first resource.
4. Add `[opendoor_directory]` to any page.
5. Optional: go to Resources > Settings to set the default map center, tile provider, and which fields show on cards.

== Frequently Asked Questions ==

= How many resources can the directory handle? =

Filtering happens in the visitor's browser, which keeps it fast and simple for directories of up to a few hundred resources. By default a directory shows at most 500 resources, and Resources shows a warning in wp-admin if you publish more than that. Larger networks can split listings into category directories (for example `category="food"`), and developers can change the limit with the `opendoor_max_resources` filter. Server side filtering for very large directories is a possible future enhancement.

= How is "Open now" calculated? =

It uses the timezone set in Settings > General. A closing time earlier than the opening time means the resource is open past midnight. Setting the same opening and closing time means open 24 hours. Leave both times empty if hours are unknown for a day. The status label and the open-first order are recalculated in the visitor's browser when the page loads, and the label refreshes every minute, so both stay correct even with page caching. The list order does not shift while someone is reading; it updates on the next page load.

= What does the quick exit button do? =

When turned on in Resources > Settings > Safety, a "Leave this site" button stays in the corner of directory and resource pages. Clicking it, or pressing Escape twice, immediately sends the visitor to a neutral page (a weather search by default) and replaces the current page in the browser history, so pressing Back does not return to your directory. It cannot erase browsing history that already exists, so organizations serving survivors of abuse should still share guidance on clearing browser history.

= Can visitors print a list of resources? =

Yes. Filter the directory, then click "Print list". The printout includes only the resources that match, in two columns, with your site name, the filters used, and the date. Each resource page also has its own Print button.

= How do I close for a holiday or set special hours for one day? =

Edit the resource and use "Holiday closures and special hours" under Hours. Add a date, then either check Closed or enter that day's hours, plus an optional note. That date replaces the regular weekly hours everywhere: the status label (for example "Closed today (Thanksgiving)"), the Open now filter, the open-first order, and the API.

= What if a resource has irregular hours? =

Use the Hours note field, for example "2nd and 4th Tuesdays" or "Call for appointment". The note appears with the hours on cards and on the resource page.

= Is it available in Spanish? =

Yes. The parts visitors see (open status, filters, cards, and resource pages) are included in Spanish and switch automatically when your site language is Spanish. Resource names and descriptions appear in whatever language your staff writes them. The Spanish translation is new and still being reviewed by native speakers; corrections are welcome.

= Can AI assistants like Claude or ChatGPT use my directory? =

Yes. Under Resources > Settings > General > AI assistants, copy the connector URL (your site address followed by `/wp-json/opendoor/v1/mcp`). Anyone can add it as a custom connector in an assistant that supports MCP, then ask questions such as "Where can I get a free meal tonight?" The assistant sees only published resources, never confidential addresses, and cannot change anything. It is told to suggest calling ahead and to point people in danger to 911 or 988. Searches are not logged. To limit traffic, a salted hash of each visitor's IP address is kept for one minute. Uncheck "Allow AI assistants to search this directory" to turn it off.

= Can I list a shelter without showing its address? =

Yes. Check "Confidential location" in the resource's Location settings. The street address, ZIP code, map pin, and directions link are hidden everywhere, including search engine data and the public API. Visitors see the city and phone number only, and staff still see the full address in wp-admin.

= Can resources without an address or coordinates be listed? =

Yes. They appear in the list but not on the map.

= Does it work with my theme or page builder? =

The directory is a shortcode and the resource details are added through the standard content filter, so it works with any theme that displays post content. It has been tested with Twenty Twenty-Five, Twenty Twenty-Four, Divi (including the Divi Builder text module), Elementor (Shortcode and Text Editor widgets), and Bootstrap 5 based themes.

= How do I change the colors and shapes? =

Go to Resources > Settings > Appearance. You can set the button color, hover color, and text color, choose square, rounded, or pill buttons, choose square or rounded cards, add a soft card shadow, and set text, link, card, border, and filter bar colors. A live preview shows your changes, and a contrast check warns when a color combination would be hard to read (WCAG AA needs 4.5:1). "Reset to defaults" restores the original look.

Developers can also override the CSS custom properties in a theme, for example:

`.opendoor-directory, .opendoor-details { --opendoor-accent: #6b21a8; --opendoor-accent-hover: #4c1d95; }`

= Can I change the map provider? =

Yes. Go to Resources > Settings and enter a tile URL template that includes `{z}`, `{x}`, and `{y}`, plus the attribution your provider requires.

= Can I customize the "Get directions" link? =

By default it links to Google Maps directions. Developers can change it with the `opendoor_directions_url` filter.

= What happens to my data if I delete the plugin? =

Nothing, unless you check "Delete all data on uninstall" in Resources > Settings. With that checked, deleting the plugin removes every resource, category, and setting. Uploaded logos stay in the Media Library.

== Screenshots ==

1. Directory with filters, map, and list side by side.
2. Resource cards with open status, hours, and directions.
3. Single resource page with details, hours table, and map.
4. Resource Details meta box with geocoding and the hours grid.
5. Settings page.
6. Appearance settings with live preview and contrast check.
7. Filter chips for service features and language.
8. Printed handout of filtered resources.
9. Quick exit button and crisis banner.
10. Shortcode builder on the Add to a page tab.

== Changelog ==

= 1.1.1 =
* Improved: AI assistant searches match any of the words asked, including simple plurals, and rank the best matches first, so "free food" finds every food resource.
* Improved: The search tool tells assistants which directory and categories it covers, so they use it for questions like "where can I get free food".
* Improved: Search results remind assistants to share phone numbers, suggest calling ahead, flag unverified listings, and point people in danger to 911 or 988.

= 1.1.0 =
* New: Connect AI assistants. A read-only MCP endpoint and WordPress abilities let Claude, ChatGPT, and other assistants search your published resources. On by default, with a setting to turn it off.
* New: Languages are checkboxes for 18 common languages, plus Other for anything else.
* New: "Interpreter available on request" service feature.
* New: Spanish translation of everything visitors see, used until a WordPress.org translation is available.
* New: Resource pages tell search engines when the information was last verified.

= 1.0.1 =
* New: Confidential locations. Hide the street address, map pin, and directions for shelters whose location must stay private.
* New: Press Escape twice to use the quick exit.
* New: `heading` shortcode option for card titles.
* New: wp-admin warns when you publish more resources than a directory can show.
* Changed: A day with no listed hours now reads "Hours not listed for today" instead of "Closed", and a midnight closing reads "Open until midnight".
* Changed: The hours editor warns about rows with only an opening or closing time.
* Fixed: Today's row in the hours table stays correct on cached pages.
* Fixed: Result counts use correct plural forms in every language.
* Improved: More consistent sample data from `wp opendoor seed`.

= 1.0.0 =
* Initial release.

== Upgrade Notice ==

= 1.1.1 =
Better AI assistant answers: smarter search and clearer guidance.

= 1.1.0 =
Lets AI assistants search your directory, adds language checkboxes and an interpreter feature, and includes Spanish.

= 1.0.1 =
Adds confidential locations for shelters whose address must stay private.

= 1.0.0 =
Initial release.
