summaryrefslogtreecommitdiff
blob: 839434dddbec0c2c1b25a2b4ff6d8314e6270e1b (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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
<?php
/**
 * Import sync module.
 *
 * @package automattic/jetpack-sync
 */

namespace Automattic\Jetpack\Sync\Modules;

use Automattic\Jetpack\Sync\Settings;

/**
 * Class to handle sync for imports.
 */
class Import extends 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',
	);

	/**
	 * Sync module name.
	 *
	 * @access public
	 *
	 * @return string
	 */
	public function name() {
		return 'import';
	}

	/**
	 * Initialize imports action listeners.
	 *
	 * @access public
	 *
	 * @param callable $callable Action handler callable.
	 */
	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' ) );
	}

	/**
	 * Set module defaults.
	 * Define an empty list of synced actions for us to fill later.
	 *
	 * @access public
	 */
	public function set_defaults() {
		$this->synced_actions = array();
	}

	/**
	 * Generic handler for import actions.
	 *
	 * @access public
	 *
	 * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
	 */
	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 = Settings::get_setting( 'known_importers' );
		if ( is_string( $importer ) && 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 1.6.3
				 * @since-jetpack 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 1.6.3
				 * @since-jetpack 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;
		}
	}

	/**
	 * Retrieve the name of the importer.
	 *
	 * @access private
	 *
	 * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
	 * @return string Name of the importer, or "Unknown Importer" if importer is unknown.
	 */
	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.
	 *
	 * @access private
	 * @static
	 *
	 * @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';
	}
}