| Server IP : 69.39.225.240 / Your IP : 216.73.216.138 Web Server : Apache System : Linux bronze7.serverhost.net 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64 User : reademotions ( 1074) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/reademotions/www/wp-content/plugins/pods/src/Pods/Tools/ |
Upload File : |
<?php
namespace Pods\Tools;
use Exception;
use Throwable;
use PodsAPI;
use PodsForm;
use Pods\Whatsit\Field;
use Pods\Whatsit\Group;
use Pods\Whatsit\Pod;
/**
* Repair tool functionality.
*
* @since 2.9.4
*/
class Repair {
/**
* @var PodsAPI
*/
private $api;
/**
* @var array
*/
private $errors = [];
/**
* Repair Groups and Fields for a Pod.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
* @param string $mode The repair mode (upgrade or full).
*
* @return array The results with information about the repair done.
*/
public function repair_groups_and_fields_for_pod( Pod $pod, $mode ) {
$this->api = pods_api();
$this->errors = [];
$is_upgrade_mode = 'upgrade' === $mode;
$is_migrated = 1 === (int) $pod->get_arg( '_migrated_28' );
// Maybe set up a new group if no groups are found for the Pod.
$group_id = $this->maybe_setup_group_if_no_groups( $pod, $mode );
$results = [];
// If no group needed to be created, attempt to find the first group ID.
if ( null === $group_id ) {
$groups = $pod->get_groups( [
'fallback_mode' => false,
'limit' => 1,
] );
$groups = wp_list_pluck( $groups, 'id' );
$groups = array_filter( $groups );
// Get the first group ID.
if ( ! empty( $groups ) ) {
$group_id = reset( $groups );
}
} else {
$results['maybe_setup_group_if_no_groups'] = __( 'First group created.', 'pods' );
}
if ( ! $is_upgrade_mode || $is_migrated ) {
// Maybe resolve group conflicts.
$results['maybe_resolve_group_conflicts'] = $this->maybe_resolve_group_conflicts( $pod );
// Maybe resolve field conflicts.
$results['maybe_resolve_field_conflicts'] = $this->maybe_resolve_field_conflicts( $pod );
}
// If we have a group to work with, use that.
if ( null !== $group_id ) {
if ( ! $is_upgrade_mode || $is_migrated ) {
// Maybe reassign fields with invalid groups.
$results['maybe_reassign_fields_with_invalid_groups'] = $this->maybe_reassign_fields_with_invalid_groups( $pod, $group_id );
}
// Maybe reassign orphan fields to the first group.
$results['maybe_reassign_orphan_fields'] = $this->maybe_reassign_orphan_fields( $pod, $group_id );
}
// Maybe fix fields with invalid field type.
$results['maybe_fix_fields_with_invalid_field_type'] = $this->maybe_fix_fields_with_invalid_field_type( $pod );
// Mark the pod as migrated if upgrading.
if ( $is_upgrade_mode ) {
$pod->set_arg( '_migrated_28', 1 );
try {
$this->api->save_pod( $pod );
} catch ( Throwable $exception ) {
// Do nothing.
}
}
$this->api->cache_flush_pods( $pod );
$pod->flush();
$results['message_html'] = $this->get_message_html( $pod, $results );
return $results;
}
/**
* Get the message HTML from the repair results.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
* @param array $results The repair results.
*
* @return string The message HTML.
*/
protected function get_message_html( Pod $pod, array $results ) {
$messages = [
sprintf(
'<h3>%s</h3>',
// translators: The Pod label.
sprintf(
esc_html__( 'Repair results for %s', 'pods' ),
$pod->get_label() . ' (' . $pod->get_name() . ')'
)
),
];
if ( ! empty( $results['maybe_setup_group_if_no_groups'] ) ) {
$repair_result = $results['maybe_setup_group_if_no_groups'];
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Setup group if there were no groups', 'pods' ),
esc_html( $repair_result )
);
}
if ( ! empty( $results['maybe_resolve_group_conflicts'] ) ) {
$repair_result = $results['maybe_resolve_group_conflicts'];
$repair_result = array_map( 'esc_html', $repair_result );
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Resolved group conflicts', 'pods' ),
implode( '</li><li>', $repair_result )
);
}
if ( ! empty( $results['maybe_resolve_field_conflicts'] ) ) {
$repair_result = $results['maybe_resolve_field_conflicts'];
$repair_result = array_map( 'esc_html', $repair_result );
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Resolved field conflicts', 'pods' ),
implode( '</li><li>', $repair_result )
);
}
if ( ! empty( $results['maybe_reassign_fields_with_invalid_groups'] ) ) {
$repair_result = $results['maybe_reassign_fields_with_invalid_groups'];
$repair_result = array_map( 'esc_html', $repair_result );
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Reassigned fields with invalid groups', 'pods' ),
implode( '</li><li>', $repair_result )
);
}
if ( ! empty( $results['maybe_reassign_orphan_fields'] ) ) {
$repair_result = $results['maybe_reassign_orphan_fields'];
$repair_result = array_map( 'esc_html', $repair_result );
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Reassigned orphan fields', 'pods' ),
implode( '</li><li>', $repair_result )
);
}
if ( ! empty( $results['maybe_fix_fields_with_invalid_field_type'] ) ) {
$repair_result = $results['maybe_fix_fields_with_invalid_field_type'];
$repair_result = array_map( 'esc_html', $repair_result );
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Fixed fields with invalid field type', 'pods' ),
implode( '</li><li>', $repair_result )
);
}
if ( ! empty( $this->errors ) ) {
$repair_result = $this->errors;
$repair_result = array_map( 'esc_html', $repair_result );
$messages[] = sprintf(
'<h4>%1$s</h4><ul class="ul-disc"><li>%2$s</li></ul>',
esc_html__( 'Repair errors', 'pods' ),
implode( '</li><li>', $repair_result )
);
}
if ( 1 === count( $messages ) ) {
$messages[] = esc_html__( 'No repair actions were needed.', 'pods' );
}
return wpautop( implode( "\n\n", $messages ) );
}
/**
* Maybe setup group if there are no groups.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
* @param string $mode The repair mode (upgrade or full).
*
* @return int|null The group ID if created, otherwise null if repair not needed.
*/
protected function maybe_setup_group_if_no_groups( Pod $pod, $mode ) {
$groups = $pod->get_groups( [
'fallback_mode' => false,
] );
// Groups exist, no need to create a group.
if ( ! empty( $groups ) ) {
return null;
}
// For upgrade mode, we create the first group even if there are no fields.
if ( 'upgrade' !== $mode ) {
$fields = $pod->get_fields( [
'fallback_mode' => false,
] );
// No fields, no need to create a group.
if ( empty( $fields ) ) {
return null;
}
}
$group_label = __( 'Details', 'pods' );
if ( in_array( $pod->get_type(), [ 'post_type', 'taxonomy', 'user', 'comment', 'media' ], true ) ) {
$group_label = __( 'More Fields', 'pods' );
}
/**
* Filter the title of the Pods Metabox used in the post editor.
*
* @since unknown
*
* @param string $title The title to use, default is 'More Fields'.
* @param obj|Pod $pod Current Pods Object.
* @param array $fields Array of fields that will go in the metabox.
* @param string $type The type of Pod.
* @param string $name Name of the Pod.
*/
$group_label = apply_filters( 'pods_meta_default_box_title', $group_label, $pod, $pod->get_fields(), $pod->get_type(), $pod->get_name() );
$group_name = sanitize_key( pods_js_name( sanitize_title( $group_label ) ) );
try {
$new_group_name = $group_name;
$counter = 2;
do {
$conflicting_group_found = $this->api->load_group( [
'pod' => $pod,
'name' => $group_name,
] );
if ( $conflicting_group_found ) {
$group_name = $new_group_name . '-' . $counter;
}
$counter ++;
} while ( $this->api->load_group( [
'pod' => $pod,
'name' => $group_name,
] ) );
// Setup first group.
$group_id = $this->api->save_group( [
'pod' => $pod,
'name' => $group_name,
'label' => $group_label,
] );
if ( $group_id && is_numeric( $group_id ) ) {
return $group_id;
}
throw new Exception( __( 'Failed to create new default group.', 'pods' ) );
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $group_name . ')';
}
return null;
}
/**
* Maybe resolve group conflicts.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
*
* @return string[] The label, name, and ID for each group resolved.
*/
protected function maybe_resolve_group_conflicts( Pod $pod ) {
// Find any group on the pod that has the same name as another group.
global $wpdb;
$sql = "
SELECT DISTINCT
`primary`.`ID`,
`primary`.`post_name`
FROM `{$wpdb->posts}` AS `primary`
LEFT JOIN `{$wpdb->posts}` AS `duplicate`
ON `duplicate`.`post_name` = `primary`.`post_name`
WHERE
`primary`.`post_type` = %s
AND `primary`.`post_parent` = %d
AND `duplicate`.`ID` != `primary`.`ID`
AND `duplicate`.`post_type` = `primary`.`post_type`
AND `duplicate`.`post_parent` = `primary`.`post_parent`
ORDER BY `primary`.`ID`
";
$duplicate_groups = $wpdb->get_results(
$wpdb->prepare(
$sql,
[
'_pods_group',
$pod->get_id(),
]
)
);
$groups_to_resolve = [];
foreach ( $duplicate_groups as $duplicate_group ) {
if ( ! isset( $groups_to_resolve[ $duplicate_group->post_name ] ) ) {
$groups_to_resolve[ $duplicate_group->post_name ] = [];
}
try {
$group = $this->api->load_group( [ 'id' => $duplicate_group->ID ] );
if ( $group ) {
$groups_to_resolve[ $duplicate_group->post_name ][] = $group;
} else {
throw new Exception( __( 'Failed to load duplicate group to resolve.', 'pods' ) );
}
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $duplicate_group->post_name . ' - #' . $duplicate_group->ID . ')';
}
}
$resolved_groups = [];
foreach ( $groups_to_resolve as $group_name => $groups ) {
if ( 1 < count( $groups ) ) {
// Remove the first group.
array_shift( $groups );
}
foreach ( $groups as $group ) {
/** @var Group $group */
try {
$this->api->save_group( [
'id' => $group->get_id(),
'pod_data' => $pod,
'group' => $group,
'new_name' => $group_name . '_' . $group->get_id(),
] );
$resolved_groups[] = sprintf(
'%1$s (%2$s: %3$s | %4$s: %5$s | %6$s: %7$d)',
$group->get_label(),
__( 'Old Name', 'pods' ),
$group_name,
__( 'New Name', 'pods' ),
$group_name . '_' . $group->get_id(),
__( 'ID', 'pods' ),
$group->get_id()
);
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $group->get_name() . ' - #' . $group->get_id() . ')';
}
}
}
return $resolved_groups;
}
/**
* Maybe resolve field conflicts.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
*
* @return string[] The label, name, and ID for each field resolved.
*/
protected function maybe_resolve_field_conflicts( Pod $pod ) {
// Find any field on the pod that has the same name as another field.
global $wpdb;
$sql = "
SELECT DISTINCT
`primary`.`ID`,
`primary`.`post_name`
FROM `{$wpdb->posts}` AS `primary`
LEFT JOIN `{$wpdb->posts}` AS `duplicate`
ON `duplicate`.`post_name` = `primary`.`post_name`
WHERE
`primary`.`post_type` = %s
AND `primary`.`post_parent` = %d
AND `duplicate`.`ID` != `primary`.`ID`
AND `duplicate`.`post_type` = `primary`.`post_type`
AND `duplicate`.`post_parent` = `primary`.`post_parent`
ORDER BY `primary`.`ID`
";
$duplicate_fields = $wpdb->get_results(
$wpdb->prepare(
$sql,
[
'_pods_field',
$pod->get_id(),
]
)
);
$fields_to_resolve = [];
foreach ( $duplicate_fields as $duplicate_field ) {
if ( ! isset( $fields_to_resolve[ $duplicate_field->post_name ] ) ) {
$fields_to_resolve[ $duplicate_field->post_name ] = [];
}
try {
$field = $this->api->load_field( [ 'id' => $duplicate_field->ID ] );
if ( $field ) {
$fields_to_resolve[ $duplicate_field->post_name ][] = $field;
} else {
throw new Exception( __( 'Failed to load duplicate field to resolve.', 'pods' ) );
}
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $duplicate_field->post_name . ' - #' . $duplicate_field->ID . ')';
}
}
$resolved_fields = [];
foreach ( $fields_to_resolve as $field_name => $fields ) {
if ( 1 < count( $fields ) ) {
// Remove the first field.
array_shift( $fields );
}
foreach ( $fields as $field ) {
/** @var Field $field */
try {
$this->api->save_field( [
'id' => $field->get_id(),
'pod_data' => $pod,
'field' => $field,
'new_name' => $field_name . '_' . $field->get_id(),
], false );
$resolved_fields[] = sprintf(
'%1$s (%2$s: %3$s | %4$s: %5$s | %6$s: %7$d)',
$field->get_label(),
__( 'Old Name', 'pods' ),
$field_name,
__( 'New Name', 'pods' ),
$field_name . '_' . $field->get_id(),
__( 'ID', 'pods' ),
$field->get_id()
);
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $field->get_name() . ' - #' . $field->get_id() . ')';
}
}
}
return $resolved_fields;
}
/**
* Maybe reassign fields with invalid groups.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
* @param int $group_id The group ID.
*
* @return string[] The label, name, and ID for each field reassigned.
*/
protected function maybe_reassign_fields_with_invalid_groups( Pod $pod, $group_id ) {
// Get all known group IDs.
$groups = $pod->get_groups( [
'fallback_mode' => false,
] );
$groups = wp_list_pluck( $groups, 'id' );
$groups = array_filter( $groups );
$fields = $pod->get_fields( [
'fallback_mode' => false,
'meta_query' => [
[
'key' => 'group',
'value' => $groups,
'compare' => 'NOT IN',
],
],
] );
return $this->reassign_fields_to_group( $fields, $group_id, $pod );
}
/**
* Maybe reassign orphan fields.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
* @param int $group_id The group ID.
*
* @return string[] The label, name, and ID for each field reassigned.
*/
protected function maybe_reassign_orphan_fields( Pod $pod, $group_id ) {
$fields = $pod->get_fields( [
'fallback_mode' => false,
'group' => null,
] );
return $this->reassign_fields_to_group( $fields, $group_id, $pod );
}
/**
* Reassign fields to a specific group.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
* @param int $group_id The group ID.
*
* @return string[] The label, name, and ID for each field reassigned.
*/
protected function reassign_fields_to_group( $fields, $group_id, $pod ) {
$reassigned_fields = [];
foreach ( $fields as $field ) {
try {
$this->api->save_field( [
'id' => $field->get_id(),
'pod_data' => $pod,
'field' => $field,
'new_group_id' => $group_id,
] );
$field->set_arg( 'group', $group_id );
$reassigned_fields[] = sprintf(
'%1$s (%2$s: %3$s | %4$s: %5$d)',
$field->get_label(),
__( 'Name', 'pods' ),
$field->get_name(),
__( 'ID', 'pods' ),
$field->get_id()
);
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $field->get_name() . ' - #' . $field->get_id() . ')';
}
}
return $reassigned_fields;
}
/**
* Maybe fix fields with invalid field type.
*
* @since 2.9.4
*
* @param Pod $pod The Pod object.
*
* @return string[] The label, name, and ID for each field fixed.
*/
protected function maybe_fix_fields_with_invalid_field_type( Pod $pod ) {
$supported_field_types = PodsForm::field_types_list();
$fields = $pod->get_fields( [
'fallback_mode' => false,
'meta_query' => [
'relation' => 'OR',
[
'key' => 'type',
'value' => $supported_field_types,
'compare' => 'NOT IN',
],
[
'key' => 'type',
'compare' => 'NOT EXISTS',
],
],
] );
$fixed_fields = [];
foreach ( $fields as $field ) {
try {
$old_type = $field->get_type();
if ( empty( $old_type ) ) {
$old_type = __( 'N/A', 'pods' );
}
$this->api->save_field( [
'id' => $field->get_id(),
'pod_data' => $pod,
'field' => $field,
'type' => 'text',
] );
$field->set_arg( 'type', 'text' );
$fixed_fields[] = sprintf(
'%1$s (%2$s: %3$s | %4$s: %5$s | %6$s: %7$d)',
$field->get_label(),
__( 'Old Type', 'pods' ),
$old_type,
__( 'Name', 'pods' ),
$field->get_name(),
__( 'ID', 'pods' ),
$field->get_id()
);
} catch ( Throwable $exception ) {
$this->errors[] = ucwords( str_replace( '_', ' ', __FUNCTION__ ) ) . ' > ' . $exception->getMessage() . ' (' . $field->get_name() . ' - #' . $field->get_id() . ')';
}
}
return $fixed_fields;
}
}