Informacje techniczne

Documentation E-Toolkit Page Duplicator

Instructions, technical specifications and change history — always in line with the current version of the product.

Current version v1.2.5

Technical compliance

WordPress

From version 6.4

Tested up to 7.1

WooCommerce

Optional integration

PHP

From version 7.4

Knowledge base

Documentation

User Manual Set-up, operation and troubleshooting.

E-Toolkit Page Duplicator — User Guide

What the plugin does

E-Toolkit Page Duplicator creates independent copies of pages, posts, and — when enabled — WooCommerce products. You can choose which elements are copied, save settings as presets, copy child pages, and schedule automatic page duplication.

Quick start

  1. Open ET Page Duplicator → Settings in WordPress.
  2. On General, select the status and contents of a new copy.
  3. Click Save settings.
  4. Open the Pages or Posts list.
  5. Click Duplicate or Copy as draft for an item.
  6. Open the copy and check its content, URL, SEO settings, and links.

Copy as draft always creates a draft. Duplicate uses the default status selected in the plugin. If the user cannot publish the content type, the copy safely remains a draft.

Use Customize copy when one operation needs a different preset, status, title or URL, or when you want to create several copies at once. These choices do not change the global plugin settings.

General settings

Copy status and title

Copy status determines whether the standard duplication action creates a draft, published item, or private item.

Text before title and Text after title add text to the copied title and work only when title copying is enabled. If Title is disabled, the new item has no title.

Data that can be copied

  • title, content, and excerpt,
  • featured image,
  • source author, publication date, password, and comments,
  • attached media as independent attachment objects and files,
  • categories, tags, and other taxonomies,
  • additional plugin data,
  • ACF fields,
  • Yoast SEO and Rank Math settings,
  • menu items linking to the source content,
  • parent, menu order, and page template.

Disabling Additional plugin data does not prevent separately enabled ACF fields or SEO settings from being copied.

Copy URL

Available options are:

  • Automatic (recommended) — adds the configured prefix,
  • Based on original — WordPress creates a unique variant of the original URL,
  • Add text before URL,
  • Add text after URL.

WordPress always keeps the final URL unique.

Title and URL prefixes or suffixes support the following tokens:

  • [Counter] — the copy number in a multi-copy operation,
  • [CurrentDate] — the creation date,
  • [CurrentTime] — the creation time,
  • [SourceID] — the source content ID.

Advanced settings

Copying a page tree

Enable Allow tree duplication to add an action that copies a hierarchical page with its descendants. Maximum depth limits the copied levels; 0 means no limit. Only child pages the current user can edit are copied.

Permissions

Under Permissions, select roles that can see duplication actions. Administrators are always enabled. The role does not bypass WordPress capabilities: the user must also be able to edit the specific item.

WooCommerce

When product duplication is enabled, variations, galleries, and attributes can be controlled separately. Resetting inventory and SKU helps prevent a copy from being published with the source product's stock and identifier.

Test one product before using this in production, especially when the store has additional inventory or product plugins.

Presets

A preset stores the current duplication settings.

  1. Configure General or Advanced options.
  2. Enter a preset name in the side panel.
  3. Click Save current settings.

You can later select and apply a preset. Deletion requires confirmation. Apply and delete buttons remain disabled until at least one preset exists.

Use Export presets to download all presets as a versioned JSON file. Use Import presets on another site to add them there. Existing presets are not overwritten; imported key collisions receive a new suffix.

Working copies and republishing

For published content, use Create working copy in the row actions. The public original remains unchanged while you edit the draft. The working-copy panel links back to the original and provides Replace original.

Republishing updates the original title, content, excerpt, password, hierarchy, taxonomies, and the metadata managed by the working copy. The original ID, URL, publication status, date, and author remain unchanged. The working copy is then moved to Trash.

The plugin records a source fingerprint when the working copy is created. If the original is edited in the meantime, republishing stops instead of overwriting those concurrent changes. Compare both versions and create a fresh working copy if necessary.

This workflow currently republishes immediately; it does not schedule a future replacement of the original.

Schedules

A schedule copies selected content daily, weekly, or every 30 days. Pages, posts, and visible custom post types are available; WooCommerce products appear when product duplication is enabled. Select a content type, search for the source by title in the content language currently selected in the admin panel, then choose the frequency, copy status, and either the current default duplication settings or a saved preset.

Use the filters above the list to show all, active, disabled, or failed rules. A schedule's action menu lets you edit it, run it immediately, disable or enable it, or delete it. A failed rule shows its last error, the number of consecutive failures, the next retry time, and a Retry now action.

