Menu

Content Import Overview

Relevant source files

Purpose and Scope

This document describes the architecture and workflow of the Content Import system, which allows administrators to duplicate or link content from one blog to another in a WordPress multisite network. The system orchestrates the transfer of post fields, custom fields, taxonomy terms, featured images, and attachments between blogs while maintaining translation relationships.

For details about specific importer types and their duplication/linking strategies, see Import Strategies and Importers. For information about logging, error tracking, and bidirectional relationship management, see Import Logging and Relations.


System Overview

The Content Import system enables bulk content transfer between blogs through a meta box interface on post edit screens. When an editor initiates an import by selecting a source post, the system:

  1. Validates the request and parses source/destination coordinates
  2. Creates or identifies the destination post
  3. Coordinates multiple specialized importers to transfer different content types
  4. Logs all operations and outcomes
  5. Establishes bidirectional translation links
  6. Displays results to the user via admin notices

The import process can either duplicate content (creating new copies) or link to existing content (referencing across blogs), depending on the importer strategy configured.

Key Components:

ComponentFilePurpose
ContentImporterincludes/ContentImport/ContentImporter.phpMain orchestrator that handles import requests and coordinates all importers
ImportCoordinatesincludes/ContentImport/ImportCoordinates.phpData object carrying source/destination context
Mapincludes/ContentImport/Importers/Map.phpFactory system for creating importer instances
ImportLoggerincludes/ContentImport/ImportLogger.phpTracks success/error messages (see 5.4)
Relationsincludes/ContentImport/Relations.phpManages bidirectional translation links (see 5.4)

Sources: includes/ContentImport/ContentImporter.php1-410 includes/ContentImport/ImportCoordinates.php1-115 includes/ContentImport/Importers/Map.php1-69


Request Lifecycle

Request Initiation Flow:

The import process begins when an editor submits a form containing the msls_import POST parameter. This parameter carries the source blog ID and post ID in the format blog_id|post_id.

Sources: includes/ContentImport/ContentImporter.php93-143 includes/ContentImport/ContentImporter.php172-185


Request Parsing and Validation

Pre-flight Checks

Before processing begins, the ContentImporter performs security and validity checks:

The parse_sources() method extracts and validates the source blog and post IDs:

  • Retrieves msls_import from POST data via MslsRequest::get_var()
  • Splits on pipe delimiter: "1|123" becomes [1, 123]
  • Filters non-numeric values
  • Validates exactly two integers are present
  • Returns [source_blog_id, source_post_id] or false

Sources: includes/ContentImport/ContentImporter.php150-165 includes/ContentImport/ContentImporter.php172-185


ImportCoordinates Data Object

The ImportCoordinates class is a simple data transfer object that carries all context needed for the import operation:

Public Properties

PropertyTypeDescription
source_blog_idintID of the blog containing the source post
source_post_idintID of the post being imported from
dest_blog_idintID of the destination blog
dest_post_idintID of the destination post (may be newly created)
source_postWP_PostThe source post object
source_langstringLanguage code of source blog (e.g., "en_US")
dest_langstringLanguage code of destination blog (e.g., "de_DE")
importersarrayMap of importer type to strategy slug

Importer Configuration

The importers array specifies which strategy to use for each content type:

The parse_importers_from_request() method populates this array from the msls_importers POST/GET parameter, which is sent as a multidimensional array from the meta box form.

Validation:

The validate() method ensures:

  • Both source and destination posts exist (get_blog_post() checks)
  • source_post is a valid WP_Post instance
  • Language mappings are correct (MslsBlogCollection::get_blog_language())

Sources: includes/ContentImport/ImportCoordinates.php8-114


ContentImporter Orchestration

Component Architecture

Destination Post Handling

The get_the_blog_post_ID() method determines or creates the destination post:

  1. Switches to destination blog context
  2. Attempts to get current post ID via get_the_ID()
  3. Falls back to post parameter from request
  4. Creates draft post if needed:
    • Post type from request (defaults to 'post')
    • Title: "MSLS Content Import Draft - 2024-01-15 10:30:00"
    • Status: 'auto-draft' initially
  5. Temporarily disables import handling during wp_insert_post() to prevent recursion

When a post is created, has_created_post is set, triggering a redirect to the edit screen after import completion.

Sources: includes/ContentImport/ContentImporter.php192-214 includes/ContentImport/ContentImporter.php222-242

Import Content Method

The import_content() method is the core orchestration function:

Pre-import Phase:

  1. Validates coordinates via ImportCoordinates::validate()
  2. Fires 'msls_content_import_before_import' action hook
  3. Applies 'msls_content_import_data_before_import' filter to post fields

