String locator - Version 2.2.0

Version Description

  • Fixed some lingering potential HTTPS issues.
  • Fixed result previews not cutting the excerpt making them excessively long.
  • Fixed archive file skipping not accounting for letter casing in extensions.
  • Introduced common media types to the file skipping procedure.
  • Added default timeout periods, the plugin will no longer allow indefinite execution to work around http proxies.
  • Added more translatable strings that were missed.
  • Added Must-Use Plugins to individual plugin search.
  • Made changes to the uninstall routine to ensure we remove any related database entries on removal.
Download this release

Release Info

Developer Clorith
Plugin Icon 128x128 String locator
Version 2.2.0
Comparing to
See all releases

Code changes from version 2.1.2 to 2.2.0

Files changed (7) hide show
  1. changelog.txt +18 -0
  2. editor.php +11 -16
  3. options.php +5 -0
  4. readme.txt +19 -41
  5. resources/js/string-locator-search.js +21 -15
  6. string-locator.php +284 -166
  7. uninstall.php +7 -2
changelog.txt CHANGED
@@ -1,3 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = 2.0.3 =
2
  * Added support for HHVM
3
  * Improved inline documentation
1
+ = 2.1.2 =
2
+ * Fix for max execution times some times being interpreted as strings and failing when you it should be able to run indefinitely
3
+ * Fix for regex being enabled when you return to the search results, but you hadn't performed a regex search
4
+ * Resolved some code issues with functions being called improperly (future proofing)
5
+
6
+ = 2.1.1 =
7
+ * Improved error messages
8
+ * Add regex pattern validation before performing a search
9
+ * Fixed bug causing some searches to be identified as regex when they are not, leading to errors
10
+ * Fixed a bug that could cause the first file in every search chunk from being ignored
11
+
12
+ = 2.1.0 =
13
+ * Add support for configurations with infinite execution times
14
+ * Better code handling on RTL sites
15
+ * Exclude archive files, that we can't modify any way, from searches
16
+ * Display file path in the editor to identify which file is being modified
17
+ * Add support for RegEx string searches
18
+
19
  = 2.0.3 =
20
  * Added support for HHVM
21
  * Improved inline documentation
editor.php CHANGED
@@ -13,7 +13,8 @@
13
  'uri' => 'https://wordpress.org/',
14
  'name' => 'WordPress'
15
  ),
16
- 'description' => 'WordPress is web software you can use to create a beautiful website or blog. We like to say that WordPress is both free and priceless at the same time.'
 
17
  );
18
  }
19
  elseif ( 'theme' == $_GET['file-type'] ) {
@@ -65,12 +66,15 @@
65
  }
66
  ?>
67
  <div class="wrap">
68
- <h2>
69
- <?php _e( 'String Locator - Code Editor', 'string-locator' ); ?>
 
 
 
70
  <a href="<?php echo esc_url( $this_url . '&restore=true' ); ?>" class="button button-primary"><?php _e( 'Return to search results', 'string-locator' ); ?></a>
71
- </h2>
72
 
73
- <form action="<?php echo ( is_ssl() ? 'http://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>" id="string-locator-edit-form" method="post">
74
  <div class="string-locator-edit-wrap">
75
  <textarea name="string-locator-editor-content" class="string-locator-editor" id="code-editor" data-editor-goto-line="<?php echo $_GET['string-locator-line']; ?>" data-editor-language="<?php echo $string_locator->string_locator_language; ?>" autofocus="autofocus"><?php echo esc_html( $editor_content ); ?></textarea>
76
  </div>
@@ -97,7 +101,6 @@
97
  </p>
98
 
99
  <?php if ( isset( $details['parent'] ) && ! $details['parent'] ) { ?>
100
- <!--
101
  <div class="notice notice-warning inline below-h2">
102
  <p>
103
  <?php _e( 'It seems you are making direct edits to a theme.', 'string-locator' ); ?>
@@ -106,28 +109,20 @@
106
  <p>
107
  <?php _e( 'When making changes to a theme, it is recommended you make a <a href="https://codex.wordpress.org/Child_Themes">Child Theme</a>.', 'string-locator' ); ?>
108
  </p>
109
-
110
- <p>
111
- <label>
112
- <input type="checkbox" name="string-locator-make-child-theme" checked="checked">
113
- <?php _e( 'Automatically create a new child theme for these edits', 'string-locator' ); ?>
114
- </label>
115
- </p>
116
  </div>
117
 
118
  <p>
119
 
120
  </p>
121
- -->
122
  <?php } ?>
123
 
124
  <?php if ( ! stristr( $file, 'wp-content' ) ) { ?>
125
  <div class="notice notice-warning inline below-h2">
126
  <p>
127
- <strong>Warning:</strong> You appear to be editing a Core file.
128
  </p>
129
  <p>
130
- Keep in mind that edits to core files will be lost when WordPress is updated.
131
  </p>
132
  </div>
133
  <?php } ?>
13
  'uri' => 'https://wordpress.org/',
14
  'name' => 'WordPress'
15
  ),
16
+ /* translators: The WordPress description, used when a core file is opened in the editor. */
17
+ 'description' => __( 'WordPress is web software you can use to create a beautiful website or blog. We like to say that WordPress is both free and priceless at the same time.', 'string-locator' )
18
  );
19
  }
20
  elseif ( 'theme' == $_GET['file-type'] ) {
66
  }
67
  ?>
68
  <div class="wrap">
69
+ <h1>
70
+ <?php
71
+ /* translators: Title on the editor page. */
72
+ _e( 'String Locator - Code Editor', 'string-locator' );
73
+ ?>
74
  <a href="<?php echo esc_url( $this_url . '&restore=true' ); ?>" class="button button-primary"><?php _e( 'Return to search results', 'string-locator' ); ?></a>
75
+ </h1>
76
 
77
+ <form action="<?php echo ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; ?>" id="string-locator-edit-form" method="post">
78
  <div class="string-locator-edit-wrap">
79
  <textarea name="string-locator-editor-content" class="string-locator-editor" id="code-editor" data-editor-goto-line="<?php echo $_GET['string-locator-line']; ?>" data-editor-language="<?php echo $string_locator->string_locator_language; ?>" autofocus="autofocus"><?php echo esc_html( $editor_content ); ?></textarea>
80
  </div>
101
  </p>
102
 
103
  <?php if ( isset( $details['parent'] ) && ! $details['parent'] ) { ?>
 
104
  <div class="notice notice-warning inline below-h2">
105
  <p>
106
  <?php _e( 'It seems you are making direct edits to a theme.', 'string-locator' ); ?>
109
  <p>
110
  <?php _e( 'When making changes to a theme, it is recommended you make a <a href="https://codex.wordpress.org/Child_Themes">Child Theme</a>.', 'string-locator' ); ?>
111
  </p>
 
 
 
 
 
 
 
112
  </div>
113
 
114
  <p>
115
 
116
  </p>
 
117
  <?php } ?>
118
 
119
  <?php if ( ! stristr( $file, 'wp-content' ) ) { ?>
120
  <div class="notice notice-warning inline below-h2">
121
  <p>
122
+ <strong><?php _e( 'Warning:', 'string-locator' ); ?></strong> <?php _e( 'You appear to be editing a Core file.', 'string-locator' ); ?>
123
  </p>
124
  <p>
125
+ <?php _e( 'Keep in mind that edits to core files will be lost when WordPress is updated. Please consider <a href="https://make.wordpress.org/core/handbook/">contributing to WordPress core</a> instead.', 'string-locator' ); ?>
126
  </p>
127
  </div>
128
  <?php } ?>
