Skip to content
Merged
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ this adapter builds from the raw files, so its reader needs no assembler of its
own. The admin screen's export, its ZIP and GitHub delivery never carry it: the
site's repository holds the store, not a raw copy of every record.

ACF Options Pages reach RawIR as `acf_options` (from `bridge/acf-options.json`):
each page's slug, title, storage `post_id` and fields — the same
secret-filtered, default-language values its own `acf-options-*` singleton
holds, keyed by field name like a post's `acf`, with each field's ACF type and
label.

## REST export API (for Migrate)

An administrator's application password is enough to start, advance and read an
Expand Down
6 changes: 5 additions & 1 deletion includes/class-contentrain-bridge-jobs.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@ public static function create( $input, $remote = false ) {
foreach ( $menus as $menu ) {
Coverage::tally( $job, array( 'menu_items' ), 'exported', count( $menu['items'] ) );
}
$acf_options = array();
foreach ( Source::options_pages() as $options_page ) {
Models::options_page( $job, $options_page );
$acf_options[] = Models::options_page( $job, $options_page );
}
if ( $acf_options ) {
Models::file( $job, 'bridge/acf-options.json', Policy::json( $acf_options ) );
}
self::warning( $job, array( 'source' => 'rendered-states', 'reason' => 'Source scan does not execute dynamic WordPress/plugin states. Rendered coverage requires the Migrate capture adapter.' ) );
self::save( $job );
Expand Down
4 changes: 4 additions & 0 deletions includes/class-contentrain-bridge-models.php
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@ private static function menu_url( $url ) {
* title field regardless of what those configured fields are named; ACF
* fields are prefixed the same way a post's own are, so a field legitimately
* named `page_title` cannot collide with it.
*
* Returns the page as RawIR carries it (`RawAcfOptionsPage`): the same
* secret-filtered values, keyed by field name, for the REST-path conversion.
*/
public static function options_page( &$job, $page ) {
$post_id = $page['post_id'] ?: 'options';
Expand Down Expand Up @@ -522,6 +525,7 @@ public static function options_page( &$job, $page ) {
} elseif ( defined( 'BEA_ACF_OPTIONS_FOR_POLYLANG_VERSION' ) ) {
Jobs::warning( $job, array( 'source' => 'acf-options/' . $slug, 'reason' => 'acf-options-translation-plugin-detected: ACF Options for Polylang can translate this Options Page; only its default-language values are exported' ) );
}
return array( 'slug' => $slug, 'title' => $page['page_title'] ?: $page['menu_slug'] ?: $slug, 'post_id' => (string) $post_id, 'fields' => (object) $raw );
}

/** Check short writes so disk exhaustion cannot produce a successful truncated table. */
Expand Down
5 changes: 5 additions & 0 deletions includes/class-contentrain-bridge-rawir.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public static function write( &$job ) {
$field( 'language_pairs' );
self::rows( $stream, $job, 'bridge/language-pairs.json', false );
$field( 'options', $file( 'bridge/options.json' ) ?? '{}' );
// RawAcfOptionsPage[]: each ACF Options Page's site-wide fields, default language only.
$acf_options = $file( 'bridge/acf-options.json' );
if ( null !== $acf_options ) {
$field( 'acf_options', $acf_options );
}
// RawIR.redirects is RawRedirect[]: what the site serves; redirects_excluded what it holds but does not
// serve as a plain redirect, each with its reason. Sources and counts stay in bridge/redirects.json.
$redirects = json_decode( $file( 'bridge/redirects.json' ) ?? '{}' );
Expand Down
2 changes: 1 addition & 1 deletion includes/class-contentrain-bridge-source.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public static function acf_fields_for_options_page( $post_id, $slug, $source_pre
$excluded[] = array( 'source' => $source_prefix . '/' . $name, 'reason' => 'sensitive-field' );
continue;
}
$raw[ $name ] = array( 'value' => self::acf_value( $loaded, $loaded['value'], $excluded, $source_prefix . '/' . $name ), 'field_key' => $loaded['key'] );
$raw[ $name ] = array( 'value' => self::acf_value( $loaded, $loaded['value'], $excluded, $source_prefix . '/' . $name ), 'field_key' => $loaded['key'], 'type' => $loaded['type'], 'label' => $loaded['label'] );
$schema[ $name ] = self::schema( $loaded );
}
}
Expand Down
10 changes: 10 additions & 0 deletions tests/integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,16 @@ function rejects( $callback, $message ) {
$locale_options_id = 'acf-options-bridge-locale-options';
$locale_entry = json_decode( Files::read( $dir, Models::content_path( $job, $locale_options_id, $job['default_locale'] ) ), true );
check( 'Default language value' === $locale_entry['acf_locale_note'], 'an Options Page with a detected per-language copy still exports only its default-language value' );

// RawIR carries every Options Page too (`acf_options`), for the REST-path conversion: the same values, keyed by
// field name like a post's `acf`, each page with its own fields only.
$raw_options = array_column( json_decode( Files::read( $dir, 'bridge/acf-options.json' ), true ), null, 'slug' );
check( array( 'bridge-footer', 'bridge-header', 'bridge-locale-options', 'bridge-options' ) === array_values( array_intersect( array( 'bridge-footer', 'bridge-header', 'bridge-locale-options', 'bridge-options' ), array_keys( $raw_options ) ) ), 'RawIR acf_options carries every Options Page' );
$raw_site_options = $raw_options['bridge-options'] ?? array();
check( 'Bridge Options' === ( $raw_site_options['title'] ?? null ) && 'options' === ( $raw_site_options['post_id'] ?? null ), 'an Options Page in RawIR keeps its title and storage post_id' );
check( 'Owns its content' === ( $raw_site_options['fields']['site_wide_tagline']['value'] ?? null ) && 'field_bridge_opt_tagline' === ( $raw_site_options['fields']['site_wide_tagline']['field_key'] ?? null ) && 'text' === ( $raw_site_options['fields']['site_wide_tagline']['type'] ?? null ), 'an Options Page field in RawIR has its value, field key and type' );
check( 2 === count( $raw_site_options['fields']['social_links']['value'] ?? array() ), 'a repeater on an Options Page keeps its rows in RawIR' );
check( array( 'header_text' ) === array_keys( $raw_options['bridge-header']['fields'] ?? array() ) && 'bridge_locale_options' === ( $raw_options['bridge-locale-options']['post_id'] ?? null ), 'each Options Page in RawIR carries only its own fields, and a custom post_id as set' );
}

if ( $has_polylang ) {
Expand Down
3 changes: 3 additions & 0 deletions tools/prepare-migrate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export function prepareMigrate(source, destination) {
if (seo) raw.seo = { ...seo, entries: json('bridge/seo-entries.json', {}) }
const routing = json('bridge/routing.json', null)
if (routing) raw.routing = routing
// RawAcfOptionsPage[]: each ACF Options Page's site-wide fields, default language only.
const acfOptions = json('bridge/acf-options.json', null)
if (acfOptions) raw.acf_options = acfOptions
// B-08: every interface-text candidate with its one outcome; not yet a RawIR field (proposed).
// B-07: services to reconnect; not yet a RawIR field (proposed).
const integrations = json('bridge/integrations.json', null)
Expand Down
Loading