{"id":160100,"date":"2022-08-01T03:18:07","date_gmt":"2022-08-01T03:18:07","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/ultimate-classified-listings\/"},"modified":"2026-06-14T04:03:46","modified_gmt":"2026-06-14T04:03:46","slug":"ultimate-classified-listings","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/ultimate-classified-listings\/","author":17556873,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.0","stable_tag":"2.0","tested":"7.0","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"Ultimate Classified Listings \u2013 Classifieds, Directory & Marketplace","header_author":"WebCodingPlace","header_description":"A simple yet complete classifieds and listings system for WordPress.","assets_banners_color":"d5d9e3","last_updated":"2026-06-14 04:03:46","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/wpclassifiedlistings.com\/","header_author_uri":"https:\/\/webcodingplace.com\/","rating":5,"author_block_rating":0,"active_installs":20,"downloads":3504,"num_ratings":2,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.1":{"tag":"1.1","author":"webcodingplace","date":"2022-09-02 14:49:31"},"1.2":{"tag":"1.2","author":"webcodingplace","date":"2024-06-28 02:29:20"},"1.3":{"tag":"1.3","author":"webcodingplace","date":"2024-06-29 07:10:23"},"1.4":{"tag":"1.4","author":"webcodingplace","date":"2024-09-01 07:50:27"},"1.5":{"tag":"1.5","author":"webcodingplace","date":"2025-02-16 03:58:12"},"1.6":{"tag":"1.6","author":"webcodingplace","date":"2025-03-06 09:55:00"},"1.7":{"tag":"1.7","author":"webcodingplace","date":"2026-06-14 04:03:46"},"2.0":{"tag":"2.0","author":"webcodingplace","date":"2026-06-14 04:03:46"}},"upgrade_notice":{"2.0":"<p>Fixes the archive top bar wiping active search filters when the sort dropdown changes. Strongly recommended for anyone using [uclwp_search_results top_bar=&quot;enable&quot;].<\/p>","1.16.0":"<p>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&#039;t applying.<\/p>","1.15.0":"<p>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.<\/p>","1.14.2":"<p>Fixes the &quot;Invalid LatLng object&quot; 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.<\/p>","1.14.1":"<p>Bug fix: restores Bootstrap CSS, icons, media uploader and map on the wp-admin Listing Information metabox. No behaviour change anywhere else.<\/p>","1.14.0":"<p>Release-candidate compliance pass \u2014 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.<\/p>","1.13.0":"<p>Second WordPress.org Plugin Check compliance pass. Adds ABSPATH guards everywhere, wp_send_json\/wp_json_encode adoption, wp_remote timeout, rel=&quot;noopener noreferrer&quot; on target=_blank links, removes deprecated load_plugin_textdomain, prefixes orphan filters. No behaviour change.<\/p>","1.12.0":"<p>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.<\/p>","1.11.0":"<p>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 \u2014 the new &quot;cards&quot; display becomes the default on single-listing pages.<\/p>","1.10.0":"<p>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.<\/p>","1.9.0":"<p>Adds a setup wizard, auto-page creation, and a polite review prompt. Builds on the 1.8.0 security hardening.<\/p>","1.8.0":"<p>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.<\/p>"},"ratings":{"1":0,"2":0,"3":0,"4":0,"5":2},"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3571494,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3571494,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.jpg":{"filename":"banner-1544x500.jpg","revision":3571494,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.jpg":{"filename":"banner-772x250.jpg","revision":3571494,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.1","1.2","1.3","1.4","1.5","1.6","1.7","2.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":2764251,"resolution":"1","location":"assets","locale":"","width":1592,"height":2538},"screenshot-2.png":{"filename":"screenshot-2.png","revision":2764251,"resolution":"2","location":"assets","locale":"","width":1686,"height":687},"screenshot-3.png":{"filename":"screenshot-3.png","revision":2764251,"resolution":"3","location":"assets","locale":"","width":1692,"height":1132},"screenshot-4.png":{"filename":"screenshot-4.png","revision":2764251,"resolution":"4","location":"assets","locale":"","width":1565,"height":3260}},"screenshots":{"1":"Listings archive","2":"Sections Manager","3":"Fields Builder","4":"Frontend Management"}},"plugin_section":[],"plugin_tags":[4531,4527,2466,4528,8379],"plugin_category":[35,39],"plugin_contributors":[187022],"plugin_business_model":[],"class_list":["post-160100","plugin","type-plugin","status-publish","hentry","plugin_tags-classified-ads","plugin_tags-classifieds","plugin_tags-directory","plugin_tags-listings","plugin_tags-marketplace","plugin_category-advertising","plugin_category-business","plugin_contributors-webcodingplace","plugin_committers-webcodingplace"],"banners":{"banner":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/banner-772x250.jpg?rev=3571494","banner_2x":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/banner-1544x500.jpg?rev=3571494","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/icon-128x128.png?rev=3571494","icon_2x":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/icon-256x256.png?rev=3571494","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/screenshot-1.png?rev=2764251","caption":"Listings archive"},{"src":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/screenshot-2.png?rev=2764251","caption":"Sections Manager"},{"src":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/screenshot-3.png?rev=2764251","caption":"Fields Builder"},{"src":"https:\/\/ps.w.org\/ultimate-classified-listings\/assets\/screenshot-4.png?rev=2764251","caption":"Frontend Management"}],"raw_content":"<!--section=description-->\n<p>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.<\/p>\n\n<p>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.<\/p>\n\n<h4>Why Ultimate Classified Listings<\/h4>\n\n<ul>\n<li><strong>Free where the leaders paywall<\/strong> \u2014 maps, AJAX search, frontend submission and custom fields are all free.<\/li>\n<li><strong>Stable and secure<\/strong> \u2014 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.<\/li>\n<li><strong>Theme-friendly<\/strong> \u2014 full template override system, works with any WordPress theme.<\/li>\n<li><strong>Multilingual ready<\/strong> \u2014 WPML and Polylang aware.<\/li>\n<li><strong>Developer friendly<\/strong> \u2014 40+ hooks and filters, REST API support on the listing CPT.<\/li>\n<\/ul>\n\n<h4>Core Features<\/h4>\n\n<ul>\n<li>Frontend listing submission with custom field sections<\/li>\n<li>Unlimited custom fields and field sections (drag-and-drop builder)<\/li>\n<li>Categories and tags with custom icons \/ images<\/li>\n<li>Gallery sliders and image grids<\/li>\n<li>AJAX-powered search with filters, price range and sort<\/li>\n<li>Google Maps <strong>or<\/strong> OpenStreetMap (Leaflet) with location markers<\/li>\n<li>Seller dashboard: add \/ edit \/ delete listings, profile management<\/li>\n<li>Two seller-registration modes: auto-approve or manual approval<\/li>\n<li>Two submission modes: publish immediately or pending review<\/li>\n<li>Listing comparison<\/li>\n<li>Contact-seller form with reCAPTCHA + email-injection protection<\/li>\n<li>Compare and share listings<\/li>\n<li>Full theme template overrides under <code>\/your-theme\/ucl\/...<\/code><\/li>\n<\/ul>\n\n<h4>Quick Links<\/h4>\n\n<ul>\n<li><strong><a href=\"https:\/\/www.wpclassifiedlistings.com\/demo\/\">Live Demo<\/a><\/strong><\/li>\n<li><strong><a href=\"https:\/\/www.wpclassifiedlistings.com\/documentation\/\">Documentation<\/a><\/strong><\/li>\n<li><strong><a href=\"https:\/\/www.wpclassifiedlistings.com\/demo\/search-listings-ajax\/\">AJAX Search<\/a><\/strong><\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Navigate to the WordPress Dashboard -&gt; Plugins -&gt; Add New<\/li>\n<li>Search for the Ultimate Classified Listings<\/li>\n<li>Install and Activate<\/li>\n<li>Now configure the settings from the Ultimate Classified Listings -&gt; Settings<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"is%20this%20plugin%20free%3F\"><h3>Is this plugin free?<\/h3><\/dt>\n<dd><p>Yes. The core plugin is free, including maps, AJAX search, frontend submission, and custom fields.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20elementor%2C%20gutenberg%20or%20divi%3F\"><h3>Does it work with Elementor, Gutenberg or Divi?<\/h3><\/dt>\n<dd><p>The plugin uses shortcodes that work in any builder. Native Gutenberg blocks are on the v3.0 roadmap.<\/p><\/dd>\n<dt id=\"do%20i%20need%20a%20google%20maps%20api%20key%3F\"><h3>Do I need a Google Maps API key?<\/h3><\/dt>\n<dd><p>No. The plugin ships OpenStreetMap via Leaflet by default. If you prefer Google Maps, you can add your API key in Settings.<\/p><\/dd>\n<dt id=\"can%20i%20let%20users%20pay%20to%20post%20listings%3F\"><h3>Can I let users pay to post listings?<\/h3><\/dt>\n<dd><p>Built-in payment gateways are on the v3.0 roadmap. In v2.x you can use Settings \u2192 Submission Mode to require admin approval before listings are published.<\/p><\/dd>\n<dt id=\"how%20are%20seller%20passwords%20stored%3F\"><h3>How are seller passwords stored?<\/h3><\/dt>\n<dd><p>As of 2.0, passwords are no longer stored in plain text in <code>wp_options<\/code>. 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.<\/p><\/dd>\n<dt id=\"how%20does%20the%20plugin%20protect%20against%20csrf%3F\"><h3>How does the plugin protect against CSRF?<\/h3><\/dt>\n<dd><p>Every AJAX endpoint requires a nonce verified by <code>check_ajax_referer()<\/code>. Public requests use a dedicated public nonce and admin requests use a separate admin nonce, both rotated by WordPress.<\/p><\/dd>\n<dt id=\"i%27m%20migrating%20from%20another%20plugin.%20is%20there%20a%20migration%20tool%3F\"><h3>I'm migrating from another plugin. Is there a migration tool?<\/h3><\/dt>\n<dd><p>Migration tools from Directorist, WP Adverts and Classified Listing are on the v3.x roadmap. For now you can use WP All Import\/Export\/<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.0 - 14 June 2026<\/h4>\n\n<ul>\n<li>Bug fix: When the archive\/search-results top bar was rendered (via <code>[uclwp_search_results top_bar=\"enable\"]<\/code> or anywhere the <code>uclwp_archive_topbar<\/code> action fires) and a visitor changed the sort dropdown, the resulting URL kept only <code>sort_by<\/code> and <code>layout<\/code> and dropped every other active query arg \u2014 keywords, price range, custom-field filters, etc. The top bar now re-emits every other <code>$_GET<\/code> value as a hidden input inside the form (using PHP bracket notation for nested sub-arrays like <code>regular_price[min]<\/code>), so the sort round-trip preserves the active filter set. The grid\/list switcher anchors already used <code>add_query_arg()<\/code> and were unaffected.<\/li>\n<li>Added: New helper <code>uclwp_flatten_query_args()<\/code> that flattens nested arrays into <code>name[sub][sub]<\/code> \/ value pairs suitable for hidden form inputs. Reusable from any template that needs to pass current GET state through a <code>&lt;form method=\"GET\"&gt;<\/code>.<\/li>\n<\/ul>\n\n<h4>1.16.0<\/h4>\n\n<ul>\n<li>Feature: Two new single-listing styles. Pick one in Settings \u2192 Templates Settings \u2192 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 \u2014 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 (<code>ucl-listing-style-1\/2\/3<\/code>) added on single-listing pages; templates are unchanged so all theme overrides keep working.<\/li>\n<li>Feature: Price quick-pick chips are now editable from the admin. New \"Price Quick-Pick Presets\" textarea in Settings \u2192 Search Settings (right after Total Listings) \u2014 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 <code>uclwp_search_price_chips<\/code>.<\/li>\n<li>Feature: <code>[uclwp_search_results]<\/code> now supports a <code>top_bar=\"enable\"<\/code> attribute (default <code>disable<\/code>) that adds the same sort + grid\/list switcher the <code>[uclwp_listings]<\/code> shortcode renders.<\/li>\n<li>Feature: Per-listing map zoom level is now saved. The admin metabox map writes the user-chosen zoom into a hidden <code>ucl_listing_zoom<\/code> input (Leaflet <code>zoomend<\/code> and Google <code>zoom_changed<\/code> 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.<\/li>\n<li>Improvement: Search style 2 polish \u2014 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 <code>columns<\/code> shortcode attribute (1\u20136, default 4) controls how many secondary-field cells appear per row via CSS grid. Mobile collapses to 2 columns at \u2264768px and 1 column at \u2264480px.<\/li>\n<li>Bug fix: Card image aspect ratio (Settings \u2192 Listings \u2192 Card Image Aspect Ratio) was not being applied. The CSS selector keyed off <code>[style*=\"--ucl-card-image-ratio\"]<\/code>, which never matched because the variable is emitted via an inline <code>&lt;style&gt;<\/code> tag, not the element's <code>style<\/code> attribute. The aspect-ratio rule now applies directly to <code>.uclwp-grid-box-wrap .uclwp-image<\/code> and <code>.uclwp-list-box-wrap .uclwp-image<\/code>, so all four card styles honour the chosen ratio.<\/li>\n<\/ul>\n\n<h4>1.15.0<\/h4>\n\n<ul>\n<li>Feature: Two new search-form styles, selectable via <code>[uclwp_search_form style=\"2\"]<\/code> and <code>[uclwp_search_form style=\"3\"]<\/code>.\n\n<ul>\n<li><strong>Style 2 \u2014 Bar<\/strong>: Compact horizontal pill bar with rounded inputs, designed to sit above an archive grid. Trulia\/Zillow feel.<\/li>\n<li><strong>Style 3 \u2014 Hero<\/strong>: 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 <code>search_form_heading<\/code> \/ <code>search_form_subheading<\/code> settings.<\/li>\n<\/ul><\/li>\n<li>Feature: New <code>uclwp_render_price_range_search()<\/code> 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 <code>uclwp_search_price_chips<\/code>.<\/li>\n<li>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 <code>.ucl-display-listings<\/code> so single-listing sidebars and the compare box are unaffected.<\/li>\n<li>Bug fix: <code>[uclwp_categories]<\/code> (and the underlying <code>render_category_image()<\/code>) no longer renders the saved Bootstrap-icon class. The previous wrapper produced <code>class=\"bi bi-{bi bi-house}\"<\/code>, an invalid class that resolved to nothing. Output is now <code>class=\"bi bi-house\"<\/code> (or the admin-set value). Categories with a saved icon will start rendering it immediately on update.<\/li>\n<li>Bug fix: Long seller email addresses were overflowing the contact box in the single-listing sidebar. The <code>&lt;li&gt;<\/code> rows now apply <code>overflow: hidden; text-overflow: ellipsis; white-space: nowrap;<\/code> and pick up the missing right padding so the email truncates inside the bordered chip.<\/li>\n<\/ul>\n\n<h4>1.14.2<\/h4>\n\n<ul>\n<li>Bug fix: When creating a new listing in wp-admin, Leaflet threw <code>Invalid LatLng object: (undefined, undefined)<\/code> 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 (<code>assets\/js\/location.js<\/code>) on the post-edit screen. That script expects a different localize payload (<code>ucl_location_settings.latitude\/longitude<\/code>) than the metabox renderer provides (<code>ucl_map_settings.def_lat\/def_long<\/code>), so its values came up undefined. The redundant enqueue has been removed \u2014 the location field renderer enqueues the correct script (<code>assets\/fields\/location.js<\/code>) on its own when the section actually renders.<\/li>\n<li>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.<\/li>\n<li>Bug fix: <code>Control.Geocoder.js<\/code> now declares <code>ucl-leaflet-js<\/code> as a dependency so the geocoder loads after Leaflet on the post-edit screen.<\/li>\n<\/ul>\n\n<h4>1.14.1<\/h4>\n\n<ul>\n<li>Bug fix: The Listing Information metabox on the wp-admin post-edit screen (<code>post.php<\/code> \/ <code>post-new.php<\/code> for the <code>uclwp_listing<\/code> CPT) was rendering without Bootstrap CSS, the icon font, the iconpicker JS, the media uploader, or the map widget. The <code>load_admin_scripts()<\/code> 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 <code>post_type<\/code> is <code>uclwp_listing<\/code>. Bootstrap stays scoped to <code>.uclwp-bs-wrapper<\/code> so it does not affect the surrounding wp-admin UI.<\/li>\n<\/ul>\n\n<h4>1.14.0<\/h4>\n\n<p>Final WordPress.org Plugin Check pass \u2014 clears all reported errors for plugin-directory submission.<\/p>\n\n<ul>\n<li>Plugin name: Renamed to \"Ultimate Classified Listings \u2013 Classifieds, Directory &amp; Marketplace\" so the readme title matches the plugin header and the word \"Plugin\" (a restricted term) is no longer part of the displayed name.<\/li>\n<li>Tested up to: bumped to 7.0 to match the current WordPress release as required by .org.<\/li>\n<li>Escaping: Wrapped the dynamic CSS variable block emitted by <code>assets\/css\/styles.php<\/code> through <code>wp_strip_all_tags()<\/code> after each value is individually escaped via <code>esc_attr()<\/code>, with phpcs:ignore annotations explaining why the final concatenation is safe.<\/li>\n<li>Escaping: <code>paginate_links()<\/code> output in <code>render_pagination()<\/code> now passes through <code>wp_kses_post()<\/code> before echoing.<\/li>\n<li>i18n: Added <code>\/* translators: %s ... *\/<\/code> comments above two <code>printf( esc_html__( ... ), ... )<\/code> calls in the setup-wizard \"next steps\" panel that were missing them.<\/li>\n<li>Sanitization: Added <code>sanitize_text_field( wp_unslash( ... ) )<\/code> wrappers around <code>$_REQUEST['listing_id']<\/code> reads in <code>class-shortcodes.php delete_listing<\/code>, <code>class-email.php contact_seller<\/code>, <code>class-favorites.php ajax_toggle<\/code>, and <code>shortcodes\/dashboard\/edit.php<\/code>. The values are still validated through <code>uclwp_validate_listing_id()<\/code>.<\/li>\n<li>Sanitization: <code>$_SERVER['REQUEST_URI']<\/code> used to build the favorites login-redirect URL now passes through <code>esc_url_raw( wp_unslash( ... ) )<\/code> before <code>home_url()<\/code>.<\/li>\n<li>Sanitization: Recursive array inputs (<code>$_POST['uclwp_data']<\/code>, <code>$_REQUEST['sections']<\/code>, <code>$_REQUEST['fields']<\/code>) now have phpcs:ignore annotations documenting that each leaf is sanitized further down. The actual per-leaf sanitization was already in place.<\/li>\n<li>Sanitization: Login\/register password reads carry phpcs:ignore annotations explaining why passwords must not be passed through <code>sanitize_text_field()<\/code> (would strip control characters needed for hash comparison).<\/li>\n<li>File uploads in seller registration carry phpcs:ignore annotations pointing to the nonce check at the top of the handler.<\/li>\n<\/ul>\n\n<h4>1.13.0<\/h4>\n\n<p>Second WordPress.org Plugin Check pass \u2014 addresses the warnings flagged by the official PCP tool that the manual sanitization audit missed.<\/p>\n\n<ul>\n<li>Plugin Check: Added <code>defined( 'ABSPATH' ) || exit;<\/code> 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.<\/li>\n<li>Plugin Check: Replaced all <code>json_encode( $resp )<\/code> AJAX response calls with <code>wp_send_json( $resp )<\/code> so character encoding follows WP's UTF-8 conventions and the response is properly closed via <code>wp_die()<\/code>.<\/li>\n<li>Plugin Check: Replaced remaining non-AJAX <code>json_encode()<\/code> calls (Slick \/ image-grid data-attributes) with <code>wp_json_encode()<\/code>.<\/li>\n<li>Plugin Check: Added explicit <code>timeout<\/code> argument (10 s) to every <code>wp_remote_post()<\/code> call.<\/li>\n<li>Plugin Check: Added <code>rel=\"noopener noreferrer\"<\/code> to every <code>target=\"_blank\"<\/code> link in the admin settings help text, the post-update messages, and the setup-wizard \"next steps\" panel.<\/li>\n<li>Plugin Check: Removed <code>load_plugin_textdomain()<\/code> \u2014 WordPress 4.6+ loads translations automatically for plugins hosted on .org and the call is now flagged as discouraged.<\/li>\n<li>Plugin Check: Replaced bare <code>die();<\/code> \/ <code>die(0);<\/code> with <code>wp_die();<\/code> in AJAX handlers.<\/li>\n<li>Plugin Check: Added missing version arguments to every <code>wp_enqueue_style<\/code> \/ <code>wp_enqueue_script<\/code> call (used <code>UCLWP_VERSION<\/code> for bundled assets and <code>null<\/code> for third-party CDN scripts whose URL already carries a version key).<\/li>\n<li>Plugin Check: Renamed three filters that were missing the plugin prefix: <code>no_results_message<\/code> \u2192 <code>uclwp_no_results_message<\/code>, <code>seller_contact_email_message<\/code> \u2192 <code>uclwp_seller_contact_email_message<\/code>, <code>raw_uclwp_price<\/code> \u2192 <code>uclwp_raw_price<\/code>, <code>formatted_uclwp_price<\/code> \u2192 <code>uclwp_formatted_price<\/code>.<\/li>\n<li>Plugin Check: Removed nested <code>esc_attr( sanitize_text_field( \u2026 ) )<\/code> double-escape pattern in the search-form field renderer; the value is now sanitized once on input and the existing <code>esc_attr()<\/code> at output point handles HTML-attribute escaping.<\/li>\n<li>Plugin Check: Google-Maps script URL now built via <code>add_query_arg()<\/code> instead of string concatenation, and routed through HTTPS unconditionally.<\/li>\n<li>Plugin Check: Direct <code>$wpdb-&gt;update()<\/code> 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).<\/li>\n<\/ul>\n\n<h4>1.12.0<\/h4>\n\n<p>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.<\/p>\n\n<ul>\n<li>Sanitization: All <code>$_GET<\/code> \/ <code>$_POST<\/code> \/ <code>$_REQUEST<\/code> access across the plugin now runs through <code>wp_unslash()<\/code> before sanitization, matching WordPress core conventions and <code>WPCS<\/code> requirements.<\/li>\n<li>Escaping: Replaced <code>esc_attr_e()<\/code> \/ <code>esc_attr__()<\/code> with the correct <code>esc_html_e()<\/code> \/ <code>esc_html__()<\/code> everywhere the output is HTML body content (labels, table cells, headings, paragraph text). Attribute-context calls (<code>placeholder=\"\"<\/code>, <code>title=\"\"<\/code>, <code>value=\"\"<\/code>, <code>data-*=\"\"<\/code>) remain <code>esc_attr_e()<\/code>.<\/li>\n<li>Escaping: Replaced <code>esc_attr()<\/code> with <code>esc_html()<\/code> on user-controlled body text \u2014 listing titles, seller names, emails, phone numbers, category names, etc.<\/li>\n<li>Escaping: Textarea content now uses <code>esc_textarea()<\/code> instead of <code>esc_attr()<\/code>.<\/li>\n<li>Bugfix: <code>compare-box.php<\/code> printed nothing for compare table column headers due to <code>esc_attr( $label )<\/code> (which returns instead of echoing). Now properly <code>echo esc_html( $label )<\/code>.<\/li>\n<li>Hardening: Removed the second hardcoded reCAPTCHA fallback key in <code>templates\/sidebar\/default.php<\/code>. CAPTCHA now fails closed when not configured.<\/li>\n<li>Hardening: <code>gdpr_message<\/code> setting in the contact-seller form is now output via <code>wp_kses_post()<\/code> so admins can include safe HTML formatting (was <code>esc_attr()<\/code> which broke the HTML).<\/li>\n<li>Hardening: Search query builder (<code>uclwp_get_search_query<\/code>) sanitizes all dynamic <code>orderby<\/code>, <code>meta_key<\/code>, <code>tag<\/code>, and <code>range<\/code> inputs and validates against an allow-list before passing to <code>WP_Query<\/code>.<\/li>\n<li>Hardening: HTML emails (contact-seller) now <code>esc_html()<\/code> user-controlled fields (client name, email, phone, message, listing title) before HTML interpolation.<\/li>\n<li>Hardening: Compare AJAX listing IDs cast through <code>absint()<\/code>; meta_key segments through <code>sanitize_key()<\/code>.<\/li>\n<li>Hardening: <code>class-shortcodes::update_profile<\/code> casts and unslashes every <code>$_REQUEST<\/code> field.<\/li>\n<li>Hardening: Term-meta image\/icon callbacks (<code>save_category_image<\/code>, <code>updated_category_image<\/code>) now check <code>current_user_can('manage_categories')<\/code> and <code>wp_unslash()<\/code> the inputs (core verifies the nonce upstream on the term-edit screen).<\/li>\n<li>Hardening: Dashboard \"My Listings\" search query reads <code>$_GET['uclwp_status']<\/code> through an enum allow-list (<code>publish<\/code>, <code>pending<\/code>, <code>draft<\/code>, <code>future<\/code>, <code>any<\/code>).<\/li>\n<li>Compatibility: <code>selected()<\/code> and <code>selected( $a, $b )<\/code> used in place of inline <code>($a == $b) ? 'selected' : ''<\/code> ternaries where the markup allowed it.<\/li>\n<li>Compatibility: <code>(int) $cat-&gt;count<\/code> and <code>(int) count_user_posts(...)<\/code> casts in <code>page-sellers.php<\/code> and other admin lists in place of <code>esc_attr()<\/code> on integers.<\/li>\n<\/ul>\n\n<h4>1.11.0<\/h4>\n\n<ul>\n<li>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 \u2014 \"List\" (definition-list rows) and \"Inline\" (compact icon pills). Choose via Settings \u2192 Listings \u2192 Field Display on Single Listing Pages.<\/li>\n<li>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).<\/li>\n<li>Feature: Checkbox \/ features fields now render as accent-coloured chips with check icons, instead of plain bulleted text.<\/li>\n<li>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 <code>[uclwp_favorites]<\/code> shortcode. Toggle the feature in Settings \u2192 Listings \u2192 Favorites.<\/li>\n<li>Feature: Dark mode. Five options \u2014 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.<\/li>\n<li>Improvement: Field-display helpers now resolve safely when a value is empty so cards never render orphan labels.<\/li>\n<li>Improvement: <code>uclwp_listing_action_buttons<\/code> filter added so add-ons can inject extra card buttons without forking templates.<\/li>\n<li>Improvement: <code>uclwp_dashboard_allowed_pages<\/code> filter added.<\/li>\n<\/ul>\n\n<h4>1.10.0<\/h4>\n\n<ul>\n<li>Feature: Three new listing card styles. Style 2 \u2014 Minimal (flat, bordered, generous whitespace), Style 3 \u2014 Bold Overlay (full-bleed image with gradient and meta overlaid), Style 4 \u2014 Compact \/ Magazine (dense layout with prominent CTA). Each ships with both a grid and a list variant. Switch globally from Listings \u2192 Listing Card Style, or per-shortcode via <code>[uclwp_listings style=\"3\"]<\/code>.<\/li>\n<li>Feature: 14 new colour controls under Colors and CSS \u2014 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 <code>.uclwp-bs-wrapper<\/code> so themes and custom CSS can override them too.<\/li>\n<li>Feature: Card Corner Radius setting (0\u201364px).<\/li>\n<li>Feature: Card Image Aspect Ratio setting (Original \/ 4:3 \/ 16:9 \/ 1:1 \/ 3:4) \u2014 useful when seller-uploaded images vary in dimensions.<\/li>\n<li>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.<\/li>\n<\/ul>\n\n<h4>1.9.0<\/h4>\n\n<ul>\n<li>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.<\/li>\n<li>Feature: Auto-create essential pages with the correct shortcodes pre-inserted.<\/li>\n<li>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.<\/li>\n<li>Improvement: Admin classes now load only in <code>is_admin()<\/code> context to reduce frontend overhead.<\/li>\n<\/ul>\n\n<h4>1.8.0<\/h4>\n\n<p>This is a <strong>major security hardening release<\/strong>. All sites should update.<\/p>\n\n<ul>\n<li>Security: Added CSRF nonce verification to every public AJAX endpoint (<code>uclwp_search_listing<\/code>, <code>uclwp_seller_login<\/code>, <code>uclwp_seller_register<\/code>, <code>uclwp_create_listing_frontend<\/code>, <code>uclwp_delete_listing<\/code>, <code>uclwp_contact_seller<\/code>, <code>uclwp_compare_listings<\/code>).<\/li>\n<li>Security: Added CSRF nonce verification to admin AJAX endpoints (<code>uclwp_save_field_sections<\/code>, <code>uclwp_save_custom_fields<\/code>, <code>uclwp_reset_custom_fields<\/code>, <code>wcp_uclwp_save_settings<\/code>, <code>ucl_deny_seller<\/code>, <code>ucl_approve_seller<\/code>).<\/li>\n<li>Security: Pending-seller passwords are no longer stored in plaintext in <code>wp_options<\/code>. New registrations store only a salted hash; legacy entries are routed through the standard password-reset flow on approval.<\/li>\n<li>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.<\/li>\n<li>Security: Removed the hardcoded reCAPTCHA secret-key fallback. CAPTCHA verification now fails closed when not configured.<\/li>\n<li>Security: Replaced unvalidated <code>$_SERVER['REMOTE_ADDR']<\/code> use with <code>filter_var(... FILTER_VALIDATE_IP)<\/code>.<\/li>\n<li>Security: Listing IDs in delete and edit flows are now validated against the <code>uclwp_listing<\/code> post type and authorized via <code>current_user_can('edit_post'|'delete_post', $id)<\/code> instead of manual ownership checks.<\/li>\n<li>Security: Pending-seller <code>userindex<\/code> is cast to integer and validated against existing keys before use.<\/li>\n<li>Security: Permalink-settings save path now requires the core <code>update-permalink<\/code> nonce.<\/li>\n<li>Security: Added transient-based rate limiting on login, registration and contact-seller endpoints.<\/li>\n<li>Security: Asset URLs now include <code>UCLWP_VERSION<\/code> for cache busting after security updates.<\/li>\n<li>Bumped minimum WordPress to 6.0 and minimum PHP to 7.4.<\/li>\n<li>License header reconciled to GPLv2-or-later.<\/li>\n<\/ul>\n\n<h4>1.7<\/h4>\n\n<ul>\n<li>Bug Fixed: Security related issues<\/li>\n<\/ul>\n\n<h4>1.6<\/h4>\n\n<ul>\n<li>Bug Fixed: Security related issues<\/li>\n<\/ul>\n\n<h4>1.5<\/h4>\n\n<ul>\n<li>Bug Fixed: Security issues fixes, notified by Wordfence<\/li>\n<\/ul>\n\n<h4>1.4<\/h4>\n\n<ul>\n<li>Feature Added: Ability to choose category from the frontend<\/li>\n<li>Feature Added: Ability to assign tags from the frontend<\/li>\n<li>Bug Fixed: Empty categories are not displaying<\/li>\n<li>Bug Fixed: Security issues fixes, notified by WPScan Team<\/li>\n<\/ul>\n\n<h4>1.3<\/h4>\n\n<ul>\n<li>Bug Fixed: Security issues fixes, notified by WPScan Team<\/li>\n<\/ul>\n\n<h4>1.2<\/h4>\n\n<ul>\n<li>Bug Fixed: Iframes not saving from frontend<\/li>\n<\/ul>\n\n<h4>1.1<\/h4>\n\n<ul>\n<li>Feature Added: Iframes support in the shortcode field<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial Release<\/li>\n<\/ul>","raw_excerpt":"Free classifieds &amp; directory plugin with maps, frontend submission, AJAX search, packages-ready architecture and a hardened security model.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/160100","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=160100"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/webcodingplace"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=160100"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=160100"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=160100"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=160100"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=160100"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=160100"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}