Importer Creation:

  1. Applies 'msls_content_import_importers' filter (returns null by default)
  2. If filter returns null, creates importers via Map::instance()->make()
  3. If filter returns array, uses custom importer set

Logger and Relations Setup:

  1. Creates ImportLogger if not already set
  2. Creates Relations object if not already set
  3. Schedules initial relation creation for source post

Import Loop:

  1. Iterates through each importer in the map
  2. Calls importer->import($post_fields) for each
  3. Merges importer's logger into main logger
  4. Merges importer's relations into main relations object
  5. Receives modified $post_fields array

Post-import Phase:

  1. Creates all relations via relations->create()
  2. Saves log via logger->save() (stores in transient)
  3. Fires 'msls_content_import_after_import' action hook
  4. Applies 'msls_content_import_data_after_import' filter
  5. Returns modified post fields array

Sources: includes/ContentImport/ContentImporter.php270-358


Importer Factory System

The Map class provides a factory pattern for creating importers based on import coordinates.

Factory Architecture

Default Factory Map

The factories() method returns a map of importer type slugs to factory instances:

SlugFactory ClassHandles
'post-fields'PostFieldsImportersTitle, content, excerpt, status, etc.
'post-meta'PostMetaImportersCustom field values
'terms'TermsImportersCategory and tag assignments
'post-thumbnail'PostThumbnailImportersFeatured image
'attachments'AttachmentsImportersMedia library items

Each factory examines the ImportCoordinates::$importers array to determine which strategy to instantiate (e.g., "duplicating", "linking", "shallow-duplicating").

Filter Hooks

Two filters allow customization:

msls_content_import_importers_factories_map - Modifies factories before importers are built:

  • Applied in factories() method
  • Can add/remove/replace factory instances
  • Executes before any importer is instantiated

msls_content_import_importers_map - Modifies importers after they're built:

  • Applied in make() method
  • Can add/remove/replace importer instances
  • Executes after factories have created importers

Sources: includes/ContentImport/Importers/Map.php8-69


Attachment URL Rewriting

The AttachmentPathFinder class handles a special case where attachments are linked rather than duplicated. When an attachment on the destination blog references media on the source blog, WordPress needs to serve the correct URLs.

Filter Integration

The class hooks into two WordPress filters:

  • 'wp_calculate_image_srcset' - Rewrites responsive image srcset attributes
  • 'wp_get_attachment_url' - Rewrites the base attachment URL

When an attachment has the _msls_linked post meta (set by the Attachments importer), the finder:

  1. Retrieves the source blog and post IDs from meta
  2. Fetches the source attachment post via get_blog_post()
  3. Returns the source post's GUID (actual file URL)
  4. For srcsets, replaces size variants with source blog paths

This allows the destination blog to display images that physically exist only on the source blog.

Sources: includes/ContentImport/AttachmentPathFinder.php1-107


Post-Import Actions

Draft Post Update

If a new draft was created, update_inserted_blog_post_data():

  • Sets the post ID in the data array
  • Changes status from 'auto-draft' to 'draft'
  • Calls insert_blog_post() again to update the draft with imported content

Redirection

When has_created_post is set, redirect_to_blog_post():

  • Switches to the destination blog
  • Gets the edit post link via get_edit_post_link()
  • Performs wp_safe_redirect() to the edit screen
  • Terminates execution with die()

This ensures editors immediately see the newly created post with all imported content.

Empty Post Filter

The filter_empty() method hooks into WordPress's empty post check:

  • Normally WordPress prevents saving posts with no title/content
  • During imports, the initial draft may be empty
  • This filter returns false to allow the empty draft creation
  • Only applies when msls_import POST parameter is present

Sources: includes/ContentImport/ContentImporter.php366-386 includes/ContentImport/ContentImporter.php398-409


Extension Points

The Content Import system provides multiple extension points for developers:

Action Hooks

HookParametersFires
'msls_content_import_before_import'ImportCoordinatesBefore any importers run
'msls_content_import_after_import'ImportCoordinates, ImportLogger, RelationsAfter all importers complete

Filter Hooks

HookParametersPurpose
'msls_content_import_data_before_import'post_fields, ImportCoordinatesModify post data before import
'msls_content_import_importers'null|array, ImportCoordinatesOverride entire importer set
'msls_content_import_importers_factories_map'factories_arrayModify factory instances
'msls_content_import_importers_map'importers_array, ImportCoordinatesModify built importers
'msls_content_import_data_after_import'post_fields, ImportCoordinates, ImportLogger, RelationsModify post data after import

These hooks allow plugins to inject custom importers, modify data flows, or perform additional operations during the import lifecycle.

Sources: includes/ContentImport/ContentImporter.php276-357 includes/ContentImport/Importers/Map.php56-65