ContentImporter::update_inserted_blog_post_data()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 3
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace lloc\Msls\ContentImport;
4
5
use lloc\Msls\ContentImport\Importers\Importer;
6
use lloc\Msls\ContentImport\Importers\Map;
7
use lloc\Msls\ContentImport\Importers\WithRequestPostAttributes;
8
use lloc\Msls\MslsBlogCollection;
9
use lloc\Msls\MslsMain;
10
use lloc\Msls\MslsOptionsPost;
11
use lloc\Msls\MslsRegistryInstance;
12
13
/**
14
 * Class ContentImporter
15
 *
16
 * Handles the request for a content import.
17
 *
18
 * @package lloc\Msls\ContentImport
19
 */
20
class ContentImporter extends MslsRegistryInstance {
21
	use WithRequestPostAttributes;
22
23
	/**
24
	 * @var MslsMain
25
	 */
26
	protected $main;
27
28
	/**
29
	 * @var ImportLogger
30
	 */
31
	protected $logger;
32
	/**
33
	 * @var Relations
34
	 */
35
	protected $relations;
36
37
	/**
38
	 * @var bool Whether the class should handle requests or not.
39
	 */
40
	protected $handle = true;
41
42
	/**
43
	 * @var int The ID of the post the class created while handling the request, if any.
44
	 */
45
	protected $has_created_post = 0;
46
47
	/**
48
	 * ContentImporter constructor.
49
	 *
50
	 * @param \lloc\Msls\MslsMain|null $main
51
	 */
52
	public function __construct( MslsMain $main = null ) {
53
		$this->main = $main ?: MslsMain::init();
54
	}
55
56
	/**
57
	 * @return \lloc\Msls\ContentImport\ImportLogger
58
	 */
59
	public function get_logger() {
60
		return $this->logger;
61
	}
62
63
	/**
64
	 * @param \lloc\Msls\ContentImport\ImportLogger $logger
65
	 */
66
	public function set_logger( $logger ) {
67
		$this->logger = $logger;
68
	}
69
70
	/**
71
	 * @return \lloc\Msls\ContentImport\Relations
72
	 */
73
	public function get_relations() {
74
		return $this->relations;
75
	}
76
77
	/**
78
	 * @param \lloc\Msls\ContentImport\Relations $relations
79
	 */
80
	public function set_relations( $relations ) {
81
		$this->relations = $relations;
82
	}
83
84
	/**
85
	 * Handles an import request happening during a post save or a template redirect.
86
	 *
87
	 * @param array|null $data
88
	 *
89
	 * @return array The updated, if needed, data array.
90
	 */
91
	public function handle_import( array $data = [] ) {
92
		if ( ! $this->pre_flight_check() || false === $sources = $this->parse_sources() ) {
93
			return $data;
94
		}
95
96
		list( $source_blog_id, $source_post_id ) = $sources;
97
98
		if ( $source_blog_id === get_current_blog_id() ) {
99
			return $data;
100
		}
101
102
		$source_lang  = MslsBlogCollection::get_blog_language( $source_blog_id );
103
		$dest_blog_id = get_current_blog_id();
104
		$dest_lang    = MslsBlogCollection::get_blog_language( get_current_blog_id() );
105
106
		$dest_post_id = $this->get_the_blog_post_ID( $dest_blog_id );
107
108
		if ( empty( $dest_post_id ) ) {
109
			return $data;
110
		}
111
112
		switch_to_blog( $source_blog_id );
113
		$source_post = get_post( $source_post_id );
114
		restore_current_blog();
115
116
		if ( ! $source_post instanceof \WP_Post ) {
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
117
			return $data;
118
		}
119
120
		$import_coordinates = new ImportCoordinates();
121
122
		$import_coordinates->source_blog_id = $source_blog_id;
123
		$import_coordinates->source_post_id = $source_post_id;
124
		$import_coordinates->dest_blog_id   = $dest_blog_id;
125
		$import_coordinates->dest_post_id   = $dest_post_id;
126
		$import_coordinates->source_post    = $source_post;
127
		$import_coordinates->source_lang    = $source_lang;
128
		$import_coordinates->dest_lang      = $dest_lang;
129
130
		$import_coordinates->parse_importers_from_request();
131
132
		$data = $this->import_content( $import_coordinates, $data );
133
134
		if ( $this->has_created_post ) {
135
			$this->update_inserted_blog_post_data($dest_blog_id, $dest_post_id, $data );
136
			$this->redirect_to_blog_post( $dest_blog_id, $dest_post_id );
137
		}
138
139
		return $data;
140
	}
141
142
	/**
143
	 * Whether the importer should run or not.
144
	 *
145
	 * @return bool
146
	 */
147
	protected function pre_flight_check( array $data = [] ) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
148
		if ( ! $this->handle ) {
149
			return false;
150
		}
151
152
		if ( ! $this->main->verify_nonce() ) {
153
			return false;
154
		}
155
156
		if ( ! isset( $_POST['msls_import'] ) ) {
157
			return false;
158
		}
159
160
		return true;
161
	}
