=== Ultimate Classified Listings – Classifieds, Directory & Marketplace ===
Contributors: webcodingplace
Tags: classifieds, directory, listings, classified ads, marketplace
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 2.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Free classifieds & directory plugin with maps, frontend submission, AJAX search, packages-ready architecture and a hardened security model.

== Description ==

Ultimate Classified Listings is a responsive classifieds and directory plugin for WordPress. It ships nice, clean templates and built-in features like unlimited custom fields, gallery sliders + grids, advanced AJAX search and Google Maps or OpenStreetMap support out of the box.

It's also a full listing management system: register sellers, approve submissions, manage seller profiles, and run your own classifieds, jobs, real-estate, services or community marketplace.

= Why Ultimate Classified Listings =

* **Free where the leaders paywall** — maps, AJAX search, frontend submission and custom fields are all free.
* **Stable and secure** — A comprehensive security hardening pass: CSRF nonces on every AJAX endpoint, plaintext passwords no longer stored in options, email header injection closed, and rate limiting on auth flows.
* **Theme-friendly** — full template override system, works with any WordPress theme.
* **Multilingual ready** — WPML and Polylang aware.
* **Developer friendly** — 40+ hooks and filters, REST API support on the listing CPT.

= Core Features =

* Frontend listing submission with custom field sections
* Unlimited custom fields and field sections (drag-and-drop builder)
* Categories and tags with custom icons / images
* Gallery sliders and image grids
* AJAX-powered search with filters, price range and sort
* **Map search** — results plotted on a clustered map with a "Near me" / radius filter
* **One-click demo importer** — seed realistic sample listings from the setup wizard
* Google Maps **or** OpenStreetMap (Leaflet) with location markers
* Seller dashboard: add / edit / delete listings, profile management
* Two seller-registration modes: auto-approve or manual approval
* Two submission modes: publish immediately or pending review
* Listing comparison
* Contact-seller form with reCAPTCHA + email-injection protection
* Compare and share listings
* Full theme template overrides under `/your-theme/ucl/...`

= Quick Links =

