Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions globals/migrations.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* @package migrations.php
*/

use Neve\Compatibility\Starter_Content;
use Neve\Core\Migration_Flags;

/**
Expand Down Expand Up @@ -134,3 +135,4 @@ function neve_run_migration_flags() {
add_action( 'init', 'neve_migrate_blog_layout' );
add_action( 'init', 'neve_migrate_blog_columns' );
add_action( 'init', 'neve_run_migration_flags', PHP_INT_MIN );
add_action( 'init', [ Starter_Content::class, 'migrate_legacy_content_images' ] );
186 changes: 186 additions & 0 deletions inc/compatibility/starter_content.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ class Starter_Content {
const PRICING_SLUG = 'pricing';
const WORK_SLUG = 'work';

/**
* Option name for the one-time migration that repoints already-published starter pages.
*/
const IMAGES_MIGRATED_OPTION = 'neve_starter_content_migrated';

/**
* Run the hooks and filters.
Expand Down Expand Up @@ -210,6 +214,7 @@ public function after_publish() {
}
$this->apply_starter_custom_css();
$this->maybe_enable_pretty_permalinks();
$this->sync_content_images();
}

/**
Expand All @@ -232,6 +237,187 @@ private function get_published_starter_page( $slug ) {
return empty( $pages ) ? null : $pages[0];
}

/**
* Get the starter content image attachments, keyed by their content-image symbol.
*
* @return array<string, array{post_title: string, post_name: string, file: string}>
*/
private function get_content_image_attachments() {
return [
'folio-design' => [
'post_title' => 'Folio Design',
'post_name' => 'folio-design',
'file' => 'assets/img/starter-content/folio-design.jpeg',
],
'folio-code' => [
'post_title' => 'Folio Code',
'post_name' => 'folio-code',
'file' => 'assets/img/starter-content/folio-code.jpeg',
],
'folio-process' => [
'post_title' => 'Folio Process',
'post_name' => 'folio-process',
'file' => 'assets/img/starter-content/folio-process.jpeg',
],
'folio-dashboard' => [
'post_title' => 'Folio Dashboard',
'post_name' => 'folio-dashboard',
'file' => 'assets/img/starter-content/folio-dashboard.jpeg',
],
'folio-hero' => [
'post_title' => 'Folio Hero',
'post_name' => 'folio-hero',
'file' => 'assets/img/starter-content/folio-hero.jpeg',
],
];
}

/**
* Get starter content attachment, if not exists import it.
*
* @param array{post_title: string, post_name: string, file: string} $definition Attachment definition.
*
* @return int|null Attachment ID, or null if it doesn't exist and couldn't be imported.
*/
private function get_or_import_attachment( $definition ) {
$existing = get_posts(
array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'posts_per_page' => 1,
'meta_key' => '_customize_draft_post_name',
'meta_value' => $definition['post_name'], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
)
);
if ( ! empty( $existing ) ) {
$attached_file = get_attached_file( $existing[0]->ID );
if ( $attached_file && file_exists( $attached_file ) ) {
return $existing[0]->ID;
}
}

$file_path = get_template_directory() . '/' . $definition['file'];
if ( ! file_exists( $file_path ) ) {
return null;
}

if ( ! function_exists( 'media_handle_sideload' ) ) {
require_once ABSPATH . 'wp-admin/includes/image.php';
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
}

$temp_file_name = wp_tempnam( wp_basename( $file_path ) );
if ( ! $temp_file_name || ! copy( $file_path, $temp_file_name ) ) {
return null;
}

$file_array = array(
'name' => wp_basename( $file_path ),
'tmp_name' => $temp_file_name,
);

$attachment_id = media_handle_sideload( $file_array, 0, null, array( 'post_title' => $definition['post_title'] ) );
if ( is_wp_error( $attachment_id ) ) {
return null;
}

update_post_meta( $attachment_id, '_customize_draft_post_name', $definition['post_name'] );
update_post_meta( $attachment_id, '_starter_content_theme', get_stylesheet() );

return $attachment_id;
}

/**
* Migrate a starter page's content images from the legacy theme asset URLs to the
* Media Library attachments that were sideloaded for them.
*
* @param \WP_Post $page Published starter page.
*
* @return bool True if all content images were successfully migrated, false if any were missing.
*/
private function migrate_page_content_images( $page ) {
$theme_uri = trailingslashit( get_template_directory_uri() );
$content = $page->post_content;
$changed = false;
$fully_synced = true;

foreach ( $this->get_content_image_attachments() as $definition ) {
$old_url = $theme_uri . $definition['file'];
if ( false === strpos( $content, $old_url ) ) {
continue;
}

$attachment_id = $this->get_or_import_attachment( $definition );
if ( ! $attachment_id ) {
$fully_synced = false;
continue;
}

$new_url = wp_get_attachment_url( $attachment_id );
if ( ! $new_url ) {
$fully_synced = false;
continue;
}

$content = str_replace( $old_url, $new_url, $content );
$changed = true;
}

if ( $changed ) {
wp_update_post(
array(
'ID' => $page->ID,
'post_content' => $content,
)
);
}

return $fully_synced;
}

/**
* Sync content images for every currently published starter page right after publish.
*
* @return void
*/
private function sync_content_images() {
foreach ( [ self::HOME_SLUG, self::ABOUT_SLUG, self::SERVICES_SLUG, self::WORK_SLUG ] as $slug ) {
$page = $this->get_published_starter_page( $slug );
if ( $page ) {
$this->migrate_page_content_images( $page );
}
}
}

/**
* Migrate the legacy starter content images to the Media Library, if they haven't been already.
*
* @return void
*/
public static function migrate_legacy_content_images() {
if ( get_option( self::IMAGES_MIGRATED_OPTION ) ) {
return;
}

$instance = new self();
$fully_synced = true;

foreach ( [ self::HOME_SLUG, self::ABOUT_SLUG, self::SERVICES_SLUG, self::WORK_SLUG ] as $slug ) {
$page = $instance->get_published_starter_page( $slug );
if ( ! $page ) {
continue;
}
if ( ! $instance->migrate_page_content_images( $page ) ) {
$fully_synced = false;
}
}

if ( $fully_synced ) {
update_option( self::IMAGES_MIGRATED_OPTION, 1 );
}
}

/**
* Allow the inline SVG icons used by the starter pages through KSES.
*
Expand Down
Loading