PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / trunk
Pods – Custom Content Types and Fields vtrunk
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 3 months ago
Migrate-PHP.php
469 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
134 /** @var WP_Filesystem_Base $wp_filesystem */
135 global $wp_filesystem;
136
137 if ( ! WP_Filesystem() || ! $wp_filesystem ) {
138 return '<e>' . esc_html__( 'Error: There was a problem accessing the filesystem.', 'pods' );
139 }
140
141 [
142 'pod_templates' => $pod_templates_available_to_migrate,
143 'pod_pages' => $pod_pages_available_to_migrate,
144 ] = $this->get_objects_that_need_migration();
145
146 $objects_can_be_migrated = ! empty( $pod_templates_available_to_migrate ) || ! empty( $pod_pages_available_to_migrate );
147
148 $cleanup = 1 === (int) pods_v( 'cleanup', $params, 0 );
149
150 $pod_templates = [];
151 $pod_pages = [];
152
153 $pod_templates_selected = (array) pods_v( 'templates', $params, [] );
154 $pod_templates_selected = array_filter( $pod_templates_selected );
155
156 $pod_pages_selected = (array) pods_v( 'pages', $params, [] );
157 $pod_pages_selected = array_filter( $pod_pages_selected );
158
159 $has_objects_to_migrate = ! empty( $pod_templates_selected ) || ! empty( $pod_pages_selected );
160
161 foreach ( $pod_templates_selected as $object_id => $checked ) {
162 if ( true === (boolean) $checked && isset( $pod_templates_available_to_migrate[ (int) $object_id ] ) ) {
163 $pod_templates[] = $object_id;
164 }
165 }
166
167 foreach ( $pod_pages_selected as $object_id => $checked ) {
168 if ( true === (boolean) $checked && isset( $pod_pages_available_to_migrate[ (int) $object_id ] ) ) {
169 $pod_pages[] = $object_id;
170 }
171 }
172
173 $pod_templates_file_paths = [];
174 $pod_pages_file_paths = [];
175
176 $migrated = false;
177
178 foreach ( $pod_templates as $object_id ) {
179 $migrated = true;
180
181 $pod_templates_file_paths[] = $this->migrate_template( $object_id, $cleanup );
182 }
183
184 foreach ( $pod_pages as $object_id ) {
185 $migrated = true;
186
187 $pod_pages_file_paths[] = $this->migrate_page( $object_id, $cleanup );
188 }
189
190 $content = '<div class="pods-wizard-content">' . "\n";
191
192 if ( ! $has_objects_to_migrate ) {
193 $content .= '<p>' . esc_html__( 'No Pod Templates or Pod Pages were selected.', 'pods' ) . '</p>' . "\n";
194 } elseif ( ! $objects_can_be_migrated ) {
195 $content .= '<p>' . esc_html__( 'The selected Pod Templates or Pod Pages are not available for migration. They no longer contain PHP.', 'pods' ) . '</p>' . "\n";
196 } elseif ( ! $migrated ) {
197 $content .= '<p>' . esc_html__( 'The selected Pod Templates or Pod Pages were not successfully migrated.', 'pods' ) . '</p>' . "\n";
198 } else {
199 $content .= '<p>' . esc_html__( 'Migration Complete! The following paths were saved:', 'pods' ) . '</p>' . "\n";
200
201 if ( ! empty( $pod_templates_file_paths ) ) {
202 $content .= '<h4>' . esc_html__( 'Pod Templates saved', 'pods' ) . '</h4>' . "\n";
203 $content .= '<ul class="normal">' . "\n";
204
205 foreach ( $pod_templates_file_paths as $file_path ) {
206 $content .= '<li>' . esc_html( $file_path ) . '</li>' . "\n";
207 }
208
209 $content .= '</ul>' . "\n";
210 }
211
212 if ( ! empty( $pod_pages_file_paths ) ) {
213 $content .= '<h4>' . esc_html__( 'Pod Pages saved', 'pods' ) . '</h4>' . "\n";
214 $content .= '<ul class="normal">' . "\n";
215
216 foreach ( $pod_pages_file_paths as $file_path ) {
217 $content .= '<li>' . esc_html( $file_path ) . '</li>' . "\n";
218 }
219
220 $content .= '</ul>' . "\n";
221 }
222
223 if ( $cleanup ) {
224 $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";
225 } else {
226 $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";
227 }
228 }
229
230 return $content;
231 }
232
233 private function setup_file_path( $file_path ) {
234 /**
235 * @var $wp_filesystem WP_Filesystem_Base
236 */
237 global $wp_filesystem;
238
239 if ( ! $wp_filesystem->is_dir( dirname( $file_path ) ) ) {
240 $pods_path = trailingslashit( get_stylesheet_directory() ) . 'pods';
241
242 if ( ! $wp_filesystem->is_dir( $pods_path ) && ! $wp_filesystem->mkdir( $pods_path, FS_CHMOD_DIR ) ) {
243 // translators: %s is the directory path.
244 pods_error( sprintf( esc_html__( 'Unable to create the directory: %s', 'pods' ), $pods_path ) );
245 }
246
247 $grandparent_path = dirname( dirname( $file_path ) );
248
249 if ( $pods_path !== $grandparent_path && ! $wp_filesystem->is_dir( $grandparent_path ) && ! $wp_filesystem->mkdir( $grandparent_path, FS_CHMOD_DIR ) ) {
250 // translators: %s is the directory path.
251 pods_error( sprintf( esc_html__( 'Unable to create the directory: %s', 'pods' ), $grandparent_path ) );
252 }
253
254 if ( ! $wp_filesystem->mkdir( dirname( $file_path ), FS_CHMOD_DIR ) ) {
255 // translators: %s is the directory path.
256 pods_error( sprintf( esc_html__( 'Unable to create the directory: %s', 'pods' ), $file_path ) );
257 }
258 } elseif ( ! $wp_filesystem->is_writable( dirname( $file_path ) ) ) {
259 // translators: %s is the directory path.
260 pods_error( sprintf( esc_html__( 'Unable to write to the directory: %s', 'pods' ), $file_path ) );
261 }
262 }
263
264 private function migrate_template( $object_id, bool $cleanup ) {
265 /**
266 * @var $wp_filesystem WP_Filesystem_Base
267 */
268 global $wp_filesystem;
269
270 $api = pods_api();
271
272 /** @var Template $object */
273 $object = $api->load_template( [ 'id' => $object_id ] );
274
275 if ( ! $object ) {
276 // translators: %s is the object ID.
277 pods_error( sprintf( esc_html__( 'Unable to find the Pod Template by ID: %s', 'pods' ), $object_id ) );
278 }
279
280 $files = Pods_Templates::get_templates_for_pod_template( $object );
281
282 if ( count( $files ) < 2 ) {
283 // translators: %s is the file paths found.
284 pods_error( sprintf( esc_html__( 'Unable to detect the file path: %s', 'pods' ), json_encode( $files, JSON_PRETTY_PRINT ) ) );
285 }
286
287 $file_path = trailingslashit( get_stylesheet_directory() ) . array_shift( $files );
288
289 $this->setup_file_path( $file_path );
290
291 $template_code = $object->get_description();
292
293 $extra_headers = '';
294
295 if ( false !== strpos( $template_code, '{@' ) ) {
296 $extra_headers = "\n * Magic Tags: Enabled";
297 }
298
299 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
300 $contents = <<<PHPTEMPLATE
301 <?php
302 /**
303 * Pod Template: {$object->get_label()}{$extra_headers}
304 *
305 * @var Pods \$obj
306 */
307 ?>
308
309 {$template_code}
310 PHPTEMPLATE;
311
312 if ( ! $wp_filesystem->put_contents( $file_path, $contents, FS_CHMOD_FILE ) ) {
313 // translators: %s is the file path.
314 pods_error( sprintf( esc_html__( 'Unable to write to the file: %s', 'pods' ), $file_path ) );
315 }
316
317 if ( $cleanup ) {
318 $api->save_template( [
319 'id' => $object->get_id(),
320 'name' => $object->get_label(),
321 'code' => '',
322 ] );
323 }
324
325 return str_replace( ABSPATH, '', $file_path );
326 }
327
328 private function migrate_page( $object_id, bool $cleanup ) {
329 /**
330 * @var $wp_filesystem WP_Filesystem_Base
331 */
332 global $wp_filesystem;
333
334 $api = pods_api();
335
336 /** @var Page $object */
337 $object = $api->load_page( [ 'id' => $object_id ] );
338
339 if ( ! $object ) {
340 // translators: %s is the object ID.
341 pods_error( sprintf( esc_html__( 'Unable to find the Pod Page by ID: %s', 'pods' ), $object_id ) );
342 }
343
344 $files = Pods_Pages::get_templates_for_pod_page( $object );
345 $files_for_content = Pods_Pages::get_templates_for_pod_page_content( $object );
346
347 if ( count( $files ) < 2 ) {
348 // translators: %s is the file paths found.
349 pods_error( sprintf( esc_html__( 'Unable to detect the file path: %s', 'pods' ), json_encode( $files, JSON_PRETTY_PRINT ) ) );
350 }
351
352 $file_path = trailingslashit( get_stylesheet_directory() ) . array_shift( $files );
353 $file_path_for_content = trailingslashit( get_stylesheet_directory() ) . array_shift( $files_for_content );
354
355 $this->setup_file_path( $file_path );
356
357 $precode = (string) $object->get_arg( 'precode' );
358 $page_template = (string) $object->get_arg( 'page_template' );
359
360 if ( false !== strpos( $precode, '<?' ) && false === strpos( $precode, '?>' ) ) {
361 $precode .= "\n?>";
362 }
363
364 $precode_template = '';
365
366 if ( ! empty( $precode ) ) {
367 $precode_template .= "\n";
368 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
369 $precode_template .= <<<PHPTEMPLATE
370 /*
371 * Precode goes below.
372 */
373 ?>
374 {$precode}
375 PHPTEMPLATE;
376 $precode_template .= "\n";
377 }
378
379 $template_code = trim( $object->get_description() );
380
381 $has_page_template = ! empty( $page_template );
382
383 $precode_has_end_tag = false !== strpos( $precode, '?>' );
384
385 if ( false === strpos( $template_code, '<?' ) ) {
386 $template_code = "?>\n" . $template_code . ( ! $has_page_template ? '' : "\n<?php" );
387 } elseif ( ( ! $has_page_template || ! $precode_has_end_tag ) && 0 === strpos( $template_code, '<?php' ) ) {
388 $template_code = substr( $template_code, strlen( '<?php' ) );
389 } elseif ( ( ! $has_page_template || ! $precode_has_end_tag ) && 0 === strpos( $template_code, '<?' ) ) {
390 $template_code = substr( $template_code, strlen( '<?' ) );
391 }
392
393 $extra_headers = '';
394 $extra_notes = '';
395
396 if ( ! $has_page_template ) {
397 $start_tag = '';
398
399 if ( $precode_has_end_tag ) {
400 $start_tag = "\n<?php\n";
401 }
402
403 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
404 $template_code = <<<PHPTEMPLATE
405 get_header();
406
407 // Pod Page content goes here.
408 {$template_code}
409
410 get_sidebar();
411 get_footer();
412 PHPTEMPLATE;
413
414 $template_code = $start_tag . $template_code;
415 } else {
416 // Set the code and save it for the content path.
417 $this->setup_file_path( $file_path_for_content );
418
419 if ( '_custom' !== $page_template && 'blank' !== $page_template ) {
420 $extra_notes .= "\n *\n * @see {$page_template} for the template where this will get called from.";
421 }
422
423 // Set the file path we will write to as the one for the content specific template.
424 $file_path = $file_path_for_content;
425 $extra_notes .= "\n *\n * This template is only used for pods_content() calls.";
426 }
427
428 if ( false !== strpos( $template_code, '{@' ) ) {
429 $extra_headers = "\n * Magic Tags: Enabled";
430 }
431
432 // phpcs:ignore PluginCheck.CodeAnalysis.Heredoc.NotAllowed
433 $contents = <<<PHPTEMPLATE
434 <?php
435 /**
436 * Pod Page URI: {$object->get_label()}{$extra_headers}{$extra_notes}
437 *
438 * @var Pods \$pods
439 */
440
441 {$precode_template}
442
443 {$template_code}
444 PHPTEMPLATE;
445
446 // Clean up the PHP tags that open and close too often.
447 $contents = preg_replace( '/\?>\s*<\?php(\s*)/Umi', '$1', $contents );
448 $contents = preg_replace( '/\?>\s*<\?(\s*)/Umi', '$1', $contents );
449 $contents = preg_replace( '/\n{3,}/', "\n\n", $contents );
450
451 if ( ! $wp_filesystem->put_contents( $file_path, $contents, FS_CHMOD_FILE ) ) {
452 // translators: %s is the file path.
453 pods_error( sprintf( esc_html__( 'Unable to write to the file: %s', 'pods' ), $file_path ) );
454 }
455
456 if ( $cleanup ) {
457 $api->save_page( [
458 'id' => $object->get_id(),
459 'name' => $object->get_label(),
460 'code' => '',
461 'precode' => '',
462 ] );
463 }
464
465 return str_replace( ABSPATH, '', $file_path );
466 }
467
468 }
469