To the best of my knowledge, you cannot sort a query by two different parameters without an additional iteration over the retrieved posts.
Since that ideally should be avoided, let me suggest a different approach:
How about incorporating an additional meta value “sort_date”, or the like? And then, in your saving routine, save either the end date of the exhibition or the publish date as “sort_date”.
Saving:
function save_sort_meta( $post_id ) {
$prev_val = get_post_meta( $post_id, 'sort_date', true );
/* set value only if not set yet (i.e. if this is a new post and not an update ) */
if ( empty($prev_val) ) {
if ( 'exhibitions' === $_POST['post_type'] ) {
update_post_meta( $post_id, 'sort_date', $_POST['end_date'] );
} else {
/* using time() assuming "end_date" is a unix timestamp, adjust if not */
update_post_meta( $post_id, 'sort_date', time() );
}
}
}
add_action( 'save_post', 'save_sort_meta' );
The Query:
$args = array(
'post_status' => 'publish',
'post_type' = array( 'exhibitions', 'posts' ),
'meta_key' => 'sort_date',
'order' => 'DESC',
'orderby' => 'meta_value_num'
);
$exhibitions_and_posts = new WP_Query( $args );
As an aside, I’d recommend giving the post type a singular name.