Retries use progressively longer delays: 1 hour, 6 hours, 1 day, and 3 days. After five consecutive failures, the rule is automatically disabled to prevent an endless failure loop. Enable it manually after correcting the problem; this starts a new series of attempts. If a schedule's preset is later deleted, the job safely falls back to the current default settings.

Scheduling uses WP-Cron, which runs when the site receives traffic, so the displayed time is approximate.

Content actions

  • Duplicate — uses the status from settings.
  • Copy as draft — always creates a draft.
  • Customize copy — opens the shared modal where you can select a preset, status, create 1–20 copies, and optionally override the title and URL for this operation.
  • Create working copy — creates an editable draft connected to a published original.
  • Duplicate with descendants — copies a page and permitted child pages.
  • bulk Duplicate — copies selected items.
  • duplication buttons are also available in Quick Edit and the page editor sidebar.

Logs

Logs shows operations in pages of 20 entries: date, user or automatic task, source content, created copy, and action type. Titles link to edit screens while the content exists. An administrator can remove one entry with its trash action or clear the entire history after confirmation. Removing a log never deletes the source or duplicated content.

Troubleshooting

  • No duplication action — check the role, permission to edit the item, and license status.
  • Copy remains a draft despite “Published” — the account cannot publish this content type.
  • A field or SEO setting was not copied — check the corresponding option and active preset.
  • A scheduled task ran late — WP-Cron depends on traffic.
  • A schedule was automatically disabled — correct the displayed source or duplication error, then enable the rule to start a new retry series.
  • Product SKU is empty or stock is zero — check product reset options.
  • Preset buttons are unavailable — save at least one preset.

Best practices

  • create drafts by default,
  • check links, forms, SEO, and plugin data before publishing,
  • test large page trees with a limited depth,
  • check prices, variations, inventory, and external integrations after copying a product,
  • disable schedules that may be needed again and remove only obsolete schedules and presets.
Developer documentation Functions, hooks, filters and integration tips.

E-Toolkit Page Duplicator — Developer Guide

Public integration points

ET Page Duplicator exposes the WP-CLI commands, filters, and actions described below. Treat their names and argument order as a public compatibility contract.

WP-CLI

Commands operate on the current WordPress site. On Multisite, select the site with the standard --url=<site-url> global argument.

# List preset keys available for direct duplication.
wp et-page-duplicator preset-list
wp et-page-duplicator preset-list --format=json

# Duplicate one or more IDs with the current settings.
wp et-page-duplicator duplicate 12
wp et-page-duplicator duplicate 12,15,18 --format=json

# Apply a preset and override the resulting status.
wp et-page-duplicator duplicate 12,15 \
  --preset=campaign-copy-1234567890 \
  --status=draft

# List schedule IDs and their current state.
wp et-page-duplicator schedule-list
wp et-page-duplicator schedule-list --format=json

# Run one enabled schedule immediately, even when next_run is in the future.
wp et-page-duplicator schedule-run 'etpd_68b123.45678900'

# Run all enabled schedules whose next_run time is due.
wp et-page-duplicator schedules-run-due

duplicate accepts draft, publish, and private as status overrides. It returns one row per source with source_id, copy_id, the final status, and a stable result code. Successful rows use success; failures use source_not_found, unsupported_post_type, products_disabled, forbidden, or copy_failed. If any row fails, the command exits with a non-zero status after printing all results.

The command uses the same duplication engine, settings resolution, logs, language preservation, WooCommerce behavior, and public duplication hooks as the admin actions. A supplied preset must exist; unlike schedules, direct CLI duplication does not silently fall back when its explicitly requested preset is missing. WP-CLI without --user is treated as a trusted server operation. When the standard global --user=<login|id> argument is supplied, the selected account must have both et_duplicate_pages and permission to edit each source object; publication capability may still reduce the final status to a draft.

The execution commands use the same license check, preset resolution, status override, logging, retry date, and hooks as WP-Cron. A failed scheduled copy makes the command return a non-zero exit code. schedule-run does not enable a disabled schedule.

Duplication filters and actions

Naming tokens and provenance

Title and slug affixes accept [Counter], [CurrentDate], [CurrentTime], and [SourceID]. Multi-copy operations pass a one-based counter; ordinary actions resolve it as 1. URL tokens are normalized with URL-safe date and time values.

Every successful copy stores its direct source in _etpd_source_post_id, the source site in _etpd_source_blog_id, the context in _etpd_duplication_context, and the UTC creation time in _etpd_duplicated_at. These keys are internal provenance metadata and are not recursively copied from an older copy.

When copy_attachments is enabled, etpd_duplicate_attachments($attachments, $source_id, $copy_id) can filter the attachment objects considered for copying. Files must resolve inside the current uploads directory. Each successful attachment gets an independent file and attachment post, and common content URL/CSS-ID references are rewritten.

