get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) ) ) ) {
return 0;
}
$cursor = (int) \Academy\Options::get( \Academy\Options::MIGRATIONS, self::CURSOR_KEY, 0 );
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPressVIPMinimum.Variables.RestrictedVariables.user_meta__wpdb__users -- one-time data migration; only $wpdb table names are interpolated, values are prepared.
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT umeta_id, user_id, meta_key, meta_value FROM {$wpdb->usermeta}
WHERE umeta_id > %d AND meta_key LIKE %s
ORDER BY umeta_id ASC
LIMIT %d",
$cursor,
$wpdb->esc_like( 'academy_' ) . '%' . $wpdb->esc_like( 'lesson_note_' ) . '%',
(int) $batch_size
)
);
foreach ( (array) $rows as $row ) {
$cursor = (int) $row->umeta_id;
if ( ! preg_match( '/^academy_([0-9]*)lesson_note_([0-9]+)$/', $row->meta_key, $m ) ) {
continue;
}
$raw = (string) $row->meta_value;
$content = self::to_note_text( $raw );
if ( '' === $content ) {
continue;
}
$user_id = (int) $row->user_id;
$course_id = (int) $m[1];
$now = current_time( 'mysql' );
// Already there? Either this note was migrated (as text) before, or
// an earlier migration copied the raw HTML — convert that in place.
$existing = $wpdb->get_row(
$wpdb->prepare(
"SELECT id, content FROM {$table}
WHERE user_id = %d AND course_id = %d AND topic_id = 0 AND note_type = 'text'
AND ( content = %s OR content = %s )
LIMIT 1",
$user_id,
$course_id,
$content,
$raw
)
);
if ( $existing ) {
if ( $existing->content !== $content ) {
$wpdb->update( $table, array( 'content' => $content ), array( 'id' => (int) $existing->id ), array( '%s' ), array( '%d' ) );
}
continue;
}
$wpdb->insert(
$table,
array(
'user_id' => $user_id,
'course_id' => $course_id,
'topic_id' => 0,
'topic_type' => '',
'note_type' => 'text',
'video_time' => null,
'content' => $content,
'created_at' => $now,
'updated_at' => $now,
),
array( '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s' )
);
}//end foreach
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPressVIPMinimum.Variables.RestrictedVariables.user_meta__wpdb__users
\Academy\Options::set( \Academy\Options::MIGRATIONS, self::CURSOR_KEY, $cursor );
$count = is_array( $rows ) ? count( $rows ) : 0;
if ( $count < $batch_size ) {
\Academy\Options::set( \Academy\Options::MIGRATIONS, self::STATUS_KEY, 'done' );
} elseif ( function_exists( 'as_enqueue_async_action' ) && ! as_has_scheduled_action( self::BATCH_ACTION ) ) {
as_enqueue_async_action( self::BATCH_ACTION, array(), 'academy' );
}
return $count;
}
/**
* Legacy notes are HTML from a contenteditable box; notes are plain text
* shown with preserved line breaks. Turn block/line breaks into newlines,
* drop the rest of the markup, and store it the way the Notes API does.
*
* @param string $html Legacy note.
* @return string
*/
public static function to_note_text( $html ) {
$text = preg_replace( '#
#i', "\n", (string) $html );
$text = preg_replace( '#