- **[Live Demo](https://www.wpclassifiedlistings.com/demo/)**
- **[Documentation](https://www.wpclassifiedlistings.com/documentation/)**
- **[AJAX Search](https://www.wpclassifiedlistings.com/demo/search-listings-ajax/)**

== Installation ==

1. Navigate to the WordPress Dashboard -> Plugins -> Add New
2. Search for the Ultimate Classified Listings
3. Install and Activate
4. Now configure the settings from the Ultimate Classified Listings -> Settings

== Frequently Asked Questions ==

= Is this plugin free? =

Yes. The core plugin is free, including maps, AJAX search, frontend submission, and custom fields.

= Does it work with Elementor, Gutenberg or Divi? =

The plugin uses shortcodes that work in any builder. Native Gutenberg blocks are on the v3.0 roadmap.

= Do I need a Google Maps API key? =

No. The plugin ships OpenStreetMap via Leaflet by default. If you prefer Google Maps, you can add your API key in Settings.

= Can I let users pay to post listings? =

Built-in payment gateways are on the v3.0 roadmap. In v2.x you can use Settings → Submission Mode to require admin approval before listings are published.

= How are seller passwords stored? =

As of 2.0, passwords are no longer stored in plain text in `wp_options`. Newly registered users that require manual approval have their password hashed before storage. Legacy entries from prior versions will receive a one-time password reset email when approved.

= How does the plugin protect against CSRF? =

Every AJAX endpoint requires a nonce verified by `check_ajax_referer()`. Public requests use a dedicated public nonce and admin requests use a separate admin nonce, both rotated by WordPress.

= I'm migrating from another plugin. Is there a migration tool? =

Migration tools from Directorist, WP Adverts and Classified Listing are on the v3.x roadmap. For now you can use WP All Import/Export/

= Can I try it with sample content? =

Yes. The setup wizard offers a one-click "Load sample listings" option that seeds a handful of realistic listings — complete with categories, prices and map locations — tailored to the site type you pick. You can remove them all again in one click from the wizard's final screen whenever you're ready to add your own.

= How do I show listings on a map? =

Add the `[uclwp_search_map]` shortcode to any page (the auto-created Search page includes it by default), or enable it inline on results with `[uclwp_search_results map="enable"]`. The map plots every matching listing that has coordinates, clusters nearby pins automatically, and — when enabled — offers a "Near me" button plus a radius selector so visitors can filter to listings around them.

== Screenshots ==

1. Listings archive
2. Sections Manager
3. Fields Builder
4. Frontend Management

== Changelog ==

= 2.1 =

- Feature: One-click demo importer. The setup wizard now offers a "Load sample listings" option that seeds a small set of realistic, published listings for the chosen site type (classifieds, real estate, jobs, local services or automotive). Each sample carries a category, price, custom fields and real map coordinates deliberately clustered across several metros so the new map view has something to show immediately. Every imported post is flagged with `_ucl_sample` and tracked in the `uclwp_sample_ids` option; a "Remove sample listings" button on the wizard's final screen deletes the whole set in one click. No external images are fetched, so the importer works on sites with no outbound network.
- Feature: Map-based search results with clustering. New `[uclwp_search_map]` shortcode renders the listings matching the current search on an interactive Leaflet map, using the bundled `leaflet.markercluster` library to group nearby pins. Each pin opens a popup with the listing thumbnail, title, city, price and a "View listing" link. The map reads the same query args as `[uclwp_search_results]`, so it honours the active filters. Also available inline via a new `map="enable"` attribute on `[uclwp_search_results]` (with `map_height`). The auto-created Search page now includes the map between the form and the results.
- Feature: "Near me" / radius search. The map's optional toolbar adds a geolocation button and a radius selector (km). Choosing a radius reloads with `ucl_lat` / `ucl_lng` / `ucl_radius` query args, and the search query now restricts results to a bounding box around that point (coordinates cast as `DECIMAL` so fractional degrees aren't truncated). Radius presets are filterable via `uclwp_search_map_radius_presets`, the value via `uclwp_search_radius_km`, and the marker cap via `uclwp_search_map_max_markers`.
- Both features are backward compatible: existing installs see no change until they enable the map shortcode/attribute or run the importer, and the map template is theme-overridable at `/your-theme/ucl/shortcodes/search/map.php`.

= 2.0 - 14 June 2026 =

- Bug fix: When the archive/search-results top bar was rendered (via `[uclwp_search_results top_bar="enable"]` or anywhere the `uclwp_archive_topbar` action fires) and a visitor changed the sort dropdown, the resulting URL kept only `sort_by` and `layout` and dropped every other active query arg — keywords, price range, custom-field filters, etc. The top bar now re-emits every other `$_GET` value as a hidden input inside the form (using PHP bracket notation for nested sub-arrays like `regular_price[min]`), so the sort round-trip preserves the active filter set. The grid/list switcher anchors already used `add_query_arg()` and were unaffected.
- Added: New helper `uclwp_flatten_query_args()` that flattens nested arrays into `name[sub][sub]` / value pairs suitable for hidden form inputs. Reusable from any template that needs to pass current GET state through a `<form method="GET">`.

= 1.16.0 =

- Feature: Two new single-listing styles. Pick one in Settings → Templates Settings → Single Listing Style. Style 1 (Classic, default) is the existing look. Style 2 (Card Stack) renders each section as a raised card with a coloured left rail and a chip-style heading — e-commerce / property-listing feel. Style 3 (Minimal) drops the card chrome entirely and separates sections with thin top dividers. Implemented as a body class (`ucl-listing-style-1/2/3`) added on single-listing pages; templates are unchanged so all theme overrides keep working.
- Feature: Price quick-pick chips are now editable from the admin. New "Price Quick-Pick Presets" textarea in Settings → Search Settings (right after Total Listings) — one numeric preset per line; each becomes a "Up to {price}" chip under the price range control in search styles 2 and 3. Leave blank to hide the chips entirely. Still filterable in code via `uclwp_search_price_chips`.
- Feature: `[uclwp_search_results]` now supports a `top_bar="enable"` attribute (default `disable`) that adds the same sort + grid/list switcher the `[uclwp_listings]` shortcode renders.
- Feature: Per-listing map zoom level is now saved. The admin metabox map writes the user-chosen zoom into a hidden `ucl_listing_zoom` input (Leaflet `zoomend` and Google `zoom_changed` events both wired up); both the admin metabox save path and the frontend create/edit handler persist it; the single-listing template reads it back and the map opens at exactly that zoom on the frontend.
- Improvement: Search style 2 polish — reduced cell min-height from 46px to 38px, button is now a properly sized pill (not a tall circle), inputs no longer have huge top/bottom padding. New `columns` shortcode attribute (1–6, default 4) controls how many secondary-field cells appear per row via CSS grid. Mobile collapses to 2 columns at ≤768px and 1 column at ≤480px.
- Bug fix: Card image aspect ratio (Settings → Listings → Card Image Aspect Ratio) was not being applied. The CSS selector keyed off `[style*="--ucl-card-image-ratio"]`, which never matched because the variable is emitted via an inline `<style>` tag, not the element's `style` attribute. The aspect-ratio rule now applies directly to `.uclwp-grid-box-wrap .uclwp-image` and `.uclwp-list-box-wrap .uclwp-image`, so all four card styles honour the chosen ratio.

= 1.15.0 =

- Feature: Two new search-form styles, selectable via `[uclwp_search_form style="2"]` and `[uclwp_search_form style="3"]`.
  - **Style 2 — Bar**: Compact horizontal pill bar with rounded inputs, designed to sit above an archive grid. Trulia/Zillow feel.
  - **Style 3 — Hero**: Tall hero panel with a prominent keyword box, neat secondary-field grid below, and a final CTA row with a "Clear filters" link. Designed to anchor landing or category-archive pages. Configurable heading + sub-heading via the new `search_form_heading` / `search_form_subheading` settings.
- Feature: New `uclwp_render_price_range_search()` helper renders a modern two-input price control with the currency symbol affixed inside each input, plus optional quick-pick chips ("Up to $100", "Up to $500", etc.). Used by styles 2 and 3. Preset chip values are filterable via `uclwp_search_price_chips`.
- Improvement: Equal-height listing cards. Cards within the same row now stretch to match the tallest neighbour and push price/CTA footer rows to the bottom, so grids look tidy even when titles/excerpts vary in length. Scoped to `.ucl-display-listings` so single-listing sidebars and the compare box are unaffected.
- Bug fix: `[uclwp_categories]` (and the underlying `render_category_image()`) no longer renders the saved Bootstrap-icon class. The previous wrapper produced `class="bi bi-{bi bi-house}"`, an invalid class that resolved to nothing. Output is now `class="bi bi-house"` (or the admin-set value). Categories with a saved icon will start rendering it immediately on update.
- Bug fix: Long seller email addresses were overflowing the contact box in the single-listing sidebar. The `<li>` rows now apply `overflow: hidden; text-overflow: ellipsis; white-space: nowrap;` and pick up the missing right padding so the email truncates inside the bordered chip.

= 1.14.2 =

- Bug fix: When creating a new listing in wp-admin, Leaflet threw `Invalid LatLng object: (undefined, undefined)` and the map widget failed to render. Root cause was the v1.14.1 admin-enqueue fix, which preloaded the frontend single-listing map script (`assets/js/location.js`) on the post-edit screen. That script expects a different localize payload (`ucl_location_settings.latitude/longitude`) than the metabox renderer provides (`ucl_map_settings.def_lat/def_long`), so its values came up undefined. The redundant enqueue has been removed — the location field renderer enqueues the correct script (`assets/fields/location.js`) on its own when the section actually renders.
- Bug fix: Belt-and-braces hardening of the location renderer so that even if a stored listing has empty latitude/longitude meta the JS still receives a numeric default (US geographic centre, 37.0902 / -95.7129). The previous longitude default lost its negative sign so the placeholder pin showed up in central Asia instead of Kansas.
- Bug fix: `Control.Geocoder.js` now declares `ucl-leaflet-js` as a dependency so the geocoder loads after Leaflet on the post-edit screen.

= 1.14.1 =

- Bug fix: The Listing Information metabox on the wp-admin post-edit screen (`post.php` / `post-new.php` for the `uclwp_listing` CPT) was rendering without Bootstrap CSS, the icon font, the iconpicker JS, the media uploader, or the map widget. The `load_admin_scripts()` enqueue gate previously matched only the plugin's own subpages (slug contained "uclwp"); it now also enqueues all required assets when the current admin screen's `post_type` is `uclwp_listing`. Bootstrap stays scoped to `.uclwp-bs-wrapper` so it does not affect the surrounding wp-admin UI.

= 1.14.0 =

Final WordPress.org Plugin Check pass — clears all reported errors for plugin-directory submission.

- Plugin name: Renamed to "Ultimate Classified Listings – Classifieds, Directory & Marketplace" so the readme title matches the plugin header and the word "Plugin" (a restricted term) is no longer part of the displayed name.
- Tested up to: bumped to 7.0 to match the current WordPress release as required by .org.
- Escaping: Wrapped the dynamic CSS variable block emitted by `assets/css/styles.php` through `wp_strip_all_tags()` after each value is individually escaped via `esc_attr()`, with phpcs:ignore annotations explaining why the final concatenation is safe.
- Escaping: `paginate_links()` output in `render_pagination()` now passes through `wp_kses_post()` before echoing.
- i18n: Added `/* translators: %s ... */` comments above two `printf( esc_html__( ... ), ... )` calls in the setup-wizard "next steps" panel that were missing them.
- Sanitization: Added `sanitize_text_field( wp_unslash( ... ) )` wrappers around `$_REQUEST['listing_id']` reads in `class-shortcodes.php delete_listing`, `class-email.php contact_seller`, `class-favorites.php ajax_toggle`, and `shortcodes/dashboard/edit.php`. The values are still validated through `uclwp_validate_listing_id()`.
- Sanitization: `$_SERVER['REQUEST_URI']` used to build the favorites login-redirect URL now passes through `esc_url_raw( wp_unslash( ... ) )` before `home_url()`.
- Sanitization: Recursive array inputs (`$_POST['uclwp_data']`, `$_REQUEST['sections']`, `$_REQUEST['fields']`) now have phpcs:ignore annotations documenting that each leaf is sanitized further down. The actual per-leaf sanitization was already in place.
- Sanitization: Login/register password reads carry phpcs:ignore annotations explaining why passwords must not be passed through `sanitize_text_field()` (would strip control characters needed for hash comparison).
- File uploads in seller registration carry phpcs:ignore annotations pointing to the nonce check at the top of the handler.

= 1.13.0 =

Second WordPress.org Plugin Check pass — addresses the warnings flagged by the official PCP tool that the manual sanitization audit missed.

- Plugin Check: Added `defined( 'ABSPATH' ) || exit;` guard to 32 files that didn't have one, including all template files, shortcode partials, admin subviews, the inc/arrays/ data files and assets/css/styles.php.
- Plugin Check: Replaced all `json_encode( $resp )` AJAX response calls with `wp_send_json( $resp )` so character encoding follows WP's UTF-8 conventions and the response is properly closed via `wp_die()`.
- Plugin Check: Replaced remaining non-AJAX `json_encode()` calls (Slick / image-grid data-attributes) with `wp_json_encode()`.
- Plugin Check: Added explicit `timeout` argument (10 s) to every `wp_remote_post()` call.
- Plugin Check: Added `rel="noopener noreferrer"` to every `target="_blank"` link in the admin settings help text, the post-update messages, and the setup-wizard "next steps" panel.
- Plugin Check: Removed `load_plugin_textdomain()` — WordPress 4.6+ loads translations automatically for plugins hosted on .org and the call is now flagged as discouraged.
- Plugin Check: Replaced bare `die();` / `die(0);` with `wp_die();` in AJAX handlers.
- Plugin Check: Added missing version arguments to every `wp_enqueue_style` / `wp_enqueue_script` call (used `UCLWP_VERSION` for bundled assets and `null` for third-party CDN scripts whose URL already carries a version key).
- Plugin Check: Renamed three filters that were missing the plugin prefix: `no_results_message` → `uclwp_no_results_message`, `seller_contact_email_message` → `uclwp_seller_contact_email_message`, `raw_uclwp_price` → `uclwp_raw_price`, `formatted_uclwp_price` → `uclwp_formatted_price`.
- Plugin Check: Removed nested `esc_attr( sanitize_text_field( … ) )` double-escape pattern in the search-form field renderer; the value is now sanitized once on input and the existing `esc_attr()` at output point handles HTML-attribute escaping.
- Plugin Check: Google-Maps script URL now built via `add_query_arg()` instead of string concatenation, and routed through HTTPS unconditionally.
- Plugin Check: Direct `$wpdb->update()` call in seller-approve flow now has type-specifier arrays for $where and $data and a justification comment explaining why the WP user-API helpers can't be used (the password is already hashed).

= 1.12.0 =

This release is a full sanitization + escaping pass for WordPress.org plugin review compliance. No behaviour changes; every output path is now explicitly escaped and every input path is unslash + sanitized.

- Sanitization: All `$_GET` / `$_POST` / `$_REQUEST` access across the plugin now runs through `wp_unslash()` before sanitization, matching WordPress core conventions and `WPCS` requirements.
- Escaping: Replaced `esc_attr_e()` / `esc_attr__()` with the correct `esc_html_e()` / `esc_html__()` everywhere the output is HTML body content (labels, table cells, headings, paragraph text). Attribute-context calls (`placeholder=""`, `title=""`, `value=""`, `data-*=""`) remain `esc_attr_e()`.
- Escaping: Replaced `esc_attr()` with `esc_html()` on user-controlled body text — listing titles, seller names, emails, phone numbers, category names, etc.
- Escaping: Textarea content now uses `esc_textarea()` instead of `esc_attr()`.
- Bugfix: `compare-box.php` printed nothing for compare table column headers due to `esc_attr( $label )` (which returns instead of echoing). Now properly `echo esc_html( $label )`.
- Hardening: Removed the second hardcoded reCAPTCHA fallback key in `templates/sidebar/default.php`. CAPTCHA now fails closed when not configured.
- Hardening: `gdpr_message` setting in the contact-seller form is now output via `wp_kses_post()` so admins can include safe HTML formatting (was `esc_attr()` which broke the HTML).
- Hardening: Search query builder (`uclwp_get_search_query`) sanitizes all dynamic `orderby`, `meta_key`, `tag`, and `range` inputs and validates against an allow-list before passing to `WP_Query`.
- Hardening: HTML emails (contact-seller) now `esc_html()` user-controlled fields (client name, email, phone, message, listing title) before HTML interpolation.
- Hardening: Compare AJAX listing IDs cast through `absint()`; meta_key segments through `sanitize_key()`.
- Hardening: `class-shortcodes::update_profile` casts and unslashes every `$_REQUEST` field.
- Hardening: Term-meta image/icon callbacks (`save_category_image`, `updated_category_image`) now check `current_user_can('manage_categories')` and `wp_unslash()` the inputs (core verifies the nonce upstream on the term-edit screen).
- Hardening: Dashboard "My Listings" search query reads `$_GET['uclwp_status']` through an enum allow-list (`publish`, `pending`, `draft`, `future`, `any`).
- Compatibility: `selected()` and `selected( $a, $b )` used in place of inline `($a == $b) ? 'selected' : ''` ternaries where the markup allowed it.
- Compatibility: `(int) $cat->count` and `(int) count_user_posts(...)` casts in `page-sellers.php` and other admin lists in place of `esc_attr()` on integers.

= 1.11.0 =

- Feature: Modern field rendering. Custom fields on single-listing pages now render as icon + label + value cards by default, using each field's saved icon (from the Fields Builder). Two additional display modes are available — "List" (definition-list rows) and "Inline" (compact icon pills). Choose via Settings → Listings → Field Display on Single Listing Pages.
- Feature: Card meta strip redesigned as inline icon pills with the field's icon, replacing the previous flat title-pipe-value rows. Customize which fields appear via the new Card Meta Fields setting (comma-separated keys).
- Feature: Checkbox / features fields now render as accent-coloured chips with check icons, instead of plain bulleted text.
- Feature: Favorites (save listings). Adds a heart button to every listing card. Saved listings are stored per-user and viewable via the new "Saved Listings" item in the seller dashboard or the `[uclwp_favorites]` shortcode. Toggle the feature in Settings → Listings → Favorites.
- Feature: Dark mode. Five options — Off, Auto (follow OS), Toggle (manual button in top-bar), Auto + Toggle, and Force. Six dark-mode colour overrides for surface, card background, card border, heading, body and muted text.
- Improvement: Field-display helpers now resolve safely when a value is empty so cards never render orphan labels.
- Improvement: `uclwp_listing_action_buttons` filter added so add-ons can inject extra card buttons without forking templates.
- Improvement: `uclwp_dashboard_allowed_pages` filter added.

= 1.10.0 =

- Feature: Three new listing card styles. Style 2 — Minimal (flat, bordered, generous whitespace), Style 3 — Bold Overlay (full-bleed image with gradient and meta overlaid), Style 4 — Compact / Magazine (dense layout with prominent CTA). Each ships with both a grid and a list variant. Switch globally from Listings → Listing Card Style, or per-shortcode via `[uclwp_listings style="3"]`.
- Feature: 14 new colour controls under Colors and CSS — Primary, Primary Hover, Secondary, Accent, Heading, Body Text, Muted Text, Card Background, Card Border, Surface, Price, Badge Background, Badge Text, Button Background/Text/Hover. Each is emitted as a CSS custom property on `.uclwp-bs-wrapper` so themes and custom CSS can override them too.
- Feature: Card Corner Radius setting (0–64px).
- Feature: Card Image Aspect Ratio setting (Original / 4:3 / 16:9 / 1:1 / 3:4) — useful when seller-uploaded images vary in dimensions.
- Compat: Existing installs see no visual change. The legacy two-colour palette (Primary / Secondary Color) is preserved and used as the fallback when the new individual settings are blank.

= 1.9.0 =

- Feature: Setup wizard. A short, dismissible welcome screen guides new installs through site type, page creation (Listings, Search, Categories, Seller Dashboard), currency, map provider and submission workflow. Triggered on activation and surfaced via a one-line admin notice until completed.
- Feature: Auto-create essential pages with the correct shortcodes pre-inserted.
- Feature: Polite, non-blocking "leave a review" prompt. Appears only after 7 days + 3 published listings, with Remind Later (14 days) and Permanent Dismiss options.
- Improvement: Admin classes now load only in `is_admin()` context to reduce frontend overhead.

= 1.8.0 =

This is a **major security hardening release**. All sites should update.

- Security: Added CSRF nonce verification to every public AJAX endpoint (`uclwp_search_listing`, `uclwp_seller_login`, `uclwp_seller_register`, `uclwp_create_listing_frontend`, `uclwp_delete_listing`, `uclwp_contact_seller`, `uclwp_compare_listings`).
- Security: Added CSRF nonce verification to admin AJAX endpoints (`uclwp_save_field_sections`, `uclwp_save_custom_fields`, `uclwp_reset_custom_fields`, `wcp_uclwp_save_settings`, `ucl_deny_seller`, `ucl_approve_seller`).
- Security: Pending-seller passwords are no longer stored in plaintext in `wp_options`. New registrations store only a salted hash; legacy entries are routed through the standard password-reset flow on approval.
- Security: Fixed email header injection in the contact-seller form. From: header now sanitized via a dedicated helper, newlines stripped from name, Reply-To added, Cc recipients individually validated.
- Security: Removed the hardcoded reCAPTCHA secret-key fallback. CAPTCHA verification now fails closed when not configured.
- Security: Replaced unvalidated `$_SERVER['REMOTE_ADDR']` use with `filter_var(... FILTER_VALIDATE_IP)`.
- Security: Listing IDs in delete and edit flows are now validated against the `uclwp_listing` post type and authorized via `current_user_can('edit_post'|'delete_post', $id)` instead of manual ownership checks.
- Security: Pending-seller `userindex` is cast to integer and validated against existing keys before use.
- Security: Permalink-settings save path now requires the core `update-permalink` nonce.
- Security: Added transient-based rate limiting on login, registration and contact-seller endpoints.
- Security: Asset URLs now include `UCLWP_VERSION` for cache busting after security updates.
- Bumped minimum WordPress to 6.0 and minimum PHP to 7.4.
- License header reconciled to GPLv2-or-later.

= 1.7 =

- Bug Fixed: Security related issues

= 1.6 =

- Bug Fixed: Security related issues

= 1.5 =

- Bug Fixed: Security issues fixes, notified by Wordfence

= 1.4 =

- Feature Added: Ability to choose category from the frontend
- Feature Added: Ability to assign tags from the frontend
- Bug Fixed: Empty categories are not displaying
- Bug Fixed: Security issues fixes, notified by WPScan Team

= 1.3 =

- Bug Fixed: Security issues fixes, notified by WPScan Team

= 1.2 =

- Bug Fixed: Iframes not saving from frontend

= 1.1 =

- Feature Added: Iframes support in the shortcode field

= 1.0.0 =

- Initial Release

== Upgrade Notice ==

= 2.1 =
Adds a clustered map view for search results with a "Near me" radius filter, plus a one-click demo-listings importer in the setup wizard. Backward compatible.

= 2.0 =
Fixes the archive top bar wiping active search filters when the sort dropdown changes. Strongly recommended for anyone using [uclwp_search_results top_bar="enable"].

= 1.16.0 =
Adds two new single-listing styles, admin-editable price chips, a top bar option on [uclwp_search_results], per-listing map zoom persistence, a columns attribute on search style 2, and a fix for the card image aspect-ratio setting that wasn't applying.

= 1.15.0 =
Adds two new search-form styles (Bar + Hero) with a modern price-range control. Fixes equal-height grid cards, the empty category icon, and overflowing seller emails in the sidebar.

= 1.14.2 =
Fixes the "Invalid LatLng object" Leaflet error when creating a new listing in wp-admin, introduced by the 1.14.1 metabox-assets fix. Strongly recommended for anyone on 1.14.1.

= 1.14.1 =
Bug fix: restores Bootstrap CSS, icons, media uploader and map on the wp-admin Listing Information metabox. No behaviour change anywhere else.

= 1.14.0 =
Release-candidate compliance pass — clears all Plugin Check errors for WordPress.org submission (escape on dynamic CSS, paginate_links output, translator comments, Tested up to 7.0, plugin name rules). No behaviour change.

= 1.13.0 =
Second WordPress.org Plugin Check compliance pass. Adds ABSPATH guards everywhere, wp_send_json/wp_json_encode adoption, wp_remote timeout, rel="noopener noreferrer" on target=_blank links, removes deprecated load_plugin_textdomain, prefixes orphan filters. No behaviour change.

= 1.12.0 =
Full sanitization and escaping audit for WordPress.org plugin review compliance. Fixes one display bug in the compare-listings table; removes a second hardcoded reCAPTCHA fallback key; no breaking changes.

= 1.11.0 =
Field display redesigned on single pages and cards (icon + label + value cards, definition lists, or inline pills). Adds Favorites and Dark Mode. Visually backward-compatible — the new "cards" display becomes the default on single-listing pages.

= 1.10.0 =
Adds three new listing card styles (Minimal, Bold Overlay, Compact), a 14-colour palette wired through CSS variables, card radius and image aspect-ratio settings. Visually backward-compatible.

= 1.9.0 =
Adds a setup wizard, auto-page creation, and a polite review prompt. Builds on the 1.8.0 security hardening.

= 1.8.0 =
Major security hardening release. Adds CSRF protection to every AJAX endpoint, removes plaintext password storage, fixes email header injection, and rate-limits authentication. All sites should update.