Use etpd_duplicate_settings to adjust the final settings for all duplication entry points, including admin actions, child copies, product variations, and schedules:

add_filter(
    'etpd_duplicate_settings',
    function (array $settings, WP_Post $source, string $context, array $override): array {
        if ($context === 'scheduled_duplicate') {
            $settings['title_prefix'] = '[Scheduled] ';
        }

        return $settings;
    },
    10,
    4
);

The duplication lifecycle actions are:

// Before wp_insert_post().
do_action('etpd_before_duplicate_post', $source_id, $settings, $context);

// After the copy, related data, and log entry have been created successfully.
do_action('etpd_after_duplicate_post', $copy_id, $source_id, $settings, $context);

Common context values include duplicate, quick_duplicate, custom_duplicate, bulk_duplicate, duplicate_tree, duplicate_child, duplicate_variation, and scheduled_duplicate. Custom callers may provide their own value. The custom_duplicate context is emitted once for each copy created from the per-operation modal.

For granular integrations, the duplicator also exposes:

add_filter('etpd_duplicate_post_types', function (array $types, WP_Post $source, string $context): array {
    return $context === 'my_import' ? array_unique(array_merge($types, ['portfolio'])) : $types;
}, 10, 3);

add_filter('etpd_duplicate_post_data', function (array $data, WP_Post $source, array $settings, string $context): array {
    $data['post_excerpt'] = 'Created by ' . $context;
    return $data;
}, 10, 4);

add_filter('etpd_duplicate_taxonomies', function (array $taxonomies): array {
    return array_diff($taxonomies, ['internal_label']);
});

add_filter('etpd_duplicate_meta_enabled', function (bool $enabled, string $key): bool {
    return $key === '_integration_cache' ? false : $enabled;
}, 10, 2);

add_filter('etpd_duplicate_meta_value', function ($value, string $key) {
    return $key === '_external_state' ? 'pending' : $value;
}, 10, 2);

Technical WordPress meta such as edit locks and trash metadata is always excluded before the meta filters run. The final post_type from etpd_duplicate_post_data must also be present in etpd_duplicate_post_types.

The complete argument order is:

etpd_duplicate_post_types($post_types, $source_post, $context)
etpd_duplicate_post_data($post_data, $source_post, $settings, $context)
etpd_duplicate_taxonomies($taxonomies, $source_id, $copy_id, $settings, $context)
etpd_duplicate_meta_enabled($enabled, $key, $raw_values, $source_id, $copy_id, $settings, $context)
etpd_duplicate_meta_value($value, $key, $source_id, $copy_id, $settings, $context)

Schedule filters and actions

etpd_schedule_post_types controls which visible post types may be selected as a schedule source. Attachments, revisions, menu items, and products disabled in the plugin settings remain unavailable:

add_filter(
    'etpd_schedule_post_types',
    function (array $post_types, array $settings): array {
        unset($settings);
        $post_types[] = 'portfolio';
        return array_values(array_unique($post_types));
    },
    10,
    2
);

etpd_schedule_intervals adds or removes frequencies used by both the settings screen and scheduler. Values shorter than one minute are rejected:

add_filter('etpd_schedule_intervals', function (array $intervals): array {
    $intervals['every_twelve_hours'] = [
        'label' => __('Every 12 hours', 'my-plugin'),
        'seconds' => 12 * HOUR_IN_SECONDS,
    ];
    return $intervals;
});

The source selector uses a capability-protected AJAX endpoint and searches server-side in bounded pages of 20 without exposing a separate pagination control. Results inherit the content language selected in the ET Lingua or WPML admin context, so translations of the same object are not mixed. Use etpd_schedule_post_types to control its post-type allowlist; the page size is intentionally fixed.

etpd_schedule_should_run may veto a selected execution. It also runs for an immediate WP-CLI/API execution, where $force is true:

add_filter(
    'etpd_schedule_should_run',
    function (bool $should_run, array $schedule, string $schedule_id, bool $force, int $now): bool {
        if (get_option('my_maintenance_mode')) {
            return false;
        }

        return $should_run;
    },
    10,
    5
);

etpd_schedule_copy_settings runs after current/default or preset settings are resolved and after the schedule status is applied:

add_filter(
    'etpd_schedule_copy_settings',
    function (array $settings, array $schedule, string $schedule_id): array {
        $settings['title_suffix'] = ' ' . wp_date('Y-m-d');
        return $settings;
    },
    10,
    3
);

The schedule lifecycle actions are:

do_action('etpd_before_schedule_run', $schedule_id, $schedule, $copy_settings);
do_action('etpd_after_schedule_run', $schedule_id, $updated_schedule, $result);

