Skip to content

Instantly share code, notes, and snippets.

@andrasguseo
Last active May 2, 2024 10:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrasguseo/5b78351d38b93f74482eadb409672849 to your computer and use it in GitHub Desktop.
Save andrasguseo/5b78351d38b93f74482eadb409672849 to your computer and use it in GitHub Desktop.
TEC > CSV imports > Convert imported event content to block editor
<?php
/**
* When importing events via CSV import convert imported event content to block editor.
*
* Plugins required: The Events Calendar
* Author: Andras Guseo
* Created: October 16, 2023
* Last updated: October 17, 2023
*/
add_action( 'tribe_events_update_meta', 'tec_csv_import_to_block', 10, 3 );
/**
*
*
* @param int $event_id The event ID we are modifying meta for.
* @param array $data The meta fields we want saved.
* @param WP_Post $event The event itself.
*/
function tec_csv_import_to_block( $event_id, $data, $event ) {
// Bail if we're not doing a CSV import.
if (
! isset( $_POST['aggregator']['origin'] )
|| $_POST['aggregator']['origin'] != "csv"
) {
return;
}
/**
* The ID of the post we want to use as a template.
* Create your own template with the block editor. For this, create a draft event and add and arrange the blocks
* the way you want your single event page to look. Use the `[tec_ea_content]` placeholder in a paragraph block,
* this is where the main event description will appear.
*/
$template_post_id = 0;
$template_post = get_post( $template_post_id );
// If there is a template use it. Otherwise, fall back.
if (
$template_post instanceof \WP_Post
&& $template_post->post_type == 'tribe_events'
&& $template_post->post_status == 'draft'
) {
$template = $template_post->post_content;
}
else {
$blocks = [
'datetime' => '<!-- wp:tribe/event-datetime /-->',
'featured_image' => '<!-- wp:tribe/featured-image /-->',
'content_start' => '<!-- wp:paragraph {"placeholder":"Add Description..."} -->',
'content' => '<p>[tec_ea_content]</p>',
'content_end' => '<!-- /wp:paragraph -->',
'organizer' => '<!-- wp:tribe/event-organizer /-->',
'venue' => '<!-- wp:tribe/event-venue /-->',
'sharing' => '<!-- wp:tribe/event-links /-->',
'related' => '<!-- wp:tribe/related-events /-->',
'comments' => '<!-- wp:post-comments-form /-->',
];
/**
* Allows filtering the block template.
*
* @var array $blocks The HTML markup of block elements in an array.
*/
$blocks = apply_filters( 'tec_csv_blocks', $blocks );
/**
* Allows filtering the block order.
* (See example at the end.)
*
* @var bool|array $block_order False|The array of the block order.
*/
$block_order = false;
$block_order = apply_filters( 'tec_csv_block_order', $block_order );
if ( is_array( $block_order ) ) {
// Create a new array to store the ordered items
$ordered_blocks = [];
// Loop through the order array and add items to the new ordered array.
foreach ( $block_order as $item ) {
foreach ( $blocks as $key => $value ) {
if ( strpos( $key, $item ) === 0 ) {
$ordered_blocks[ $key ] = $value;
}
}
}
// Feed it back to $blocks.
$blocks = $ordered_blocks;
}
$template = implode( "\n", $blocks );
}
// The post content from the source site.
$imported_post_content = $event->post_content;
// If there is an organizer in the source, then change it in the template.
if ( isset( $data['organizer'] ) && $data['organizer']['OrganizerID'][0] > 0 ) {
$organizer_replace = '<!-- wp:tribe/event-organizer {"organizer":' . $data['organizer']['OrganizerID'][0] . '} /-->';
} else {
$organizer_replace = '';
}
$template = str_replace(
'<!-- wp:tribe/event-organizer /-->',
$organizer_replace,
$template
);
// If there is an organizer in the source, then change it in the template.
if ( isset( $data['venue'] ) && $data['venue']['VenueID'] > 0 ) {
$venue_replace = '<!-- wp:tribe/event-venue {"venue":' . $data['venue']['VenueID'] . '} /-->';
} else {
$venue_replace = '';
}
$template = str_replace(
'<!-- wp:tribe/event-venue /-->',
$venue_replace,
$template
);
// Convert line breaks to paragraphs.
$search_line_breaks = [
'(\n\n)',
'(\n)',
];
$replace_line_breaks = [
'</p><!-- /wp:paragraph --><!-- wp:paragraph --><p>',
'<br>',
];
$imported_post_content = preg_replace(
$search_line_breaks,
$replace_line_breaks,
$imported_post_content
);
// Replace the placeholder with the content from the source.
// Replace double paragraphs, just in case.
$template = str_replace(
[
'[tec_ea_content]',
'<p><p>',
'</p></p>',
],
[
$imported_post_content,
'<p>',
'</p>',
],
$template
);
wp_update_post( [
'ID' => $event_id,
'post_content' => $template,
]
);
}
/**
* EXAMPLE!
* Optional filter to rearrange the order of the blocks.
*/
add_filter( 'tec_csv_block_order', 'my_custom_block_order' );
/**
* A function to order the blocks used in CSV imports for The Events Calendar.
*
* @param $order
*
* @return string[]
*/
function my_custom_block_order( $order ) {
$order = [
'featured_image',
'organizer',
'datetime',
'content_start',
'content',
'content_end',
'venue',
'sharing',
'related',
'comments',
];
return $order;
}
@designzwang
Copy link

