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.
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:
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:
| Component | File | Purpose |
|---|---|---|
ContentImporter | includes/ContentImport/ContentImporter.php | Main orchestrator that handles import requests and coordinates all importers |
ImportCoordinates | includes/ContentImport/ImportCoordinates.php | Data object carrying source/destination context |
Map | includes/ContentImport/Importers/Map.php | Factory system for creating importer instances |
ImportLogger | includes/ContentImport/ImportLogger.php | Tracks success/error messages (see 5.4) |
Relations | includes/ContentImport/Relations.php | Manages bidirectional translation links (see 5.4) |
Sources: includes/ContentImport/ContentImporter.php1-410 includes/ContentImport/ImportCoordinates.php1-115 includes/ContentImport/Importers/Map.php1-69
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
Before processing begins, the ContentImporter performs security and validity checks:
The parse_sources() method extracts and validates the source blog and post IDs:
msls_import from POST data via MslsRequest::get_var()"1|123" becomes [1, 123][source_blog_id, source_post_id] or falseSources: includes/ContentImport/ContentImporter.php150-165 includes/ContentImport/ContentImporter.php172-185
The ImportCoordinates class is a simple data transfer object that carries all context needed for the import operation:
| Property | Type | Description |
|---|---|---|
source_blog_id | int | ID of the blog containing the source post |
source_post_id | int | ID of the post being imported from |
dest_blog_id | int | ID of the destination blog |
dest_post_id | int | ID of the destination post (may be newly created) |
source_post | WP_Post | The source post object |
source_lang | string | Language code of source blog (e.g., "en_US") |
dest_lang | string | Language code of destination blog (e.g., "de_DE") |
importers | array | Map of importer type to strategy slug |
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:
get_blog_post() checks)source_post is a valid WP_Post instanceMslsBlogCollection::get_blog_language())Sources: includes/ContentImport/ImportCoordinates.php8-114
The get_the_blog_post_ID() method determines or creates the destination post:
get_the_ID()post parameter from request'post')"MSLS Content Import Draft - 2024-01-15 10:30:00"'auto-draft' initiallywp_insert_post() to prevent recursionWhen 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
The import_content() method is the core orchestration function:
Pre-import Phase:
ImportCoordinates::validate()'msls_content_import_before_import' action hook'msls_content_import_data_before_import' filter to post fieldsImporter Creation:
'msls_content_import_importers' filter (returns null by default)null, creates importers via Map::instance()->make()Logger and Relations Setup:
ImportLogger if not already setRelations object if not already setImport Loop:
importer->import($post_fields) for each$post_fields arrayPost-import Phase:
relations->create()logger->save() (stores in transient)'msls_content_import_after_import' action hook'msls_content_import_data_after_import' filterSources: includes/ContentImport/ContentImporter.php270-358
The Map class provides a factory pattern for creating importers based on import coordinates.
The factories() method returns a map of importer type slugs to factory instances:
| Slug | Factory Class | Handles |
|---|---|---|
'post-fields' | PostFieldsImporters | Title, content, excerpt, status, etc. |
'post-meta' | PostMetaImporters | Custom field values |
'terms' | TermsImporters | Category and tag assignments |
'post-thumbnail' | PostThumbnailImporters | Featured image |
'attachments' | AttachmentsImporters | Media library items |
Each factory examines the ImportCoordinates::$importers array to determine which strategy to instantiate (e.g., "duplicating", "linking", "shallow-duplicating").
Two filters allow customization:
msls_content_import_importers_factories_map - Modifies factories before importers are built:
factories() methodmsls_content_import_importers_map - Modifies importers after they're built:
make() methodSources: includes/ContentImport/Importers/Map.php8-69
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.
The class hooks into two WordPress filters:
'wp_calculate_image_srcset' - Rewrites responsive image srcset attributes'wp_get_attachment_url' - Rewrites the base attachment URLWhen an attachment has the _msls_linked post meta (set by the Attachments importer), the finder:
get_blog_post()This allows the destination blog to display images that physically exist only on the source blog.
Sources: includes/ContentImport/AttachmentPathFinder.php1-107
If a new draft was created, update_inserted_blog_post_data():
'auto-draft' to 'draft'insert_blog_post() again to update the draft with imported contentWhen has_created_post is set, redirect_to_blog_post():
get_edit_post_link()wp_safe_redirect() to the edit screendie()This ensures editors immediately see the newly created post with all imported content.
The filter_empty() method hooks into WordPress's empty post check:
false to allow the empty draft creationmsls_import POST parameter is presentSources: includes/ContentImport/ContentImporter.php366-386 includes/ContentImport/ContentImporter.php398-409
The Content Import system provides multiple extension points for developers:
| Hook | Parameters | Fires |
|---|---|---|
'msls_content_import_before_import' | ImportCoordinates | Before any importers run |
'msls_content_import_after_import' | ImportCoordinates, ImportLogger, Relations | After all importers complete |
| Hook | Parameters | Purpose |
|---|---|---|
'msls_content_import_data_before_import' | post_fields, ImportCoordinates | Modify post data before import |
'msls_content_import_importers' | null|array, ImportCoordinates | Override entire importer set |
'msls_content_import_importers_factories_map' | factories_array | Modify factory instances |
'msls_content_import_importers_map' | importers_array, ImportCoordinates | Modify built importers |
'msls_content_import_data_after_import' | post_fields, ImportCoordinates, ImportLogger, Relations | Modify 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
Refresh this wiki