Cookbook (Developers)¶
Task-oriented recipes combining the helpers, filters, and actions from the other developer docs. Each is a copy-paste starting point. Drop them in a mu-plugin, child theme functions.php, or an extension.
Recipes hooked on a dms_* action or filter need no "is DMS active?" guard - those hooks only fire when DMS is active. Add a function_exists() check only for code that runs outside a DMS hook (e.g. the standalone DMS_Query loop below).
Change the SRP query¶
Add or override listing query arguments before the search results run.
add_filter( 'dms_query_args', function ( $args ) {
// Show 24 per page and hide listings older than 2 years.
$args['posts_per_page'] = 24;
$args['date_query'] = [
[ 'after' => '2 years ago', 'column' => 'post_date' ],
];
return $args;
} );
See dms_query_args.
Customize a form notification email¶
These filters affect the notification email for every DMS form submission (lead capture, contact, trade-in, any form built with the DMS form builder), not just lead forms. The recipient, subject, and body all come from per-form settings; the filters override them for all forms, or use $form_id to target a specific one. They do not affect non-form emails such as stock-alert notifications, which have their own hooks.
// Reroute every DMS form's notification email.
add_filter( 'dms_form_mail_to', function ( $to, $form_id, $post ) {
return 'sales@example.com, manager@example.com';
}, 10, 3 );
// Override the subject for one specific form only.
add_filter( 'dms_form_mail_subject', function ( $subject, $form_id, $post ) {
return $form_id === 42 ? 'New website lead: ' . get_the_title( $post ) : $subject;
}, 10, 3 );
// Runs before each DMS form email is sent - inspect, log, or short-circuit.
add_action( 'dms_before_mail_send', function ( $mail_data ) {
// $mail_data: post, form_fields, mail_to, mail_subject, mail_content, attachments, mail_type
}, 10, 1 );
See the dms_form_mail_* filters.
Trigger custom action on a saved listing (sync to an external system)¶
dms_on_listing_save fires on manual edits, imports, and programmatic saves.
add_action( 'dms_on_listing_save', function ( $listing_id, $post, $update ) {
$listing = dms_get_listing( $listing_id );
if ( ! $listing->exists() || ! $listing->is_published() ) {
return;
}
$payload = [
'id' => $listing->get_id(),
'title' => $listing->get_title(),
'price' => $listing->get_final_price(),
'url' => $listing->get_permalink(),
];
// wp_remote_post( 'https://feed.example.com/listings', [ 'body' => wp_json_encode( $payload ) ] );
}, 10, 3 );
See dms_on_listing_save.
Trigger custom action on a status change¶
add_action( 'dms_listing_status_changed', function ( $new_status, $old_status, $listing_id ) {
if ( $new_status === 'sold' ) {
$listing = dms_get_listing( $listing_id );
// e.g. remove from an external feed, notify accounting
}
}, 10, 3 );
See dms_listing_status_changed.
Sync new leads to a CRM¶
add_action( 'dms_add_lead', function ( $lead_data, $listing_id, $form_data ) {
$listing = $listing_id ? dms_get_listing( $listing_id ) : null;
// Push $lead_data (+ optional $listing context) to your CRM API.
}, 10, 3 );
See dms_add_lead.
Adjust displayed prices¶
Filter the numeric base price everywhere it's shown.
// Add a mandatory $299 prep fee to every base price.
add_filter( 'dms_listing_price', function ( $price, $listing ) {
return $price + 299;
}, 10, 2 );
For fees/discounts modelled properly (so they appear as line items), configure them under the type's Fees & Discounts settings and read them via get_fees_discounts(). See Listings API - pricing.
Add a custom listing field and read it back¶
Register a metabox on dms_init, then read the value off the Listing.
add_action( 'dms_init', function () {
dms_register_metabox( [
'id' => 'dealer_notes',
'title' => __( 'Dealer Notes', 'my-plugin' ),
'object_types' => dms_get_types( true ),
'context' => 'side',
'fields' => [
[ 'id' => 'internal_note', 'name' => 'Internal Note', 'type' => 'textarea_small' ],
],
] );
} );
// Read it anywhere:
add_action( 'dms_after_single_listing', function () {
$listing = dms_get_current_listing();
if ( $listing && $listing->exists() && current_user_can( 'edit_posts' ) ) {
echo esc_html( $listing->get_meta( 'internal_note' ) );
}
} );
See Building Extensions - metabox.
Modify export output¶
Add a column and populate it for every exported row.
add_filter( 'dms_export_columns', function ( $columns, $type ) {
$columns['final_price'] = __( 'Final Price', 'my-plugin' );
return $columns;
}, 10, 2 );
add_filter( 'dms_export_data', function ( $data, $export ) {
foreach ( $data as $i => $row ) {
if ( ! empty( $row['ID'] ) ) {
$data[ $i ]['final_price'] = dms_get_listing( (int) $row['ID'] )->get_final_price();
}
}
return $data;
}, 10, 2 );
Inject markup into the SRP and VDP¶
// A ribbon on each sold result card.
add_action( 'dms_after_sri', function ( $listing, $classes ) {
if ( $listing->is_sold() ) {
echo '<div class="sold-ribbon">Sold</div>';
}
}, 10, 2 );
// A block at the bottom of every VDP.
add_action( 'dms_vdp_footer', function () {
$listing = dms_get_current_listing();
if ( $listing && $listing->exists() ) {
printf( '<p>Days on lot: %d</p>', $listing->get_days_on_lot() );
}
} );
See the front-end render hooks.
Build a custom listing loop outside DMS pages¶
Use DMS_Query so category meta keys and price ordering behave like the SRP.
$type = dms_get_type( 'vehicles' );
$query = new DMS_Query( [
'type_id' => $type->get_id(),
'posts_per_page' => 6,
'fields' => 'ids',
] );
foreach ( $query->get_posts() as $listing_id ) {
$listing = dms_get_listing( $listing_id );
printf(
'<a href="%s">%s - %s</a>',
esc_url( $listing->get_permalink() ),
esc_html( $listing->get_title() ),
esc_html( $listing->get_formatted_final_price() )
);
}
See also¶
- Listings API · Filters · Actions · Extensions