PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.6
Pods – Custom Content Types and Fields v3.3.6
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / components / Migrate-PHP / Migrate-PHP.php
pods / components / Migrate-PHP Last commit date
ui 4 months ago Migrate-PHP.php 4 months ago
Migrate-PHP.php
481 lines
1 <?php
2 /**
3 * Name: Migrate: Pod Page and Pod Template PHP into File-based templates
4 *
5 * Menu Name: Migrate PHP Templates
6 *
7 * Description: Pod Pages and Pod Templates will be migrated into their corresponding file-based locations in the theme. This will overwrite existing files and this is one-way. After migrating the files it will clear the PHP code from the DB content. <a href="https://docs.pods.io/displaying-pods/pod-page-template-hierarchy-for-themes/">More information about Pod Page template hierarchy</a> | <a href="https://docs.pods.io/displaying-pods/pod-template-hierarchy-for-themes/">More information about Pod Template hierarchy</a>
8 *
9 * Category: Migration
10 *
11 * Version: 1.0
12 *
13 * Plugin: pods-migrate-php/pods-migrate-php.php
14 *
15 * @package Pods\Components
16 * @subpackage Migrate-PHP
17 */
18
19 // Don't load directly.
20 if ( ! defined( 'ABSPATH' ) ) {
21 die( '-1' );
22 }
23
24 use Pods\Whatsit;
25 use Pods\Whatsit\Page;
26 use Pods\Whatsit\Template;
27
28 if ( class_exists( 'Pods_Migrate_PHP' ) ) {
29 return;
30 }
31
32 /**
33 * Class Pods_Migrate_PHP
34 */
35 class Pods_Migrate_PHP extends PodsComponent {
36
37 /**
38 * {@inheritdoc}
39 */
40 public function init() {
41 // Nothing to do here.
42 }
43
44 /**
45 * Enqueue styles
46 */
47 public function admin_assets() {
48 wp_enqueue_style( 'pods-wizard' );
49 }
50
51 /**
52 * Get the list of objects that need migration.
53 *
54 * @return array{pod_templates:Template[],pod_pages:Page[]} The list of objects that need migration.
55 */
56 public function get_objects_that_need_migration(): array {
57 $pod_templates = [];
58 $pod_pages = [];
59
60 $api = pods_api();
61
62 if ( class_exists( 'Pods_Templates' ) ) {
63 $pod_templates = array_filter(
64 $api->load_templates(),
65 static function( $object ) {
66 if ( ! $object instanceof Whatsit ) {
67 return false;
68 }
69
70 return ! empty( $object->get_id() ) && false !== strpos( $object->get_description(), '<?' );
71 }
72 );
73 }
74
75 if ( class_exists( 'Pods_Pages' ) ) {
76 $pod_pages = array_filter(
77 $api->load_pages(),
78 static function( $object ) {
79 if ( ! $object instanceof Whatsit ) {
80 return false;
81 }
82
83 return (
84 ! empty( $object->get_id() )
85 && (
86 false !== strpos( $object->get_description(), '<?' )
87 || false !== strpos( (string) $object->get_arg( 'precode' ), '<?' )
88 )
89 );
90 }
91 );
92 }
93
94 // Rekey the objects by ID.
95 $pod_templates = array_combine( array_map( static function( $object ) {
96 return $object->get_id();
97 }, $pod_templates ), $pod_templates );
98
99 $pod_pages = array_combine( array_map( static function( $object ) {
100 return $object->get_id();
101 }, $pod_pages ), $pod_pages );
102
103 return compact( 'pod_templates', 'pod_pages' );
104 }
105
106 /**
107 * Show the Admin
108 *
109 * @param $options
110 * @param $component
111 */
112 public function admin( $options, $component ) {
113 $method = 'migrate';
114
115 [
116 'pod_templates' => $pod_templates,
117 'pod_pages' => $pod_pages,
118 ] = $this->get_objects_that_need_migration();
119
120 $has_objects_to_migrate = ! empty( $pod_templates ) || ! empty( $pod_pages );
121
122 // ajax_migrate
123 pods_view( __DIR__ . '/ui/wizard.php', compact( array_keys( get_defined_vars() ) ) );
124 }
125
126 /**
127 * Handle the Migration AJAX
128 *
129 * @param $params
130 */
131 public function ajax_migrate( $params ) {
132 require_once ABSPATH . '/wp-admin/includes/file.php';
133 WP_Filesystem();
134
135 [
136 'pod_templates' => $pod_templates_available_to_migrate,
137 'pod_pages' => $pod_pages_available_to_migrate,
138 ] = $this->get_objects_that_need_migration();
139
140 $objects_can_be_migrated = ! empty( $pod_templates_available_to_migrate ) || ! empty( $pod_pages_available_to_migrate );
141
142 $cleanup = 1 === (int) pods_v( 'cleanup', $params, 0 );
143
144 $pod_templates = [];
145 $pod_pages = [];
146
147 $pod_templates_selected = (array) pods_v( 'templates', $params, [] );
148 $pod_templates_selected = array_filter( $pod_templates_selected );
149
150 $pod_pages_selected = (array) pods_v( 'pages', $params, [] );
151 $pod_pages_selected = array_filter( $pod_pages_selected );
152
153 $has_objects_to_migrate = ! empty( $pod_templates_selected ) || ! empty( $pod_pages_selected );
154
155 foreach ( $pod_templates_selected as $object_id => $checked ) {
156 if ( true === (boolean) $checked && isset( $pod_templates_available_to_migrate[ (int) $object_id ] ) ) {
157 $pod_templates[] = $object_id;
158 }
159 }
160
161 foreach ( $pod_pages_selected as $object_id => $checked ) {
162 if ( true === (boolean) $checked && isset( $pod_pages_available_to_migrate[ (int) $object_id ] ) ) {
163 $pod_pages[] = $object_id;
164 }
165 }
166
167 $pod_templates_file_paths = [];
168 $pod_pages_file_paths = [];
169
170 $migrated = false;
171
172 foreach ( $pod_templates as $object_id ) {
173 $migrated = true;
174
175 $pod_templates_file_paths[] = $this->migrate_template( $object_id, $cleanup );
176 }
177
178 foreach ( $pod_pages as $object_id ) {
179 $migrated = true;
180
181 $pod_pages_file_paths[] = $this->migrate_page( $object_id, $cleanup );
182 }
183
184 $content = '<div class="pods-wizard-content">' . "\n";
185
186 if ( ! $has_objects_to_migrate ) {
187 $content .= '<p>' . esc_html__( 'No Pod Templates or Pod Pages were selected.', 'pods' ) . '</p>' . "\n";
188 } elseif ( ! $objects_can_be_migrated ) {
189 $content .= '<p>' . esc_html__( 'The selected Pod Templates or Pod Pages are not available for migration. They no longer contain PHP.', 'pods' ) . '</p>' . "\n";
190 } elseif ( ! $migrated ) {
191 $content .= '<p>' . esc_html__( 'The selected Pod Templates or Pod Pages were not successfully migrated.', 'pods' ) . '</p>' . "\n";
192 } else {
193 $content .= '<p>' . esc_html__( 'Migration Complete! The following paths were saved:', 'pods' ) . '</p>' . "\n";
194
195 if ( ! empty( $pod_templates_file_paths ) ) {
196 $content .= '<h4>' . esc_html__( 'Pod Templates saved', 'pods' ) . '</h4>' . "\n";
197 $content .= '<ul class="normal">' . "\n";
198
199 foreach ( $pod_templates_file_paths as $file_path ) {
200 $content .= '<li>' . esc_html( $file_path ) . '</li>' . "\n";
201 }
202
203 $content .= '</ul>' . "\n";
204 }
205
206 if ( ! empty( $pod_pages_file_paths ) ) {
207 $content .= '<h4>' . esc_html__( 'Pod Pages saved', 'pods' ) . '</h4>' . "\n";
208 $content .= '<ul class="normal">' . "\n";
209
210 foreach ( $pod_pages_file_paths as $file_path ) {
211 $content .= '<li>' . esc_html( $file_path ) . '</li>' . "\n";
212 }
213
214 $content .= '</ul>' . "\n";
215 }
216
217 if ( $cleanup ) {
218 $content .= '<p>' . esc_html__( 'The Pod Page(s) and/or Pod Template(s) were cleaned up and will now load directly from the theme files.', 'pods' ) . '</p>' . "\n";
219 } else {
220 $content .= '<p>' . esc_html__( 'The Pod Page(s) and/or Pod Template(s) were not modified. You will need to empty the content on them before they will load from the theme files.', 'pods' ) . '</p>' . "\n";
221 }
222 }
223
224 return $content;
225 }
226
227 private function setup_file_path( $file_path ) {
228 /**
229 * @var $wp_filesystem WP_Filesystem_Base
230 */
231 global $wp_filesystem;
232
233 if ( ! $wp_filesystem->is_dir( dirname( $file_path ) ) ) {
234 $pods_path = trailingslashit( get_stylesheet_directory() ) . 'pods';
235
236 if ( ! $wp_filesystem->is_dir( $pods_path ) && ! $wp_filesystem->mkdir( $pods_path, FS_CHMOD_DIR ) ) {
237 // translators: %s is the directory path.
238 pods_error( sprintf( esc_html__( 'Unable to create the directory: %s', 'pods' ), $pods_path ) );
239 }
240
241 $grandparent_path = dirname( dirname( $file_path ) );
242
243 if ( $pods_path !== $grandparent_path && ! $wp_filesystem->is_dir( $grandparent_path ) && ! $wp_filesystem->mkdir( $grandparent_path, FS_CHMOD_DIR ) ) {
244 // translators: %s is the directory path.
245 pods_error( sprintf( esc_html__( 'Unable to create the directory: %s', 'pods' ), $grandparent_path ) );
246 }
247
248 if ( ! $wp_filesystem->mkdir( dirname( $file_path ), FS_CHMOD_DIR ) ) {
249 // translators: %s is the directory path.
250 pods_error( sprintf( esc_html__( 'Unable to create the directory: %s', 'pods' ), $file_path ) );
251 }
252 } elseif ( ! $wp_filesystem->is_writable( dirname( $file_path ) ) ) {
253 // translators: %s is the directory path.
254 pods_error( sprintf( esc_html__( 'Unable to write to the directory: %s', 'pods' ), $file_path ) );
255 }
256 }
257
258 private function migrate_template( $object_id, bool $cleanup ) {
259 /**
260 * @var $wp_filesystem WP_Filesystem_Base
261 */
262 global $wp_filesystem;
263
264 $api = pods_api();
265
266 /** @var Template $object */
267 $object = $api->load_template( [ 'id' => $object_id ] );
268
269 if ( ! $object ) {
270 // translators: %s is the object ID.
271 pods_error( sprintf( esc_html__( 'Unable to find the Pod Template by ID: %s', 'pods' ), $object_id ) );
272 }
273
274 $files = Pods_Templates::get_templates_for_pod_template( $object );
275
276 if ( count( $files ) < 2 ) {
277 // translators: %s is the file paths found.
278 pods_error( sprintf( esc_html__( 'Unable to detect the file path: %s', 'pods' ), json_encode( $files, JSON_PRETTY_PRINT ) ) );
279 }
280
281 $file_path = trailingslashit( get_stylesheet_directory() ) . array_shift( $files );
282
283 $this->setup_file_path( $file_path );
284
285 $template_code = $object->get_description();
286
287 $extra_headers = '';
288
289 if ( false !== strpos( $template_code, '{@' ) ) {
290 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
291 $extra_headers = <<<PHPTEMPLATE
292 * Magic Tags: Enabled
293 PHPTEMPLATE;
294
295 }
296
297 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
298 $contents = <<<PHPTEMPLATE
299 <?php
300 /**
301 * Pod Template: {$object->get_label()}{$extra_headers}
302 *
303 * @var Pods \$obj
304 */
305 ?>
306
307 {$template_code}
308 PHPTEMPLATE;
309
310 if ( ! $wp_filesystem->put_contents( $file_path, $contents, FS_CHMOD_FILE ) ) {
311 // translators: %s is the file path.
312 pods_error( sprintf( esc_html__( 'Unable to write to the file: %s', 'pods' ), $file_path ) );
313 }
314
315 if ( $cleanup ) {
316 $api->save_template( [
317 'id' => $object->get_id(),
318 'name' => $object->get_label(),
319 'code' => '',
320 ] );
321 }
322
323 return str_replace( ABSPATH, '', $file_path );
324 }
325
326 private function migrate_page( $object_id, bool $cleanup ) {
327 /**
328 * @var $wp_filesystem WP_Filesystem_Base
329 */
330 global $wp_filesystem;
331
332 $api = pods_api();
333
334 /** @var Page $object */
335 $object = $api->load_page( [ 'id' => $object_id ] );
336
337 if ( ! $object ) {
338 // translators: %s is the object ID.
339 pods_error( sprintf( esc_html__( 'Unable to find the Pod Page by ID: %s', 'pods' ), $object_id ) );
340 }
341
342 $files = Pods_Pages::get_templates_for_pod_page( $object );
343 $files_for_content = Pods_Pages::get_templates_for_pod_page_content( $object );
344
345 if ( count( $files ) < 2 ) {
346 // translators: %s is the file paths found.
347 pods_error( sprintf( esc_html__( 'Unable to detect the file path: %s', 'pods' ), json_encode( $files, JSON_PRETTY_PRINT ) ) );
348 }
349
350 $file_path = trailingslashit( get_stylesheet_directory() ) . array_shift( $files );
351 $file_path_for_content = trailingslashit( get_stylesheet_directory() ) . array_shift( $files_for_content );
352
353 $this->setup_file_path( $file_path );
354
355 $precode = (string) $object->get_arg( 'precode' );
356 $page_template = (string) $object->get_arg( 'page_template' );
357
358 if ( false !== strpos( $precode, '<?' ) && false === strpos( $precode, '?>' ) ) {
359 $precode .= "\n?>";
360 }
361
362 $precode_template = '';
363
364 if ( ! empty( $precode ) ) {
365 $precode_template .= "\n";
366 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
367 $precode_template .= <<<PHPTEMPLATE
368 /*
369 * Precode goes below.
370 */
371 ?>
372 {$precode}
373 PHPTEMPLATE;
374 $precode_template .= "\n";
375 }
376
377 $template_code = trim( $object->get_description() );
378
379 $has_page_template = ! empty( $page_template );
380
381 $precode_has_end_tag = false !== strpos( $precode, '?>' );
382
383 if ( false === strpos( $template_code, '<?' ) ) {
384 $template_code = "?>\n" . $template_code . ( ! $has_page_template ? '' : "\n<?php" );
385 } elseif ( ( ! $has_page_template || ! $precode_has_end_tag ) && 0 === strpos( $template_code, '<?php' ) ) {
386 $template_code = substr( $template_code, strlen( '<?php' ) );
387 } elseif ( ( ! $has_page_template || ! $precode_has_end_tag ) && 0 === strpos( $template_code, '<?' ) ) {
388 $template_code = substr( $template_code, strlen( '<?' ) );
389 }
390
391 $extra_headers = '';
392 $extra_notes = '';
393
394 if ( ! $has_page_template ) {
395 $start_tag = '';
396
397 if ( $precode_has_end_tag ) {
398 $start_tag = "\n<?php\n";
399 }
400
401 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
402 $template_code = <<<PHPTEMPLATE
403 get_header();
404
405 // Pod Page content goes here.
406 {$template_code}
407
408 get_sidebar();
409 get_footer();
410 PHPTEMPLATE;
411
412 $template_code = $start_tag . $template_code;
413 } else {
414 // Set the code and save it for the content path.
415 $this->setup_file_path( $file_path_for_content );
416
417 if ( '_custom' !== $page_template && 'blank' !== $page_template ) {
418 $extra_notes .= "\n";
419 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
420 $extra_notes .= <<<PHPTEMPLATE
421 *
422 * @see {$page_template} for the template where this will get called from.
423 PHPTEMPLATE;
424 }
425
426 // Set the file path we will write to as the one for the content specific template.
427 $file_path = $file_path_for_content;
428 $extra_notes .= "\n";
429 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
430 $extra_notes .= <<<PHPTEMPLATE
431 *
432 * This template is only used for pods_content() calls.
433 PHPTEMPLATE;
434 }
435
436 if ( false !== strpos( $template_code, '{@' ) ) {
437 $extra_headers = "\n";
438 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
439 $extra_headers .= <<<PHPTEMPLATE
440 * Magic Tags: Enabled
441 PHPTEMPLATE;
442 }
443
444 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
445 $contents = <<<PHPTEMPLATE
446 <?php
447 /**
448 * Pod Page URI: {$object->get_label()}{$extra_headers}{$extra_notes}
449 *
450 * @var Pods \$pods
451 */
452
453 {$precode_template}
454
455 {$template_code}
456 PHPTEMPLATE;
457
458 // Clean up the PHP tags that open and close too often.
459 $contents = preg_replace( '/\?>\s*<\?php(\s*)/Umi', '$1', $contents );
460 $contents = preg_replace( '/\?>\s*<\?(\s*)/Umi', '$1', $contents );
461 $contents = preg_replace( '/\n{3,}/', "\n\n", $contents );
462
463 if ( ! $wp_filesystem->put_contents( $file_path, $contents, FS_CHMOD_FILE ) ) {
464 // translators: %s is the file path.
465 pods_error( sprintf( esc_html__( 'Unable to write to the file: %s', 'pods' ), $file_path ) );
466 }
467
468 if ( $cleanup ) {
469 $api->save_page( [
470 'id' => $object->get_id(),
471 'name' => $object->get_label(),
472 'code' => '',
473 'precode' => '',
474 ] );
475 }
476
477 return str_replace( ABSPATH, '', $file_path );
478 }
479
480 }
481