$result contains status (success, failed, or skipped), copy_id, next_run, and where applicable reason. Failure reasons are stable keys (invalid_source or copy_failed); skipped reasons are filtered or locked. Skipped executions do not fire the before/after schedule actions.

Failed executions use an escalating retry policy: 1 hour, 6 hours, 1 day, and 3 days. The fifth consecutive failure disables the rule by default. Both contracts are filterable:

add_filter('etpd_schedule_retry_delays', function (array $delays, array $schedule, string $schedule_id): array {
    return [15 * MINUTE_IN_SECONDS, HOUR_IN_SECONDS, 6 * HOUR_IN_SECONDS];
}, 10, 3);

add_filter('etpd_schedule_failure_limit', function (int $limit, array $schedule, string $schedule_id): int {
    return 3; // Return 0 to disable automatic deactivation.
}, 10, 3);

Notification integrations should use the actions fired after the updated schedule has been persisted:

do_action('etpd_schedule_failed', $schedule_id, $failed_schedule, $result);
do_action('etpd_schedule_auto_disabled', $schedule_id, $disabled_schedule, $result);

etpd_schedule_failed runs for every failed attempt. etpd_schedule_auto_disabled runs once when the consecutive-failure limit is reached and is the preferred integration point for e-mail, Slack, or monitoring alerts. $result additionally contains failure_count, failure_limit, retry_delay, and auto_disabled.

Each rule uses an atomic option lock to prevent WP-Cron and a manual/CLI invocation from creating two copies concurrently. Locks expire after 15 minutes by default; long-running integrations can adjust the lifetime:

add_filter('etpd_schedule_lock_ttl', function (int $seconds, string $schedule_id): int {
    return 30 * MINUTE_IN_SECONDS;
}, 10, 2);

PHP integrations can run the same public scheduler methods used by WP-CLI:

$scheduler = ETPD_Plugin::instance()->scheduler;
$result = $scheduler->run_schedule_now($schedule_id); // array|WP_Error
$results = $scheduler->run_due_schedules();           // array|WP_Error

Working-copy lifecycle

The working-copy workflow preserves the original ID, slug, public status, publication date, and author. Before replacing content it compares a stored source fingerprint with the current original, preventing silent overwrites after a concurrent edit.

Integrations can observe the successful replacement lifecycle:

do_action('etpd_before_republish', $source_id, $working_copy_id);
do_action('etpd_after_republish', $source_id, $working_copy_id);

The working copy uses the working_copy duplication context. A completed replacement is logged as rewrite_republish and moves the working copy to Trash.

Preset transfer format

Preset exports are JSON documents with format: et-page-duplicator-presets, a numeric format version, UTC exported_at, and a presets object. Imports accept at most 100 presets and a 1 MB JSON upload. Unknown settings are discarded, values are normalized against current defaults, administrators remain enabled, and existing keys are never overwritten.

Duplication capability

A custom interface can check whether the user has access to duplication features through the et_duplicate_pages capability:

if (
    current_user_can('et_duplicate_pages')
    && current_user_can('edit_post', $post_id)
) {
    // The user may see an additional action for this content item.
}

The plugin capability does not replace standard WordPress permissions for editing or publishing a specific content item.

Schedule settings contract

Schedules are stored in the etpd_schedules option. A schedule may contain a sanitized preset_key referencing etpd_presets. An empty or missing key uses the current duplication settings, which keeps existing schedule records backward compatible. If the referenced preset no longer exists at execution time, the scheduler also falls back to the current settings. The schedule's own status continues to override the preset's default status. The boolean enabled field controls whether WP-Cron considers the rule; disabling a rule preserves its configuration and execution history. The scheduler also stores last_status, last_copy_id, last_error, consecutive_failures, and — after automatic deactivation — disabled_reason and auto_disabled_at. Manually enabling a rule starts a new failure series without removing the previous error details.

Duplication log storage

Duplication history is stored per site in the dedicated {prefix}et_page_dup_log table. The table is schema-versioned through the etpd_log_schema_version option and indexed by creation date and ID for deterministic pagination. Admin deletion actions remove only log rows; they never delete source or copied posts. Deactivation preserves the table and its history.

Links on the Plugins screen

Add an extra link with the standard WordPress filter:

add_filter(
    'plugin_action_links_et-page-duplicator/et-page-duplicator.php',
    function (array $links): array {
        $links[] = sprintf(
            '<a href="%s">%s</a>',
            esc_url(admin_url('admin.php?page=my-integration')),
            esc_html__('Integration', 'my-plugin')
        );

        return $links;
    },
    20
);

If the integration controls the installation path, build the hook name with plugin_basename() instead of assuming a fixed directory name.