| 开发者 | webcodingplace |
|---|---|
| 更新时间 | 2026年6月14日 12:03 |
| PHP版本: | 7.4 及以上 |
| WordPress版本: | 7.0 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
/your-theme/ucl/...Yes. The core plugin is free, including maps, AJAX search, frontend submission, and custom fields.
The plugin uses shortcodes that work in any builder. Native Gutenberg blocks are on the v3.0 roadmap.
No. The plugin ships OpenStreetMap via Leaflet by default. If you prefer Google Maps, you can add your API key in Settings.
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.
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.
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.
Migration tools from Directorist, WP Adverts and Classified Listing are on the v3.x roadmap. For now you can use WP All Import/Export/
[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.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">.ucl-listing-style-1/2/3) added on single-listing pages; templates are unchanged so all theme overrides keep working.uclwp_search_price_chips.[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.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.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.[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.[uclwp_search_form style="2"] and [uclwp_search_form style="3"].search_form_heading / search_form_subheading settings.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..ucl-display-listings so single-listing sidebars and the compare box are unaffected.[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.<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.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.Control.Geocoder.js now declares ucl-leaflet-js as a dependency so the geocoder loads after Leaflet on the 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.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.paginate_links() output in render_pagination() now passes through wp_kses_post() before echoing./* translators: %s ... */ comments above two printf( esc_html__( ... ), ... ) calls in the setup-wizard "next steps" panel that were missing them.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().$_SERVER['REQUEST_URI'] used to build the favorites login-redirect URL now passes through esc_url_raw( wp_unslash( ... ) ) before home_url().$_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.sanitize_text_field() (would strip control characters needed for hash comparison).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.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().json_encode() calls (Slick / image-grid data-attributes) with wp_json_encode().timeout argument (10 s) to every wp_remote_post() call.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.load_plugin_textdomain() — WordPress 4.6+ loads translations automatically for plugins hosted on .org and the call is now flagged as discouraged.die(); / die(0); with wp_die(); in AJAX handlers.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).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.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.add_query_arg() instead of string concatenation, and routed through HTTPS unconditionally.$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).$_GET / $_POST / $_REQUEST access across the plugin now runs through wp_unslash() before sanitization, matching WordPress core conventions and WPCS requirements.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().esc_attr() with esc_html() on user-controlled body text — listing titles, seller names, emails, phone numbers, category names, etc.esc_textarea() instead of esc_attr().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 ).templates/sidebar/default.php. CAPTCHA now fails closed when not configured.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).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.esc_html() user-controlled fields (client name, email, phone, message, listing title) before HTML interpolation.absint(); meta_key segments through sanitize_key().class-shortcodes::update_profile casts and unslashes every $_REQUEST field.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).$_GET['uclwp_status'] through an enum allow-list (publish, pending, draft, future, any).selected() and selected( $a, $b ) used in place of inline ($a == $b) ? 'selected' : '' ternaries where the markup allowed it.(int) $cat->count and (int) count_user_posts(...) casts in page-sellers.php and other admin lists in place of esc_attr() on integers.[uclwp_favorites] shortcode. Toggle the feature in Settings → Listings → Favorites.uclwp_listing_action_buttons filter added so add-ons can inject extra card buttons without forking templates.uclwp_dashboard_allowed_pages filter added.[uclwp_listings style="3"]..uclwp-bs-wrapper so themes and custom CSS can override them too.is_admin() context to reduce frontend overhead.uclwp_search_listing, uclwp_seller_login, uclwp_seller_register, uclwp_create_listing_frontend, uclwp_delete_listing, uclwp_contact_seller, uclwp_compare_listings).uclwp_save_field_sections, uclwp_save_custom_fields, uclwp_reset_custom_fields, wcp_uclwp_save_settings, ucl_deny_seller, ucl_approve_seller).wp_options. New registrations store only a salted hash; legacy entries are routed through the standard password-reset flow on approval.$_SERVER['REMOTE_ADDR'] use with filter_var(... FILTER_VALIDATE_IP).uclwp_listing post type and authorized via current_user_can('edit_post'|'delete_post', $id) instead of manual ownership checks.userindex is cast to integer and validated against existing keys before use.update-permalink nonce.UCLWP_VERSION for cache busting after security updates.