# Skill: Migration Tools (WPML to International Press Zone)

## Identity
- **Skill ID**: `migration-tools`
- **Domain**: WPML Migration & Data Import
- **Technologies**: WPML Data Structures, WordPress Database, Translation Management
- **Source Agent**: `api-integration-expert.md`, `frontend-php-expert.md`

## When to Load This Skill
- Task involves migrating data FROM WPML
- Importing WPML translation jobs into International Press Zone
- Converting WPML language codes and relationships
- Building migration UI or CLI tools
- Files matching: `includes/**/Migration/*.php`, `includes/**/class-*-migration*.php`

## Core Patterns

### WPML Detection for Migration
```php
function presszone_international_can_migrate_from_wpml() {
    // Check if WPML data exists (even if plugin is deactivated)
    global $wpdb;

    $wpml_languages_table = $wpdb->prefix . 'icl_languages';
    $table_exists = $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = %s AND table_name = %s",
        DB_NAME,
        $wpml_languages_table
    ));

    return (bool) $table_exists;
}
```

### WPML Translation Job Import
```php
function presszone_international_import_wpml_jobs() {
    global $wpdb;

    // Verify WPML tables exist
    if (!presszone_international_can_migrate_from_wpml()) {
        return new WP_Error('no_wpml_data', __('No WPML data found to migrate.', 'international-press-zone'));
    }

    $wpml_translations = $wpdb->prefix . 'icl_translations';
    $ipz_translations = $wpdb->prefix . 'presszone_international_translations';

    // Read WPML translation relationships
    $wpml_data = $wpdb->get_results($wpdb->prepare(
        "SELECT trid, element_id, language_code, source_language_code, element_type
         FROM {$wpml_translations}
         WHERE element_type LIKE %s",
        'post_%'
    ));

    if (empty($wpml_data)) {
        return new WP_Error('no_translations', __('No WPML translations found.', 'international-press-zone'));
    }

    $wpdb->query('START TRANSACTION');

    try {
        $imported = 0;

        foreach ($wpml_data as $row) {
            $element_id = absint($row->element_id);
            $lang_code = sanitize_key($row->language_code);
            $source_lang = sanitize_key($row->source_language_code);
            $trid = absint($row->trid);

            // Skip if already imported
            $exists = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(1) FROM {$ipz_translations} WHERE post_id = %d AND language_code = %s",
                $element_id,
                $lang_code
            ));

            if ($exists) {
                continue;
            }

            $result = $wpdb->insert(
                $ipz_translations,
                [
                    'post_id' => $element_id,
                    'language_code' => $lang_code,
                    'source_language' => $source_lang ?: null,
                    'translation_group' => $trid,
                    'status' => 'imported',
                    'created_at' => current_time('mysql'),
                ],
                ['%d', '%s', '%s', '%d', '%s', '%s']
            );

            if ($result !== false) {
                $imported++;
            }
        }

        $wpdb->query('COMMIT');

        return [
            'imported' => $imported,
            'total' => count($wpml_data),
        ];

    } catch (Exception $e) {
        $wpdb->query('ROLLBACK');
        error_log('WPML migration error: ' . $e->getMessage());
        return new WP_Error('migration_failed', __('Migration failed.', 'international-press-zone'));
    }
}
```

### WPML Language Configuration Import
```php
function presszone_international_import_wpml_languages() {
    global $wpdb;

    $wpml_languages = $wpdb->prefix . 'icl_languages';
    $wpml_active = $wpdb->prefix . 'icl_languages_translations';
    $ipz_languages = $wpdb->prefix . 'presszone_international_languages';

    // Get WPML active languages
    $languages = $wpdb->get_results(
        "SELECT l.code, lt.name, l.default_locale, l.active
         FROM {$wpml_languages} l
         LEFT JOIN {$wpml_active} lt ON l.code = lt.language_code AND lt.display_language_code = 'en'
         WHERE l.active = 1"
    );

    if (empty($languages)) {
        return new WP_Error('no_languages', __('No WPML languages found.', 'international-press-zone'));
    }

    $imported = 0;

    foreach ($languages as $lang) {
        $code = sanitize_key($lang->code);
        $name = sanitize_text_field($lang->name);

        // Check if already exists
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(1) FROM {$ipz_languages} WHERE code = %s",
            $code
        ));

        if ($exists) {
            continue;
        }

        $result = $wpdb->insert(
            $ipz_languages,
            [
                'code' => $code,
                'name' => $name,
                'native_name' => $name,
                'is_active' => 1,
                'created_at' => current_time('mysql'),
            ],
            ['%s', '%s', '%s', '%d', '%s']
        );

        if ($result !== false) {
            $imported++;
        }
    }

    return [
        'imported' => $imported,
        'total' => count($languages),
    ];
}
```

### Language Code Validation
```php
function presszone_international_validate_language_code($code) {
    // Sanitize first
    $code = sanitize_key($code);

    // Whitelist of supported languages
    $allowed = presszone_international_get_supported_languages();

    if (!array_key_exists($code, $allowed)) {
        return new WP_Error('invalid_language', __('Unsupported language code', 'international-press-zone'));
    }

    return $code;
}

function presszone_international_get_supported_languages() {
    return [
        'en' => 'English',
        'es' => 'Espanol',
        'fr' => 'Francais',
        'de' => 'Deutsch',
        'it' => 'Italiano',
        'pt' => 'Portugues',
        'ru' => 'Russian',
        'zh' => 'Chinese',
        'ja' => 'Japanese',
        'ko' => 'Korean',
        'ar' => 'Arabic',
        'nl' => 'Nederlands',
        'pl' => 'Polski',
        'tr' => 'Turkish',
        'vi' => 'Vietnamese'
    ];
}
```

