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