'Region',
'orderby' => 'tax_name',
'selected' => !empty($_GET['region']) ? sanitize_text_field($_GET['region']) : '',
'hierarchical' => true,
'name' => 'region',
'taxonomy' => 'tsml_region',
'hide_if_empty' => true,
]);
$types = [];
foreach ($tsml_types_in_use as $type) {
if (!isset($tsml_programs[$tsml_program]['types'][$type])) {
continue;
}
$types[$type] = $tsml_programs[$tsml_program]['types'][$type];
}
asort($types);
echo '';
$data_sources = [
-1 => __('None', '12-step-meeting-list')
];
foreach (array_values($tsml_data_sources) as $index => $data_source) {
$data_sources[$index] = $data_source['name'];
}
echo '';
}, 10, 1);
// if filter is set, restrict results
add_filter(
'pre_get_posts',
function ($query) {
global $post_type, $pagenow, $wpdb;
if ($pagenow === 'edit.php' && $post_type === 'tsml_meeting' && $query->is_main_query()) {
$meta_query = [];
$tsml_data_sources = tsml_get_option_array('tsml_data_sources');
if (!empty($_GET['region'])) {
$parent_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT p.ID FROM $wpdb->posts p
JOIN $wpdb->term_relationships r ON r.object_id = p.ID
JOIN $wpdb->term_taxonomy x ON x.term_taxonomy_id = r.term_taxonomy_id
WHERE x.term_id = %d",
intval(sanitize_text_field($_GET['region']))
)
);
$query->query_vars['post_parent__in'] = empty($parent_ids) ? [0] : $parent_ids;
}
if (!empty($_GET['type'])) {
$meta_query[] = [
'key' => 'types',
'value' => '"' . sanitize_text_field($_GET['type']) . '"',
'compare' => 'LIKE',
];
}
if (isset($_GET['data_source']) && is_numeric($_GET['data_source'])) {
$index = intval($_GET['data_source']);
if (-1 === $index) {
$meta_query[] = [
'key' => 'data_source',
'compare' => 'NOT EXISTS',
];
} else {
$data_source = array_keys($tsml_data_sources)[$index];
if ($data_source) {
$meta_query[] = [
'key' => 'data_source',
'value' => $data_source,
'compare' => '=',
];
}
}
}
if (!empty($meta_query)) {
$query->set('meta_query', $meta_query);
}
}
}
);
// custom columns for meetings
add_filter(
'manage_edit-tsml_meeting_columns',
function () {
return [
'cb' => '',
'title' => __('Meeting', '12-step-meeting-list'),
'day' => __('Day', '12-step-meeting-list'),
'time' => __('Time', '12-step-meeting-list'),
'region' => __('Region', '12-step-meeting-list'),
'data_source' => __('Data Source', '12-step-meeting-list'),
'date' => __('Date', '12-step-meeting-list'),
];
}
);
// if you're deleting meetings, also delete locations
add_action(
'delete_post',
function ($post_id) {
$post = get_post($post_id);
if ($post->post_type == 'tsml_meeting') {
tsml_require_meetings_permission();
tsml_delete_orphans();
}
}
);
// custom list values for meetings
add_action('manage_tsml_meeting_posts_custom_column', function ($column_name, $post_ID) {
global $tsml_days, $wpdb;
if ($column_name == 'day') {
$day = get_post_meta($post_ID, 'day', true);
echo (empty($day) && $day !== '0') ? esc_html__('Appointment', '12-step-meeting-list') : esc_html($tsml_days[$day]);
} elseif ($column_name == 'time') {
echo esc_html(tsml_format_time(get_post_meta($post_ID, 'time', true)));
} elseif ($column_name == 'region') {
// don't know how to do this with fewer queries
echo esc_html($wpdb->get_var('SELECT t.name
FROM ' . $wpdb->terms . ' t
JOIN ' . $wpdb->term_taxonomy . ' x ON t.term_id = x.term_id
JOIN ' . $wpdb->term_relationships . ' r ON x.term_taxonomy_id = r.term_taxonomy_id
JOIN ' . $wpdb->posts . ' p ON r.object_id = p.post_parent
WHERE p.ID = ' . intval($post_ID)));
} elseif ($column_name == 'data_source') {
$tsml_data_sources = tsml_get_option_array('tsml_data_sources');
$data_source = get_post_meta($post_ID, 'data_source', true);
if ($data_source && isset($tsml_data_sources[$data_source])) {
echo $tsml_data_sources[$data_source]['name'];
}
}
}, 10, 2);
// set custom meetings columns to be sortable
add_filter('manage_edit-tsml_meeting_sortable_columns', function ($columns) {
$columns['day'] = 'day';
$columns['time'] = 'time';
return $columns;
});
// apply sorting
add_filter('request', function ($vars) {
if (isset($vars['orderby'])) {
switch ($vars['orderby']) {
case 'day':
return array_merge($vars, [
'meta_key' => 'day',
'orderby' => 'meta_value',
]);
case 'time':
return array_merge($vars, [
'meta_key' => 'time',
'orderby' => 'meta_value',
]);
}
}
return $vars;
});
// remove quick edit because meetings could get messed up without custom fields
add_filter('post_row_actions', function ($actions) {
global $post;
if ($post->post_type == 'tsml_meeting') {
unset($actions['inline hide-if-no-js']);
}
return $actions;
}, 10, 2);
// adding "Remove Temporary Closure" to Bulk Actions dropdown
add_filter('bulk_actions-edit-tsml_meeting', function ($bulk_array) {
$bulk_array['tsml_open_in_person'] = __('Reopen for In Person Attendees', '12-step-meeting-list');
return $bulk_array;
});
// handle removing Temporary Closures
add_filter('handle_bulk_actions-edit-tsml_meeting', function ($redirect, $doaction, $object_ids) {
tsml_require_meetings_permission();
// handle tsml_add_tc
// let's remove query args first
$redirect = remove_query_arg(['tsml_add_tc'], $redirect);
$redirect = remove_query_arg(['tsml_remove_tc'], $redirect);
$redirect = remove_query_arg(['tsml_open_in_person'], $redirect);
// do something for "Add Temporary Closure" bulk action
if ($doaction == 'tsml_add_tc') {
$count = 0;
foreach ($object_ids as $post_id) {
// for each select post, add TC if it's not selected in "types"
$types = get_post_meta($post_id, 'types', false)[0];
if (!in_array('TC', array_values($types))) {
$types[] = 'TC';
update_post_meta($post_id, 'types', array_map('esc_attr', $types));
$count++;
}
}
if ($count > 0) {
// rebuild cache
tsml_cache_rebuild();
// update types in use
tsml_update_types_in_use();
// add number of meetings changed to query args
$redirect = add_query_arg('tsml_add_tc', $count, $redirect);
}
}
// do something for "Remove Temporary Closure" bulk action
if ($doaction == 'tsml_remove_tc') {
$count = 0;
foreach ($object_ids as $post_id) {
// For each select post, remove TC if it's selected in "types"
$types = get_post_meta($post_id, 'types', false)[0];
if (!empty($types) && in_array('TC', array_values($types))) {
$types = array_diff($types, ['TC']);
if (empty($types)) {
delete_post_meta($post_id, 'types');
} else {
update_post_meta($post_id, 'types', array_map('esc_attr', $types));
}
$count++;
}
}
if ($count > 0) {
// rebuild cache
tsml_cache_rebuild();
// update types in use
tsml_update_types_in_use();
// add number of meetings changed to query args
$redirect = add_query_arg('tsml_remove_tc', $count, $redirect);
}
}
// do something for "Remove Temporary Closure" bulk action
if ($doaction == 'tsml_open_in_person') {
$count = 0;
foreach ($object_ids as $post_id) {
$meeting = tsml_get_meeting($post_id);
if ($meeting->attendance_option == 'in_person' || $meeting->attendance_option == 'hybrid') {
continue;
}
if (empty($meeting->formatted_address) || tsml_geocode($meeting->formatted_address)['approximate'] != 'no') {
continue;
}
// for each select post, remove TC if it's selected in "types"
$types = get_post_meta($post_id, 'types', false)[0];
if (!empty($types) && in_array('TC', array_values($types))) {
$types = array_diff($types, ['TC']);
if (empty($types)) {
delete_post_meta($post_id, 'types');
} else {
update_post_meta($post_id, 'types', array_map('esc_attr', $types));
}
}
$count++;
}
if ($count > 0) {
// rebuild cache
tsml_cache_rebuild();
// update types in use
tsml_update_types_in_use();
// add number of meetings changed to query args
$redirect = add_query_arg('tsml_open_in_person', $count, $redirect);
}
}
return $redirect;
}, 10, 3);
// notify how many Temporary Closures were removed
add_action(
'admin_notices',
function () {
if (!empty($_REQUEST['tsml_add_tc'])) {
// depending on how many posts were changed, make the message different
tsml_alert(sprintf(
// translators: %s is the number of meetings that were changed
_n(
'Temporary Closure added to %s meeting',
'Temporary Closure added to %s meetings',
intval($_REQUEST['tsml_add_tc']),
'12-step-meeting-list'
),
intval($_REQUEST['tsml_add_tc'])
));
}
if (!empty($_REQUEST['tsml_remove_tc'])) {
// depending on how many posts were changed, make the message different
tsml_alert(sprintf(
// translators: %s is the number of meetings that were changed
_n(
'Temporary Closure removed from %s meeting',
'Temporary Closure removed from %s meetings',
intval($_REQUEST['tsml_remove_tc']),
'12-step-meeting-list'
),
intval($_REQUEST['tsml_remove_tc'])
));
}
if (!empty($_REQUEST['tsml_open_in_person'])) {
// depending on how many posts were changed, make the message different
tsml_alert(sprintf(
// translators: %s is the number of meetings that were changed
_n(
'%s meeting reopened for in person attendees',
'%s meetings reopended for in person attendees',
intval($_REQUEST['tsml_open_in_person']),
'12-step-meeting-list'
),
intval($_REQUEST['tsml_open_in_person'])
));
}
}
);
// special global var to store region counts
$tsml_region_counts = [];
// customizing the "count" column for regions - would be nice if we could use the existing 'posts' column for sorting
add_filter('manage_edit-tsml_region_columns', function ($columns) {
global $wpdb, $tsml_region_counts;
// get region meeting counts (regions are associated with locations, not meetings)
$results = $wpdb->get_results('SELECT
x.term_id,
(SELECT COUNT(*) FROM ' . $wpdb->posts . ' p WHERE p.post_parent IN
(SELECT tr.object_id FROM ' . $wpdb->term_relationships . ' tr WHERE tr.term_taxonomy_id = x.term_taxonomy_id )
) AS meetings
FROM wp_term_taxonomy x
WHERE x.taxonomy = "tsml_region"');
foreach ($results as $result) {
$tsml_region_counts[$result->term_id] = $result->meetings;
}
unset($columns['posts']);
$columns['meetings'] = __('Meetings', '12-step-meeting-list');
return $columns;
});
// customizing the "count" column for regions
add_action('manage_tsml_region_custom_column', function ($string, $column_name, $term_id) {
global $tsml_region_counts;
if ($column_name === 'meetings') {
$term = get_term($term_id, 'tsml_region');
$query = http_build_query([
'post_status' => 'all',
'post_type' => 'tsml_meeting',
'region' => $term->term_id,
'filter_action' => 'Filter',
'paged' => 1,
]);
return '' .
@$tsml_region_counts[$term->term_id] .
'';
}
return $string;
}, 10, 3);