@andrasguseo The check for the import origin $_POST['aggregator']['origin'] will return true only for the first 5 CSV records being processed ( I guess 5 is probably the default batch size). Subsequent records fail since _POST is empty then or at least does not contain the aggregator origin you check for.

@andrasguseo
Copy link
Author

Thanks @designzwang, I will check it out. The first 5 are imported on form POST, the rest is done via AJAX, so probably that's why.

@designzwang
Copy link

Glad to hear that you look into this, @andrasguseo !
To avoid double work, I guess you should know that there is a request pending at https://[you-probably-know-where].zendesk.com/hc/en-us/requests/588530
Note that not only the post_content modification from this gist is skipped after the first batch of 5 records, but also some other post meta changes do not happen ( like - assigning the author or, even though present in csv, a category to an imported record ) so it is not really a bug here but somewhere else in the import handling. However, fixing the template change in this gist to work for all imported records would already be quite helpful.

@andrasguseo
Copy link
Author

Oh, right. That might be related to a bug in ECP. If you have ECP active, deactivate it and try it like that, please.
I'm in the process of submitting a fix for that bug and I hope it'll go out with the next release.

@designzwang
Copy link

And ECP would be ... the Pro version? No option unfortunately, since we need some additional custom fields ( the Pro feature).
I first tried the CLI command ( wp event-aggregator import-from csv /path/to/events.csv .... ) but that did not work for the custom fields, so currently my workaround is simply
add_filter( 'tribe_aggregator_batch_size', function ( $size ) {
return 190;
} );
190 is, at this time, the largest CSV we faced, and to be safe I probably will set the "Import Quantity Limit" value in the GUI to the same number until .

At least in my dev environment this seems to work so that all imported records now get an author, a category, and the Block template post_content assigned.
This is a cheap hack though, so a final fix is of course appreciated. Thx,

@andrasguseo
Copy link
Author

andrasguseo commented Apr 24, 2024

@designzwang Try this snippet, this should fix it.
This is going to go into one of the upcoming releases, it just has to go through code review and testing.

add_filter( 'tribe_events_import_event_duplicate_matches', 'temp_fix_csv_import' );

function temp_fix_csv_import( $matches ) {
	$normalized_matches = array_map( function ( $match ) {
		return \TEC\Events\Custom_Tables\V1\Models\Occurrence::normalize_id( $match );
	}, $matches );

	return $normalized_matches;
}

@designzwang
Copy link

@andrasguseo OK I tried this (with the default batch size, with PRO enabled, and with all events deleted before every imort).
Both WITH and WITHOUT the suggested fix even a short CSV with 10 lines led to the confirmation:
"Import complete!
5 new events were imported. 5 existing events were updated." Strange. Then 10 new events showed up in the backend, 5 of them incomplete.

Only if I enable my Batch size hack all imported events are ok.

@designzwang
Copy link

designzwang commented May 2, 2024

@andrasguseo additional observations and fixes:

  1. if using a template post ( postID of a draft event as per the instructions) , the imported items do NOT show up in the calendar even though they are in the "publish" status in wp_posts and have the TEC "scheduled" status . If I open one of these imported items in the backend editor, the message "Event Published - View Event" appears. So obviously some activities happen upon loading into the editor leading to the post being published eventually.
    Current workaround : not using a template post.

  2. due to the nature of our imported content ( the description text may be long and may contain html) the wp-paragraph was not a good choice to import the content into. It ended up with nested wp-paragraphs and some other mis-formattings like duplicate blocks etc.
    Current workaround: wrapping the whole imported content, whatever it may be, into one single wp-freeform block.

  3. Even those events that are imported OK and show up fine in the public calendar view display the message "Event Published" if they are opened up for Editing in the admin backend. So it seems as if the imported items are still not "complete" and TEC is performing additional activities whith these posts. Comparing the output of "wp post get" and "wp meta list" for such an item shows:

  • the post itself is unchanged
  • the post meta has two changes:
    The field _EventOccurrencesCount with content a:2:{s:11:"is_inserted";b:0;s:10:"is_updated";b:1;} is removed, and the field _tribe_modified_fields shows a change for _EventOccurrencesCount , no other timestamps are changed.
    I wonder whether this is something to worry about or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment