Initial commit: Speakers for The Events Calendar v1.0.0
This commit is contained in:
parent
935c98dfeb
commit
4bb7173476
1 changed files with 572 additions and 0 deletions
572
speakers-for-the-events-calendar.php
Normal file
572
speakers-for-the-events-calendar.php
Normal file
|
|
@ -0,0 +1,572 @@
|
|||
<?php
|
||||
/**
|
||||
* Plugin Name: Speakers for The Events Calendar
|
||||
* Plugin URI: https://github.com/lhorrocks-barlow/speakers-for-the-events-calendar
|
||||
* Description: Add speaker profiles to The Events Calendar events. Registers a Speaker custom post type, provides a searchable speaker picker on event edit screens, displays speakers on event pages, and includes a CSV bulk importer.
|
||||
* Version: 1.0.0
|
||||
* Author: Lauz Horrocks-Barlow
|
||||
* Author URI: https://ocf.co.uk
|
||||
* Text Domain: speakers-for-tec
|
||||
* Domain Path: /languages
|
||||
* License: GPLv2 or later
|
||||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 8.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
final class Speakers_For_TEC {
|
||||
|
||||
const CPT = 'tec_speaker';
|
||||
const META_KEY = '_tec_event_speakers'; // stored on tribe_events posts
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Boot
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'init', [ $this, 'register_cpt' ] );
|
||||
add_action( 'add_meta_boxes', [ $this, 'add_meta_boxes' ] );
|
||||
add_action( 'save_post', [ $this, 'save_meta' ], 10, 2 );
|
||||
add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
|
||||
add_action( 'admin_post_sftec_import', [ $this, 'handle_csv_import' ] );
|
||||
add_action( 'admin_notices', [ $this, 'admin_notices' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
|
||||
add_shortcode( 'tec_speakers', [ $this, 'speakers_shortcode' ] );
|
||||
|
||||
// TEC single-event display — hook fires after the event meta block
|
||||
add_action( 'tribe_events_single_event_after_the_meta', [ $this, 'display_event_speakers' ] );
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Custom post type
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function register_cpt(): void {
|
||||
register_post_type( self::CPT, [
|
||||
'labels' => [
|
||||
'name' => __( 'Speakers', 'speakers-for-tec' ),
|
||||
'singular_name' => __( 'Speaker', 'speakers-for-tec' ),
|
||||
'add_new_item' => __( 'Add New Speaker', 'speakers-for-tec' ),
|
||||
'edit_item' => __( 'Edit Speaker', 'speakers-for-tec' ),
|
||||
'new_item' => __( 'New Speaker', 'speakers-for-tec' ),
|
||||
'view_item' => __( 'View Speaker', 'speakers-for-tec' ),
|
||||
'search_items' => __( 'Search Speakers', 'speakers-for-tec' ),
|
||||
'not_found' => __( 'No speakers found', 'speakers-for-tec' ),
|
||||
'not_found_in_trash' => __( 'No speakers in trash','speakers-for-tec' ),
|
||||
'all_items' => __( 'All Speakers', 'speakers-for-tec' ),
|
||||
'menu_name' => __( 'Speakers', 'speakers-for-tec' ),
|
||||
],
|
||||
'public' => true,
|
||||
'publicly_queryable' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => [ 'slug' => 'speakers' ],
|
||||
'capability_type' => 'post',
|
||||
'has_archive' => true,
|
||||
'hierarchical' => false,
|
||||
'menu_position' => 6,
|
||||
'menu_icon' => 'dashicons-groups',
|
||||
'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt' ],
|
||||
'show_in_rest' => true,
|
||||
] );
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Meta boxes
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function add_meta_boxes(): void {
|
||||
// Extra fields on speaker edit screen
|
||||
add_meta_box(
|
||||
'sftec_speaker_details',
|
||||
__( 'Speaker Details', 'speakers-for-tec' ),
|
||||
[ $this, 'render_speaker_details_meta' ],
|
||||
self::CPT,
|
||||
'side',
|
||||
'default'
|
||||
);
|
||||
|
||||
// Speaker picker on TEC event edit screen
|
||||
if ( post_type_exists( 'tribe_events' ) ) {
|
||||
add_meta_box(
|
||||
'sftec_event_speakers',
|
||||
__( 'Speakers', 'speakers-for-tec' ),
|
||||
[ $this, 'render_event_speakers_meta' ],
|
||||
'tribe_events',
|
||||
'normal',
|
||||
'default'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function render_speaker_details_meta( \WP_Post $post ): void {
|
||||
wp_nonce_field( 'sftec_speaker_details_save', '_sftec_speaker_nonce' );
|
||||
$website = get_post_meta( $post->ID, '_tec_speaker_website', true );
|
||||
$email = get_post_meta( $post->ID, '_tec_speaker_email', true );
|
||||
?>
|
||||
<p>
|
||||
<label><strong><?php esc_html_e( 'Website URL', 'speakers-for-tec' ); ?></strong></label><br>
|
||||
<input type="url" name="sftec_speaker_website" value="<?php echo esc_attr( $website ); ?>" style="width:100%;margin-top:4px">
|
||||
</p>
|
||||
<p>
|
||||
<label><strong><?php esc_html_e( 'Email', 'speakers-for-tec' ); ?></strong></label><br>
|
||||
<input type="email" name="sftec_speaker_email" value="<?php echo esc_attr( $email ); ?>" style="width:100%;margin-top:4px">
|
||||
</p>
|
||||
<p style="font-size:0.85em;color:#666"><?php esc_html_e( 'Use the Featured Image box above to set the speaker photo.', 'speakers-for-tec' ); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_event_speakers_meta( \WP_Post $post ): void {
|
||||
wp_nonce_field( 'sftec_event_speakers_save', '_sftec_event_speakers_nonce' );
|
||||
|
||||
$selected = (array) ( get_post_meta( $post->ID, self::META_KEY, true ) ?: [] );
|
||||
|
||||
$speakers = get_posts( [
|
||||
'post_type' => self::CPT,
|
||||
'posts_per_page' => -1,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'post_status' => 'publish',
|
||||
] );
|
||||
|
||||
if ( empty( $speakers ) ) {
|
||||
printf(
|
||||
'<p><em>%s <a href="%s">%s</a></em></p>',
|
||||
esc_html__( 'No speakers yet.', 'speakers-for-tec' ),
|
||||
esc_url( admin_url( 'post-new.php?post_type=' . self::CPT ) ),
|
||||
esc_html__( 'Add speakers first, then return here to attach them.', 'speakers-for-tec' )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<select id="sftec-speaker-select" name="sftec_event_speakers[]" multiple style="width:100%">';
|
||||
foreach ( $speakers as $speaker ) {
|
||||
printf(
|
||||
'<option value="%d"%s>%s</option>',
|
||||
$speaker->ID,
|
||||
in_array( $speaker->ID, $selected, true ) ? ' selected' : '',
|
||||
esc_html( $speaker->post_title )
|
||||
);
|
||||
}
|
||||
echo '</select>';
|
||||
echo '<p style="margin-top:6px;font-size:0.82em;color:#666">'
|
||||
. esc_html__( 'Type to search. Click a name to add, click again to remove.', 'speakers-for-tec' )
|
||||
. '</p>';
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Saving meta
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function save_meta( int $post_id, \WP_Post $post ): void {
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Speaker detail fields
|
||||
if (
|
||||
isset( $_POST['_sftec_speaker_nonce'] )
|
||||
&& wp_verify_nonce( $_POST['_sftec_speaker_nonce'], 'sftec_speaker_details_save' )
|
||||
&& $post->post_type === self::CPT
|
||||
&& current_user_can( 'edit_post', $post_id )
|
||||
) {
|
||||
update_post_meta( $post_id, '_tec_speaker_website', sanitize_url( $_POST['sftec_speaker_website'] ?? '' ) );
|
||||
update_post_meta( $post_id, '_tec_speaker_email', sanitize_email( $_POST['sftec_speaker_email'] ?? '' ) );
|
||||
}
|
||||
|
||||
// Event → speaker associations
|
||||
if (
|
||||
isset( $_POST['_sftec_event_speakers_nonce'] )
|
||||
&& wp_verify_nonce( $_POST['_sftec_event_speakers_nonce'], 'sftec_event_speakers_save' )
|
||||
&& $post->post_type === 'tribe_events'
|
||||
&& current_user_can( 'edit_post', $post_id )
|
||||
) {
|
||||
$ids = array_map( 'intval', (array) ( $_POST['sftec_event_speakers'] ?? [] ) );
|
||||
update_post_meta( $post_id, self::META_KEY, array_filter( $ids ) );
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Front-end: speakers on event pages
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function display_event_speakers(): void {
|
||||
$post_id = get_the_ID();
|
||||
$speaker_ids = (array) ( get_post_meta( $post_id, self::META_KEY, true ) ?: [] );
|
||||
|
||||
if ( empty( $speaker_ids ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$speakers = get_posts( [
|
||||
'post_type' => self::CPT,
|
||||
'post__in' => $speaker_ids,
|
||||
'posts_per_page' => -1,
|
||||
'orderby' => 'post__in',
|
||||
'post_status' => 'publish',
|
||||
] );
|
||||
|
||||
if ( empty( $speakers ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->enqueue_styles();
|
||||
|
||||
$heading = count( $speakers ) > 1
|
||||
? __( 'Speakers', 'speakers-for-tec' )
|
||||
: __( 'Speaker', 'speakers-for-tec' );
|
||||
|
||||
echo '<section class="sftec-event-speakers">';
|
||||
echo '<h3 class="sftec-speakers-heading">' . esc_html( $heading ) . '</h3>';
|
||||
echo '<div class="sftec-speakers-list">';
|
||||
|
||||
foreach ( $speakers as $speaker ) {
|
||||
$this->render_speaker_card( $speaker, 'event' );
|
||||
}
|
||||
|
||||
echo '</div></section>';
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Shortcode: [tec_speakers limit="20" search=""]
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function speakers_shortcode( array $atts ): string {
|
||||
$atts = shortcode_atts( [ 'limit' => 50, 'search' => '' ], $atts, 'tec_speakers' );
|
||||
|
||||
$args = [
|
||||
'post_type' => self::CPT,
|
||||
'posts_per_page' => (int) $atts['limit'],
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
'post_status' => 'publish',
|
||||
];
|
||||
if ( $atts['search'] ) {
|
||||
$args['s'] = sanitize_text_field( $atts['search'] );
|
||||
}
|
||||
|
||||
$speakers = get_posts( $args );
|
||||
if ( empty( $speakers ) ) {
|
||||
return '<p>' . esc_html__( 'No speakers found.', 'speakers-for-tec' ) . '</p>';
|
||||
}
|
||||
|
||||
$this->enqueue_styles();
|
||||
|
||||
ob_start();
|
||||
echo '<div class="sftec-speakers-archive">';
|
||||
foreach ( $speakers as $speaker ) {
|
||||
$this->render_speaker_card( $speaker, 'archive' );
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Shared card renderer
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private function render_speaker_card( \WP_Post $speaker, string $context = 'event' ): void {
|
||||
$permalink = get_permalink( $speaker->ID );
|
||||
$website = get_post_meta( $speaker->ID, '_tec_speaker_website', true );
|
||||
|
||||
echo '<article class="sftec-speaker-card sftec-speaker-card--' . esc_attr( $context ) . '">';
|
||||
|
||||
if ( has_post_thumbnail( $speaker->ID ) ) {
|
||||
echo '<div class="sftec-speaker-photo">';
|
||||
echo '<a href="' . esc_url( $permalink ) . '">';
|
||||
echo get_the_post_thumbnail( $speaker->ID, [ 80, 80 ], [ 'class' => 'sftec-speaker-img' ] );
|
||||
echo '</a>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '<div class="sftec-speaker-info">';
|
||||
echo '<h4 class="sftec-speaker-name"><a href="' . esc_url( $permalink ) . '">' . esc_html( $speaker->post_title ) . '</a></h4>';
|
||||
|
||||
$excerpt = $speaker->post_excerpt ?: wp_trim_words( wp_strip_all_tags( $speaker->post_content ), 30 );
|
||||
if ( $excerpt ) {
|
||||
echo '<p class="sftec-speaker-bio">' . esc_html( $excerpt ) . '</p>';
|
||||
}
|
||||
|
||||
if ( $website ) {
|
||||
echo '<a href="' . esc_url( $website ) . '" class="sftec-speaker-link" target="_blank" rel="noopener noreferrer">'
|
||||
. esc_html__( 'Website', 'speakers-for-tec' ) . ' →</a>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
echo '</article>';
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Styles (enqueued once per page load)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private function enqueue_styles(): void {
|
||||
static $done = false;
|
||||
if ( $done ) {
|
||||
return;
|
||||
}
|
||||
$done = true;
|
||||
|
||||
echo '<style id="sftec-speakers-css">
|
||||
.sftec-event-speakers { margin: 2em 0; border-top: 1px solid #e0e0e0; padding-top: 1.5em; }
|
||||
.sftec-speakers-heading { font-size: 1.15em; font-weight: 600; margin: 0 0 1em; }
|
||||
.sftec-speakers-list,
|
||||
.sftec-speakers-archive { display: flex; flex-wrap: wrap; gap: 1.25em; }
|
||||
.sftec-speaker-card { display: flex; gap: 0.85em; align-items: flex-start; flex: 1 1 260px; max-width: 420px; }
|
||||
.sftec-speaker-photo img { border-radius: 50%; width: 64px; height: 64px; object-fit: cover; display: block; }
|
||||
.sftec-speaker-info { flex: 1; min-width: 0; }
|
||||
.sftec-speaker-name { margin: 0 0 0.3em; font-size: 1em; font-weight: 600; }
|
||||
.sftec-speaker-name a { text-decoration: none; color: inherit; }
|
||||
.sftec-speaker-name a:hover { text-decoration: underline; }
|
||||
.sftec-speaker-bio { margin: 0 0 0.4em; font-size: 0.875em; color: #555; line-height: 1.4; }
|
||||
.sftec-speaker-link { font-size: 0.825em; }
|
||||
</style>';
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Admin: Select2 on event edit screens
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function admin_enqueue_scripts( string $hook ): void {
|
||||
$screen = get_current_screen();
|
||||
if ( ! $screen || $screen->post_type !== 'tribe_events' || ! in_array( $hook, [ 'post.php', 'post-new.php' ], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use WP-bundled Select2 if available (TEC ships it), otherwise CDN fallback
|
||||
if ( wp_script_is( 'select2', 'registered' ) ) {
|
||||
wp_enqueue_script( 'select2' );
|
||||
wp_enqueue_style( 'select2' );
|
||||
$handle = 'select2';
|
||||
} else {
|
||||
wp_enqueue_script(
|
||||
'sftec-select2',
|
||||
'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js',
|
||||
[ 'jquery' ],
|
||||
'4.1.0',
|
||||
true
|
||||
);
|
||||
wp_enqueue_style(
|
||||
'sftec-select2',
|
||||
'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css',
|
||||
[],
|
||||
'4.1.0'
|
||||
);
|
||||
$handle = 'sftec-select2';
|
||||
}
|
||||
|
||||
wp_add_inline_script(
|
||||
$handle,
|
||||
"jQuery(function($){
|
||||
$('#sftec-speaker-select').select2({
|
||||
placeholder: '" . esc_js( __( 'Search for a speaker…', 'speakers-for-tec' ) ) . "',
|
||||
allowClear: true,
|
||||
width: '100%'
|
||||
});
|
||||
});"
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Admin: CSV import page
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function add_admin_menu(): void {
|
||||
add_submenu_page(
|
||||
'edit.php?post_type=' . self::CPT,
|
||||
__( 'Import Speakers from CSV', 'speakers-for-tec' ),
|
||||
__( 'Import CSV', 'speakers-for-tec' ),
|
||||
'manage_options',
|
||||
'sftec-import-speakers',
|
||||
[ $this, 'render_import_page' ]
|
||||
);
|
||||
}
|
||||
|
||||
public function render_import_page(): void {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Import Speakers from CSV', 'speakers-for-tec' ); ?></h1>
|
||||
|
||||
<p><?php esc_html_e( 'Upload a CSV file with the following columns (header row required):', 'speakers-for-tec' ); ?></p>
|
||||
<code>name, bio, email, website, image</code>
|
||||
|
||||
<ul style="list-style:disc;margin-left:1.5em;margin-top:0.5em">
|
||||
<li><strong>name</strong> — <?php esc_html_e( "speaker's full name (required)", 'speakers-for-tec' ); ?></li>
|
||||
<li><strong>bio</strong> — <?php esc_html_e( 'plain-text biography', 'speakers-for-tec' ); ?></li>
|
||||
<li><strong>email</strong> — <?php esc_html_e( 'contact email (stored as private meta)', 'speakers-for-tec' ); ?></li>
|
||||
<li><strong>website</strong> — <?php esc_html_e( 'full URL including https://', 'speakers-for-tec' ); ?></li>
|
||||
<li><strong>image</strong> — <?php esc_html_e( 'ignored during import (add photos via Featured Image)', 'speakers-for-tec' ); ?></li>
|
||||
</ul>
|
||||
|
||||
<p><?php esc_html_e( 'Speakers whose name matches an existing speaker are skipped — no overwrites.', 'speakers-for-tec' ); ?></p>
|
||||
|
||||
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data">
|
||||
<?php wp_nonce_field( 'sftec_import_speakers', '_sftec_import_nonce' ); ?>
|
||||
<input type="hidden" name="action" value="sftec_import">
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><label for="sftec_csv"><?php esc_html_e( 'CSV File', 'speakers-for-tec' ); ?></label></th>
|
||||
<td><input type="file" id="sftec_csv" name="speakers_csv" accept=".csv" required></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Options', 'speakers-for-tec' ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="skip_header" value="1" checked>
|
||||
<?php esc_html_e( 'First row is a header (skip it)', 'speakers-for-tec' ); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php submit_button( __( 'Import Speakers', 'speakers-for-tec' ) ); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function handle_csv_import(): void {
|
||||
if (
|
||||
! current_user_can( 'manage_options' )
|
||||
|| ! wp_verify_nonce( $_POST['_sftec_import_nonce'] ?? '', 'sftec_import_speakers' )
|
||||
) {
|
||||
wp_die( 'Unauthorised' );
|
||||
}
|
||||
|
||||
if ( empty( $_FILES['speakers_csv']['tmp_name'] ) ) {
|
||||
wp_redirect( $this->import_redirect( [ 'sftec_err' => 'no_file' ] ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
$handle = fopen( $_FILES['speakers_csv']['tmp_name'], 'r' );
|
||||
if ( ! $handle ) {
|
||||
wp_redirect( $this->import_redirect( [ 'sftec_err' => 'read_fail' ] ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
$skip_header = ! empty( $_POST['skip_header'] );
|
||||
|
||||
// Build name index to prevent duplicates
|
||||
$existing_ids = get_posts( [
|
||||
'post_type' => self::CPT,
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'post_status' => 'any',
|
||||
] );
|
||||
$existing_names = [];
|
||||
foreach ( $existing_ids as $id ) {
|
||||
$existing_names[ $this->normalise_name( get_the_title( $id ) ) ] = true;
|
||||
}
|
||||
|
||||
$created = $skipped = $errors = 0;
|
||||
$row_num = 0;
|
||||
|
||||
while ( ( $row = fgetcsv( $handle ) ) !== false ) {
|
||||
$row_num++;
|
||||
if ( $skip_header && $row_num === 1 ) {
|
||||
continue;
|
||||
}
|
||||
if ( empty( array_filter( $row ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[ $name, $bio, $email, $website ] = array_pad( $row, 4, '' );
|
||||
$name = trim( $name );
|
||||
|
||||
if ( ! $name ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = $this->normalise_name( $name );
|
||||
if ( isset( $existing_names[ $key ] ) ) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$post_id = wp_insert_post( [
|
||||
'post_type' => self::CPT,
|
||||
'post_title' => sanitize_text_field( $name ),
|
||||
'post_content' => wp_kses_post( $bio ),
|
||||
'post_excerpt' => wp_trim_words( wp_strip_all_tags( $bio ), 30 ),
|
||||
'post_status' => 'publish',
|
||||
] );
|
||||
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
$errors++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $email ) {
|
||||
update_post_meta( $post_id, '_tec_speaker_email', sanitize_email( trim( $email ) ) );
|
||||
}
|
||||
if ( $website ) {
|
||||
update_post_meta( $post_id, '_tec_speaker_website', sanitize_url( trim( $website ) ) );
|
||||
}
|
||||
|
||||
$existing_names[ $key ] = true;
|
||||
$created++;
|
||||
}
|
||||
|
||||
fclose( $handle );
|
||||
|
||||
wp_redirect( $this->import_redirect( [
|
||||
'sftec_created' => $created,
|
||||
'sftec_skipped' => $skipped,
|
||||
'sftec_errors' => $errors,
|
||||
] ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function import_redirect( array $args ): string {
|
||||
return add_query_arg(
|
||||
array_merge( [ 'post_type' => self::CPT, 'page' => 'sftec-import-speakers' ], $args ),
|
||||
admin_url( 'edit.php' )
|
||||
);
|
||||
}
|
||||
|
||||
private function normalise_name( string $name ): string {
|
||||
$name = strtolower( trim( $name ) );
|
||||
$name = preg_replace( '/^(dr\.?|prof\.?|mr\.?|mrs\.?|ms\.?|miss\.?)\s+/u', '', $name );
|
||||
return preg_replace( '/\s+/', ' ', $name );
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Admin notices: import result banner
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public function admin_notices(): void {
|
||||
if ( ! isset( $_GET['sftec_created'] ) && ! isset( $_GET['sftec_err'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $_GET['sftec_err'] ) ) {
|
||||
$msgs = [
|
||||
'no_file' => __( 'No file was uploaded.', 'speakers-for-tec' ),
|
||||
'read_fail' => __( 'Could not read the uploaded file.', 'speakers-for-tec' ),
|
||||
];
|
||||
$msg = $msgs[ $_GET['sftec_err'] ] ?? __( 'An unknown error occurred.', 'speakers-for-tec' );
|
||||
printf(
|
||||
'<div class="notice notice-error is-dismissible"><p>%s %s</p></div>',
|
||||
esc_html__( 'Import failed:', 'speakers-for-tec' ),
|
||||
esc_html( $msg )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<div class="notice notice-success is-dismissible"><p>'
|
||||
. __( 'Import complete — <strong>%d created</strong>, %d skipped (already exist), %d errors.', 'speakers-for-tec' )
|
||||
. '</p></div>',
|
||||
(int) $_GET['sftec_created'],
|
||||
(int) $_GET['sftec_skipped'],
|
||||
(int) $_GET['sftec_errors']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
new Speakers_For_TEC();
|
||||
Loading…
Add table
Add a link
Reference in a new issue