options.php CHANGED
@@ -35,6 +35,11 @@ if ( isset( $_GET['restore'] ) ) {
35
  <optgroup label="<?php _e( 'Themes', 'string-locator' ); ?>">
36
  <?php echo String_Locator::get_themes_options( $search_location ); ?>
37
  </optgroup>
 
 
 
 
 
38
  <optgroup label="<?php _e( 'Plugins', 'string-locator' ); ?>">
39
  <?php echo String_Locator::get_plugins_options( $search_location ); ?>
40
  </optgroup>
35
  <optgroup label="<?php _e( 'Themes', 'string-locator' ); ?>">
36
  <?php echo String_Locator::get_themes_options( $search_location ); ?>
37
  </optgroup>
38
+ <?php if ( String_Locator::has_mu_plugins() ) : ?>
39
+ <optgroup label="<?php _e( 'Must Use Plugins', 'string-locator' ); ?>">
40
+ <?php echo String_Locator::get_mu_plugins_options( $search_location ); ?>
41
+ </optgroup>
42
+ <?php endif; ?>
43
  <optgroup label="<?php _e( 'Plugins', 'string-locator' ); ?>">
44
  <?php echo String_Locator::get_plugins_options( $search_location ); ?>
45
  </optgroup>
readme.txt CHANGED
@@ -5,36 +5,23 @@ Plugin URI: http://wordpress.org/plugins/string-locator/
5
  Donate link: https://www.paypal.me/clorith
6
  Tags: theme, plugin, text, search, find, editor, syntax, highlight
7
  Requires at least: 3.6
8
- Tested up to: 4.7
9
- Stable tag: 2.1.2
10
  License: GPLv2 or later
11
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
12
 
13
- Find and edit code in your themes and plugins
14
 
15
  == Description ==
16
 
17
- When working on themes and plugins you often notice a piece of text that appears hardcoded into the files, you need to modify it, but you don't know where it's located in the theme files.
18
 
19
- Easily search through your themes, plugins or the WordPress core and be presented with a list of files, the matched text and what line of the file matched your search.
20
  You can then quickly make edits directly in your browser by clicking the link from the search results.
21
 
22
- By default a Smart-Scan is enabled when making edits, this will look for inconsistencies with braces, brackets and parenthesis that are often accidentally left.
23
- This drastically reduces the risk of breaking your site when making edits, but is not an absolute guarantee.
24
 
25
- ** Translations**
26
-
27
- српски (Serbian) - Ognjen Djuraskovic
28
-
29
- Español (Spanish) - Ognjen Djuraskovic
30
-
31
- Deutsch (German) - [pixolin](http://profiles.wordpress.org/pixolin/)
32
-
33
- == Installation ==
34
-
35
- 1. Upload the `string-locator` folder to the `/wp-content/plugins/` directory
36
- 2. Activate the plugin through the 'Plugins' menu in WordPress
37
- 3. You will find the String Locator option under then `Tools` menu
38
 
39
  == Frequently asked questions ==
40
 
@@ -56,25 +43,16 @@ When writing your search string, make sure to wrap your search in forward slashe
56
  3. Smart-Scan has detected an inconsistency in the use of braces
57
 
58
  == Changelog ==
59
- = 2.1.2 =
60
- * Fix for max execution times some times being interpreted as strings and failing when you it should be able to run indefinitely
61
- * Fix for regex being enabled when you return to the search results, but you hadn't performed a regex search
62
- * Resolved some code issues with functions being called improperly (future proofing)
63
-
64
- = 2.1.1 =
65
- * Improved error messages
66
- * Add regex pattern validation before performing a search
67
- * Fixed bug causing some searches to be identified as regex when they are not, leading to errors
68
- * Fixed a bug that could cause the first file in every search chunk from being ignored
69
-
70
- = 2.1.0 =
71
- * Add support for configurations with infinite execution times
72
- * Better code handling on RTL sites
73
- * Exclude archive files, that we can't modify any way, from searches
74
- * Display file path in the editor to identify which file is being modified
75
- * Add support for RegEx string searches
76
-
77
-
78
- == Upgrade notice ==
79
 
80
- Add support for HHVM setups
 
 
 
 
 
 
 
 
 
 
 
5
  Donate link: https://www.paypal.me/clorith
6
  Tags: theme, plugin, text, search, find, editor, syntax, highlight
7
  Requires at least: 3.6
8
+ Tested up to: 4.8
9
+ Stable tag: 2.2.0
10
  License: GPLv2 or later
11
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
12
 
13
+ Find and edit code or texts in your themes and plugins
14
 
15
  == Description ==
16
 
17
+ When working on themes and plugins you often notice a piece of text that appears hardcoded into the files, you need to modify it, but you don't know what theme or plugin it's in, and certainly not which individual file to look in.
18
 
19
+ Easily search through your themes, plugins or even WordPress core and be presented with a list of files, the matched text and what line of the file matched your search.
20
  You can then quickly make edits directly in your browser by clicking the link from the search results.
21
 
22
+ By default a consistency check is performed when making edits to files, this will look for inconsistencies with braces, brackets and parenthesis that are often accidentally left in.
23
+ This drastically reduces the risk of breaking your site when making edits, but is in no way an absolute guarantee.
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  == Frequently asked questions ==
27
 
43
  3. Smart-Scan has detected an inconsistency in the use of braces
44
 
45
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ = 2.2.0 =
48
+ * Fixed some lingering potential HTTPS issues.
49
+ * Fixed result previews not cutting the excerpt making them excessively long.
50
+ * Fixed archive file skipping not accounting for letter casing in extensions.
51
+ * Introduced common media types to the file skipping procedure.
52
+ * Added default timeout periods, the plugin will no longer allow indefinite execution to work around http proxies.
53
+ * Added more translatable strings that were missed.
54
+ * Added Must-Use Plugins to individual plugin search.
55
+ * Made changes to the uninstall routine to ensure we remove any related database entries on removal.
56
+
57
+ = Older entries =
58
+ See changelog.txt for the version history
resources/js/string-locator-search.js CHANGED
@@ -31,7 +31,7 @@ jQuery(document).ready(function ($) {
31
  }
32
  }
33
  ).fail(function(xhr, textStatus, errorThrown) {
34
- throw_error( errorThrown, string_locator.search_error );
35
  });
36
  };
37
 
@@ -58,13 +58,14 @@ jQuery(document).ready(function ($) {
58
  string_locator.ajax_url,
59
  search_request,
60
  function ( response ) {
 
61
  if ( ! response.success ) {
62
  add_notice( string_locator.warning_title, response.data, 'warning' );
63
  }
64
 
65
  if ( undefined !== response.data.search ) {
66
  $("#string-locator-search-progress").val( response.data.filenum );
67
- $("#string-locator-feedback-text").html( response.data.next_file );
68
 
69
  string_locator_append_result( response.data.search );
70
  }
@@ -73,7 +74,7 @@ jQuery(document).ready(function ($) {
73
  },
74
  'json'
75
  ).fail(function(xhr, textStatus, errorThrown) {
76
- throw_error( errorThrown, string_locator.search_error );
77
  });
78
  };
79
 
@@ -81,20 +82,25 @@ jQuery(document).ready(function ($) {
81
  if ( $(".no-items", ".tools_page_string-locator").is(':visible') ) {
82
  $(".no-items", ".tools_page_string-locator").hide();
83
  }
 
 
 
84
 
85
  total_entries.forEach( function ( entries ) {
86
- for (var i = 0, amount = entries.length; i < amount; i++) {
 
87
 
88
- var entry = entries[i];
89
 
90
- if (undefined !== entry.stringresult) {
91
- var builtHTML = '<tr>' +
92
- '<td>' + entry.stringresult + '<div class="row-actions"><span class="edit"><a href="' + entry.editurl + '" aria-label="Edit">Edit</a></span></div></td>' +
93
- '<td>' + entry.filename + '</td>' +
94
- '<td>' + entry.linenum + '</td>' +
95
- '</tr>';
96
 
97
- $("tbody", ".tools_page_string-locator").append(builtHTML);
 
98
  }
99
  }
100
  } );
@@ -103,7 +109,7 @@ jQuery(document).ready(function ($) {
103
 
104
  $("#string-locator-search-form").on( 'submit', function (e) {
105
  e.preventDefault();
106
- $("#string-locator-feedback-text").text( string_locator.search_preparing );
107
  $(".string-locator-feedback").show();
108
  string_locator_search_active = true;
109
  clear_string_locator_result_area();
@@ -127,12 +133,12 @@ jQuery(document).ready(function ($) {
127
  return;
128
  }
129
  $("#string-locator-search-progress").attr( 'max', response.data.total ).val( response.data.current );
130
- $("#string-locator-feedback-text").text( string_locator.search_started );
131
  perform_string_locator_single_search( response.data.total, 0 );
132
  },
133
  'json'
134
  ).fail(function(xhr, textStatus, errorThrown) {
135
- throw_error( errorThrown, string_locator.search_error );
136
  });
137
  });
138
  });
31
  }
32
  }
33
  ).fail(function(xhr, textStatus, errorThrown) {
34
+ throw_error( xhr.status + ' ' + errorThrown, string_locator.search_error );
35
  });
36
  };
37
 
58
  string_locator.ajax_url,
59
  search_request,
60
  function ( response ) {
61
+ console.dir( response)
62
  if ( ! response.success ) {
63
  add_notice( string_locator.warning_title, response.data, 'warning' );
64
  }
65
 
66
  if ( undefined !== response.data.search ) {
67
  $("#string-locator-search-progress").val( response.data.filenum );
68
+ $("#string-locator-feedback-text").html( string_locator.search_current_prefix + response.data.next_file );
69
 
70
  string_locator_append_result( response.data.search );
71
  }
74
  },
75
  'json'
76
  ).fail(function(xhr, textStatus, errorThrown) {
77
+ throw_error( xhr.status + ' ' + errorThrown, string_locator.search_error );
78
  });
79
  };
80
 
82
  if ( $(".no-items", ".tools_page_string-locator").is(':visible') ) {
83
  $(".no-items", ".tools_page_string-locator").hide();
84
  }
85
+ if ( Array !== total_entries.constructor ) {
86
+ return false;
87
+ }
88
 
89
  total_entries.forEach( function ( entries ) {
90
+ if ( entries ) {
91
+ for (var i = 0, amount = entries.length; i < amount; i++) {
92
 
93
+ var entry = entries[i];
94
 
95
+ if (undefined !== entry.stringresult) {
96
+ var builtHTML = '<tr>' +
97
+ '<td>' + entry.stringresult + '<div class="row-actions"><span class="edit"><a href="' + entry.editurl + '" aria-label="Edit">Edit</a></span></div></td>' +
98
+ '<td>' + entry.filename + '</td>' +
99
+ '<td>' + entry.linenum + '</td>' +
100
+ '</tr>';
101
 
102
+ $("tbody", ".tools_page_string-locator").append(builtHTML);
103
+ }
104
  }
105
  }
106
  } );
109
 
110
  $("#string-locator-search-form").on( 'submit', function (e) {
111
  e.preventDefault();
112
+ $("#string-locator-feedback-text").text(string_locator.search_preparing );
113
  $(".string-locator-feedback").show();
114
  string_locator_search_active = true;
115
  clear_string_locator_result_area();
133
  return;
134
  }
135
  $("#string-locator-search-progress").attr( 'max', response.data.total ).val( response.data.current );
136
+ $("#string-locator-feedback-text").text(string_locator.search_started );
137
  perform_string_locator_single_search( response.data.total, 0 );
138
  },
139
  'json'
140
  ).fail(function(xhr, textStatus, errorThrown) {
141
+ throw_error( xhr.status + ' ' + errorThrown, string_locator.search_error );
142
  });
143
  });
144
  });
string-locator.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: String Locator
4
  * Plugin URI: http://www.clorith.net/wordpress-string-locator/
5
  * Description: Scan through theme and plugin files looking for text strings
6
- * Version: 2.1.2
7
  * Author: Clorith
8
  * Author URI: http://www.clorith.net
9
  * Text Domain: string-locator
@@ -28,33 +28,50 @@
28
  /**
29
  * Class String_Locator
30
  */
31
- class String_Locator
32
- {
33
  /**
34
- * @var string $string_locator_language The code language used for the editing page
35
- * @var string $version String Locator version number
36
- * @var array $notice Array containing all notices to display
37
- * @var bool $failed_edit Has there been a failed edit
38
- * @var string $plugin_url The URL to the plugins directory
 
 
 
 
 
 
 
39
  */
40
- public $string_locator_language = '';
41
- public $version = '2.1.2';
42
- public $notice = array();
43
- public $failed_edit = false;
44
- private $plugin_url = '';
45
- private $path_to_use = '';
46
- private $bad_http_codes = array( '500' );
47
- private $bad_file_types = array( 'rar', '7z', 'zip', 'tar', 'gz' );
48
- private $excerpt_length = 25;
49
- private $max_execution_time = null;
50
- private $start_execution_timer = 0;
51
- private $max_memory_consumption = 0;
52
 
53
  /**
54
  * Construct the plugin
55
  */
56
- function __construct()
57
- {
 
 
 
 
 
 
 
 
 
 
 
58
  /**
59
  * Define class variables requiring expressions
60
  */
@@ -62,9 +79,13 @@ class String_Locator
62
  $this->path_to_use = ( is_multisite() ? 'network/admin.php' : 'tools.php' );
63
  $this->excerpt_length = apply_filters( 'string_locator_excerpt_length', 25 );
64
 
65
- $this->max_execution_time = absint( ini_get( 'max_execution_time' ) );
66
  $this->start_execution_timer = microtime( true );
67
 
 
 
 
 
68
  $this->set_memory_limit();
69
 
70
  add_action( 'admin_menu', array( $this, 'populate_menu' ) );
@@ -85,7 +106,7 @@ class String_Locator
85
  }
86
 
87
  /**
88
- * Sets up the memory limit variables
89
  *
90
  * @since 2.0.0
91
  *
@@ -108,8 +129,10 @@ class String_Locator
108
  }
109
 
110
  /**
111
- * @param $meta
112
- * @param $plugin_file
 
 
113
  *
114
  * @return array
115
  */
@@ -125,9 +148,9 @@ class String_Locator
125
  }
126
 
127
  /**
128
- * Create a set of drop-down options for picking one of the available themes
129
  *
130
- * @param string $current The current selection option to match against
131
  *
132
  * @return string
133
  */
@@ -141,9 +164,9 @@ class String_Locator
141
 
142
  $string_locate_themes = wp_get_themes();
143
 
144
- foreach( $string_locate_themes AS $string_locate_theme_slug => $string_locate_theme ) {
145
  $string_locate_theme_data = wp_get_theme( $string_locate_theme_slug );
146
- $string_locate_value = 't-' . $string_locate_theme_slug;
147
 
148
  $options .= sprintf(
149
  '<option value="%s" %s>%s</option>',
@@ -157,9 +180,9 @@ class String_Locator
157
  }
158
 
159
  /**
160
- * Create a set of drop-down options for picking one of the available plugins
161
  *
162
- * @param string $current The current selection option to match against
163
  *
164
  * @return string
165
  */
@@ -173,8 +196,7 @@ class String_Locator
173
 
174
  $string_locate_plugins = get_plugins();
175
 
176
- foreach( $string_locate_plugins AS $string_locate_plugin_path => $string_locate_plugin )
177
- {
178
  $string_locate_value = 'p-' . $string_locate_plugin_path;
179
 
180
  $options .= sprintf(
@@ -189,18 +211,67 @@ class String_Locator
189
  }
190
 
191
  /**
192
- * Handles the AJAX request to prepare the search hierarchy
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  *
194
- * @return object
195
  */
196
  function ajax_get_directory_structure() {
197
- if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) { wp_send_json_error( __( 'Authentication failed', 'string-locator' ) ); }
 
 
198
 
199
  $scan_path = $this->prepare_scan_path( $_POST['directory'] );
200
  if ( is_file( $scan_path->path ) ) {
201
  $files = array( $scan_path->path );
202
- }
203
- else {
204
  $files = $this->ajax_scan_path( $scan_path->path );
205
  }
206
 
@@ -229,7 +300,7 @@ class String_Locator
229
  update_option( 'string-locator-search-overview', serialize( $store ), true );
230
  update_option( 'string-locator-search-history', serialize( array() ) );
231
 
232
- foreach( $file_chunks AS $count => $file_chunk ) {
233
  update_option( 'string-locator-search-files-' . $count, serialize( $file_chunk ) );
234
  }
235
 
@@ -244,8 +315,8 @@ class String_Locator
244
  * @return bool
245
  */
246
  function nearing_execution_limit() {
247
- // Max execution time is 0 (infinite) in server config
248
- if ( 0 === $this->max_execution_time ) {
249
  return false;
250
  }
251
 
@@ -255,6 +326,7 @@ class String_Locator
255
  if ( $execution_time >= $this->max_execution_time ) {
256
  return $execution_time;
257
  }
 
258
  return false;
259
  }
260
 
@@ -266,25 +338,29 @@ class String_Locator
266
  * @return bool
267
  */
268
  function nearing_memory_limit() {
 
 
 
 
 
269
  // We give our selves a 256k memory buffer, as we need to close off the script properly as well
270
  $built_in_buffer = apply_filters( 'string-locator-extra-memory-buffer', 256000 );
271
- $memory_use = ( memory_get_usage( true ) + $built_in_buffer );
272
 
273
  if ( $memory_use >= $this->max_memory_consumption ) {
274
  return $memory_use;
275
  }
 
276
  return false;
277
  }
278
 
279
  public static function absbool( $value ) {
280
  if ( is_bool( $value ) ) {
281
  $bool = $value;
282
- }
283
- else {
284
  if ( 'false' == $value ) {
285
  $bool = false;
286
- }
287
- else {
288
  $bool = true;
289
  }
290
  }
@@ -293,17 +369,19 @@ class String_Locator
293
  }
294
 
295
  /**
296
- * Search an individual file supplied via AJAX
297
  *
298
  * @since 1.9.0
299
  *
300
- * @return object
301
  */
302
  function ajax_file_search() {
303
- if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) { wp_send_json_error( __( 'Authentication failed', 'string-locator' ) ); }
 
 
304
 
305
  $files_per_chunk = apply_filters( 'string-locator-files-per-array', 500 );
306
- $response = array(
307
  'search' => array(),
308
  'filenum' => absint( $_POST['filenum'] )
309
  );
@@ -326,6 +404,7 @@ class String_Locator
326
  if ( ! isset( $file_data[ $filenum ] ) ) {
327
  wp_send_json_error(
328
  sprintf(
 
329
  __( 'The file-number, %d, that was sent could not be found.', 'string-locator' ),
330
  $filenum
331
  )
@@ -335,7 +414,8 @@ class String_Locator
335
  if ( $this->nearing_execution_limit() ) {
336
  wp_send_json_error(
337
  sprintf(
338
- __( 'The maximum time your server allows a script to run (%d) is too low for the plugin to run as intended, at startup %d seconds have passed', 'string-locator' ),
 
339
  $this->max_execution_time,
340
  $this->nearing_execution_limit()
341
  )
@@ -344,7 +424,8 @@ class String_Locator
344
  if ( $this->nearing_memory_limit() ) {
345
  wp_send_json_error(
346
  sprintf(
347
- __( 'The memory limit is about to be exceeded before the search has started, this could be an early indicator that your site may soon struggle as well, unfortunately this means the plugin is unable to perform any searches. Current memory consumption: %d of %d bytes', 'string-locator' ),
 
348
  $this->nearing_memory_limit(),
349
  $this->max_memory_consumption
350
  )
@@ -367,9 +448,10 @@ class String_Locator
367
  }
368
  }
369
 
370
- while ( ! $this->nearing_execution_limit() && ! $this->nearing_memory_limit() && isset( $file_data[ $filenum ]) ) {
371
- $filenum = absint( $_POST['filenum'] );
372
- $next_file = $filenum + 1;
 
373
 
374
  $next_chunk = ( ceil( ( $next_file ) / $files_per_chunk ) - 1 );
375
  $chunk = ( ceil( $filenum / $files_per_chunk ) - 1 );
@@ -393,21 +475,21 @@ class String_Locator
393
  * Check the file type, if it's an unsupported type, we skip it
394
  */
395
  $file_type = explode( '.', $file_name );
396
- $file_type = end( $file_type );
397
-
398
- if ( in_array( $file_type, $this->bad_file_types ) ) {
399
- continue;
400
- }
401
 
402
  /*
403
- * Scan the file and look for our string
404
  */
405
- $search_results = $this->scan_file( $file_data[ $filenum ], $scan_data->search, $file_data[ $filenum ], $scan_data->scan_path->type, '', $is_regex );
 
 
406
 
407
  $response['last_file'] = $file_data[ $filenum ];
408
  $response['filenum'] = $filenum;
409
  $response['filename'] = $file_name;
410
- $response['search'][] = $search_results;
 
 
411
 
412
  if ( $next_chunk != $chunk ) {
413
  $file_data = unserialize( get_option( 'string-locator-search-files-' . $next_chunk ) );
@@ -421,22 +503,24 @@ class String_Locator
421
  update_option( 'string-locator-search-history', serialize( $history ), false );
422
  }
423
 
424
- $_POST['filenum']++;
425
  }
426
 
427
  wp_send_json_success( $response );
428
  }
429
 
430
  /**
431
- * Clean up our options used to help during the search
432
  *
433
- * @return object
434
  */
435
  function ajax_clean_search() {
436
- if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) { wp_send_json_error( __( 'Authentication failed', 'string-locator' ) ); }
 
 
437
 
438
  $scan_data = unserialize( get_option( 'string-locator-search-overview' ) );
439
- for( $i = 0; $i < $scan_data->chunks; $i++ ) {
440
  delete_option( 'string-locator-search-files-' . $i );
441
  }
442
 
@@ -444,9 +528,9 @@ class String_Locator
444
  }
445
 
446
  /**
447
- * Create a table row for insertion into the search results list
448
  *
449
- * @param array|object $item The table row item
450
  *
451
  * @return string
452
  */
@@ -469,10 +553,10 @@ class String_Locator
469
  }
470
 
471
  /**
472
- * Create a full table populated with the supplied items
473
  *
474
- * @param array $items An array of table rows
475
- * @param array $table_class An array of items to append to the table class along with the defaults
476
  *
477
  * @return string
478
  */
@@ -497,7 +581,7 @@ class String_Locator
497
  );
498
 
499
  $table_rows = array();
500
- foreach( $items AS $item ) {
501
  $table_rows[] = self::prepare_table_row( $item );
502
  }
503
 
@@ -513,10 +597,10 @@ class String_Locator
513
  }
514
 
515
  /**
516
- * Create an admin edit link for the supplied path
517
  *
518
- * @param string $path
519
- * @param int $line
520
  *
521
  * @return string
522
  */
@@ -525,8 +609,8 @@ class String_Locator
525
  $file_slug = '';
526
  $content_path = str_replace( '\\', '/', WP_CONTENT_DIR );
527
 
528
- $path = str_replace( '\\', '/', $path );
529
- $paths = explode( '/', $path );
530
 
531
  $url_args = array(
532
  'page=string-locator',
@@ -535,11 +619,11 @@ class String_Locator
535
 
536
  switch ( true ) {
537
  case ( in_array( 'wp-content', $paths ) && in_array( 'plugins', $paths ) ) :
538
- $file_type = 'plugin';
539
  $content_path .= '/plugins/';
540
  break;
541
  case ( in_array( 'wp-content', $paths ) && in_array( 'themes', $paths ) ) :
542
- $file_type = 'theme';
543
  $content_path .= '/themes/';
544
  break;
545
  }
@@ -562,9 +646,9 @@ class String_Locator
562
  }
563
 
564
  /**
565
- * Parse the search option to determine what kind of search we are performing and what directory to start in
566
  *
567
- * @param $option
568
  *
569
  * @return bool|object
570
  */
@@ -589,6 +673,10 @@ class String_Locator
589
  $data['path'] = WP_CONTENT_DIR . '/plugins/';
590
  $data['type'] = 'plugin';
591
  break;
 
 
 
 
592
  case ( strlen( $option ) > 3 && 'p-' == substr( $option, 0, 2 ) ):
593
  $slug = explode( '/', substr( $option, 2 ) );
594
 
@@ -614,9 +702,10 @@ class String_Locator
614
  }
