Crocoblock Listing Website: Front End Submission to Contact Button

To build a Crocoblock listing website with front end submission, create a JetFormBuilder form whose Insert/Update Post action saves the title, meta fields, terms and featured image into your JetEngine post type. Set Post Status to anything other than Published so you can review each listing first. Then give each post type its own search page with a listing grid and JetSmartFilters filters, a single template, and a contact button that reads the phone number from the listing author's user meta.
This guide assumes your JetEngine post types, taxonomies and meta fields already exist.
- What you are building and what you need
- Prepare the post types
- Build the Add listing form
- Set the post-submit actions
- Let users manage their listings
- One search page for each listing type
- The single listing template
- Add a contact-the-seller button
- Troubleshooting
What you are building and what you need
The flow: a seller submits a listing from their account page, you approve it, it appears on the search page for its type and on its own single page, and buyers contact the seller from there.
You need:
- JetEngine with the Profile Builder module, for post types, listings and the account area
- JetFormBuilder, for the add and edit forms
- JetSmartFilters, for search and filters
- Elementor, plus Elementor Pro (Theme Builder) or JetThemeCore for single templates
Update JetFormBuilder to 3.6.7 (WordPress 6.1+ and PHP 7.0+). Several 3.6.x fixes listed under Troubleshooting matter for submission sites.
Prepare the post types for front end submission
Open each listing post type in JetEngine and go to the Advanced Settings tab. Under Supports, turn on Title, Excerpt, Thumbnail (Featured Image) and Custom Fields, as Crocoblock's front end submission guide asks. Attach every taxonomy you use in the form to the listing post type.
For the contact button, add two user meta fields in JetEngine: a text field, for example whatsapp_phone, where each seller stores their full international number, and a Switcher field, for example whatsapp_consent, which the seller turns on to allow that number to be shown on their listings. Sellers who never open wp-admin cannot reach the profile screen, so let them set it from the front end, for example with a JetFormBuilder profile form that uses the Update User action, or set it for them. Whichever way it is set, store a single value such as true or 1, which is what the code below checks for.
Build the Add listing form
Create a new form in JetFormBuilder and add:
- Text fields named exactly like your meta keys, for example
priceorlocation, so the Fields Map is easy to check. - Select fields for taxonomies. Set Fill Options From to Terms and choose the Taxonomy.
- A Media field for the main photo. Turn on Insert attachment and set the Field Value to Attachment ID: since JetFormBuilder 3.5.2.1 the Insert/Update Post action accepts other formats for the thumbnail too, but Attachment ID is the format Crocoblock recommends. Choose a User Access option (required since 3.6.3): Any registered user, users allowed to upload files, users allowed to edit posts, or Any user (incl. Guest). Only the guest option lets logged-out visitors upload, and the Media Field guide warns that it can attract spam, so keep captcha on. For a drag-and-drop uploader, see our JetFormBuilder drag and drop file upload guide.
- A Textarea for a short description (the excerpt), or a WYSIWYG field for the full content.
- Captcha protection. Any public form needs it.
Avoid the post_id trap. New forms often include a Hidden field named post_id. In an add form, delete it (the new post's ID is created on submission) or keep it with Field Value "Current Post ID" and leave it unmapped in the Fields Map. If you map it to "Post ID (will update the post)", the action tries to update the post with that ID instead of creating a new one. Since JetFormBuilder 3.3.3 that update fails with an error when the ID belongs to a different post type, for example the Account page the form sits on; when it belongs to another listing of the same type, that listing is overwritten. Map post_id only in the edit form.
Set the post-submit actions
Insert/Update Post
- Post Type: the listing post type. The list includes post types made with JetEngine or ACF.
- Post Status: Published makes the post live immediately. For moderation, pick any status other than Published from the list in your version, for example Draft or Pending, and publish each listing from the admin after checking it.
- Fields Map:
- Title field: Post Title
- Each meta text field: Post Meta plus the meta field name
- Each term select: Post Terms plus the taxonomy
- Media field: Post Thumbnail
- Textarea: Post Excerpt
- WYSIWYG: Post Content
- Default Fields (optional): a Meta Key plus a Meta Value, written only when that meta field is not connected to a form field.
Send Email and Redirect to Page
Add a Send Email action addressed to the admin so you know a listing is waiting. Then add Redirect to Page: set Redirect to to "Static Page" and choose a thank-you or My listings page in Select page. The URL QUERY ARGUMENTS select can add chosen field values to the redirect URL.
Submit a test listing. In the admin, it should have your chosen status, filled meta fields, the selected term and a featured image.
Let users manage their listings
Set up the account area
- Go to WordPress Dashboard > JetEngine > JetEngine, switch on Profile Builder, and click Save.
- Turn on the Forms (Legacy) module as well, because the preset import needs it. Then, in Skins Manager > Presets, import User profile with editable content. This creates an Account page, its templates and a listing of the user's posts. Do it on a fresh setup: the import replaces any Profile Builder content you have already made.
- In JetEngine > Profile Builder, add subpages such as
add-listingandmy-listings. Slugs may use only lowercase Latin letters and "-" or "_". Use Available for the user role to limit who sees each subpage. - Place the add form in the
add-listingsubpage template.
Create the edit form
- Go to JetFormBuilder > Forms, hover over the add form, and click Duplicate.
- Add a Hidden field named
post_id. Set Field Value to "URL Query Variable" and Query Variable Key to_post_id. - In the Insert/Update Post action, map
post_idto "Post ID (will update the post)" in the Fields Map, and set Post Status to "Keep current status (when updating post)". With Published, a seller could publish an unapproved listing just by editing it; with Pending, every edit would take a live listing offline. - Turn on JetForm > Preset Settings. Set Source to "Post", Get post ID from to "URL Query Variable", and Query variable name to
_post_id. Connect each field to its post data:post_idto Post ID, then the title, meta fields, term selects, thumbnail, excerpt and content. Without this, the edit form opens empty. - Put the form on an edit page and link to it from the user-posts listing with the ID in the URL, for example
/edit-listing/?_post_id=123.
One search page for each listing type
Create a separate page for each post type, for example Cars and Properties, so results never mix and the filter wiring stays simple.
Add a JetEngine Listing Grid that uses a listing item template built for that post type. Alternatively, JetSmartFilters has its own Listing Builder under WordPress Dashboard > Smart Filters > Listing Builder, with QUERY TYPE "Posts", POST TYPES and POST STATUS settings; the Listing Builder overview explains how it connects to filters.
Then create filters in JetSmartFilters and place them next to the grid:
- a Search filter for keywords
- Select filters for taxonomies such as category or location
- Checkbox filters for features stored as terms or meta
Connect each filter to the grid with Crocoblock's guide on using filters with Listing Grid. With two grids on one page, read the Query ID guide. If you switch to a custom query, our JetEngine Query Builder mistakes tip covers why results come back empty.
The single listing template
With Elementor Pro's Theme Builder or JetThemeCore, create a single template for each listing post type and set its display condition to that post type. Use JetEngine's dynamic widgets for the title, featured image, meta fields, terms and author details. Every value comes from the current post, so one template serves every listing of that type.
Add a contact-the-seller button
Do not type a phone number into the template. Read it from the listing author's user meta, so every listing contacts the person who posted it. The shortcode below builds a WhatsApp click-to-chat link with the listing title and URL in the message. It prints the seller's number in the public HTML, so the code shows the button only when the seller has turned on the whatsapp_consent field in their profile. If you would rather not publish numbers at all, use a contact form instead.
Add it to your child theme's functions.php file or a code snippets plugin. Replace whatsapp_phone and whatsapp_consent with your user meta keys, and cars and properties with your listing post type slugs:
Code
/**
* [listing_contact_whatsapp] - WhatsApp button for the listing author.
* Optional: [listing_contact_whatsapp label="Message the seller"]
*/
if ( ! function_exists( 'bmc_listing_contact_whatsapp_shortcode' ) ) {
function bmc_listing_contact_whatsapp_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'label' => __( 'Contact the seller on WhatsApp', 'bmc' ),
),
$atts,
'listing_contact_whatsapp'
);
// Replace with the meta key of your JetEngine user field.
$meta_key = 'whatsapp_phone';
// Replace with the meta key of the seller's consent field (a JetEngine Switcher in the user meta box).
// Sellers turn it on in their profile; without it the button is never shown.
$consent_key = 'whatsapp_consent';
$post_id = get_the_ID();
if ( ! $post_id ) {
return '';
}
// Only on listings. Replace 'cars', 'properties' with your own listing post type slugs.
if ( ! in_array( get_post_type( $post_id ), array( 'cars', 'properties' ), true ) ) {
return '';
}
// Always use the listing author, never the logged-in visitor.
$author_id = (int) get_post_field( 'post_author', $post_id );
if ( ! $author_id ) {
return '';
}
// Never publish a number the seller has not agreed to show. A JetEngine Switcher can save the word "false",
// which is not an empty value, so test the stored value itself: only true, 1, yes or on counts as consent.
if ( ! filter_var( get_user_meta( $author_id, $consent_key, true ), FILTER_VALIDATE_BOOLEAN ) ) {
return '';
}
$raw = get_user_meta( $author_id, $meta_key, true );
if ( ! is_scalar( $raw ) ) {
return '';
}
// Numbers like "+44 (0)20 ..." or "( 0 )": remove the (0) before keeping digits.
$raw = (string) preg_replace( '/\(\s*0\s*\)/', '', (string) $raw );
$phone = (string) preg_replace( '/\D+/', '', $raw );
// Numbers saved as 0044... : drop the leading 00 international prefix.
if ( 0 === strpos( $phone, '00' ) ) {
$phone = (string) substr( $phone, 2 );
}
// Only a plausible international number (7 to 15 digits, no leading 0) gets a button. This catches the
// obvious cases; a national number typed without its country code can still slip through.
if ( ! preg_match( '/^[1-9][0-9]{6,14}$/', $phone ) ) {
return '';
}
// Titles saved through a front-end form keep entities such as &: decode them so the message reads naturally.
$title = html_entity_decode( wp_strip_all_tags( get_post_field( 'post_title', $post_id ) ), ENT_QUOTES, 'UTF-8' );
$message = sprintf(
/* translators: 1: listing title, 2: listing URL */
__( 'Hi, I am interested in your listing "%1$s": %2$s', 'bmc' ),
$title,
get_permalink( $post_id )
);
$url = 'https://wa.me/' . $phone . '?text=' . rawurlencode( $message );
return sprintf(
'<a class="bmc-whatsapp-button" href="%1$s" target="_blank" rel="noopener">%2$s</a>',
esc_url( $url ),
esc_html( $atts['label'] )
);
}
add_shortcode( 'listing_contact_whatsapp', 'bmc_listing_contact_whatsapp_shortcode' );
}Place it and check how it works
Add an Elementor Shortcode widget to the single listing template and enter [listing_contact_whatsapp], or [listing_contact_whatsapp label="Message the seller"] for custom text. Style it through the bmc-whatsapp-button class.
The function:
- Returns nothing unless the current post is one of your listing post types, so a normal page or blog post never shows its author's (often the admin's) number.
- Reads the phone from the listing author, found with
get_post_field( 'post_author', ... ), never the logged-in user. - Shows the button only when that author's
whatsapp_consentuser meta holds a yes value (true, 1, yes or on); an empty field or the word false hides the button, so a seller who has not agreed is never exposed. - Removes a "(0)" in any spacing, keeps only digits and drops a leading
00. - Hides the button unless the digits look like an international number: 7 to 15 digits that do not start with 0. That catches the obvious cases, not every unusable number.
- Encodes the message with
rawurlencode(), escapes output withesc_url()andesc_html(), and returns the HTML instead of echoing it.
Test on the front end. The Elementor editor preview may use the template instead of a real listing, so the button may not appear there. Logged out, a listing whose author has a number should open WhatsApp with the title and link; one without a number shows no button. Logged in as another user, the number should still be the author's.
Ask sellers to enter the full international number without the national leading zero: +44 7700 900123, not +44 (0)7700 900123 or +44 07700 900123. A number saved with that extra 0 after the country code passes the check but builds a link that goes nowhere; the check catches the obvious cases, not every unusable number. Before launch, confirm the link format against WhatsApp's current click-to-chat documentation, or keep it consistent with our WhatsApp button with a custom dynamic message tip. Facebook or Instagram links work the same way: read another user meta field for the same $author_id, skip it when empty, and output it with esc_url().
Troubleshooting
- The add form fails with an error, or overwrites an existing listing.
post_idis mapped to "Post ID (will update the post)". On a page the update fails because the post type would change (since 3.3.3); with the ID of another listing of the same type, that listing is overwritten. Delete the Hidden field or leave it unmapped. - New listings have no featured image. Map the Media field to Post Thumbnail with Insert attachment on and the Attachment ID Field Value (the recommended format), and turn on Thumbnail (Featured Image) support as Crocoblock's guide asks. For guest submissions, set User Access to Any user (incl. Guest) and test one listing.
- The chosen category is not saved. Fill the Select from Terms and map it to Post Terms plus the taxonomy, not Post Meta.
- The edit form opens empty. Connect each field in Preset Settings and check the link carries
_post_id. - Listings go live at once, or never appear. Published goes live immediately; any other status stays off the search page until you publish the listing.
- Uploads fail, edits clear other meta, or the form breaks in a JetPopup AJAX popup. Choose a User Access option in the Media field (required since 3.6.3) and update to 3.6.7, which includes the 3.6.4 popup fix, the 3.6.5 meta fix and the 3.6.6 and 3.6.6.1 security fixes for media and anonymous posts. See the WordPress.org changelog.
- The contact button is wrong or missing. Check the meta keys, the consent field, the post type slugs in the code, the number format, and that the listing's author is the seller, not an admin.
Build the add form first, test one listing end to end, then add search pages and the contact button. If you would rather have it built for you, post a request on BoostMyCroco.
Frequently Asked Questions
Can guests submit listings without creating an account?
Should I use JetEngine Listing Grid or the JetSmartFilters Listing Builder for the search page?
Can one form create listings for several post types?
Why does the WhatsApp button show the wrong number or not appear?
Do I need to approve every edit a seller makes?
This guide answers a question from our community: Setting up crocoblock listing forms, filters (search, select, & more), listing grids, product pages.
Sources
- JetFormBuilder: WordPress Front-End Post Submission Form (jetformbuilder.com)
- Listings Functionality Overview: JetSmartFilters (crocoblock.com)
- How to Use Filters with Listing Grid: JetSmartFilters (crocoblock.com)
- Query ID Guide for JetSmartFilters (crocoblock.com)
- JetFormBuilder: Dynamic Blocks Form Builder (wordpress.org)
- Local Business Directory With Bricks, JetEngine, and JetSmartFilters - Crocoblock (crocoblock.com)
- JetFormBuilder: Insert/Update Post Action Overview (jetformbuilder.com)
- Building a WordPress Classified Website with JetPlugins (crocoblock.com)
- How to Build Front End Post Management System: JetFormBuilder (jetformbuilder.com)
- JetFormBuilder: Media Field Overview (jetformbuilder.com)