162
163
	/**
164
	 * Parses the source blog and post IDs from the $_POST array validating them.
165
	 *
166
	 * @return array|bool
167
	 */
168
	public function parse_sources() {
169
		if ( ! isset( $_POST['msls_import'] ) ) {
170
			return false;
171
		}
172
173
		$import_data = array_filter( explode( '|', trim( $_POST['msls_import'] ) ), 'is_numeric' );
174
175
		if ( count( $import_data ) !== 2 ) {
176
			return false;
177
		}
178
179
		return array_map( 'intval', $import_data );
180
	}
181
182
	protected function get_the_blog_post_ID( $blog_id ) {
183
		switch_to_blog( $blog_id );
184
185
		$id = get_the_ID();
186
187
		if ( ! empty( $id ) ) {
188
			restore_current_blog();
189
190
			return $id;
191
		}
192
193
		if ( isset( $_REQUEST['post'] ) && filter_var( $_REQUEST['post'], FILTER_VALIDATE_INT ) ) {
194
			return (int) $_REQUEST['post'];
195
		}
196
197
		$data = [
198
			'post_type'  => $this->read_post_type_from_request( 'post' ),
199
			'post_title' => 'MSLS Content Import Draft - ' . date( 'Y-m-d H:i:s' )
200
		];
201
202
		return $this->insert_blog_post( $blog_id, $data );
203
	}
204
205
	protected function insert_blog_post( $blog_id, array $data = [] ) {
206
		if ( empty( $data ) ) {
207
			return false;
208
		}
209
210
		switch_to_blog( $blog_id );
211
212
		$this->handle( false );
213
		if ( isset( $data['ID'] ) ) {
214
			$post_id = wp_update_post( $data );
215
		} else {
216
			$post_id = wp_insert_post( $data );
217
		}
218
		$this->handle( true );
219
220
		$this->has_created_post = $post_id ?: false;
221
222
		restore_current_blog();
223
224
		return $this->has_created_post;
225
	}
226
227
	public function handle( $handle ) {
228
		$this->handle = $handle;
229
230
		// also prevent MSLS from saving
231
		if ( false === $handle ) {
232
			add_action( 'msls_main_save', '__return_false' );
233
		} else {
234
			remove_action( 'msls_main_save', '__return_false' );
235
		}
236
	}
237
238
	/**
239
	 * Imports content according to the provided coordinates.
240
	 *
241
	 * @param       ImportCoordinates $import_coordinates
242
	 * @param array $post_fields An optional array of post fields; this can be
243
	 *                                             left empty if the method is not called as a consequence
244
	 *                                             of filtering the `wp_insert_post_data` filter.
245
	 *
246
	 * @return array An array of modified post fields.
247
	 */
248
	public function import_content( ImportCoordinates $import_coordinates, array $post_fields = [] ) {
249
		if ( ! $import_coordinates->validate() ) {
250
			return $post_fields;
251
		}
252
253
		/**
254
		 * Fires before the import runs.
255
		 *
256
		 * @param ImportCoordinates $import_coordinates
257
		 */
258
		do_action( 'msls_content_import_before_import', $import_coordinates );
259
260
		/**
261
		 * Filters the data before the import runs.
262
		 *
263
		 * @since TBD
264
		 *
265
		 * @param array $post_fields
266
		 * @param ImportCoordinates $import_coordinates
267
		 */
268
		$post_fields = apply_filters( 'msls_content_import_data_before_import', $post_fields, $import_coordinates );
269
270
		/**
271
		 * Filters the importers map before it's populated.
272
		 *
273
		 * Returning a non `null` value here will override the creation of the importers map completely
274
		 * and use the one returned in the filter.
275
		 *
276
		 * @param null $importers
277
		 * @param ImportCoordinates $import_coordinates
278
		 */
279
		$importers = apply_filters( 'msls_content_import_importers', null, $import_coordinates );
280
281
		if ( null === $importers ) {
282
			$importers = Map::instance()->make( $import_coordinates );
283
		}
284
285
		$this->logger    = $this->logger ?: new ImportLogger( $import_coordinates );
286
		$this->relations = $this->relations ?: new Relations( $import_coordinates );
287
288
		if ( ! empty( $importers ) && is_array( $importers ) ) {
289
			$source_post_id = $import_coordinates->source_post_id;
290
			$dest_lang      = $import_coordinates->dest_lang;
291
			$dest_post_id   = $import_coordinates->dest_post_id;
292
			$this->relations->should_create( MslsOptionsPost::create( $source_post_id ), $dest_lang, $dest_post_id );
0 ignored issues
show
Bug introduced by
It seems like \lloc\Msls\MslsOptionsPo...create($source_post_id) can be null; however, should_create() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
293
294
			foreach ( $importers as $key => $importer ) {
295
				/** @var Importer $importer */
296
				$post_fields = $importer->import( $post_fields );
297
				$this->logger->merge( $importer->get_logger() );
298
				$this->relations->merge( $importer->get_relations() );
299
			}
300
301
			$this->relations->create();
302
			$this->logger->save();
303
		}
304
305
		/**
306
		 * Fires after the import ran.
307
		 *
308
		 * @since TBD
309
		 *
310
		 * @param ImportCoordinates $import_coordinates
311
		 * @param ImportLogger $logger
312
		 * @param Relations $relations
313
		 */
314
		do_action( 'msls_content_import_after_import', $import_coordinates, $this->logger, $this->relations );
315
316
		/**
317
		 * Filters the data after the import ran.
318
		 *
319
		 * @param array $post_fields
320
		 * @param ImportCoordinates $import_coordinates
321
		 * @param ImportLogger $logger
322
		 * @param Relations $relations
323
		 */
324
		return apply_filters( 'msls_content_import_data_after_import', $post_fields, $import_coordinates, $this->logger, $this->relations );
325
	}
326
327
	/**
328
	 * @param array $data
329
	 * @param int $post_id
330
	 *
331
	 * @return array
332
	 */
333
	protected function update_inserted_blog_post_data($blog_id, $post_id, array $data ) {
334
		$data['ID']          = $post_id;
335
		$data['post_status'] = empty( $data['post_status'] ) || $data['post_status'] === 'auto-draft'
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $data['post_status'] (integer) and 'auto-draft' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
336
			? 'draft'
337
			: $data['post_status'];
338
		$this->insert_blog_post( $blog_id, $data );
339
340
		return $data;
341
	}
342
343
	protected function redirect_to_blog_post( $dest_blog_id, $post_id ) {
344
		switch_to_blog( $dest_blog_id );
345
		$edit_post_link = html_entity_decode( get_edit_post_link( $post_id ) );
346
		wp_redirect( $edit_post_link );
347
		die();
348
	}
349
350
	/**
351
	 * Filters whether the post should be considered empty or not.
352
	 *
353
	 * Empty posts would not be saved to database but it's fine if in
354
	 * the context of a content import as it will be populated.
355
	 *
356
	 * @param bool $empty
357
	 *
358
	 * @return bool
359
	 */
360
	public function filter_empty( $empty ) {
361
		if ( ! $this->main->verify_nonce() ) {
362
			return $empty;
363
		}
364
365
		if ( ! isset( $_POST['msls_import'] ) ) {
366
			return $empty;
367
		}
368
369
		return false;
370
	}
371
}
372