615
 
616
  /**
617
- * Check if a file path is valid for editing
 
 
618
  *
619
- * @param string $path Path to file
620
  * @return bool
621
  */
622
  function is_valid_location( $path ) {
@@ -638,7 +727,9 @@ class String_Locator
638
  }
639
 
640
  /**
641
- * Set the text domain for translated plugin content
 
 
642
  */
643
  function load_i18n() {
644
  $i18n_dir = 'string-locator/languages/';
@@ -646,7 +737,9 @@ class String_Locator
646
  }
647
 
648
  /**
649
- * Load up JavaScript and CSS for our plugin on the appropriate admin pages
 
 
650
  */
651
  function admin_enqueue_scripts( $hook ) {
652
  // Break out early if we are not on a String Locator page
@@ -664,10 +757,10 @@ class String_Locator
664
  */
665
  wp_register_script( 'string-locator-search', plugin_dir_url( __FILE__ ) . '/resources/js/string-locator-search.js', array( 'jquery' ), $this->version );
666
 
667
- if ( isset( $_GET['edit-file'] )) {
668
  $filename = explode( '.', $_GET['edit-file'] );
669
- $filext = end( $filename );
670
- switch( $filext ) {
671
  case 'js':
672
  $this->string_locator_language = 'javascript';
673
  break;
@@ -742,6 +835,7 @@ class String_Locator
742
  wp_localize_script( 'string-locator-search', 'string_locator', array(
743
  'ajax_url' => admin_url( 'admin-ajax.php' ),
744
  'search_nonce' => wp_create_nonce( 'string-locator-search' ),
 
745
  'saving_results_string' => __( 'Saving search results&hellip;', 'string-locator' ),
746
  'search_preparing' => __( 'Preparing search&hellip;', 'string-locator' ),
747
  'search_started' => __( 'Preparations completed, search started&hellip;', 'string-locator' ),
@@ -752,10 +846,11 @@ class String_Locator
752
  }
753
 
754
  /**
755
- * Add our plugin to the 'Tools' menu
 
 
756
  */
757
- function populate_menu()
758
- {
759
  if ( is_multisite() ) {
760
  return;
761
  }
@@ -769,22 +864,27 @@ class String_Locator
769
  add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function );
770
  }
771
 
772
- function populate_network_menu()
773
- {
774
- $page_title = __( 'String Locator', 'string-locator' );
775
- $menu_title = __( 'String Locator', 'string-locator' );
776
- $capability = 'edit_themes';
777
- $menu_slug = 'string-locator';
778
- $function = array( $this, 'options_page' );
 
 
 
 
779
 
780
  add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, 'dashicons-edit' );
781
  }
782
 
783
  /**
784
- * Function for including the actual plugin Admin UI page
 
 
785
  */
786
- function options_page()
787
- {
788
  /**
789
  * Don't load anything if the user can't edit themes any way
790
  */
@@ -800,27 +900,29 @@ class String_Locator
800
  */
801
  if ( isset( $_GET['string-locator-path'] ) && $this->is_valid_location( $_GET['string-locator-path'] ) ) {
802
  include_once( dirname( __FILE__ ) . '/editor.php' );
803
- }
804
- else {
805
  include_once( dirname( __FILE__ ) . '/options.php' );
806
  }
807
  }
808
 
809
  /**
810
- * @param string $start Start delimited
811
- * @param string $end End delimiter
812
- * @param string $string The string to scan
 
 
 
813
  * @return array
814
  */
815
  function SmartScan( $start, $end, $string ) {
816
  $opened = array();
817
 
818
  $lines = explode( "\n", $string );
819
- for ( $i = 0; $i < count( $lines ); $i++ ) {
820
- if ( stristr( $lines[$i], $start ) ) {
821
  $opened[] = $i;
822
  }
823
- if ( stristr( $lines[$i], $end ) ) {
824
  array_pop( $opened );
825
  }
826
  }
@@ -829,25 +931,29 @@ class String_Locator
829
  }
830
 
831
  /**
832
- * Handler for storing the content of the code editor
833
- * Also runs over the Smart-Scan if enabled
 
 
 
834
  */
835
  function editor_save() {
836
  if ( isset( $_POST['string-locator-editor-content'] ) && check_admin_referer( 'string-locator-edit_' . $_GET['edit-file'] ) && current_user_can( 'edit_themes' ) ) {
837
 
838
  if ( $this->is_valid_location( $_GET['string-locator-path'] ) ) {
839
- $path = urldecode( $_GET['string-locator-path'] );
840
  $content = stripslashes( $_POST['string-locator-editor-content'] );
841
 
842
  /**
843
  * Send an error notice if the file isn't writable
844
  */
845
  if ( ! is_writeable( $path ) ) {
846
- $this->notice[] = array(
847
  'type' => 'error',
848
  'message' => __( 'The file could not be written to, please check file permissions or edit it manually.', 'string-locator' )
849
  );
850
  $this->failed_edit = true;
 
851
  return;
852
  }
853
 
@@ -855,37 +961,37 @@ class String_Locator
855
  * If enabled, run the Smart-Scan on the content before saving it
856
  */
857
  if ( isset( $_POST['string-locator-smart-edit'] ) ) {
858
- $open_brace = substr_count( $content, '{' );
859
  $close_brace = substr_count( $content, '}' );
860
  if ( $open_brace != $close_brace ) {
861
  $this->failed_edit = true;
862
 
863
  $opened = $this->SmartScan( '{', '}', $content );
864
 
865
- foreach( $opened AS $line ) {
866
  $this->notice[] = array(
867
  'type' => 'error',
868
  'message' => sprintf(
869
  __( 'There is an inconsistency in the opening and closing braces, { and }, of your file on line %s', 'string-locator' ),
870
- '<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ). '">' . ( $line + 1 ) . '</a>'
871
  )
872
  );
873
  }
874
  }
875
 
876
- $open_bracket = substr_count( $content, '[' );
877
  $close_bracket = substr_count( $content, ']' );
878
  if ( $open_bracket != $close_bracket ) {
879
  $this->failed_edit = true;
880
 
881
  $opened = $this->SmartScan( '[', ']', $content );
882
 
883
- foreach( $opened AS $line ) {
884
  $this->notice[] = array(
885
  'type' => 'error',
886
  'message' => sprintf(
887
  __( 'There is an inconsistency in the opening and closing braces, [ and ], of your file on line %s', 'string-locator' ),
888
- '<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ). '">' . ( $line + 1 ) . '</a>'
889
  )
890
  );
891
  }
@@ -898,12 +1004,12 @@ class String_Locator
898
 
899
  $opened = $this->SmartScan( '(', ')', $content );
900
 
901
- foreach( $opened AS $line ) {
902
  $this->notice[] = array(
903
  'type' => 'error',
904
  'message' => sprintf(
905
  __( 'There is an inconsistency in the opening and closing braces, ( and ), of your file on line %s', 'string-locator' ),
906
- '<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ). '">' . ( $line + 1 ) . '</a>'
907
  )
908
  );
909
  }
@@ -939,8 +1045,7 @@ class String_Locator
939
  'type' => 'error',
940
  'message' => __( 'A 500 server error was detected on your site after updating your file. We have restored the previous version of the file for you.', 'string-locator' )
941
  );
942
- }
943
- else {
944
  $this->notice[] = array(
945
  'type' => 'updated',
946
  'message' => __( 'The file has been saved', 'string-locator' )
@@ -950,6 +1055,13 @@ class String_Locator
950
  }
951
  }
952
 
 
 
 
 
 
 
 
953
  private function create_child_theme( $theme ) {
954
  $child_theme = sprintf( '%s/%s-child', get_theme_root(), $theme );
955
  mkdir( $child_theme );
@@ -961,11 +1073,13 @@ class String_Locator
961
  }
962
 
963
  /**
964
- * When editing a file, this is where we write all the new content
965
- * We will break early if the user isn't allowed to edit files
 
 
 
966
  *
967
- * @param string $path - The path to the file
968
- * @param string $content - The content to write to the file
969
  */
970
  private function write_file( $path, $content ) {
971
  if ( ! current_user_can( 'edit_themes' ) ) {
@@ -973,7 +1087,7 @@ class String_Locator
973
  }
974
 
975
  if ( apply_filters( 'string-locator-filter-closing-php-tags', true ) ) {
976
- $content = preg_replace( "/\?>$/si", '', trim( $content ), -1, $replaced_strings );
977
 
978
  if ( $replaced_strings >= 1 ) {
979
  $this->notice[] = array(
@@ -987,7 +1101,7 @@ class String_Locator
987
  $lines = explode( "\n", str_replace( array( "\r\n", "\r" ), "\n", $content ) );
988
  $total_lines = count( $lines );
989
 
990
- for( $i = 0; $i < $total_lines; $i++ ) {
991
  $write_line = $lines[ $i ];
992
 
993
  if ( ( $i + 1 ) < $total_lines ) {
@@ -1001,11 +1115,13 @@ class String_Locator
1001
  }
1002
 
1003
  /**
1004
- * Hook the admin notices and loop over any notices we've registered in the plugin
 
 
1005
  */
1006
  function admin_notice() {
1007
  if ( ! empty( $this->notice ) ) {
1008
- foreach( $this->notice AS $note ) {
1009
  printf(
1010
  '<div class="%s"><p>%s</p></div>',
1011
  esc_attr( $note['type'] ),
@@ -1016,14 +1132,14 @@ class String_Locator
1016
  }
1017
 
1018
  /**
1019
- * Scan through an individual file to look for occurrences of £string
1020
  *
1021
- * @param string $filename The path to the file
1022
- * @param string $string The search string
1023
- * @param mixed $location The file location object/string
1024
- * @param string $type File type
1025
- * @param string $slug The plugin/theme slug of the file
1026
- * @param boolean $regex Should a regex search be performed
1027
  *
1028
  * @return mixed
1029
  */
@@ -1031,16 +1147,15 @@ class String_Locator
1031
  if ( empty( $string ) || ! is_file( $filename ) ) {
1032
  return false;
1033
  }
1034
- $output = array();
1035
- $linenum = 0;
1036
  $match_count = 0;
1037
 
1038
  if ( ! is_object( $location ) ) {
1039
- $path = $location;
1040
  $location = explode( DIRECTORY_SEPARATOR, $location );
1041
- $file = end( $location );
1042
- }
1043
- else {
1044
  $path = $location->getPathname();
1045
  $file = $location->getFilename();
1046
  }
@@ -1049,8 +1164,12 @@ class String_Locator
1049
  * Check if the filename matches our search pattern
1050
  */
1051
  if ( stristr( $file, $string ) || ( $regex && preg_match( $string, $file ) ) ) {
1052
- $relativepath = str_replace( array( ABSPATH, '\\', '/' ), array( '', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR ), $path );
1053
- $match_count++;
 
 
 
 
1054
 
1055
  $editurl = $this->create_edit_link( $path, $linenum );
1056
 
@@ -1075,31 +1194,32 @@ class String_Locator
1075
  }
1076
 
1077
  $readfile = @fopen( $filename, "r" );
1078
- if ( $readfile )
1079
- {
1080
- while ( ( $readline = fgets( $readfile ) ) !== false )
1081
- {
1082
  $string_preview_is_cut = false;
1083
- $linenum++;
1084
  /**
1085
  * If our string is found in this line, output the line number and other data
1086
  */
1087
- if ( ( ! $regex && stristr( $readline, $string ) ) || ( $regex && preg_match( $string, $readline ) ) )
1088
- {
1089
  /**
1090
  * Prepare the visual path for the end user
1091
  * Removes path leading up to WordPress root and ensures consistent directory separators
1092
  */
1093
- $relativepath = str_replace( array( ABSPATH, '\\', '/' ), array( '', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR ), $path );
1094
- $match_count++;
 
 
 
 
1095
 
1096
  /**
1097
  * Create the URL to take the user to the editor
1098
  */
1099
  $editurl = $this->create_edit_link( $path, $linenum );
1100
 
1101
- $string_preview = $readline;
1102
- if ( strlen( $string_preview ) > 100 ) {
1103
  $string_location = strpos( $string_preview, $string );
1104
 
1105
  $string_location_start = $string_location - $this->excerpt_length;
@@ -1107,19 +1227,18 @@ class String_Locator
1107
  $string_location_start = 0;
1108
  }
1109
 
1110
- $string_location_end = $string_location + strlen( $string ) + $this->excerpt_length;
1111
  if ( $string_location_end > strlen( $string_preview ) ) {
1112
  $string_location_end = strlen( $string_preview );
1113
  }
1114
 
1115
- $string_preview = substr( $string_preview, $string_location_start, $string_location_end );
1116
  $string_preview_is_cut = true;
1117
  }
1118
 
1119
  if ( $regex ) {
1120
  $string_preview = preg_replace( preg_replace( '/\/(.+)\//', '/($1)/', $string ), '<strong>$1</strong>', esc_html( $string_preview ) );
1121
- }
1122
- else {
1123
  $string_preview = str_ireplace( $string, '<strong>' . $string . '</strong>', esc_html( $string_preview ) );
1124
  }
1125
  if ( $string_preview_is_cut ) {
@@ -1148,8 +1267,7 @@ class String_Locator
1148
  }
1149
 
1150
  fclose( $readfile );
1151
- }
1152
- else {
1153
  /**
1154
  * The file was unreadable, give the user a friendly notification
1155
  */
3
  * Plugin Name: String Locator
4
  * Plugin URI: http://www.clorith.net/wordpress-string-locator/
5
  * Description: Scan through theme and plugin files looking for text strings
6
+ * Version: 2.2.0
7
  * Author: Clorith
8
  * Author URI: http://www.clorith.net
9
  * Text Domain: string-locator
28
  /**
29
  * Class String_Locator
30
  */
31
+ class String_Locator {
 
32
  /**
33
+ * @var string $string_locator_language The code language used for the editing page.
34
+ * @var string $version String Locator version number.
35
+ * @var array $notice An array containing all notices to display.
36
+ * @var bool $failed_edit Has there been a failed edit.
37
+ * @var string $plugin_url The URL to the plugins directory.
38
+ * @var string $path_to_use The path to the currently editable file.
39
+ * @var array $bad_http_codes An array of HTTP status codes that will trigger the rollback feature.
40
+ * @var array $bad_file_types An array of file extensions that will be ignored by the scanner.
41
+ * @var int $excerpt_length The length of the excerpt from the line containing a match.
42
+ * @var int|null $max_execution_time The server-configured max time a script can run.
43
+ * @var int $start_execution_time The current time when our script started executing.
44
+ * @var int $max_memory_consumption The server-configured max amount of memory a script can use.
45
  */
46
+ public $string_locator_language = '';
47
+ public $version = '2.2.0';
48
+ public $notice = array();
49
+ public $failed_edit = false;
50
+ private $plugin_url = '';
51
+ private $path_to_use = '';
52
+ private $bad_http_codes = array( '500' );
53
+ private $bad_file_types = array( 'rar', '7z', 'zip', 'tar', 'gz', 'jpg', 'jpeg', 'png', 'gif', 'mp3', 'mp4', 'avi', 'wmv' );
54
+ private $excerpt_length = 25;
55
+ private $max_execution_time = null;
56
+ private $start_execution_timer = 0;
57
+ private $max_memory_consumption = 0;
58
 
59
  /**
60
  * Construct the plugin
61
  */
62
+ function __construct() {
63
+ $this->init();
64
+ }
65
+
66
+ /**
67
+ * The plugin initialization, ready as a stand alone function so it can be instantiated in other
68
+ * scenarios as well.
69
+ *
70
+ * @since 2.2.0
71
+ *
72
+ * @return void
73
+ */
74
+ public function init() {
75
  /**
76
  * Define class variables requiring expressions
77
  */
79
  $this->path_to_use = ( is_multisite() ? 'network/admin.php' : 'tools.php' );
80
  $this->excerpt_length = apply_filters( 'string_locator_excerpt_length', 25 );
81
 
82
+ $this->max_execution_time = absint( ini_get( 'max_execution_time' ) );
83
  $this->start_execution_timer = microtime( true );
84
 
85
+ if ( $this->max_execution_time > 30 ) {
86
+ $this->max_execution_time = 30;
87
+ }
88
+
89
  $this->set_memory_limit();
90
 
91
  add_action( 'admin_menu', array( $this, 'populate_menu' ) );
106
  }
107
 
108
  /**
109
+ * Sets up the memory limit variables.
110
  *
111
  * @since 2.0.0
112
  *
129
  }
130
 
131
  /**
132
+ * Add a donation link to the plugins page.
133
+ *
134
+ * @param array $meta An array of meta links for this plugin.
135
+ * @param string $plugin_file The main plugin file name, used to identify our own plugin.
136
  *
137
  * @return array
138
  */
148
  }
149
 
150
  /**
151
+ * Create a set of drop-down options for picking one of the available themes.
152
  *
153
+ * @param string $current The current selection option to match against.
154
  *
155
  * @return string
156
  */
164
 
165
  $string_locate_themes = wp_get_themes();
166
 
167
+ foreach ( $string_locate_themes AS $string_locate_theme_slug => $string_locate_theme ) {
168
  $string_locate_theme_data = wp_get_theme( $string_locate_theme_slug );
169
+ $string_locate_value = 't-' . $string_locate_theme_slug;
170
 
171
  $options .= sprintf(
172
  '<option value="%s" %s>%s</option>',
180
  }
181
 
182
  /**
183
+ * Create a set of drop-down options for picking one of the available plugins.
184
  *
185
+ * @param string $current The current selection option to match against.
186
  *
187
  * @return string
188
  */
196
 
197
  $string_locate_plugins = get_plugins();
198
 
199
+ foreach ( $string_locate_plugins AS $string_locate_plugin_path => $string_locate_plugin ) {
 
200
  $string_locate_value = 'p-' . $string_locate_plugin_path;
201
 
202
  $options .= sprintf(
211
  }
212
 
213
  /**
214
+ * Create a set of drop-down options for picking one of the available must-use plugins.
215
+ *
216
+ * @param string $current The current selection option to match against.
217
+ *
218
+ * @return string
219
+ */
220
+ public static function get_mu_plugins_options( $current = null ) {
221
+ $options = sprintf(
222
+ '<option value="%s" %s>&mdash; %s &mdash;</option>',
223
+ 'mup--',
224
+ ( 'mup--' == $current ? 'selected="selected"' : '' ),
225
+ esc_html__( 'All must-use plugins', 'string-locator' )
226
+ );
227
+
228
+ $string_locate_plugins = get_mu_plugins();
229
+
230
+ foreach ( $string_locate_plugins AS $string_locate_plugin_path => $string_locate_plugin ) {
231
+ $string_locate_value = 'mup-' . $string_locate_plugin_path;
232
+
233
+ $options .= sprintf(
234
+ '<option value="%s" %s>%s</option>',
235
+ $string_locate_value,
236
+ ( $current == $string_locate_value ? 'selected="selected"' : '' ),
237
+ $string_locate_plugin['Name']
238
+ );
239
+ }
240
+
241
+ return $options;
242
+ }
243
+
244
+ /**
245
+ * Check if there are Must-Use plugins available on this WordPress install.
246
+ *
247
+ * @since 2.2.0
248
+ *
249
+ * @return bool
250
+ */
251
+ public static function has_mu_plugins() {
252
+ $mu_plugin_count = get_mu_plugins();
253
+
254
+ if ( count( $mu_plugin_count ) >= 1 ) {
255
+ return true;
256
+ }
257
+
258
+ return false;
259
+ }
260
+
261
+ /**
262
+ * Handles the AJAX request to prepare the search hierarchy.
263
  *
264
+ * @return void
265
  */
266
  function ajax_get_directory_structure() {
267
+ if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) {
268
+ wp_send_json_error( __( 'Authentication failed', 'string-locator' ) );
269
+ }
270
 
271
  $scan_path = $this->prepare_scan_path( $_POST['directory'] );
272
  if ( is_file( $scan_path->path ) ) {
273
  $files = array( $scan_path->path );
274
+ } else {
 
275
  $files = $this->ajax_scan_path( $scan_path->path );
276
  }
277
 
300
  update_option( 'string-locator-search-overview', serialize( $store ), true );
301
  update_option( 'string-locator-search-history', serialize( array() ) );
302
 
303
+ foreach ( $file_chunks AS $count => $file_chunk ) {
304
  update_option( 'string-locator-search-files-' . $count, serialize( $file_chunk ) );
305
  }
306
 
315
  * @return bool
316
  */
317
  function nearing_execution_limit() {
318
+ // Max execution time is 0 or -1 (infinite) in server config
319
+ if ( 0 === $this->max_execution_time || - 1 === $this->max_execution_time ) {
320
  return false;
321
  }
322
 
326
  if ( $execution_time >= $this->max_execution_time ) {
327
  return $execution_time;
328
  }
329
+
330
  return false;
331
  }
332
 
338
  * @return bool
339
  */
340
  function nearing_memory_limit() {
341
+ // Check if the memory limit is set t o0 or -1 (infinite) in server config
342
+ if ( 0 === $this->max_memory_consumption || - 1 === $this->max_memory_consumption ) {
343
+ return false;
344
+ }
345
+
346
  // We give our selves a 256k memory buffer, as we need to close off the script properly as well
347
  $built_in_buffer = apply_filters( 'string-locator-extra-memory-buffer', 256000 );
348
+ $memory_use = ( memory_get_usage( true ) + $built_in_buffer );
349
 
350
  if ( $memory_use >= $this->max_memory_consumption ) {
351
  return $memory_use;
352
  }
353
+
354
  return false;
355
  }
356
 
357
  public static function absbool( $value ) {
358
  if ( is_bool( $value ) ) {
359
  $bool = $value;
360
+ } else {
 
361
  if ( 'false' == $value ) {
362
  $bool = false;
363
+ } else {
 
364
  $bool = true;
365
  }
366
  }
369
  }
370
 
371
  /**
372
+ * Search an individual file supplied via AJAX.
373
  *
374
  * @since 1.9.0
375
  *
376
+ * @return void
377
  */
378
  function ajax_file_search() {
379
+ if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) {
380
+ wp_send_json_error( __( 'Authentication failed', 'string-locator' ) );
381
+ }
382
 
383
  $files_per_chunk = apply_filters( 'string-locator-files-per-array', 500 );
384
+ $response = array(
385
  'search' => array(),
386
  'filenum' => absint( $_POST['filenum'] )
387
  );
404
  if ( ! isset( $file_data[ $filenum ] ) ) {
405
  wp_send_json_error(
406
  sprintf(
407
+ /* translators: %d: The numbered reference to a file being searched. */
408
  __( 'The file-number, %d, that was sent could not be found.', 'string-locator' ),
409
  $filenum
410
  )
414
  if ( $this->nearing_execution_limit() ) {
415
  wp_send_json_error(
416
  sprintf(
417
+ /* translators: %1$d: The time a PHP file can run, as defined by the server configuration. %2$d: The amount of time used by the PHP file so far. */
418
+ __( 'The maximum time your server allows a script to run (%1$d) is too low for the plugin to run as intended, at startup %2$d seconds have passed', 'string-locator' ),
419
  $this->max_execution_time,
420
  $this->nearing_execution_limit()
421
  )
424
  if ( $this->nearing_memory_limit() ) {
425
  wp_send_json_error(
426
  sprintf(
427
+ /* translators: %1$d: Current amount of used system memory resources. %2$d: The maximum available system memory. */
428
+ __( 'The memory limit is about to be exceeded before the search has started, this could be an early indicator that your site may soon struggle as well, unfortunately this means the plugin is unable to perform any searches. Current memory consumption: %1$d of %2$d bytes', 'string-locator' ),
429
  $this->nearing_memory_limit(),
430
  $this->max_memory_consumption
431
  )
448
  }
449
  }
450
 
451
+ while ( ! $this->nearing_execution_limit() && ! $this->nearing_memory_limit() && isset( $file_data[ $filenum ] ) ) {
452
+ $filenum = absint( $_POST['filenum'] );
453
+ $search_results = null;
454
+ $next_file = $filenum + 1;
455
 
456
  $next_chunk = ( ceil( ( $next_file ) / $files_per_chunk ) - 1 );
457
  $chunk = ( ceil( $filenum / $files_per_chunk ) - 1 );
475
  * Check the file type, if it's an unsupported type, we skip it
476
  */
477
  $file_type = explode( '.', $file_name );
478
+ $file_type = strtolower( end( $file_type ) );
 
 
 
 
479
 
480
  /*
481
+ * Scan the file and look for our string, but only if it's an approved file extension
482
  */
483
+ if ( ! in_array( $file_type, $this->bad_file_types ) ) {
484
+ $search_results = $this->scan_file( $file_data[ $filenum ], $scan_data->search, $file_data[ $filenum ], $scan_data->scan_path->type, '', $is_regex );
485
+ }
486
 
487
  $response['last_file'] = $file_data[ $filenum ];
488
  $response['filenum'] = $filenum;
489
  $response['filename'] = $file_name;
490
+ if ( $search_results ) {
491
+ $response['search'][] = $search_results;
492
+ }
493
 
494
  if ( $next_chunk != $chunk ) {
495
  $file_data = unserialize( get_option( 'string-locator-search-files-' . $next_chunk ) );
503
  update_option( 'string-locator-search-history', serialize( $history ), false );
504
  }
505
 
506
+ $_POST['filenum'] ++;
507
  }
508
 
509
  wp_send_json_success( $response );
510
  }
511
 
512
  /**
513
+ * Clean up our options used to help during the search.
514
  *
515
+ * @return void
516
  */
517
  function ajax_clean_search() {
518
+ if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) {
519
+ wp_send_json_error( __( 'Authentication failed', 'string-locator' ) );
520
+ }
521
 
522
  $scan_data = unserialize( get_option( 'string-locator-search-overview' ) );
523
+ for ( $i = 0; $i < $scan_data->chunks; $i ++ ) {
524
  delete_option( 'string-locator-search-files-' . $i );
525
  }
526
 
528
  }
529
 
530
  /**
531
+ * Create a table row for insertion into the search results list.
532
  *
533
+ * @param array|object $item The table row item.
534
  *
535
  * @return string
536
  */
553
  }
554
 
555
  /**
556
+ * Create a full table populated with the supplied items.
557
  *
558
+ * @param array $items An array of table rows.
559
+ * @param array $table_class An array of items to append to the table class along with the defaults.
560
  *
561
  * @return string
562
  */
581
  );
582
 
583
  $table_rows = array();
584
+ foreach ( $items AS $item ) {
585
  $table_rows[] = self::prepare_table_row( $item );
586
  }
587
 
597
  }
598
 
599
  /**
600
+ * Create an admin edit link for the supplied path.
601
  *
602
+ * @param string $path Path to the file we'er adding a link for.
603
+ * @param int $line The line in the file where our search result was found.
604
  *
605
  * @return string
606
  */
609
  $file_slug = '';
610
  $content_path = str_replace( '\\', '/', WP_CONTENT_DIR );
611
 
612
+ $path = str_replace( '\\', '/', $path );
613
+ $paths = explode( '/', $path );
614
 
615
  $url_args = array(
616
  'page=string-locator',
619
 
620
  switch ( true ) {
621
  case ( in_array( 'wp-content', $paths ) && in_array( 'plugins', $paths ) ) :
622
+ $file_type = 'plugin';
623
  $content_path .= '/plugins/';
624
  break;
625
  case ( in_array( 'wp-content', $paths ) && in_array( 'themes', $paths ) ) :
626
+ $file_type = 'theme';
627
  $content_path .= '/themes/';
628
  break;
629
  }
646
  }
647
 
648
  /**
649
+ * Parse the search option to determine what kind of search we are performing and what directory to start in.
650
  *
651
+ * @param string $option The search-type identifier.
652
  *
653
  * @return bool|object
654
  */
673
  $data['path'] = WP_CONTENT_DIR . '/plugins/';
674
  $data['type'] = 'plugin';
675
  break;
676
+ case ( 'mup--' == $option ):
677
+ $data['path'] = WP_CONTENT_DIR . '/mu-plugins/';
678
+ $data['type'] = 'mu-plugin';
679
+ break;
680
  case ( strlen( $option ) > 3 && 'p-' == substr( $option, 0, 2 ) ):
681
  $slug = explode( '/', substr( $option, 2 ) );
682
 
702
  }
703
 
704
  /**
705
+ * Check if a file path is valid for editing.
706
+ *
707
+ * @param string $path Path to file.
708
  *
 
709
  * @return bool
710
  */
711
  function is_valid_location( $path ) {
727
  }
728
 
729
  /**
730
+ * Set the text domain for translated plugin content.
731
+ *
732
+ * @return void
733
  */
734
  function load_i18n() {
735
  $i18n_dir = 'string-locator/languages/';
737
  }
738
 
739
  /**
740
+ * Load up JavaScript and CSS for our plugin on the appropriate admin pages.
741
+ *
742
+ * @return void
743
  */
744
  function admin_enqueue_scripts( $hook ) {
745
  // Break out early if we are not on a String Locator page
757
  */
758
  wp_register_script( 'string-locator-search', plugin_dir_url( __FILE__ ) . '/resources/js/string-locator-search.js', array( 'jquery' ), $this->version );
759
 
760
+ if ( isset( $_GET['edit-file'] ) ) {
761
  $filename = explode( '.', $_GET['edit-file'] );
762
+ $filext = end( $filename );
763
+ switch ( $filext ) {
764
  case 'js':
765
  $this->string_locator_language = 'javascript';
766
  break;
835
  wp_localize_script( 'string-locator-search', 'string_locator', array(
836
  'ajax_url' => admin_url( 'admin-ajax.php' ),
837
  'search_nonce' => wp_create_nonce( 'string-locator-search' ),
838
+ 'search_current_prefix' => __( 'Next file: ', 'string-locator' ),
839
  'saving_results_string' => __( 'Saving search results&hellip;', 'string-locator' ),
840
  'search_preparing' => __( 'Preparing search&hellip;', 'string-locator' ),
841
  'search_started' => __( 'Preparations completed, search started&hellip;', 'string-locator' ),
846
  }
847
 
848
  /**
849
+ * Add our plugin to the 'Tools' menu.
850
+ *
851
+ * @return void
852
  */
853
+ function populate_menu() {
 
854
  if ( is_multisite() ) {
855
  return;
856
  }
864
  add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function );
865
  }
866
 
867
+ /**
868
+ * Add our plugin to the main menu in the Network Admin.
869
+ *
870
+ * @return void
871
+ */
872
+ function populate_network_menu() {
873
+ $page_title = __( 'String Locator', 'string-locator' );
874
+ $menu_title = __( 'String Locator', 'string-locator' );
875
+ $capability = 'edit_themes';
876
+ $menu_slug = 'string-locator';
877
+ $function = array( $this, 'options_page' );
878
 
879
  add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, 'dashicons-edit' );
880
  }
881
 
882
  /**
883
+ * Function for including the actual plugin Admin UI page.
884
+ *
885
+ * @return mixed
886
  */
887
+ function options_page() {
 
888
  /**
889
  * Don't load anything if the user can't edit themes any way
890
  */
900
  */
901
  if ( isset( $_GET['string-locator-path'] ) && $this->is_valid_location( $_GET['string-locator-path'] ) ) {
902
  include_once( dirname( __FILE__ ) . '/editor.php' );
903
+ } else {
 
904
  include_once( dirname( __FILE__ ) . '/options.php' );
905
  }
906
  }
907
 
908
  /**
909
+ * Check for inconsistencies in brackets and similar.
910
+ *
911
+ * @param string $start Start delimited.
912
+ * @param string $end End delimiter.
913
+ * @param string $string The string to scan.
914
+ *
915
  * @return array
916
  */
917
  function SmartScan( $start, $end, $string ) {
918
  $opened = array();
919
 
920
  $lines = explode( "\n", $string );
921
+ for ( $i = 0; $i < count( $lines ); $i ++ ) {
922
+ if ( stristr( $lines[ $i ], $start ) ) {
923
  $opened[] = $i;
924
  }
925
+ if ( stristr( $lines[ $i ], $end ) ) {
926
  array_pop( $opened );
927
  }
928
  }
931
  }
932
 
933
  /**
934
+ * Handler for storing the content of the code editor.
935
+ *
936
+ * Also runs over the Smart-Scan if enabled.
937
+ *
938
+ * @return void
939
  */
940
  function editor_save() {
941
  if ( isset( $_POST['string-locator-editor-content'] ) && check_admin_referer( 'string-locator-edit_' . $_GET['edit-file'] ) && current_user_can( 'edit_themes' ) ) {
942
 
943
  if ( $this->is_valid_location( $_GET['string-locator-path'] ) ) {
944
+ $path = urldecode( $_GET['string-locator-path'] );
945
  $content = stripslashes( $_POST['string-locator-editor-content'] );
946
 
947
  /**
948
  * Send an error notice if the file isn't writable
949
  */
950
  if ( ! is_writeable( $path ) ) {
951
+ $this->notice[] = array(
952
  'type' => 'error',
953
  'message' => __( 'The file could not be written to, please check file permissions or edit it manually.', 'string-locator' )
954
  );
955
  $this->failed_edit = true;
956
+
957
  return;
958
  }
959
 
961
  * If enabled, run the Smart-Scan on the content before saving it
962
  */
963
  if ( isset( $_POST['string-locator-smart-edit'] ) ) {
964
+ $open_brace = substr_count( $content, '{' );
965
  $close_brace = substr_count( $content, '}' );
966
  if ( $open_brace != $close_brace ) {
967
  $this->failed_edit = true;
968
 
969
  $opened = $this->SmartScan( '{', '}', $content );
970
 
971
+ foreach ( $opened AS $line ) {
972
  $this->notice[] = array(
973
  'type' => 'error',
974
  'message' => sprintf(
975
  __( 'There is an inconsistency in the opening and closing braces, { and }, of your file on line %s', 'string-locator' ),
976
+ '<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
977
  )
978
  );
979
  }
980
  }
981
 
982
+ $open_bracket = substr_count( $content, '[' );
983
  $close_bracket = substr_count( $content, ']' );
984
  if ( $open_bracket != $close_bracket ) {
985
  $this->failed_edit = true;
986
 
987
  $opened = $this->SmartScan( '[', ']', $content );
988
 
989
+ foreach ( $opened AS $line ) {
990
  $this->notice[] = array(
991
  'type' => 'error',
992
  'message' => sprintf(
993
  __( 'There is an inconsistency in the opening and closing braces, [ and ], of your file on line %s', 'string-locator' ),
994
+ '<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
995
  )
996
  );
997
  }
1004
 
1005
  $opened = $this->SmartScan( '(', ')', $content );
1006
 
1007
+ foreach ( $opened AS $line ) {
1008
  $this->notice[] = array(
1009
  'type' => 'error',
1010
  'message' => sprintf(
1011
  __( 'There is an inconsistency in the opening and closing braces, ( and ), of your file on line %s', 'string-locator' ),
1012
+ '<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
1013
  )
1014
  );
1015
  }
1045
  'type' => 'error',
1046
  'message' => __( 'A 500 server error was detected on your site after updating your file. We have restored the previous version of the file for you.', 'string-locator' )
1047
  );
1048
+ } else {
 
1049
  $this->notice[] = array(
1050
  'type' => 'updated',
1051
  'message' => __( 'The file has been saved', 'string-locator' )
1055
  }
1056
  }
1057
 
1058
+ /**
1059
+ * GCreate a child theme for our edits, instead of overwriting the original files.
1060
+ *
1061
+ * @param string $theme Slug of the theme being edited.
1062
+ *
1063
+ * @return string
1064
+ */
1065
  private function create_child_theme( $theme ) {
1066
  $child_theme = sprintf( '%s/%s-child', get_theme_root(), $theme );
1067
  mkdir( $child_theme );
1073
  }
1074
 
1075
  /**
1076
+ * When editing a file, this is where we write all the new content.
1077
+ * We will break early if the user isn't allowed to edit files.
1078
+ *
1079
+ * @param string $path The path to the file.
1080
+ * @param string $content The content to write to the file.
1081
  *
1082
+ * @return void
 
1083
  */
1084
  private function write_file( $path, $content ) {
1085
  if ( ! current_user_can( 'edit_themes' ) ) {
1087
  }
1088
 
1089
  if ( apply_filters( 'string-locator-filter-closing-php-tags', true ) ) {
1090
+ $content = preg_replace( "/\?>$/si", '', trim( $content ), - 1, $replaced_strings );
1091
 
1092
  if ( $replaced_strings >= 1 ) {
1093
  $this->notice[] = array(
1101
  $lines = explode( "\n", str_replace( array( "\r\n", "\r" ), "\n", $content ) );
1102
  $total_lines = count( $lines );
1103
 
1104
+ for ( $i = 0; $i < $total_lines; $i ++ ) {
1105
  $write_line = $lines[ $i ];
1106
 
1107
  if ( ( $i + 1 ) < $total_lines ) {
1115
  }
1116
 
1117
  /**
1118
+ * Hook the admin notices and loop over any notices we've registered in the plugin.
1119
+ *
1120
+ * @return void
1121
  */
1122
  function admin_notice() {
1123
  if ( ! empty( $this->notice ) ) {
1124
+ foreach ( $this->notice AS $note ) {
1125
  printf(
1126
  '<div class="%s"><p>%s</p></div>',
1127
  esc_attr( $note['type'] ),
1132
  }
1133
 
1134
  /**
1135
+ * Scan through an individual file to look for occurrences of £string.
1136
  *
1137
+ * @param string $filename The path to the file.
1138
+ * @param string $string The search string.
1139
+ * @param mixed $location The file location object/string.
1140
+ * @param string $type File type.
1141
+ * @param string $slug The plugin/theme slug of the file.
1142
+ * @param boolean $regex Should a regex search be performed.
1143
  *
1144
  * @return mixed
1145
  */
1147
  if ( empty( $string ) || ! is_file( $filename ) ) {
1148
  return false;
1149
  }
1150
+ $output = array();
1151
+ $linenum = 0;
1152
  $match_count = 0;
1153
 
1154
  if ( ! is_object( $location ) ) {
1155
+ $path = $location;
1156
  $location = explode( DIRECTORY_SEPARATOR, $location );
1157
+ $file = end( $location );
1158
+ } else {
 
1159
  $path = $location->getPathname();
1160
  $file = $location->getFilename();
1161
  }
1164
  * Check if the filename matches our search pattern
1165
  */
1166
  if ( stristr( $file, $string ) || ( $regex && preg_match( $string, $file ) ) ) {
1167
+ $relativepath = str_replace( array( ABSPATH, '\\', '/' ), array(
1168
+ '',
1169
+ DIRECTORY_SEPARATOR,
1170
+ DIRECTORY_SEPARATOR
1171
+ ), $path );
1172
+ $match_count ++;
1173
 
1174
  $editurl = $this->create_edit_link( $path, $linenum );
1175
 
1194
  }
1195
 
1196
  $readfile = @fopen( $filename, "r" );
1197
+ if ( $readfile ) {
1198
+ while ( ( $readline = fgets( $readfile ) ) !== false ) {
 
 
1199
  $string_preview_is_cut = false;
1200
+ $linenum ++;
1201
  /**
1202
  * If our string is found in this line, output the line number and other data
1203
  */
1204
+ if ( ( ! $regex && stristr( $readline, $string ) ) || ( $regex && preg_match( $string, $readline ) ) ) {
 
1205
  /**
1206
  * Prepare the visual path for the end user
1207
  * Removes path leading up to WordPress root and ensures consistent directory separators
1208
  */
1209
+ $relativepath = str_replace( array( ABSPATH, '\\', '/' ), array(
1210
+ '',
1211
+ DIRECTORY_SEPARATOR,
1212
+ DIRECTORY_SEPARATOR
1213
+ ), $path );
1214
+ $match_count ++;
1215
 
1216
  /**
1217
  * Create the URL to take the user to the editor
1218
  */
1219
  $editurl = $this->create_edit_link( $path, $linenum );
1220
 
1221
+ $string_preview = $readline;
1222
+ if ( strlen( $string_preview ) > ( strlen( $string ) + $this->excerpt_length ) ) {
1223
  $string_location = strpos( $string_preview, $string );
1224
 
1225
  $string_location_start = $string_location - $this->excerpt_length;
1227
  $string_location_start = 0;
1228
  }
1229
 
1230
+ $string_location_end = ( strlen( $string ) + ( $this->excerpt_length * 2 ) );
1231
  if ( $string_location_end > strlen( $string_preview ) ) {
1232
  $string_location_end = strlen( $string_preview );
1233
  }
1234
 
1235
+ $string_preview = substr( $string_preview, $string_location_start, $string_location_end );
1236
  $string_preview_is_cut = true;
1237
  }
1238
 
1239
  if ( $regex ) {
1240
  $string_preview = preg_replace( preg_replace( '/\/(.+)\//', '/($1)/', $string ), '<strong>$1</strong>', esc_html( $string_preview ) );
1241
+ } else {
 
1242
  $string_preview = str_ireplace( $string, '<strong>' . $string . '</strong>', esc_html( $string_preview ) );
1243
  }
1244
  if ( $string_preview_is_cut ) {
1267
  }
1268
 
1269
  fclose( $readfile );
1270
+ } else {
 
1271
  /**
1272
  * The file was unreadable, give the user a friendly notification
1273
  */
uninstall.php CHANGED
@@ -1,6 +1,11 @@
1
  <?php
2
  //if uninstall not called from WordPress exit
3
- if ( !defined( 'WP_UNINSTALL_PLUGIN' ) )
4
  exit();
 
5
 
6
- delete_option( 'string-locator-results' );
 
 
 
 
1
  <?php
2
  //if uninstall not called from WordPress exit
3
+ if ( !defined( 'WP_UNINSTALL_PLUGIN' ) ) {
4
  exit();
5
+ }
6
 
7
+ global $wpdb;
8
+ $options = $wpdb->get_results( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE 'string-locator-%'" );
9
+ foreach( $options AS $option ) {
10
+ delete_option( $option->option_name );
11
+ }