### Migration Status Tracking
```php
function presszone_international_get_migration_status() {
    $status = get_option('presszone_international_migration_status', []);

    return wp_parse_args($status, [
        'started_at' => null,
        'completed_at' => null,
        'languages_imported' => 0,
        'translations_imported' => 0,
        'errors' => [],
        'state' => 'pending', // pending, in_progress, completed, failed
    ]);
}

function presszone_international_update_migration_status($updates) {
    $current = presszone_international_get_migration_status();
    $updated = array_merge($current, $updates);
    update_option('presszone_international_migration_status', $updated);
}
```

### WPML String Translation Import
```php
function presszone_international_import_wpml_strings() {
    global $wpdb;

    if (!function_exists('icl_register_string')) {
        // WPML not active, try direct DB access
        $wpml_strings = $wpdb->prefix . 'icl_strings';
        $wpml_string_translations = $wpdb->prefix . 'icl_string_translations';

        $table_exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = %s AND table_name = %s",
            DB_NAME,
            $wpml_strings
        ));

        if (!$table_exists) {
            return new WP_Error('no_string_data', __('No WPML string translation data found.', 'international-press-zone'));
        }
    }

    $strings = $wpdb->get_results(
        "SELECT s.id, s.context, s.name, s.value, st.language, st.value AS translated_value, st.status
         FROM {$wpml_strings} s
         LEFT JOIN {$wpml_string_translations} st ON s.id = st.string_id
         WHERE st.status = 10" // 10 = completed in WPML
    );

    $ipz_strings = $wpdb->prefix . 'presszone_international_strings';
    $imported = 0;

    foreach ($strings as $string) {
        $result = $wpdb->insert(
            $ipz_strings,
            [
                'context' => sanitize_text_field($string->context),
                'name' => sanitize_key($string->name),
                'original_value' => sanitize_textarea_field($string->value),
                'translated_value' => sanitize_textarea_field($string->translated_value),
                'language_code' => sanitize_key($string->language),
                'status' => 'imported',
                'created_at' => current_time('mysql'),
            ],
            ['%s', '%s', '%s', '%s', '%s', '%s', '%s']
        );

        if ($result !== false) {
            $imported++;
        }
    }

    return ['imported' => $imported, 'total' => count($strings)];
}
```

### WPML Language Switcher Migration
```php
function presszone_international_migrate_language_switcher() {
    // Read WPML switcher widget settings
    $wpml_widget = get_option('widget_icl_lang_sel_widget', []);

    if (empty($wpml_widget)) {
        return new WP_Error('no_switcher', __('No WPML language switcher found.', 'international-press-zone'));
    }

    // Map WPML switcher settings to International Press Zone format
    foreach ($wpml_widget as $key => $instance) {
        if (!is_array($instance)) continue;

        $ipz_settings = [
            'display_mode' => isset($instance['type']) && $instance['type'] === 'dropdown' ? 'dropdown' : 'list',
            'show_flags' => !empty($instance['flags']),
            'show_native_name' => !empty($instance['native_names']),
        ];

        update_option('presszone_international_switcher_settings', $ipz_settings);
        break; // Take first instance
    }

    return true;
}
```

## Anti-Patterns (Forbidden)

| Mistake | Fix |
|---------|-----|
| Not checking WPML table existence | Always verify tables exist before querying |
| Missing language validation | Whitelist validate all language codes |
| Direct WPML function calls without check | Check `function_exists()` before calling WPML functions |
| Hardcoded language codes | Use validated language lists |
| No transaction for bulk imports | Use START TRANSACTION / COMMIT / ROLLBACK |
| Overwriting existing data | Always check for duplicates before inserting |
| No migration status tracking | Track progress for resumable migrations |

## WordPress.org Compliance

### Migration Safety
```php
// Always check if migration is needed
if (!presszone_international_can_migrate_from_wpml()) {
    // Show "no data to migrate" message
}

// Always offer dry-run first
function presszone_international_migration_dry_run() {
    // Count what would be imported without actually importing
}
```

### Language Code Format
- **WPML Format**: 2-letter ISO 639-1 codes (en, es, fr)
- **WordPress Format**: Locale codes (en_US, es_ES, fr_FR)
- **Always sanitize**: Use `sanitize_key()` for language codes

## Integration with Other Skills
- **Often combined with**: `wordpress-php-integration`, `database-operations`
- **For UI**: Load `admin-panel-fullstack` for migration wizard
- **For settings**: Load `settings-management` for migrated settings

## Quick Reference

### WPML Tables Reference

| Table | Purpose |
|-------|---------|
| `icl_translations` | Post/term translation relationships |
| `icl_languages` | Language definitions |
| `icl_languages_translations` | Language name translations |
| `icl_strings` | String translation originals |
| `icl_string_translations` | String translation values |

### WPML Language Detection (for migration)
```php
function presszone_international_get_wpml_current_language() {
    if (defined('ICL_LANGUAGE_CODE')) {
        return sanitize_key(ICL_LANGUAGE_CODE);
    }

    if (function_exists('wpml_get_current_language')) {
        return sanitize_key(wpml_get_current_language());
    }

    // Fallback to site language
    return substr(get_locale(), 0, 2);
}
```

## Validation Checklist
- [ ] Checked WPML table existence before migration
- [ ] All WPML function calls wrapped in `function_exists()`
- [ ] Language codes validated against whitelist
- [ ] All output escaped (`esc_html`, `esc_attr`, `esc_url`)
- [ ] Job/string data sanitized before import
- [ ] Error handling for migration failures
- [ ] Transaction used for bulk data imports
- [ ] Duplicate check before each insert
- [ ] Migration status tracked and resumable
- [ ] Dry-run available before actual migration
