summaryrefslogtreecommitdiff
blob: 4b5dd83d36edfebcc3e1187f0f861dee153543e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<?php

require_once dirname( __FILE__ ) . '/class.jetpack-sync-settings.php';

class Jetpack_Sync_Module_Import extends Jetpack_Sync_Module {

	/**
	 * Tracks which actions have already been synced for the import
	 * to prevent the same event from being triggered a second time
	 *
	 * @var array
	 */
	private $synced_actions = array();

	/**
	 * A mapping of action types to sync action name.
	 * Keys are the name of the import action.
	 * Values are the resulting sync action.
	 *
	 * Note: import_done and import_end both intentionally map to
	 * jetpack_sync_import_end, as they both track the same type of action,
	 * the successful completion of an import. Different import plugins use
	 * differently named actions, and this is an attempt to consolidate.
	 *
	 * @var array
	 */
	private static $import_sync_action_map = array(
		'import_start' => 'jetpack_sync_import_start',
		'import_done'  => 'jetpack_sync_import_end',
		'import_end'   => 'jetpack_sync_import_end',
	);

	public function name() {
		return 'import';
	}

	public function init_listeners( $callable ) {
		add_action( 'export_wp', $callable );
		add_action( 'jetpack_sync_import_start', $callable, 10, 2 );
		add_action( 'jetpack_sync_import_end', $callable, 10, 2 );

		// WordPress.
		add_action( 'import_start', array( $this, 'sync_import_action' ) );

		// Movable type, RSS, Livejournal.
		add_action( 'import_done', array( $this, 'sync_import_action' ) );

		// WordPress, Blogger, Livejournal, woo tax rate.
		add_action( 'import_end', array( $this, 'sync_import_action' ) );
	}

	public function set_defaults() {
		$this->synced_actions = array();
	}

	public function sync_import_action( $importer ) {
		$import_action = current_filter();
		// Map action to event name.
		$sync_action = self::$import_sync_action_map[ $import_action ];

		// Only sync each action once per import.
		if ( array_key_exists( $sync_action, $this->synced_actions ) && $this->synced_actions[ $sync_action ] ) {
			return;
		}

		// Mark this action as synced.
		$this->synced_actions[ $sync_action ] = true;

		// Prefer self-reported $importer value.
		if ( ! $importer ) {
			// Fall back to inferring by calling class name.
			$importer = self::get_calling_importer_class();
		}

		// Get $importer from known_importers.
		$known_importers = Jetpack_Sync_Settings::get_setting( 'known_importers' );
		if ( isset( $known_importers[ $importer ] ) ) {
			$importer = $known_importers[ $importer ];
		}

		$importer_name = $this->get_importer_name( $importer );

		switch ( $sync_action ) {
			case 'jetpack_sync_import_start':
				/**
				 * Used for syncing the start of an import
				 *
				 * @since 7.3.0
				 *
				 * @module sync
				 *
				 * @param string $importer      Either a string reported by the importer, the class name of the importer, or 'unknown'.
				 * @param string $importer_name The name reported by the importer, or 'Unknown Importer'.
				 */
				do_action( 'jetpack_sync_import_start', $importer, $importer_name );
				break;

			case 'jetpack_sync_import_end':
				/**
				 * Used for syncing the end of an import
				 *
				 * @since 7.3.0
				 *
				 * @module sync
				 *
				 * @param string $importer      Either a string reported by the importer, the class name of the importer, or 'unknown'.
				 * @param string $importer_name The name reported by the importer, or 'Unknown Importer'.
				 */
				do_action( 'jetpack_sync_import_end', $importer, $importer_name );
				break;
		}
	}

	private function get_importer_name( $importer ) {
		$importers = get_importers();
		return isset( $importers[ $importer ] ) ? $importers[ $importer ][0] : 'Unknown Importer';
	}

	/**
	 * Determine the class that extends `WP_Importer` which is responsible for
	 * the current action. Designed to be used within an action handler.
	 *
	 * @return string  The name of the calling class, or 'unknown'.
	 */
	private static function get_calling_importer_class() {
		// If WP_Importer doesn't exist, neither will any importer that extends it.
		if ( ! class_exists( 'WP_Importer', false ) ) {
			return 'unknown';
		}

		$action    = current_filter();
		$backtrace = debug_backtrace( false ); //phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.debug_backtrace_optionsFound,WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace

		$do_action_pos = -1;
		$backtrace_len = count( $backtrace );
		for ( $i = 0; $i < $backtrace_len; $i++ ) {
			// Find the location in the stack of the calling action.
			if ( 'do_action' === $backtrace[ $i ]['function'] && $action === $backtrace[ $i ]['args'][0] ) {
				$do_action_pos = $i;
				break;
			}
		}

		// If the action wasn't called, the calling class is unknown.
		if ( -1 === $do_action_pos ) {
			return 'unknown';
		}

		// Continue iterating the stack looking for a caller that extends WP_Importer.
		for ( $i = $do_action_pos + 1; $i < $backtrace_len; $i++ ) {
			// If there is no class on the trace, continue.
			if ( ! isset( $backtrace[ $i ]['class'] ) ) {
				continue;
			}

			$class_name = $backtrace[ $i ]['class'];

			// Check if the class extends WP_Importer.
			if ( class_exists( $class_name, false ) ) {
				$parents = class_parents( $class_name, false );
				if ( $parents && in_array( 'WP_Importer', $parents, true ) ) {
					return $class_name;
				}
			}
		}

		// If we've exhausted the stack without a match, the calling class is unknown.
		return 'unknown';
	}
}