| 1 |
<?php |
| 2 |
/** |
| 3 |
* Plugin Name: Easy Search Replace |
| 4 |
* Description: Easy Search Replace is a plugin that allows you to search and replace text within your WordPress. |
| 5 |
* Version: 1.1.2 |
| 6 |
* Author: Uzair |
| 7 |
* Author URI: https://easywpstuff.com |
| 8 |
* Text Domain: easy-search-replace |
| 9 |
* Domain Path: /languages |
| 10 |
* License: GNU General Public License v2 or later |
| 11 |
* Requires at least: 5.0 |
| 12 |
* Requires PHP: 7.2 |
| 13 |
*/ |
| 14 |
|
| 15 |
// Exit if accessed directly |
| 16 |
if ( ! defined( 'ABSPATH' ) ) { |
| 17 |
exit; |
| 18 |
} |
| 19 |
|
| 20 |
require_once 'inc/options.php'; |
| 21 |
require_once 'lib/dom.php'; |
| 22 |
|
| 23 |
/** |
| 24 |
* Perform search and replace on the output buffer. |
| 25 |
* |
| 26 |
* @param string $buffer The output buffer. |
| 27 |
* @return string The modified buffer. |
| 28 |
*/ |
| 29 |
function esrn_search_replace_start( $buffer ) { |
| 30 |
global $post; |
| 31 |
|
| 32 |
$options = get_option( 'esrn_search_replace_options' ); |
| 33 |
$fields = isset( $options['fields'] ) ? $options['fields'] : []; |
| 34 |
|
| 35 |
if ( empty( $fields ) ) { |
| 36 |
return $buffer; |
| 37 |
} |
| 38 |
|
| 39 |
// Get current post data |
| 40 |
$current_post_id = is_singular() && isset( $post->ID ) ? $post->ID : null; |
| 41 |
$current_type = is_singular() && isset( $post->post_type ) ? $post->post_type : null; |
| 42 |
if ( is_home() && get_option( 'page_for_posts' ) ) { |
| 43 |
$current_post_id = (int) get_option( 'page_for_posts' ); |
| 44 |
$current_type = 'page'; |
| 45 |
} |
| 46 |
$current_url = home_url( add_query_arg( [], $_SERVER['REQUEST_URI'] ) ); |
| 47 |
|
| 48 |
// First, apply all non-selector (global) replacements sequentially |
| 49 |
foreach ( $fields as $field ) { |
| 50 |
if ( ! empty( $field['selector'] ) ) { |
| 51 |
continue; // Skip selector-based for now |
| 52 |
} |
| 53 |
|
| 54 |
if ( ! esrn_should_apply_field( $field, $current_type, $current_post_id, $current_url ) ) { |
| 55 |
continue; |
| 56 |
} |
| 57 |
|
| 58 |
$search = $field['search']; |
| 59 |
$replace = $field['replace']; |
| 60 |
|
| 61 |
if ( isset( $field['ignore_case'] ) && $field['ignore_case'] ) { |
| 62 |
$buffer = str_ireplace( $search, $replace, $buffer ); |
| 63 |
} else { |
| 64 |
$buffer = str_replace( $search, $replace, $buffer ); |
| 65 |
} |
| 66 |
} |
| 67 |
|
| 68 |
// Check if there are any selector-based fields that apply |
| 69 |
$has_selector = false; |
| 70 |
foreach ( $fields as $field ) { |
| 71 |
if ( empty( $field['selector'] ) ) { |
| 72 |
continue; |
| 73 |
} |
| 74 |
|
| 75 |
if ( esrn_should_apply_field( $field, $current_type, $current_post_id, $current_url ) ) { |
| 76 |
$has_selector = true; |
| 77 |
break; |
| 78 |
} |
| 79 |
} |
| 80 |
|
| 81 |
if ( ! $has_selector ) { |
| 82 |
return $buffer; |
| 83 |
} |
| 84 |
|
| 85 |
// Parse DOM only once for selector-based replacements |
| 86 |
$dom = esr_str_get_html( $buffer, false, true, 'UTF-8', false, PHP_EOL, ' ' ); |
| 87 |
if ( ! $dom ) { |
| 88 |
return $buffer; // Fallback gracefully if parsing fails |
| 89 |
} |
| 90 |
|
| 91 |
// Apply selector-based replacements |
| 92 |
foreach ( $fields as $field ) { |
| 93 |
if ( empty( $field['selector'] ) ) { |
| 94 |
continue; |
| 95 |
} |
| 96 |
|
| 97 |
if ( ! esrn_should_apply_field( $field, $current_type, $current_post_id, $current_url ) ) { |
| 98 |
continue; |
| 99 |
} |
| 100 |
|
| 101 |
$search = $field['search']; |
| 102 |
$replace = $field['replace']; |
| 103 |
$selector = $field['selector']; |
| 104 |
|
| 105 |
$elements = $dom->find( $selector ); |
| 106 |
foreach ( $elements as $element ) { |
| 107 |
if ( isset( $field['ignore_case'] ) && $field['ignore_case'] ) { |
| 108 |
$element->innertext = str_ireplace( $search, $replace, $element->innertext ); |
| 109 |
} else { |
| 110 |
$element->innertext = str_replace( $search, $replace, $element->innertext ); |
| 111 |
} |
| 112 |
} |
| 113 |
} |
| 114 |
|
| 115 |
$buffer = $dom->save(); |
| 116 |
|
| 117 |
return $buffer; |
| 118 |
} |
| 119 |
|
| 120 |
/** |
| 121 |
* Check if a field should be applied based on conditions. |
| 122 |
* |
| 123 |
* @param array $field The field options. |
| 124 |
* @param string $current_type Current post type. |
| 125 |
* @param int $current_post_id Current post ID. |
| 126 |
* @param string $current_url Current URL. |
| 127 |
* @return bool Whether to apply the field. |
| 128 |
*/ |
| 129 |
function esrn_should_apply_field( $field, $current_type, $current_post_id, $current_url ) { |
| 130 |
$types = isset( $field['post_types'] ) ? (array) $field['post_types'] : []; |
| 131 |
$ids = isset( $field['post_ids'] ) ? $field['post_ids'] : ''; |
| 132 |
$urls = isset( $field['urls'] ) ? $field['urls'] : ''; |
| 133 |
|
| 134 |
// Handle post type (AND condition) |
| 135 |
if ( ! empty( $types ) && $current_type && ! in_array( $current_type, $types, true ) ) { |
| 136 |
return false; |
| 137 |
} |
| 138 |
|
| 139 |
// Handle Post IDs and URLs as OR |
| 140 |
$match_ids = false; |
| 141 |
$match_urls = false; |
| 142 |
|
| 143 |
if ( ! empty( $ids ) ) { |
| 144 |
$allowed_ids = array_map( 'trim', explode( ',', $ids ) ); |
| 145 |
if ( $current_post_id && in_array( (string) $current_post_id, $allowed_ids, true ) ) { |
| 146 |
$match_ids = true; |
| 147 |
} |
| 148 |
} |
| 149 |
|
| 150 |
if ( ! empty( $urls ) ) { |
| 151 |
$allowed_urls = array_filter( array_map( 'trim', explode( "\n", $urls ) ) ); |
| 152 |
foreach ( $allowed_urls as $url ) { |
| 153 |
if ( $url && stripos( $current_url, $url ) !== false ) { |
| 154 |
$match_urls = true; |
| 155 |
break; |
| 156 |
} |
| 157 |
} |
| 158 |
} |
| 159 |
|
| 160 |
// If either IDs or URLs are set, at least one must match |
| 161 |
if ( ( $ids || $urls ) && ! ( $match_ids || $match_urls ) ) { |
| 162 |
return false; |
| 163 |
} |
| 164 |
|
| 165 |
return true; |
| 166 |
} |
| 167 |
|
| 168 |
// Start output buffering |
| 169 |
function esrn_search_replace_buffer_start() { |
| 170 |
// Only start if there are fields to process |
| 171 |
$options = get_option( 'esrn_search_replace_options' ); |
| 172 |
if ( ! empty( $options['fields'] ) ) { |
| 173 |
ob_start( 'esrn_search_replace_start' ); |
| 174 |
} |
| 175 |
} |
| 176 |
add_action( 'template_redirect', 'esrn_search_replace_buffer_start', 101 ); |
| 177 |
|
| 178 |
// Enqueue admin scripts and styles |
| 179 |
function esrn_enqueue_admin_scripts() { |
| 180 |
if ( isset( $_GET['page'] ) && $_GET['page'] === 'easy_search_replace' ) { |
| 181 |
wp_enqueue_script( 'esrn_search_replace-script', plugin_dir_url( __FILE__ ) . 'assets/admin.js', array( 'jquery' ), '1.1.1', true ); |
| 182 |
wp_enqueue_style( 'esrn_search_replace-style', plugin_dir_url( __FILE__ ) . 'assets/admin.css', '', '1.1.1' ); |
| 183 |
if ( wp_script_is( 'select2', 'registered' ) ) { |
| 184 |
wp_enqueue_style( 'select2' ); |
| 185 |
wp_enqueue_script( 'select2' ); |
| 186 |
} else { |
| 187 |
wp_enqueue_style( 'esrn_select2', plugin_dir_url( __FILE__ ) . 'assets/select2.css', array(), '4.0.13' ); |
| 188 |
wp_enqueue_script( 'esrn_select2', plugin_dir_url( __FILE__ ) . 'assets/select2.js', array( 'jquery' ), '4.0.13', true ); |
| 189 |
} |
| 190 |
} |
| 191 |
} |
| 192 |
add_action( 'admin_enqueue_scripts', 'esrn_enqueue_admin_scripts' ); |
| 193 |
|
| 194 |
// Add settings link to plugin page |
| 195 |
function esrn_add_settings_link( $links ) { |
| 196 |
$settings_link = '<a href="options-general.php?page=easy_search_replace">' . __( 'Settings', 'easy-search-replace' ) . '</a>'; |
| 197 |
array_unshift( $links, $settings_link ); |
| 198 |
return $links; |
| 199 |
} |
| 200 |
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'esrn_add_settings_link' ); |