Version Description
(2022-02-27) = * Fixed a bug where content would have slashes stripped unexpectedly. * Improved table spacing on search results. * Improved loopback checks to also check admin access. * Hardened the search iterator so users can't accidentally perform unexpected directory traversal. * Introduced actions and filters in various places to enable extenders, and future enhancements. * Moved all ajax requests to dedicated REST endpoints. * Refactored file structure.
Download this release
Release Info
Developer | Clorith |
Plugin | String locator |
Version | 2.5.0 |
Comparing to | |
See all releases |
Code changes from version 2.4.2 to 2.5.0
- build/string-locator-search.asset.php +1 -0
- build/string-locator-search.js +1 -0
- build/string-locator.asset.php +1 -0
- build/string-locator.css +1 -0
- build/string-locator.js +1 -0
- changelog.txt +5 -0
- includes/REST/class-base.php +17 -0
- includes/REST/class-clean.php +36 -0
- includes/REST/class-directory-structure.php +53 -0
- includes/REST/class-save.php +64 -0
- includes/REST/class-search.php +43 -0
- includes/Tests/class-loopback.php +182 -0
- includes/Tests/class-smart-scan.php +202 -0
- includes/class-directory-iterator.php +166 -0
- includes/class-save.php +163 -0
- includes/class-search.php +520 -0
- includes/class-string-locator.php +175 -955
- readme.txt +10 -6
- resources/css/string-locator.css +0 -2
- resources/js/string-locator-search.js +0 -1
- resources/js/string-locator.js +0 -1
- string-locator.php +26 -3
- editor.php → views/editor.php +32 -34
- search.php → views/search.php +10 -15
build/string-locator-search.asset.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php return array('dependencies' => array(), 'version' => 'c872ff59b6ee90fa0f7b500730f6afd8');
|
build/string-locator-search.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
jQuery(document).ready((function(t){let r=!1;const o=wp.template("string-locator-search-result");function s(r,o,s){t(".notices").append('<div class="notice notice-'+s+' is-dismissible"><p><strong>'+r+"</strong><br />"+o+"</p></div>")}function a(o,a){r=!1,t(".string-locator-feedback").hide(),s(o,a,"error")}function e(n,c){if(c>=n||!r)return t("#string-locator-feedback-text").html(string_locator.saving_results_string),r=!1,t("#string-locator-feedback-text").text(""),t.post(string_locator.url.clean,{_wpnonce:string_locator.rest_nonce},(function(){t(".string-locator-feedback").hide(),t("tbody",".tools_page_string-locator").is(":empty")&&t("tbody",".tools_page_string-locator").html('<tr><td colspan="3">'+string_locator.search_no_results+"</td></tr>")})).fail((function(t,r,o){a(t.status+" "+o,string_locator.search_error)})),!1;const i={filenum:c,_wpnonce:string_locator.rest_nonce};t.post(string_locator.url.search,i,(function(r){if(!r.success){if(!1===r.data.continue)return a(string_locator.warning_title,r.data.message),!1;s(string_locator.warning_title,r.data.message,"warning")}void 0!==r.data.search&&(t("#string-locator-search-progress").val(r.data.filenum),t("#string-locator-feedback-text").html(string_locator.search_current_prefix+r.data.next_file),function(r){if(t(".no-items",".tools_page_string-locator").is(":visible")&&t(".no-items",".tools_page_string-locator").hide(),Array!==r.constructor)return!1;r.forEach((function(r){if(r)for(let s=0,a=r.length;s<a;s++){const a=r[s];void 0!==a.stringresult&&t("tbody",".tools_page_string-locator").append(o(a))}}))}(r.data.search));const c=r.data.filenum+1;e(n,c)}),"json").fail((function(t,r,o){a(t.status+" "+o,string_locator.search_error)}))}t("#string-locator-search-form").on("submit",(function(o){o.preventDefault(),t("#string-locator-feedback-text").text(string_locator.search_preparing),t(".string-locator-feedback").show(),r=!0,t(".notices").html(""),t("#string-locator-search-progress").removeAttr("value"),t("tbody",".tools_page_string-locator").html("");const n={directory:t("#string-locator-search").val(),search:t("#string-locator-string").val(),regex:t("#string-locator-regex").is(":checked"),_wpnonce:string_locator.rest_nonce};t("table.tools_page_string-locator").show(),t.post(string_locator.url.directory_structure,n,(function(r){r.success?(t("#string-locator-search-progress").attr("max",r.data.total).val(r.data.current),t("#string-locator-feedback-text").text(string_locator.search_started),e(r.data.total,0)):s(r.data,"alert")}),"json").fail((function(t,r,o){a(t.status+" "+o,string_locator.search_error)}))}))}));
|
build/string-locator.asset.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php return array('dependencies' => array(), 'version' => '1d5bc3a2fc5a7730308a2b7bc3219295');
|
build/string-locator.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.wrap>h1{margin-bottom:15px}.string-locator-italics{font-style:italic}.string-locator-feedback{background:#fff;display:inline-block;text-align:center;width:100%}.string-locator-feedback.hide{display:none}.string-locator-feedback progress{height:1.5em;width:100%}.string-locator-feedback #string-locator-feedback-text{display:inline-block;text-align:center;width:100%}body.tools_page_string-locator.file-edit-screen #wpcontent{padding-left:0}body.tools_page_string-locator.file-edit-screen #wpfooter{display:none}body.tools_page_string-locator.file-edit-screen #wpbody-content{padding-bottom:0}table.tools_page_string-locator{display:none}table.tools_page_string-locator.restore{display:table}table .string{width:60%}table .filename{width:20%}table .line,table .position{width:10%}body.wp-admin.tools_page_string-locator #wpbody-content>.notice{display:none}.string-locator-editor-wrapper{grid-gap:0;-ms-grid-columns:80% 20%;display:-ms-grid;display:grid;grid-template-columns:80% 20%;height:100%;width:100%}.string-locator-editor-wrapper .notice,.string-locator-editor-wrapper .string-locator-header{align-items:stretch;background:#fff;border-bottom:1px solid #e2e4e7;display:flex;flex-direction:row;height:40px;justify-content:space-between;left:0;padding:4px 2px;position:-webkit-sticky;position:sticky;right:0;top:0;z-index:30}@media(min-width:600px){.string-locator-editor-wrapper .notice,.string-locator-editor-wrapper .string-locator-header{padding:8px;position:fixed;top:46px}}@media(min-width:782px){.string-locator-editor-wrapper .notice,.string-locator-editor-wrapper .string-locator-header{left:160px;top:32px}}.string-locator-editor-wrapper .notice .title,.string-locator-editor-wrapper .string-locator-header .title{font-size:16px}.string-locator-editor-wrapper .notice>div,.string-locator-editor-wrapper .string-locator-header>div{align-items:center;display:inline-flex}.string-locator-editor-wrapper .notice .button,.string-locator-editor-wrapper .string-locator-header .button{margin:0 3px 0 12px}.string-locator-editor-wrapper .notice{display:block;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;margin:0;top:89px}.string-locator-editor-wrapper .notice.is-dismissible{position:-webkit-sticky;position:sticky}.string-locator-editor-wrapper .string-locator-editor{margin-top:57px}.string-locator-editor-wrapper .string-locator-sidebar{background:#fff;border-left:1px solid #e2e4e7;margin-top:57px}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel{border-top:1px solid #e2e4e7;padding-bottom:10px}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel:first-of-type{border-top:none}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel .title{border:none;box-shadow:none;color:#191e23;font-weight:600;margin:0;padding:15px}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel .row{padding:5px 15px}.string-locator-editor-wrapper .CodeMirror .CodeMirror-activeline .CodeMirror-activeline-background,.string-locator-editor-wrapper .CodeMirror .CodeMirror-activeline .CodeMirror-gutter-background{background-color:#cfe4ff}
|
build/string-locator.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
!function(){"use strict";jQuery(document).ready((function(t){let o;if(!1!==string_locator.CodeMirror&&""!==string_locator.CodeMirror){o=wp.codeEditor.initialize("code-editor",string_locator.CodeMirror);const e=wp.template("string-locator-alert");function r(t){const o=Math.max(document.documentElement.clientHeight,window.innerHeight||0)-89;t.setSize(null,parseInt(o))}t(".string-locator-editor").on("click",".string-locator-edit-goto",(function(e){e.preventDefault(),o.codemirror.scrollIntoView(parseInt(t(this).data("goto-line"))),o.codemirror.setCursor(parseInt(t(this).data("goto-line")-1),t(this).data("goto-linepos"))})),t("body").on("submit","#string-locator-edit-form",(function(o){const r=t("#string-locator-notices");return t.post(string_locator.url.save,t(this).serialize()).always((function(o){void 0===o.notices?r.append(e({type:"error",message:o.responseText})):t.each(o.notices,(function(){r.append(e(this))}))})),o.preventDefault(),!1})),r(o.codemirror),o.codemirror.scrollIntoView(parseInt(string_locator.goto_line)),o.codemirror.setCursor(parseInt(string_locator.goto_line-1),parseInt(string_locator.goto_linepos)),window.addEventListener("resize",r(o.codemirror))}else o=t("#code-editor"),o.css("width",t(".string-locator-edit-wrap").width()),o.css("height",parseInt(Math.max(document.documentElement.clientHeight,window.innerHeight||0)-89));t("#string-locator-notices").on("click",".notice-dismiss",(function(o){return t(this).closest(".notice").slideUp(400,"swing",(function(){t(this).remove()})),o.preventDefault(),!1}))}))}();
|
changelog.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
1 |
= 2.4.1 =
|
2 |
* Fixed case-sensitive class call, apparently not all PHP versions are equal in how this is treated.
|
3 |
|
1 |
+
= 2.4.2 =
|
2 |
+
* Fixed the option to restore previous search.
|
3 |
+
* Fixed respecting text capitalization in previews when doing a non-regex search.
|
4 |
+
* Changed capability checks, now works on hosts that maintain updates for their users.
|
5 |
+
|
6 |
= 2.4.1 =
|
7 |
* Fixed case-sensitive class call, apparently not all PHP versions are equal in how this is treated.
|
8 |
|
includes/REST/class-base.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\REST;
|
4 |
+
|
5 |
+
class Base extends \WP_REST_Controller {
|
6 |
+
|
7 |
+
protected $namespace = 'string-locator/v1';
|
8 |
+
|
9 |
+
public function __construct() {
|
10 |
+
add_action( 'rest_api_init', array( $this, 'register_rest_route' ) );
|
11 |
+
}
|
12 |
+
|
13 |
+
public function permission_callback() {
|
14 |
+
return current_user_can( 'edit_themes' );
|
15 |
+
}
|
16 |
+
|
17 |
+
}
|
includes/REST/class-clean.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\REST;
|
4 |
+
|
5 |
+
class Clean extends Base {
|
6 |
+
|
7 |
+
protected $rest_base = 'clean';
|
8 |
+
|
9 |
+
public function __construct() {
|
10 |
+
parent::__construct();
|
11 |
+
}
|
12 |
+
|
13 |
+
public function register_rest_route() {
|
14 |
+
register_rest_route(
|
15 |
+
$this->namespace,
|
16 |
+
$this->rest_base,
|
17 |
+
array(
|
18 |
+
'methods' => 'POST',
|
19 |
+
'callback' => array( $this, 'clean' ),
|
20 |
+
'permission_callback' => array( $this, 'permission_callback' ),
|
21 |
+
)
|
22 |
+
);
|
23 |
+
}
|
24 |
+
|
25 |
+
public function clean() {
|
26 |
+
$scan_data = get_transient( 'string-locator-search-overview' );
|
27 |
+
for ( $i = 0; $i < $scan_data->chunks; $i ++ ) {
|
28 |
+
delete_transient( 'string-locator-search-files-' . $i );
|
29 |
+
}
|
30 |
+
|
31 |
+
wp_send_json_success( true );
|
32 |
+
}
|
33 |
+
|
34 |
+
}
|
35 |
+
|
36 |
+
new Clean();
|
includes/REST/class-directory-structure.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\REST;
|
4 |
+
|
5 |
+
use JITS\StringLocator\Directory_Iterator;
|
6 |
+
|
7 |
+
class Directory_Structure extends Base {
|
8 |
+
|
9 |
+
protected $rest_base = 'get-directory-structure';
|
10 |
+
|
11 |
+
public function __construct() {
|
12 |
+
parent::__construct();
|
13 |
+
}
|
14 |
+
|
15 |
+
public function register_rest_route() {
|
16 |
+
register_rest_route(
|
17 |
+
$this->namespace,
|
18 |
+
$this->rest_base,
|
19 |
+
array(
|
20 |
+
'methods' => 'POST',
|
21 |
+
'callback' => array( $this, 'get_structure' ),
|
22 |
+
'permission_callback' => array( $this, 'permission_callback' ),
|
23 |
+
)
|
24 |
+
);
|
25 |
+
}
|
26 |
+
|
27 |
+
public function get_structure( \WP_REST_Request $request ) {
|
28 |
+
// Validate the search path to avoid unintended directory traversal.
|
29 |
+
if ( 0 !== validate_file( $request->get_param( 'directory' ) ) ) {
|
30 |
+
return new \WP_REST_Response(
|
31 |
+
array(
|
32 |
+
'success' => false,
|
33 |
+
'data' => __( 'Invalid search source provided.', 'string-locator' ),
|
34 |
+
),
|
35 |
+
400
|
36 |
+
);
|
37 |
+
}
|
38 |
+
|
39 |
+
$iterator = new Directory_Iterator(
|
40 |
+
$request->get_param( 'directory' ),
|
41 |
+
$request->get_param( 'search' ),
|
42 |
+
$request->get_param( 'regex' )
|
43 |
+
);
|
44 |
+
|
45 |
+
return array(
|
46 |
+
'success' => true,
|
47 |
+
'data' => $iterator->get_structure(),
|
48 |
+
);
|
49 |
+
}
|
50 |
+
|
51 |
+
}
|
52 |
+
|
53 |
+
new Directory_Structure();
|
includes/REST/class-save.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\REST;
|
4 |
+
|
5 |
+
class Save extends Base {
|
6 |
+
|
7 |
+
protected $rest_base = 'save';
|
8 |
+
|
9 |
+
public function __construct() {
|
10 |
+
parent::__construct();
|
11 |
+
}
|
12 |
+
|
13 |
+
public function register_rest_route() {
|
14 |
+
register_rest_route(
|
15 |
+
$this->namespace,
|
16 |
+
$this->rest_base,
|
17 |
+
array(
|
18 |
+
'methods' => 'POST',
|
19 |
+
'callback' => array( $this, 'save' ),
|
20 |
+
'permission_callback' => array( $this, 'permission_callback' ),
|
21 |
+
)
|
22 |
+
);
|
23 |
+
}
|
24 |
+
|
25 |
+
public function save( \WP_REST_Request $request ) {
|
26 |
+
$handler = new \JITS\StringLocator\Save();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Filters the REST Request parameter values that will be used for the save call.
|
30 |
+
*
|
31 |
+
* @param array $params REST Request parameters.
|
32 |
+
*/
|
33 |
+
$params = apply_filters( 'string_locator_save_params', $request->get_params() );
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Filter the save handler used to perform edits.
|
37 |
+
*
|
38 |
+
* @attr object $handler The handler performing the save.
|
39 |
+
*/
|
40 |
+
$handler = apply_filters( 'string_locator_save_handler', $handler );
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Trigger an action before the save has been performed.
|
44 |
+
*
|
45 |
+
* @attr array $params The parameters used to perform the save.
|
46 |
+
*/
|
47 |
+
do_action( 'string_locator_pre_save_action', $params );
|
48 |
+
|
49 |
+
$save_result = $handler->save( $params );
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Trigger an action after the save has been performed.
|
53 |
+
*
|
54 |
+
* @attr array $save_result The result of the save.
|
55 |
+
* @attr array $params The parameters used to perform the save.
|
56 |
+
*/
|
57 |
+
do_action( 'string_locator_post_save_action', $save_result, $params );
|
58 |
+
|
59 |
+
return $save_result;
|
60 |
+
}
|
61 |
+
|
62 |
+
}
|
63 |
+
|
64 |
+
new Save();
|
includes/REST/class-search.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\REST;
|
4 |
+
|
5 |
+
class Search extends Base {
|
6 |
+
|
7 |
+
protected $rest_base = 'search';
|
8 |
+
|
9 |
+
public function __construct() {
|
10 |
+
parent::__construct();
|
11 |
+
}
|
12 |
+
|
13 |
+
public function register_rest_route() {
|
14 |
+
register_rest_route(
|
15 |
+
$this->namespace,
|
16 |
+
$this->rest_base,
|
17 |
+
array(
|
18 |
+
'methods' => 'POST',
|
19 |
+
'callback' => array( $this, 'perform_search' ),
|
20 |
+
'permission_callback' => array( $this, 'permission_callback' ),
|
21 |
+
)
|
22 |
+
);
|
23 |
+
}
|
24 |
+
|
25 |
+
public function perform_search( \WP_REST_Request $request ) {
|
26 |
+
$handler = new \JITS\StringLocator\Search();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Filter the search handler used to find strings.
|
30 |
+
*
|
31 |
+
* @attr object $handler The handler performing searches.
|
32 |
+
*/
|
33 |
+
$handler = apply_filters( 'string_locator_search_handler', $handler );
|
34 |
+
|
35 |
+
return array(
|
36 |
+
'success' => true,
|
37 |
+
'data' => $handler->run( $request->get_param( 'filenum' ) ),
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
}
|
42 |
+
|
43 |
+
new Search();
|
includes/Tests/class-loopback.php
ADDED
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\Tests;
|
4 |
+
|
5 |
+
class Loopback {
|
6 |
+
/**
|
7 |
+
* An array of HTTP status codes that will trigger the rollback feature.
|
8 |
+
*
|
9 |
+
* @var string[]
|
10 |
+
*/
|
11 |
+
private $bad_http_codes = array( '500' );
|
12 |
+
|
13 |
+
/**
|
14 |
+
* An array holding any errors returned during testing.
|
15 |
+
*
|
16 |
+
* @var array
|
17 |
+
*/
|
18 |
+
public $errors = array();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Loopback constructor.
|
22 |
+
*/
|
23 |
+
public function __construct() {
|
24 |
+
add_action( 'string_locator_editor_checks', array( $this, 'print_checks_option' ) );
|
25 |
+
|
26 |
+
add_filter( 'string_locator_post_save', array( $this, 'maybe_perform_test' ) );
|
27 |
+
add_filter( 'string_locator_post_save_fail_notice', array( $this, 'return_failure_notices' ) );
|
28 |
+
}
|
29 |
+
|
30 |
+
public function return_failure_notices( $notices ) {
|
31 |
+
if ( empty( $this->errors ) ) {
|
32 |
+
return $notices;
|
33 |
+
}
|
34 |
+
|
35 |
+
return array_merge(
|
36 |
+
$notices,
|
37 |
+
$this->errors
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
public function maybe_perform_test( $save_successful ) {
|
42 |
+
// If another addon has determined the save is a failure, don't perform the test.
|
43 |
+
if ( ! $save_successful ) {
|
44 |
+
return $save_successful;
|
45 |
+
}
|
46 |
+
|
47 |
+
// Do not run this check if it has been disabled.
|
48 |
+
if ( ! isset( $_POST['string-locator-loopback-check'] ) ) {
|
49 |
+
return $save_successful;
|
50 |
+
}
|
51 |
+
|
52 |
+
return $this->run();
|
53 |
+
}
|
54 |
+
|
55 |
+
public function print_checks_option() {
|
56 |
+
?>
|
57 |
+
|
58 |
+
<div class="row">
|
59 |
+
<label>
|
60 |
+
<input type="checkbox" name="string-locator-loopback-check" checked="checked">
|
61 |
+
<?php esc_html_e( 'Enable loopback tests after making a save.', 'string-locator' ); ?>
|
62 |
+
</label>
|
63 |
+
<br>
|
64 |
+
<em>
|
65 |
+
<?php esc_html_e( 'This feature is highly recommended, and is what WordPress does when using the plugin- or theme-editor.', 'string-locator' ); ?>
|
66 |
+
</em>
|
67 |
+
</div>
|
68 |
+
|
69 |
+
<?php
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* A helper function to return any errors.
|
74 |
+
*
|
75 |
+
* @return array
|
76 |
+
*/
|
77 |
+
public function get_errors() {
|
78 |
+
return $this->errors;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Main test runner.
|
83 |
+
*
|
84 |
+
* @return bool
|
85 |
+
*/
|
86 |
+
public function run() {
|
87 |
+
$this->bad_http_codes = apply_filters( 'string_locator_bad_http_codes', $this->bad_http_codes );
|
88 |
+
|
89 |
+
// Clear the error log before doing a new request.
|
90 |
+
$this->errors = array();
|
91 |
+
|
92 |
+
$frontend = $this->test_frontend();
|
93 |
+
|
94 |
+
// If the frontend has a loopback error, return it immediately.
|
95 |
+
if ( ! $frontend ) {
|
96 |
+
return false;
|
97 |
+
}
|
98 |
+
|
99 |
+
// If frontend tests are fine, we return the result of the backend scan.
|
100 |
+
return $this->test_backend();
|
101 |
+
}
|
102 |
+
|
103 |
+
private function test_frontend() {
|
104 |
+
$header = wp_remote_head( site_url() );
|
105 |
+
|
106 |
+
// If we get redirected, follow the redirect.
|
107 |
+
if ( ! is_wp_error( $header ) && 301 === (int) $header['response']['code'] ) {
|
108 |
+
$header = wp_remote_head( $header['headers']['location'] );
|
109 |
+
}
|
110 |
+
|
111 |
+
if ( is_wp_error( $header ) ) {
|
112 |
+
if ( 'http_request_failed' === $header->get_error_code() ) {
|
113 |
+
$this->errors[] = array(
|
114 |
+
'type' => 'error',
|
115 |
+
'message' => __( 'Your changes were not saved, as a check of your site could not be completed afterwards. This may be due to a <a href="https://wordpress.org/support/article/loopbacks/">loopback</a> error.', 'string-locator' ),
|
116 |
+
);
|
117 |
+
|
118 |
+
return false;
|
119 |
+
}
|
120 |
+
|
121 |
+
// Fallback error message here.
|
122 |
+
$this->errors[] = array(
|
123 |
+
'type' => 'error',
|
124 |
+
'message' => $header->get_error_message(),
|
125 |
+
);
|
126 |
+
|
127 |
+
return false;
|
128 |
+
}
|
129 |
+
|
130 |
+
if ( in_array( $header['response']['code'], $this->bad_http_codes, true ) ) {
|
131 |
+
$this->errors[] = array(
|
132 |
+
'type' => 'error',
|
133 |
+
'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' ),
|
134 |
+
);
|
135 |
+
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
|
139 |
+
return true;
|
140 |
+
}
|
141 |
+
|
142 |
+
private function test_backend() {
|
143 |
+
$header = wp_remote_head( admin_url() );
|
144 |
+
|
145 |
+
// If we get redirected, follow the redirect.
|
146 |
+
if ( ! is_wp_error( $header ) && 301 === (int) $header['response']['code'] ) {
|
147 |
+
$header = wp_remote_head( $header['headers']['location'] );
|
148 |
+
}
|
149 |
+
|
150 |
+
if ( is_wp_error( $header ) ) {
|
151 |
+
if ( 'http_request_failed' === $header->get_error_code() ) {
|
152 |
+
$this->errors[] = array(
|
153 |
+
'type' => 'error',
|
154 |
+
'message' => __( 'Your changes were not saved, as a check of your sites backend could not be completed afterwards. This may be due to a <a href="https://wordpress.org/support/article/loopbacks/">loopback</a> error.', 'string-locator' ),
|
155 |
+
);
|
156 |
+
|
157 |
+
return false;
|
158 |
+
}
|
159 |
+
|
160 |
+
// Fallback error message here.
|
161 |
+
$this->errors[] = array(
|
162 |
+
'type' => 'error',
|
163 |
+
'message' => $header->get_error_message(),
|
164 |
+
);
|
165 |
+
|
166 |
+
return false;
|
167 |
+
}
|
168 |
+
|
169 |
+
if ( in_array( $header['response']['code'], $this->bad_http_codes, true ) ) {
|
170 |
+
$this->errors[] = array(
|
171 |
+
'type' => 'error',
|
172 |
+
'message' => __( 'A 500 server error was detected on your sites backend after updating your file. We have restored the previous version of the file for you.', 'string-locator' ),
|
173 |
+
);
|
174 |
+
|
175 |
+
return false;
|
176 |
+
}
|
177 |
+
|
178 |
+
return true;
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
new Loopback();
|
includes/Tests/class-smart-scan.php
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator\Tests;
|
4 |
+
|
5 |
+
class Smart_Scan {
|
6 |
+
|
7 |
+
/**
|
8 |
+
* The content that will be scanned.
|
9 |
+
*
|
10 |
+
* @var string
|
11 |
+
*/
|
12 |
+
private $content = '';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* An array holding any errors returned during testing.
|
16 |
+
*
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
public $errors = array();
|
20 |
+
|
21 |
+
/**
|
22 |
+
* SmartScan constructor.
|
23 |
+
*/
|
24 |
+
public function __construct() {
|
25 |
+
add_action( 'string_locator_editor_checks', array( $this, 'print_checks_option' ) );
|
26 |
+
|
27 |
+
add_filter( 'string_locator_pre_save', array( $this, 'maybe_perform_test' ), 10, 2 );
|
28 |
+
add_filter( 'string_locator_pre_save_fail_notice', array( $this, 'return_failure_notices' ) );
|
29 |
+
}
|
30 |
+
|
31 |
+
public function return_failure_notices( $notices ) {
|
32 |
+
if ( empty( $this->errors ) ) {
|
33 |
+
return $notices;
|
34 |
+
}
|
35 |
+
|
36 |
+
return array_merge(
|
37 |
+
$notices,
|
38 |
+
$this->errors
|
39 |
+
);
|
40 |
+
}
|
41 |
+
|
42 |
+
public function maybe_perform_test( $can_save, $content ) {
|
43 |
+
// If another addon has determined the file can not be saved, bail early.
|
44 |
+
if ( ! $can_save ) {
|
45 |
+
return $can_save;
|
46 |
+
}
|
47 |
+
|
48 |
+
// Do not perform a smart scan if the option for it is disabled.
|
49 |
+
if ( ! isset( $_POST['string-locator-smart-edit'] ) ) {
|
50 |
+
return $can_save;
|
51 |
+
}
|
52 |
+
|
53 |
+
return $this->run( $content );
|
54 |
+
}
|
55 |
+
|
56 |
+
public function print_checks_option() {
|
57 |
+
?>
|
58 |
+
|
59 |
+
<div class="row">
|
60 |
+
<label>
|
61 |
+
<input type="checkbox" name="string-locator-smart-edit" checked="checked">
|
62 |
+
<?php esc_html_e( 'Enable a smart-scan of your code to help detect bracket mismatches before saving.', 'string-locator' ); ?>
|
63 |
+
</label>
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<?php
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* A helper function to return any errors.
|
71 |
+
*
|
72 |
+
* @return array
|
73 |
+
*/
|
74 |
+
public function get_errors() {
|
75 |
+
return $this->errors;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Main test runner.
|
80 |
+
*
|
81 |
+
* @param string $content The content to scan.
|
82 |
+
*
|
83 |
+
* @return bool
|
84 |
+
*/
|
85 |
+
public function run( $content ) {
|
86 |
+
$this->content = $content;
|
87 |
+
|
88 |
+
// Reset the stored errors for a fresh run.
|
89 |
+
$this->errors = array();
|
90 |
+
|
91 |
+
$this->check_braces();
|
92 |
+
$this->check_brackets();
|
93 |
+
$this->check_parenthesis();
|
94 |
+
|
95 |
+
if ( ! empty( $this->errors ) ) {
|
96 |
+
return false;
|
97 |
+
}
|
98 |
+
|
99 |
+
return true;
|
100 |
+
}
|
101 |
+
|
102 |
+
private function check_braces() {
|
103 |
+
$open_brace = substr_count( $this->content, '{' );
|
104 |
+
$close_brace = substr_count( $this->content, '}' );
|
105 |
+
|
106 |
+
if ( $open_brace !== $close_brace ) {
|
107 |
+
$opened = $this->compare( '{', '}' );
|
108 |
+
|
109 |
+
foreach ( $opened as $line ) {
|
110 |
+
$this->errors[] = array(
|
111 |
+
'type' => 'error',
|
112 |
+
'message' => sprintf(
|
113 |
+
// translators: 1: Line number with an error.
|
114 |
+
__( 'There is an inconsistency in the opening and closing braces, { and }, of your file on line %s', 'string-locator' ),
|
115 |
+
'<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
|
116 |
+
),
|
117 |
+
);
|
118 |
+
}
|
119 |
+
|
120 |
+
return false;
|
121 |
+
}
|
122 |
+
|
123 |
+
return true;
|
124 |
+
}
|
125 |
+
|
126 |
+
private function check_brackets() {
|
127 |
+
$open_bracket = substr_count( $this->content, '[' );
|
128 |
+
$close_bracket = substr_count( $this->content, ']' );
|
129 |
+
|
130 |
+
if ( $open_bracket !== $close_bracket ) {
|
131 |
+
$opened = $this->compare( '[', ']' );
|
132 |
+
|
133 |
+
foreach ( $opened as $line ) {
|
134 |
+
$this->errors[] = array(
|
135 |
+
'type' => 'error',
|
136 |
+
'message' => sprintf(
|
137 |
+
// translators: 1: Line number with an error.
|
138 |
+
__( 'There is an inconsistency in the opening and closing braces, [ and ], of your file on line %s', 'string-locator' ),
|
139 |
+
'<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
|
140 |
+
),
|
141 |
+
);
|
142 |
+
}
|
143 |
+
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
|
147 |
+
return true;
|
148 |
+
}
|
149 |
+
|
150 |
+
private function check_parenthesis() {
|
151 |
+
$open_parenthesis = substr_count( $this->content, '(' );
|
152 |
+
$close_parenthesis = substr_count( $this->content, ')' );
|
153 |
+
|
154 |
+
if ( $open_parenthesis !== $close_parenthesis ) {
|
155 |
+
$this->failed_edit = true;
|
156 |
+
|
157 |
+
$opened = $this->compare( '(', ')' );
|
158 |
+
|
159 |
+
foreach ( $opened as $line ) {
|
160 |
+
$this->errors[] = array(
|
161 |
+
'type' => 'error',
|
162 |
+
'message' => sprintf(
|
163 |
+
// translators: 1: Line number with an error.
|
164 |
+
__( 'There is an inconsistency in the opening and closing braces, ( and ), of your file on line %s', 'string-locator' ),
|
165 |
+
'<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
|
166 |
+
),
|
167 |
+
);
|
168 |
+
}
|
169 |
+
|
170 |
+
return false;
|
171 |
+
}
|
172 |
+
|
173 |
+
return true;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Check for inconsistencies in brackets and similar.
|
178 |
+
*
|
179 |
+
* @param string $start Start delimited.
|
180 |
+
* @param string $end End delimiter.
|
181 |
+
*
|
182 |
+
* @return array
|
183 |
+
*/
|
184 |
+
function compare( $start, $end ) {
|
185 |
+
$opened = array();
|
186 |
+
|
187 |
+
$lines = explode( "\n", $this->content );
|
188 |
+
for ( $i = 0; $i < count( $lines ); $i ++ ) {
|
189 |
+
if ( stristr( $lines[ $i ], $start ) ) {
|
190 |
+
$opened[] = $i;
|
191 |
+
}
|
192 |
+
if ( stristr( $lines[ $i ], $end ) ) {
|
193 |
+
array_pop( $opened );
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
return $opened;
|
198 |
+
}
|
199 |
+
|
200 |
+
}
|
201 |
+
|
202 |
+
new Smart_Scan();
|
includes/class-directory-iterator.php
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator;
|
4 |
+
|
5 |
+
class Directory_Iterator {
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Directory that will be searched.
|
9 |
+
*
|
10 |
+
* @var
|
11 |
+
*/
|
12 |
+
private $directory;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* The search term being used.
|
16 |
+
*
|
17 |
+
* @var
|
18 |
+
*/
|
19 |
+
private $search;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* A check if regex searches are enabled or not.
|
23 |
+
*
|
24 |
+
* @var
|
25 |
+
*/
|
26 |
+
private $regex;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* DirectoryIterator constructor.
|
30 |
+
*
|
31 |
+
* @param $directory
|
32 |
+
* @param $search
|
33 |
+
* @param $regex
|
34 |
+
*
|
35 |
+
* @return array
|
36 |
+
*/
|
37 |
+
public function __construct( $directory, $search, $regex ) {
|
38 |
+
$this->directory = $directory;
|
39 |
+
$this->search = $search;
|
40 |
+
$this->regex = $regex;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Build the folder structure.
|
45 |
+
*
|
46 |
+
* @return array
|
47 |
+
*/
|
48 |
+
public function get_structure() {
|
49 |
+
$scan_path = $this->prepare_scan_path( $this->directory );
|
50 |
+
|
51 |
+
if ( is_file( $scan_path->path ) ) {
|
52 |
+
$files = array( $scan_path->path );
|
53 |
+
} else {
|
54 |
+
$files = $this->ajax_scan_path( $scan_path->path );
|
55 |
+
}
|
56 |
+
|
57 |
+
/*
|
58 |
+
* Make sure each chunk of file arrays never exceeds 500 files
|
59 |
+
* This is to prevent the SQL string from being too large and crashing everything
|
60 |
+
*/
|
61 |
+
$back_compat_filter = apply_filters( 'string-locator-files-per-array', 500 ); //phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
62 |
+
|
63 |
+
$file_chunks = array_chunk( $files, apply_filters( 'string_locator_files_per_array', $back_compat_filter ), true );
|
64 |
+
|
65 |
+
$store = (object) array(
|
66 |
+
'scan_path' => $scan_path,
|
67 |
+
'search' => wp_unslash( $this->search ),
|
68 |
+
'directory' => $this->directory,
|
69 |
+
'chunks' => count( $file_chunks ),
|
70 |
+
'regex' => $this->regex,
|
71 |
+
);
|
72 |
+
|
73 |
+
$response = array(
|
74 |
+
'total' => count( $files ),
|
75 |
+
'current' => 0,
|
76 |
+
'directory' => $scan_path,
|
77 |
+
'chunks' => count( $file_chunks ),
|
78 |
+
'regex' => $this->regex,
|
79 |
+
);
|
80 |
+
|
81 |
+
set_transient( 'string-locator-search-overview', $store );
|
82 |
+
update_option( 'string-locator-search-history', array(), false );
|
83 |
+
|
84 |
+
foreach ( $file_chunks as $count => $file_chunk ) {
|
85 |
+
set_transient( 'string-locator-search-files-' . $count, $file_chunk );
|
86 |
+
}
|
87 |
+
|
88 |
+
return $response;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Parse the search option to determine what kind of search we are performing and what directory to start in.
|
93 |
+
*
|
94 |
+
* @param string $option The search-type identifier.
|
95 |
+
*
|
96 |
+
* @return bool|object
|
97 |
+
*/
|
98 |
+
private function prepare_scan_path( $option ) {
|
99 |
+
$data = array(
|
100 |
+
'path' => '',
|
101 |
+
'type' => '',
|
102 |
+
'slug' => '',
|
103 |
+
);
|
104 |
+
|
105 |
+
switch ( true ) {
|
106 |
+
case ( 't--' === $option ):
|
107 |
+
$data['path'] = WP_CONTENT_DIR . '/themes/';
|
108 |
+
$data['type'] = 'theme';
|
109 |
+
break;
|
110 |
+
case ( strlen( $option ) > 3 && 't-' === substr( $option, 0, 2 ) ):
|
111 |
+
$data['path'] = WP_CONTENT_DIR . '/themes/' . substr( $option, 2 );
|
112 |
+
$data['type'] = 'theme';
|
113 |
+
$data['slug'] = substr( $option, 2 );
|
114 |
+
break;
|
115 |
+
case ( 'p--' === $option ):
|
116 |
+
$data['path'] = WP_CONTENT_DIR . '/plugins/';
|
117 |
+
$data['type'] = 'plugin';
|
118 |
+
break;
|
119 |
+
case ( 'mup--' === $option ):
|
120 |
+
$data['path'] = WP_CONTENT_DIR . '/mu-plugins/';
|
121 |
+
$data['type'] = 'mu-plugin';
|
122 |
+
break;
|
123 |
+
case ( strlen( $option ) > 3 && 'p-' === substr( $option, 0, 2 ) ):
|
124 |
+
$slug = explode( '/', substr( $option, 2 ) );
|
125 |
+
|
126 |
+
$data['path'] = WP_CONTENT_DIR . '/plugins/' . $slug[0];
|
127 |
+
$data['type'] = 'plugin';
|
128 |
+
$data['slug'] = $slug[0];
|
129 |
+
break;
|
130 |
+
case ( 'core' === $option ):
|
131 |
+
$data['path'] = ABSPATH;
|
132 |
+
$data['type'] = 'core';
|
133 |
+
break;
|
134 |
+
case ( 'wp-content' === $option ):
|
135 |
+
$data['path'] = WP_CONTENT_DIR;
|
136 |
+
$data['type'] = 'core';
|
137 |
+
break;
|
138 |
+
}
|
139 |
+
|
140 |
+
if ( empty( $data['path'] ) ) {
|
141 |
+
return false;
|
142 |
+
}
|
143 |
+
|
144 |
+
return (object) $data;
|
145 |
+
}
|
146 |
+
|
147 |
+
private function ajax_scan_path( $path ) {
|
148 |
+
$files = array();
|
149 |
+
|
150 |
+
$paths = new \RecursiveIteratorIterator(
|
151 |
+
new \RecursiveDirectoryIterator( $path ),
|
152 |
+
\RecursiveIteratorIterator::SELF_FIRST
|
153 |
+
);
|
154 |
+
|
155 |
+
foreach ( $paths as $name => $location ) {
|
156 |
+
if ( is_dir( $location->getPathname() ) ) {
|
157 |
+
continue;
|
158 |
+
}
|
159 |
+
|
160 |
+
$files[] = $location->getPathname();
|
161 |
+
}
|
162 |
+
|
163 |
+
return $files;
|
164 |
+
}
|
165 |
+
|
166 |
+
}
|
includes/class-save.php
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator;
|
4 |
+
|
5 |
+
use JITS\StringLocator\Tests\Loopback;
|
6 |
+
use JITS\StringLocator\Tests\Smart_Scan;
|
7 |
+
|
8 |
+
class Save {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* An array of notices to send back to the user.
|
12 |
+
*
|
13 |
+
* @var array
|
14 |
+
*/
|
15 |
+
public $notice = array();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Save constructor.
|
19 |
+
*/
|
20 |
+
public function __construct() {}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Handler for storing the content of the code editor.
|
24 |
+
*
|
25 |
+
* Also runs over the Smart-Scan if enabled.
|
26 |
+
*
|
27 |
+
* @return void|array
|
28 |
+
*/
|
29 |
+
public function save( $save_params ) {
|
30 |
+
$_POST = $save_params;
|
31 |
+
|
32 |
+
if ( String_Locator::is_valid_location( $_POST['string-locator-path'] ) ) {
|
33 |
+
$path = urldecode( $_POST['string-locator-path'] );
|
34 |
+
$content = $_POST['string-locator-editor-content'];
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Send an error notice if the file isn't writable
|
38 |
+
*/
|
39 |
+
if ( ! is_writeable( $path ) ) {
|
40 |
+
$this->notice[] = array(
|
41 |
+
'type' => 'error',
|
42 |
+
'message' => __( 'The file could not be written to, please check file permissions or edit it manually.', 'string-locator' ),
|
43 |
+
);
|
44 |
+
|
45 |
+
return array(
|
46 |
+
'notices' => $this->notice,
|
47 |
+
);
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Filter if the save process should be performed or not.
|
52 |
+
*
|
53 |
+
* @attr bool $can_save Can the save be carried out.
|
54 |
+
* @attr string $content The content to save.
|
55 |
+
* @attr string $path Path to the file being edited.
|
56 |
+
*/
|
57 |
+
$can_save = apply_filters( 'string_locator_pre_save', true, $content, $path );
|
58 |
+
|
59 |
+
if ( ! $can_save ) {
|
60 |
+
return array(
|
61 |
+
'notices' => apply_filters( 'string_locator_pre_save_fail_notice', array() ),
|
62 |
+
);
|
63 |
+
}
|
64 |
+
|
65 |
+
$original = file_get_contents( $path );
|
66 |
+
|
67 |
+
$this->write_file( $path, $content );
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Filter if the save process completed as it should or if warnings should be returned.
|
71 |
+
*
|
72 |
+
* @attr bool $save_successful Boolean indicating if the save was successful.
|
73 |
+
* @attr string $content The edited content.
|
74 |
+
* @attr string $original The original content.
|
75 |
+
* @attr string $path The path to the file being edited.
|
76 |
+
*/
|
77 |
+
$save_successful = apply_filters( 'string_locator_post_save', true, $content, $original, $path );
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Check the status of the site after making our edits.
|
81 |
+
* If the site fails, revert the changes to return the sites to its original state
|
82 |
+
*/
|
83 |
+
if ( ! $save_successful ) {
|
84 |
+
$this->write_file( $path, $original );
|
85 |
+
|
86 |
+
return array(
|
87 |
+
'notices' => apply_filters( 'string_locator_post_save_fail_notice', array() ),
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
return array(
|
92 |
+
'notices' => array(
|
93 |
+
array(
|
94 |
+
'type' => 'success',
|
95 |
+
'message' => __( 'The file has been saved', 'string-locator' ),
|
96 |
+
),
|
97 |
+
),
|
98 |
+
);
|
99 |
+
} else {
|
100 |
+
return array(
|
101 |
+
'notices' => array(
|
102 |
+
array(
|
103 |
+
'type' => 'error',
|
104 |
+
'message' => sprintf(
|
105 |
+
// translators: %s: The file location that was sent.
|
106 |
+
__( 'The file location provided, <strong>%s</strong>, is not valid.', 'string-locator' ),
|
107 |
+
$_POST['string-locator-path']
|
108 |
+
),
|
109 |
+
),
|
110 |
+
),
|
111 |
+
);
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* When editing a file, this is where we write all the new content.
|
117 |
+
* We will break early if the user isn't allowed to edit files.
|
118 |
+
*
|
119 |
+
* @param string $path The path to the file.
|
120 |
+
* @param string $content The content to write to the file.
|
121 |
+
*
|
122 |
+
* @return void
|
123 |
+
*/
|
124 |
+
private function write_file( $path, $content ) {
|
125 |
+
if ( ! current_user_can( 'edit_themes' ) ) {
|
126 |
+
return;
|
127 |
+
}
|
128 |
+
|
129 |
+
// Verify the location is valid before we try using it.
|
130 |
+
if ( ! String_Locator::is_valid_location( $path ) ) {
|
131 |
+
return;
|
132 |
+
}
|
133 |
+
|
134 |
+
$back_compat_filter = apply_filters( 'string-locator-filter-closing-php-tags', true ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
135 |
+
|
136 |
+
if ( apply_filters( 'string_locator_filter_closing_php_tags', $back_compat_filter ) ) {
|
137 |
+
$content = preg_replace( '/\?>$/si', '', trim( $content ), - 1, $replaced_strings );
|
138 |
+
|
139 |
+
if ( $replaced_strings >= 1 ) {
|
140 |
+
$this->notice[] = array(
|
141 |
+
'type' => 'error',
|
142 |
+
'message' => __( 'We detected a PHP code tag ending, this has been automatically stripped out to help prevent errors in your code.', 'string-locator' ),
|
143 |
+
);
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
$file = fopen( $path, 'w' );
|
148 |
+
$lines = explode( "\n", str_replace( array( "\r\n", "\r" ), "\n", $content ) );
|
149 |
+
$total_lines = count( $lines );
|
150 |
+
|
151 |
+
for ( $i = 0; $i < $total_lines; $i ++ ) {
|
152 |
+
$write_line = $lines[ $i ];
|
153 |
+
|
154 |
+
if ( ( $i + 1 ) < $total_lines ) {
|
155 |
+
$write_line .= PHP_EOL;
|
156 |
+
}
|
157 |
+
|
158 |
+
fwrite( $file, $write_line );
|
159 |
+
}
|
160 |
+
|
161 |
+
fclose( $file );
|
162 |
+
}
|
163 |
+
}
|
includes/class-search.php
ADDED
@@ -0,0 +1,520 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator;
|
4 |
+
|
5 |
+
class Search {
|
6 |
+
|
7 |
+
/**
|
8 |
+
* An array of file extensions that will be ignored by the scanner.
|
9 |
+
*
|
10 |
+
* @var string[]
|
11 |
+
*/
|
12 |
+
private $bad_file_types = array(
|
13 |
+
'rar',
|
14 |
+
'7z',
|
15 |
+
'zip',
|
16 |
+
'tar',
|
17 |
+
'gz',
|
18 |
+
'jpg',
|
19 |
+
'jpeg',
|
20 |
+
'png',
|
21 |
+
'gif',
|
22 |
+
'mp3',
|
23 |
+
'mp4',
|
24 |
+
'avi',
|
25 |
+
'wmv',
|
26 |
+
);
|
27 |
+
|
28 |
+
/**
|
29 |
+
* The path to the currently editable file.
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
private $path_to_use = '';
|
34 |
+
|
35 |
+
/**
|
36 |
+
* The length of the excerpt from the line containing a match.
|
37 |
+
*
|
38 |
+
* @var int
|
39 |
+
*/
|
40 |
+
private $excerpt_length = 25;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* The server-configured max time a script can run.
|
44 |
+
*
|
45 |
+
* @var int
|
46 |
+
*/
|
47 |
+
private $max_execution_time = null;
|
48 |
+
|
49 |
+
/**
|
50 |
+
* The current time when our script started executing.
|
51 |
+
*
|
52 |
+
* @var float
|
53 |
+
*/
|
54 |
+
private $start_execution_timer = 0;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* The server-configured max amount of memory a script can use.
|
58 |
+
*
|
59 |
+
* @var int
|
60 |
+
*/
|
61 |
+
private $max_memory_consumption = 0;
|
62 |
+
|
63 |
+
public function __construct() {
|
64 |
+
/**
|
65 |
+
* Define class variables requiring expressions
|
66 |
+
*/
|
67 |
+
$this->path_to_use = ( is_multisite() ? 'network/admin.php' : 'tools.php' );
|
68 |
+
$this->excerpt_length = apply_filters( 'string_locator_excerpt_length', 25 );
|
69 |
+
|
70 |
+
$this->max_execution_time = absint( ini_get( 'max_execution_time' ) );
|
71 |
+
$this->start_execution_timer = microtime( true );
|
72 |
+
|
73 |
+
if ( $this->max_execution_time > 30 ) {
|
74 |
+
$this->max_execution_time = 30;
|
75 |
+
}
|
76 |
+
|
77 |
+
$this->set_memory_limit();
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Sets up the memory limit variables.
|
82 |
+
*
|
83 |
+
* @return void
|
84 |
+
* @since 2.0.0
|
85 |
+
*
|
86 |
+
*/
|
87 |
+
function set_memory_limit() {
|
88 |
+
$memory_limit = ini_get( 'memory_limit' );
|
89 |
+
|
90 |
+
$this->max_memory_consumption = absint( $memory_limit );
|
91 |
+
|
92 |
+
if ( strstr( $memory_limit, 'k' ) ) {
|
93 |
+
$this->max_memory_consumption = ( str_replace( 'k', '', $memory_limit ) * 1000 );
|
94 |
+
}
|
95 |
+
if ( strstr( $memory_limit, 'M' ) ) {
|
96 |
+
$this->max_memory_consumption = ( str_replace( 'M', '', $memory_limit ) * 1000000 );
|
97 |
+
}
|
98 |
+
if ( strstr( $memory_limit, 'G' ) ) {
|
99 |
+
$this->max_memory_consumption = ( str_replace( 'G', '', $memory_limit ) * 1000000000 );
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Check if the script is about to exceed the max execution time.
|
105 |
+
*
|
106 |
+
* @return bool
|
107 |
+
* @since 1.9.0
|
108 |
+
*
|
109 |
+
*/
|
110 |
+
function nearing_execution_limit() {
|
111 |
+
// Max execution time is 0 or -1 (infinite) in server config
|
112 |
+
if ( 0 === $this->max_execution_time || - 1 === $this->max_execution_time ) {
|
113 |
+
return false;
|
114 |
+
}
|
115 |
+
|
116 |
+
$back_compat_filter = apply_filters( 'string-locator-extra-search-delay', 2 ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
117 |
+
|
118 |
+
$built_in_delay = apply_filters( 'string_locator_extra_search_delay', $back_compat_filter );
|
119 |
+
$execution_time = ( microtime( true ) - $this->start_execution_timer + $built_in_delay );
|
120 |
+
|
121 |
+
if ( $execution_time >= $this->max_execution_time ) {
|
122 |
+
return $execution_time;
|
123 |
+
}
|
124 |
+
|
125 |
+
return false;
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Check if the script is about to exceed the server memory limit.
|
130 |
+
*
|
131 |
+
* @return bool
|
132 |
+
* @since 2.0.0
|
133 |
+
*
|
134 |
+
*/
|
135 |
+
function nearing_memory_limit() {
|
136 |
+
// Check if the memory limit is set t o0 or -1 (infinite) in server config
|
137 |
+
if ( 0 === $this->max_memory_consumption || - 1 === $this->max_memory_consumption ) {
|
138 |
+
return false;
|
139 |
+
}
|
140 |
+
|
141 |
+
// We give our selves a 256k memory buffer, as we need to close off the script properly as well
|
142 |
+
$back_compat_filter = apply_filters( 'string-locator-extra-memory-buffer', 256000 ); //phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
143 |
+
$built_in_buffer = apply_filters( 'string_locator_extra_memory_buffer', $back_compat_filter );
|
144 |
+
$memory_use = ( memory_get_usage( true ) + $built_in_buffer );
|
145 |
+
|
146 |
+
if ( $memory_use >= $this->max_memory_consumption ) {
|
147 |
+
return $memory_use;
|
148 |
+
}
|
149 |
+
|
150 |
+
return false;
|
151 |
+
}
|
152 |
+
|
153 |
+
public function run( $filenum ) {
|
154 |
+
$_POST['filenum'] = $filenum;
|
155 |
+
|
156 |
+
$files_per_chunk = apply_filters( 'string_locator_files_per_array', 500 );
|
157 |
+
$response = array(
|
158 |
+
'search' => array(),
|
159 |
+
'filenum' => absint( $_POST['filenum'] ),
|
160 |
+
);
|
161 |
+
|
162 |
+
$filenum = absint( $_POST['filenum'] );
|
163 |
+
|
164 |
+
$chunk = ( ceil( $filenum / $files_per_chunk ) - 1 );
|
165 |
+
if ( $chunk < 0 ) {
|
166 |
+
$chunk = 0;
|
167 |
+
}
|
168 |
+
|
169 |
+
$scan_data = get_transient( 'string-locator-search-overview' );
|
170 |
+
$file_data = get_transient( 'string-locator-search-files-' . $chunk );
|
171 |
+
|
172 |
+
if ( ! isset( $file_data[ $filenum ] ) ) {
|
173 |
+
wp_send_json_error(
|
174 |
+
array(
|
175 |
+
'continue' => false,
|
176 |
+
'message' => sprintf(
|
177 |
+
/* translators: %d: The numbered reference to a file being searched. */
|
178 |
+
esc_html__( 'The file-number, %d, that was sent could not be found.', 'string-locator' ),
|
179 |
+
$filenum
|
180 |
+
),
|
181 |
+
)
|
182 |
+
);
|
183 |
+
}
|
184 |
+
|
185 |
+
if ( $this->nearing_execution_limit() ) {
|
186 |
+
wp_send_json_error(
|
187 |
+
array(
|
188 |
+
'continue' => false,
|
189 |
+
'message' => sprintf(
|
190 |
+
/* 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. */
|
191 |
+
esc_html__( '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' ),
|
192 |
+
$this->max_execution_time,
|
193 |
+
$this->nearing_execution_limit()
|
194 |
+
),
|
195 |
+
)
|
196 |
+
);
|
197 |
+
}
|
198 |
+
if ( $this->nearing_memory_limit() ) {
|
199 |
+
wp_send_json_error(
|
200 |
+
array(
|
201 |
+
'continue' => false,
|
202 |
+
'message' => sprintf(
|
203 |
+
/* translators: %1$d: Current amount of used system memory resources. %2$d: The maximum available system memory. */
|
204 |
+
esc_html__( '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' ),
|
205 |
+
$this->nearing_memory_limit(),
|
206 |
+
$this->max_memory_consumption
|
207 |
+
),
|
208 |
+
)
|
209 |
+
);
|
210 |
+
}
|
211 |
+
|
212 |
+
$is_regex = false;
|
213 |
+
if ( isset( $scan_data->regex ) ) {
|
214 |
+
$is_regex = String_Locator::absbool( $scan_data->regex );
|
215 |
+
}
|
216 |
+
|
217 |
+
if ( $is_regex ) {
|
218 |
+
if ( false === @preg_match( $scan_data->search, '' ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
219 |
+
wp_send_json_error(
|
220 |
+
array(
|
221 |
+
'continue' => false,
|
222 |
+
'message' => sprintf(
|
223 |
+
/* translators: %s: The search string used. */
|
224 |
+
__( 'Your search string, <strong>%s</strong>, is not a valid pattern, and the search has been aborted.', 'string-locator' ),
|
225 |
+
esc_html( $scan_data->search )
|
226 |
+
),
|
227 |
+
)
|
228 |
+
);
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
while ( ! $this->nearing_execution_limit() && ! $this->nearing_memory_limit() && isset( $file_data[ $filenum ] ) ) {
|
233 |
+
$filenum = absint( $_POST['filenum'] );
|
234 |
+
$search_results = null;
|
235 |
+
$next_file = $filenum + 1;
|
236 |
+
|
237 |
+
$next_chunk = ( ceil( ( $next_file ) / $files_per_chunk ) - 1 );
|
238 |
+
$chunk = ( ceil( $filenum / $files_per_chunk ) - 1 );
|
239 |
+
if ( $chunk < 0 ) {
|
240 |
+
$chunk = 0;
|
241 |
+
}
|
242 |
+
if ( $next_chunk < 0 ) {
|
243 |
+
$next_chunk = 0;
|
244 |
+
}
|
245 |
+
|
246 |
+
if ( ! isset( $file_data[ $filenum ] ) ) {
|
247 |
+
$chunk ++;
|
248 |
+
$file_data = get_transient( 'string-locator-search-files-' . $chunk );
|
249 |
+
continue;
|
250 |
+
}
|
251 |
+
|
252 |
+
$file_name = explode( '/', $file_data[ $filenum ] );
|
253 |
+
$file_name = end( $file_name );
|
254 |
+
|
255 |
+
/*
|
256 |
+
* Check the file type, if it's an unsupported type, we skip it
|
257 |
+
*/
|
258 |
+
$file_type = explode( '.', $file_name );
|
259 |
+
$file_type = strtolower( end( $file_type ) );
|
260 |
+
|
261 |
+
/*
|
262 |
+
* Scan the file and look for our string, but only if it's an approved file extension
|
263 |
+
*/
|
264 |
+
$bad_file_types = apply_filters( 'string_locator_bad_file_types', $this->bad_file_types );
|
265 |
+
if ( ! in_array( $file_type, $bad_file_types, true ) ) {
|
266 |
+
$search_results = $this->scan_file( $file_data[ $filenum ], $scan_data->search, $file_data[ $filenum ], $scan_data->scan_path->type, '', $is_regex );
|
267 |
+
}
|
268 |
+
|
269 |
+
$response['last_file'] = $file_data[ $filenum ];
|
270 |
+
$response['filenum'] = $filenum;
|
271 |
+
$response['filename'] = $file_name;
|
272 |
+
if ( $search_results ) {
|
273 |
+
$response['search'][] = $search_results;
|
274 |
+
}
|
275 |
+
|
276 |
+
if ( $next_chunk !== $chunk ) {
|
277 |
+
$file_data = get_transient( 'string-locator-search-files-' . $next_chunk );
|
278 |
+
}
|
279 |
+
|
280 |
+
$response['next_file'] = ( isset( $file_data[ $next_file ] ) ? $file_data[ $next_file ] : '' );
|
281 |
+
|
282 |
+
if ( ! empty( $search_results ) ) {
|
283 |
+
$history = get_option( 'string-locator-search-history', array() );
|
284 |
+
$history = array_merge( $history, $search_results );
|
285 |
+
update_option( 'string-locator-search-history', $history, false );
|
286 |
+
}
|
287 |
+
|
288 |
+
$_POST['filenum'] ++;
|
289 |
+
}
|
290 |
+
|
291 |
+
return $response;
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Scan through an individual file to look for occurrences of £string.
|
296 |
+
*
|
297 |
+
* @param string $filename The path to the file.
|
298 |
+
* @param string $string The search string.
|
299 |
+
* @param mixed $location The file location object/string.
|
300 |
+
* @param string $type File type.
|
301 |
+
* @param string $slug The plugin/theme slug of the file.
|
302 |
+
* @param boolean $regex Should a regex search be performed.
|
303 |
+
*
|
304 |
+
* @return array
|
305 |
+
*/
|
306 |
+
function scan_file( $filename, $string, $location, $type, $slug, $regex = false ) {
|
307 |
+
if ( empty( $string ) || ! is_file( $filename ) ) {
|
308 |
+
return array();
|
309 |
+
}
|
310 |
+
$output = array();
|
311 |
+
$linenum = 0;
|
312 |
+
$match_count = 0;
|
313 |
+
|
314 |
+
if ( ! is_object( $location ) ) {
|
315 |
+
$path = $location;
|
316 |
+
$location = explode( DIRECTORY_SEPARATOR, $location );
|
317 |
+
$file = end( $location );
|
318 |
+
} else {
|
319 |
+
$path = $location->getPathname();
|
320 |
+
$file = $location->getFilename();
|
321 |
+
}
|
322 |
+
|
323 |
+
/*
|
324 |
+
* Check if the filename matches our search pattern
|
325 |
+
*/
|
326 |
+
if ( stristr( $file, $string ) || ( $regex && preg_match( $string, $file ) ) ) {
|
327 |
+
$relativepath = str_replace(
|
328 |
+
array(
|
329 |
+
ABSPATH,
|
330 |
+
'\\',
|
331 |
+
'/',
|
332 |
+
),
|
333 |
+
array(
|
334 |
+
'',
|
335 |
+
DIRECTORY_SEPARATOR,
|
336 |
+
DIRECTORY_SEPARATOR,
|
337 |
+
),
|
338 |
+
$path
|
339 |
+
);
|
340 |
+
$match_count ++;
|
341 |
+
|
342 |
+
$editurl = $this->create_edit_link( $path, $linenum );
|
343 |
+
|
344 |
+
$path_string = sprintf(
|
345 |
+
'<a href="%s">%s</a>',
|
346 |
+
esc_url( $editurl ),
|
347 |
+
esc_html( $relativepath )
|
348 |
+
);
|
349 |
+
|
350 |
+
$output[] = array(
|
351 |
+
'ID' => $match_count,
|
352 |
+
'linenum' => sprintf(
|
353 |
+
'[%s]',
|
354 |
+
esc_html__( 'Filename matches search', 'string-locator' )
|
355 |
+
),
|
356 |
+
'linepos' => '',
|
357 |
+
'path' => $path,
|
358 |
+
'filename' => $path_string,
|
359 |
+
'filename_raw' => $relativepath,
|
360 |
+
'editurl' => ( current_user_can( 'edit_themes' ) ? $editurl : false ),
|
361 |
+
'stringresult' => $file,
|
362 |
+
);
|
363 |
+
}
|
364 |
+
|
365 |
+
$readfile = @fopen( $filename, 'r' );
|
366 |
+
if ( $readfile ) {
|
367 |
+
while ( ( $readline = fgets( $readfile ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
|
368 |
+
$string_preview_is_cut = false;
|
369 |
+
$linenum ++;
|
370 |
+
/**
|
371 |
+
* If our string is found in this line, output the line number and other data
|
372 |
+
*/
|
373 |
+
if ( ( ! $regex && stristr( $readline, $string ) ) || ( $regex && preg_match( $string, $readline, $match, PREG_OFFSET_CAPTURE ) ) ) {
|
374 |
+
/**
|
375 |
+
* Prepare the visual path for the end user
|
376 |
+
* Removes path leading up to WordPress root and ensures consistent directory separators
|
377 |
+
*/
|
378 |
+
$relativepath = str_replace(
|
379 |
+
array(
|
380 |
+
ABSPATH,
|
381 |
+
'\\',
|
382 |
+
'/',
|
383 |
+
),
|
384 |
+
array(
|
385 |
+
'',
|
386 |
+
DIRECTORY_SEPARATOR,
|
387 |
+
DIRECTORY_SEPARATOR,
|
388 |
+
),
|
389 |
+
$path
|
390 |
+
);
|
391 |
+
$match_count ++;
|
392 |
+
|
393 |
+
if ( $regex ) {
|
394 |
+
$str_pos = $match[0][1];
|
395 |
+
} else {
|
396 |
+
$str_pos = stripos( $readline, $string );
|
397 |
+
}
|
398 |
+
|
399 |
+
/**
|
400 |
+
* Create the URL to take the user to the editor
|
401 |
+
*/
|
402 |
+
$editurl = $this->create_edit_link( $path, $linenum, $str_pos );
|
403 |
+
|
404 |
+
$string_preview = $readline;
|
405 |
+
if ( strlen( $string_preview ) > ( strlen( $string ) + $this->excerpt_length ) ) {
|
406 |
+
$string_location = strpos( $string_preview, $string );
|
407 |
+
|
408 |
+
$string_location_start = $string_location - $this->excerpt_length;
|
409 |
+
if ( $string_location_start < 0 ) {
|
410 |
+
$string_location_start = 0;
|
411 |
+
}
|
412 |
+
|
413 |
+
$string_location_end = ( strlen( $string ) + ( $this->excerpt_length * 2 ) );
|
414 |
+
if ( $string_location_end > strlen( $string_preview ) ) {
|
415 |
+
$string_location_end = strlen( $string_preview );
|
416 |
+
}
|
417 |
+
|
418 |
+
$string_preview = substr( $string_preview, $string_location_start, $string_location_end );
|
419 |
+
$string_preview_is_cut = true;
|
420 |
+
}
|
421 |
+
|
422 |
+
if ( $regex ) {
|
423 |
+
$string_preview = preg_replace( preg_replace( '/\/(.+)\//', '/($1)/', $string ), '<strong>$1</strong>', esc_html( $string_preview ) );
|
424 |
+
} else {
|
425 |
+
$string_preview = preg_replace( '/(' . $string . ')/i', '<strong>$1</strong>', esc_html( $string_preview ) );
|
426 |
+
}
|
427 |
+
if ( $string_preview_is_cut ) {
|
428 |
+
$string_preview = sprintf(
|
429 |
+
'…%s…',
|
430 |
+
$string_preview
|
431 |
+
);
|
432 |
+
}
|
433 |
+
|
434 |
+
$path_string = sprintf(
|
435 |
+
'<a href="%s">%s</a>',
|
436 |
+
esc_url( $editurl ),
|
437 |
+
esc_html( $relativepath )
|
438 |
+
);
|
439 |
+
|
440 |
+
$output[] = array(
|
441 |
+
'ID' => $match_count,
|
442 |
+
'linenum' => $linenum,
|
443 |
+
'linepos' => $str_pos,
|
444 |
+
'path' => $path,
|
445 |
+
'filename' => $path_string,
|
446 |
+
'filename_raw' => $relativepath,
|
447 |
+
'editurl' => ( current_user_can( 'edit_themes' ) ? $editurl : false ),
|
448 |
+
'stringresult' => $string_preview,
|
449 |
+
);
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
fclose( $readfile );
|
454 |
+
} else {
|
455 |
+
/**
|
456 |
+
* The file was unreadable, give the user a friendly notification
|
457 |
+
*/
|
458 |
+
$output[] = array(
|
459 |
+
'linenum' => '#',
|
460 |
+
// translators: 1: Filename.
|
461 |
+
'filename' => esc_html( sprintf( __( 'Could not read file: %s', 'string-locator' ), $filename ) ),
|
462 |
+
'stringresult' => '',
|
463 |
+
);
|
464 |
+
}
|
465 |
+
|
466 |
+
return $output;
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Create an admin edit link for the supplied path.
|
471 |
+
*
|
472 |
+
* @param string $path Path to the file we'er adding a link for.
|
473 |
+
* @param int $line The line in the file where our search result was found.
|
474 |
+
* @param int $linepos The positin in the line where the search result was found.
|
475 |
+
*
|
476 |
+
* @return string
|
477 |
+
*/
|
478 |
+
function create_edit_link( $path, $line = 0, $linepos = 0 ) {
|
479 |
+
$file_type = 'core';
|
480 |
+
$file_slug = '';
|
481 |
+
$content_path = str_replace( '\\', '/', WP_CONTENT_DIR );
|
482 |
+
|
483 |
+
$path = str_replace( '\\', '/', $path );
|
484 |
+
$paths = explode( '/', $path );
|
485 |
+
|
486 |
+
$url_args = array(
|
487 |
+
'page=string-locator',
|
488 |
+
'edit-file=' . end( $paths ),
|
489 |
+
);
|
490 |
+
|
491 |
+
switch ( true ) {
|
492 |
+
case ( in_array( 'wp-content', $paths, true ) && in_array( 'plugins', $paths, true ) ):
|
493 |
+
$file_type = 'plugin';
|
494 |
+
$content_path .= '/plugins/';
|
495 |
+
break;
|
496 |
+
case ( in_array( 'wp-content', $paths, true ) && in_array( 'themes', $paths, true ) ):
|
497 |
+
$file_type = 'theme';
|
498 |
+
$content_path .= '/themes/';
|
499 |
+
break;
|
500 |
+
}
|
501 |
+
|
502 |
+
$rel_path = str_replace( $content_path, '', $path );
|
503 |
+
$rel_paths = explode( '/', $rel_path );
|
504 |
+
|
505 |
+
if ( 'core' !== $file_type ) {
|
506 |
+
$file_slug = $rel_paths[0];
|
507 |
+
}
|
508 |
+
|
509 |
+
$url_args[] = 'file-reference=' . $file_slug;
|
510 |
+
$url_args[] = 'file-type=' . $file_type;
|
511 |
+
$url_args[] = 'string-locator-line=' . absint( $line );
|
512 |
+
$url_args[] = 'string-locator-linepos=' . absint( $linepos );
|
513 |
+
$url_args[] = 'string-locator-path=' . urlencode( str_replace( '/', DIRECTORY_SEPARATOR, $path ) );
|
514 |
+
|
515 |
+
$url = admin_url( $this->path_to_use . '?' . implode( '&', $url_args ) );
|
516 |
+
|
517 |
+
return $url;
|
518 |
+
}
|
519 |
+
|
520 |
+
}
|
includes/class-string-locator.php
CHANGED
@@ -1,35 +1,24 @@
|
|
1 |
<?php
|
2 |
|
|
|
|
|
3 |
/**
|
4 |
* Class String_Locator
|
5 |
*/
|
6 |
class String_Locator {
|
7 |
/**
|
8 |
-
*
|
9 |
-
*
|
10 |
-
* @var
|
11 |
-
* @var bool $failed_edit Has there been a failed edit.
|
12 |
-
* @var string $path_to_use The path to the currently editable file.
|
13 |
-
* @var array $bad_http_codes An array of HTTP status codes that will trigger the rollback feature.
|
14 |
-
* @var array $bad_file_types An array of file extensions that will be ignored by the scanner.
|
15 |
-
* @var int $excerpt_length The length of the excerpt from the line containing a match.
|
16 |
-
* @var int|null $max_execution_time The server-configured max time a script can run.
|
17 |
-
* @var int $start_execution_time The current time when our script started executing.
|
18 |
-
* @var int $max_memory_consumption The server-configured max amount of memory a script can use.
|
19 |
*/
|
20 |
public $string_locator_language = '';
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
private $max_execution_time = null;
|
29 |
-
private $start_execution_timer = 0;
|
30 |
-
private $max_memory_consumption = 0;
|
31 |
-
|
32 |
-
private $rest_namespace = 'string-locator';
|
33 |
|
34 |
/**
|
35 |
* Construct the plugin
|
@@ -47,21 +36,6 @@ class String_Locator {
|
|
47 |
* @return void
|
48 |
*/
|
49 |
public function init() {
|
50 |
-
/**
|
51 |
-
* Define class variables requiring expressions
|
52 |
-
*/
|
53 |
-
$this->path_to_use = ( is_multisite() ? 'network/admin.php' : 'tools.php' );
|
54 |
-
$this->excerpt_length = apply_filters( 'string_locator_excerpt_length', 25 );
|
55 |
-
|
56 |
-
$this->max_execution_time = absint( ini_get( 'max_execution_time' ) );
|
57 |
-
$this->start_execution_timer = microtime( true );
|
58 |
-
|
59 |
-
if ( $this->max_execution_time > 30 ) {
|
60 |
-
$this->max_execution_time = 30;
|
61 |
-
}
|
62 |
-
|
63 |
-
$this->set_memory_limit();
|
64 |
-
|
65 |
add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
|
66 |
|
67 |
add_action( 'admin_menu', array( $this, 'populate_menu' ) );
|
@@ -71,53 +45,96 @@ class String_Locator {
|
|
71 |
|
72 |
add_action( 'plugins_loaded', array( $this, 'load_i18n' ) );
|
73 |
|
74 |
-
add_action( 'wp_ajax_string-locator-get-directory-structure', array( $this, 'ajax_get_directory_structure' ) );
|
75 |
-
add_action( 'wp_ajax_string-locator-search', array( $this, 'ajax_file_search' ) );
|
76 |
-
add_action( 'wp_ajax_string-locator-clean', array( $this, 'ajax_clean_search' ) );
|
77 |
-
|
78 |
add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
|
79 |
|
80 |
-
|
81 |
-
|
82 |
|
83 |
-
|
84 |
-
register_rest_route(
|
85 |
-
sprintf(
|
86 |
-
'%s/v1',
|
87 |
-
$this->rest_namespace
|
88 |
-
),
|
89 |
-
'/save',
|
90 |
-
array(
|
91 |
-
'methods' => 'POST',
|
92 |
-
'callback' => array( $this, 'editor_save' ),
|
93 |
-
'permission_callback' => function() {
|
94 |
-
return current_user_can( 'edit_themes' );
|
95 |
-
},
|
96 |
-
)
|
97 |
-
);
|
98 |
}
|
99 |
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
$memory_limit = ini_get( 'memory_limit' );
|
109 |
|
110 |
-
|
|
|
111 |
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
if ( strstr( $memory_limit, 'M' ) ) {
|
116 |
-
$this->max_memory_consumption = ( str_replace( 'M', '', $memory_limit ) * 1000000 );
|
117 |
}
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
120 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
}
|
122 |
|
123 |
/**
|
@@ -131,7 +148,7 @@ class String_Locator {
|
|
131 |
function plugin_row_meta( $meta, $plugin_file ) {
|
132 |
if ( 'string-locator/string-locator.php' === $plugin_file ) {
|
133 |
$meta[] = sprintf(
|
134 |
-
'<a href="https://
|
135 |
esc_html__( 'Donate to this plugin', 'string-locator' )
|
136 |
);
|
137 |
}
|
@@ -192,6 +209,8 @@ class String_Locator {
|
|
192 |
'string-locator-path' => ( isset( $_GET['string-locator-path'] ) ? $_GET['string-locator-path'] : '' ),
|
193 |
);
|
194 |
|
|
|
|
|
195 |
$field_output = array();
|
196 |
|
197 |
foreach ( $fields as $label => $value ) {
|
@@ -288,296 +307,6 @@ class String_Locator {
|
|
288 |
return false;
|
289 |
}
|
290 |
|
291 |
-
/**
|
292 |
-
* Handles the AJAX request to prepare the search hierarchy.
|
293 |
-
*
|
294 |
-
* @return void
|
295 |
-
*/
|
296 |
-
function ajax_get_directory_structure() {
|
297 |
-
if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) {
|
298 |
-
wp_send_json_error( __( 'Authentication failed', 'string-locator' ) );
|
299 |
-
}
|
300 |
-
|
301 |
-
$scan_path = $this->prepare_scan_path( $_POST['directory'] );
|
302 |
-
if ( is_file( $scan_path->path ) ) {
|
303 |
-
$files = array( $scan_path->path );
|
304 |
-
} else {
|
305 |
-
$files = $this->ajax_scan_path( $scan_path->path );
|
306 |
-
}
|
307 |
-
|
308 |
-
/*
|
309 |
-
* Make sure each chunk of file arrays never exceeds 500 files
|
310 |
-
* This is to prevent the SQL string from being too large and crashing everything
|
311 |
-
*/
|
312 |
-
$back_compat_filter = apply_filters( 'string-locator-files-per-array', 500 ); //phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
313 |
-
|
314 |
-
$file_chunks = array_chunk( $files, apply_filters( 'string_locator_files_per_array', $back_compat_filter ), true );
|
315 |
-
|
316 |
-
$store = (object) array(
|
317 |
-
'scan_path' => $scan_path,
|
318 |
-
'search' => wp_unslash( $_POST['search'] ),
|
319 |
-
'directory' => $_POST['directory'],
|
320 |
-
'chunks' => count( $file_chunks ),
|
321 |
-
'regex' => $_POST['regex'],
|
322 |
-
);
|
323 |
-
|
324 |
-
$response = array(
|
325 |
-
'total' => count( $files ),
|
326 |
-
'current' => 0,
|
327 |
-
'directory' => $scan_path,
|
328 |
-
'chunks' => count( $file_chunks ),
|
329 |
-
'regex' => $_POST['regex'],
|
330 |
-
);
|
331 |
-
|
332 |
-
set_transient( 'string-locator-search-overview', $store );
|
333 |
-
update_option( 'string-locator-search-history', array(), false );
|
334 |
-
|
335 |
-
foreach ( $file_chunks as $count => $file_chunk ) {
|
336 |
-
set_transient( 'string-locator-search-files-' . $count, $file_chunk );
|
337 |
-
}
|
338 |
-
|
339 |
-
wp_send_json_success( $response );
|
340 |
-
}
|
341 |
-
|
342 |
-
/**
|
343 |
-
* Check if the script is about to exceed the max execution time.
|
344 |
-
*
|
345 |
-
* @since 1.9.0
|
346 |
-
*
|
347 |
-
* @return bool
|
348 |
-
*/
|
349 |
-
function nearing_execution_limit() {
|
350 |
-
// Max execution time is 0 or -1 (infinite) in server config
|
351 |
-
if ( 0 === $this->max_execution_time || - 1 === $this->max_execution_time ) {
|
352 |
-
return false;
|
353 |
-
}
|
354 |
-
|
355 |
-
$back_compat_filter = apply_filters( 'string-locator-extra-search-delay', 2 ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
356 |
-
|
357 |
-
$built_in_delay = apply_filters( 'string_locator_extra_search_delay', $back_compat_filter );
|
358 |
-
$execution_time = ( microtime( true ) - $this->start_execution_timer + $built_in_delay );
|
359 |
-
|
360 |
-
if ( $execution_time >= $this->max_execution_time ) {
|
361 |
-
return $execution_time;
|
362 |
-
}
|
363 |
-
|
364 |
-
return false;
|
365 |
-
}
|
366 |
-
|
367 |
-
/**
|
368 |
-
* Check if the script is about to exceed the server memory limit.
|
369 |
-
*
|
370 |
-
* @since 2.0.0
|
371 |
-
*
|
372 |
-
* @return bool
|
373 |
-
*/
|
374 |
-
function nearing_memory_limit() {
|
375 |
-
// Check if the memory limit is set t o0 or -1 (infinite) in server config
|
376 |
-
if ( 0 === $this->max_memory_consumption || - 1 === $this->max_memory_consumption ) {
|
377 |
-
return false;
|
378 |
-
}
|
379 |
-
|
380 |
-
// We give our selves a 256k memory buffer, as we need to close off the script properly as well
|
381 |
-
$back_compat_filter = apply_filters( 'string-locator-extra-memory-buffer', 256000 ); //phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
382 |
-
$built_in_buffer = apply_filters( 'string_locator_extra_memory_buffer', $back_compat_filter );
|
383 |
-
$memory_use = ( memory_get_usage( true ) + $built_in_buffer );
|
384 |
-
|
385 |
-
if ( $memory_use >= $this->max_memory_consumption ) {
|
386 |
-
return $memory_use;
|
387 |
-
}
|
388 |
-
|
389 |
-
return false;
|
390 |
-
}
|
391 |
-
|
392 |
-
public static function absbool( $value ) {
|
393 |
-
if ( is_bool( $value ) ) {
|
394 |
-
$bool = $value;
|
395 |
-
} else {
|
396 |
-
if ( 'false' === $value ) {
|
397 |
-
$bool = false;
|
398 |
-
} else {
|
399 |
-
$bool = true;
|
400 |
-
}
|
401 |
-
}
|
402 |
-
|
403 |
-
return $bool;
|
404 |
-
}
|
405 |
-
|
406 |
-
/**
|
407 |
-
* Search an individual file supplied via AJAX.
|
408 |
-
*
|
409 |
-
* @since 1.9.0
|
410 |
-
*
|
411 |
-
* @return void
|
412 |
-
*/
|
413 |
-
function ajax_file_search() {
|
414 |
-
if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) {
|
415 |
-
wp_send_json_error( __( 'Authentication failed', 'string-locator' ) );
|
416 |
-
}
|
417 |
-
|
418 |
-
$back_compat_filter = apply_filters( 'string-locator-files-per-array', 500 ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
419 |
-
|
420 |
-
$files_per_chunk = apply_filters( 'string_locator_files_per_array', $back_compat_filter );
|
421 |
-
$response = array(
|
422 |
-
'search' => array(),
|
423 |
-
'filenum' => absint( $_POST['filenum'] ),
|
424 |
-
);
|
425 |
-
|
426 |
-
$filenum = absint( $_POST['filenum'] );
|
427 |
-
$next_file = $filenum + 1;
|
428 |
-
|
429 |
-
$next_chunk = ( ceil( ( $next_file ) / $files_per_chunk ) - 1 );
|
430 |
-
$chunk = ( ceil( $filenum / $files_per_chunk ) - 1 );
|
431 |
-
if ( $chunk < 0 ) {
|
432 |
-
$chunk = 0;
|
433 |
-
}
|
434 |
-
if ( $next_chunk < 0 ) {
|
435 |
-
$next_chunk = 0;
|
436 |
-
}
|
437 |
-
|
438 |
-
$scan_data = get_transient( 'string-locator-search-overview' );
|
439 |
-
$file_data = get_transient( 'string-locator-search-files-' . $chunk );
|
440 |
-
|
441 |
-
if ( ! isset( $file_data[ $filenum ] ) ) {
|
442 |
-
wp_send_json_error(
|
443 |
-
array(
|
444 |
-
'continue' => false,
|
445 |
-
'message' => sprintf(
|
446 |
-
/* translators: %d: The numbered reference to a file being searched. */
|
447 |
-
esc_html__( 'The file-number, %d, that was sent could not be found.', 'string-locator' ),
|
448 |
-
$filenum
|
449 |
-
),
|
450 |
-
)
|
451 |
-
);
|
452 |
-
}
|
453 |
-
|
454 |
-
if ( $this->nearing_execution_limit() ) {
|
455 |
-
wp_send_json_error(
|
456 |
-
array(
|
457 |
-
'continue' => false,
|
458 |
-
'message' => sprintf(
|
459 |
-
/* 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. */
|
460 |
-
esc_html__( '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' ),
|
461 |
-
$this->max_execution_time,
|
462 |
-
$this->nearing_execution_limit()
|
463 |
-
),
|
464 |
-
)
|
465 |
-
);
|
466 |
-
}
|
467 |
-
if ( $this->nearing_memory_limit() ) {
|
468 |
-
wp_send_json_error(
|
469 |
-
array(
|
470 |
-
'continue' => false,
|
471 |
-
'message' => sprintf(
|
472 |
-
/* translators: %1$d: Current amount of used system memory resources. %2$d: The maximum available system memory. */
|
473 |
-
esc_html__( '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' ),
|
474 |
-
$this->nearing_memory_limit(),
|
475 |
-
$this->max_memory_consumption
|
476 |
-
),
|
477 |
-
)
|
478 |
-
);
|
479 |
-
}
|
480 |
-
|
481 |
-
$is_regex = false;
|
482 |
-
if ( isset( $scan_data->regex ) ) {
|
483 |
-
$is_regex = $this->absbool( $scan_data->regex );
|
484 |
-
}
|
485 |
-
|
486 |
-
if ( $is_regex ) {
|
487 |
-
if ( false === @preg_match( $scan_data->search, '' ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
488 |
-
wp_send_json_error(
|
489 |
-
array(
|
490 |
-
'continue' => false,
|
491 |
-
'message' => sprintf(
|
492 |
-
/* translators: %s: The search string used. */
|
493 |
-
__( 'Your search string, <strong>%s</strong>, is not a valid pattern, and the search has been aborted.', 'string-locator' ),
|
494 |
-
esc_html( $scan_data->search )
|
495 |
-
),
|
496 |
-
)
|
497 |
-
);
|
498 |
-
}
|
499 |
-
}
|
500 |
-
|
501 |
-
while ( ! $this->nearing_execution_limit() && ! $this->nearing_memory_limit() && isset( $file_data[ $filenum ] ) ) {
|
502 |
-
$filenum = absint( $_POST['filenum'] );
|
503 |
-
$search_results = null;
|
504 |
-
$next_file = $filenum + 1;
|
505 |
-
|
506 |
-
$next_chunk = ( ceil( ( $next_file ) / $files_per_chunk ) - 1 );
|
507 |
-
$chunk = ( ceil( $filenum / $files_per_chunk ) - 1 );
|
508 |
-
if ( $chunk < 0 ) {
|
509 |
-
$chunk = 0;
|
510 |
-
}
|
511 |
-
if ( $next_chunk < 0 ) {
|
512 |
-
$next_chunk = 0;
|
513 |
-
}
|
514 |
-
|
515 |
-
if ( ! isset( $file_data[ $filenum ] ) ) {
|
516 |
-
$chunk ++;
|
517 |
-
$file_data = get_transient( 'string-locator-search-files-' . $chunk );
|
518 |
-
continue;
|
519 |
-
}
|
520 |
-
|
521 |
-
$file_name = explode( '/', $file_data[ $filenum ] );
|
522 |
-
$file_name = end( $file_name );
|
523 |
-
|
524 |
-
/*
|
525 |
-
* Check the file type, if it's an unsupported type, we skip it
|
526 |
-
*/
|
527 |
-
$file_type = explode( '.', $file_name );
|
528 |
-
$file_type = strtolower( end( $file_type ) );
|
529 |
-
|
530 |
-
/*
|
531 |
-
* Scan the file and look for our string, but only if it's an approved file extension
|
532 |
-
*/
|
533 |
-
$bad_file_types = apply_filters( 'string_locator_bad_file_types', $this->bad_file_types );
|
534 |
-
if ( ! in_array( $file_type, $bad_file_types, true ) ) {
|
535 |
-
$search_results = $this->scan_file( $file_data[ $filenum ], $scan_data->search, $file_data[ $filenum ], $scan_data->scan_path->type, '', $is_regex );
|
536 |
-
}
|
537 |
-
|
538 |
-
$response['last_file'] = $file_data[ $filenum ];
|
539 |
-
$response['filenum'] = $filenum;
|
540 |
-
$response['filename'] = $file_name;
|
541 |
-
if ( $search_results ) {
|
542 |
-
$response['search'][] = $search_results;
|
543 |
-
}
|
544 |
-
|
545 |
-
if ( $next_chunk !== $chunk ) {
|
546 |
-
$file_data = get_transient( 'string-locator-search-files-' . $next_chunk );
|
547 |
-
}
|
548 |
-
|
549 |
-
$response['next_file'] = ( isset( $file_data[ $next_file ] ) ? $file_data[ $next_file ] : '' );
|
550 |
-
|
551 |
-
if ( ! empty( $search_results ) ) {
|
552 |
-
$history = get_option( 'string-locator-search-history', array() );
|
553 |
-
$history = array_merge( $history, $search_results );
|
554 |
-
update_option( 'string-locator-search-history', $history, false );
|
555 |
-
}
|
556 |
-
|
557 |
-
$_POST['filenum'] ++;
|
558 |
-
}
|
559 |
-
|
560 |
-
wp_send_json_success( $response );
|
561 |
-
}
|
562 |
-
|
563 |
-
/**
|
564 |
-
* Clean up our options used to help during the search.
|
565 |
-
*
|
566 |
-
* @return void
|
567 |
-
*/
|
568 |
-
function ajax_clean_search() {
|
569 |
-
if ( ! check_ajax_referer( 'string-locator-search', 'nonce', false ) ) {
|
570 |
-
wp_send_json_error( __( 'Authentication failed', 'string-locator' ) );
|
571 |
-
}
|
572 |
-
|
573 |
-
$scan_data = get_transient( 'string-locator-search-overview' );
|
574 |
-
for ( $i = 0; $i < $scan_data->chunks; $i ++ ) {
|
575 |
-
delete_transient( 'string-locator-search-files-' . $i );
|
576 |
-
}
|
577 |
-
|
578 |
-
wp_send_json_success( true );
|
579 |
-
}
|
580 |
-
|
581 |
/**
|
582 |
* Create a table row for insertion into the search results list.
|
583 |
*
|
@@ -647,10 +376,10 @@ class String_Locator {
|
|
647 |
|
648 |
$table_columns = sprintf(
|
649 |
'<tr>
|
650 |
-
<th scope="col" class="manage-column column-stringresult column-primary">%s</th>
|
651 |
-
<th scope="col" class="manage-column column-filename">%s</th>
|
652 |
-
<th scope="col" class="manage-column column-linenum">%s</th>
|
653 |
-
<th scope="col" class="manage-column column-linepos">%s</th>
|
654 |
</tr>',
|
655 |
esc_html( __( 'String', 'string-locator' ) ),
|
656 |
esc_html( __( 'File', 'string-locator' ) ),
|
@@ -675,150 +404,34 @@ class String_Locator {
|
|
675 |
}
|
676 |
|
677 |
/**
|
678 |
-
*
|
679 |
-
*
|
680 |
-
* @param string $path Path to the file we'er adding a link for.
|
681 |
-
* @param int $line The line in the file where our search result was found.
|
682 |
-
* @param int $linepos The positin in the line where the search result was found.
|
683 |
-
*
|
684 |
-
* @return string
|
685 |
-
*/
|
686 |
-
function create_edit_link( $path, $line = 0, $linepos = 0 ) {
|
687 |
-
$file_type = 'core';
|
688 |
-
$file_slug = '';
|
689 |
-
$content_path = str_replace( '\\', '/', WP_CONTENT_DIR );
|
690 |
-
|
691 |
-
$path = str_replace( '\\', '/', $path );
|
692 |
-
$paths = explode( '/', $path );
|
693 |
-
|
694 |
-
$url_args = array(
|
695 |
-
'page=string-locator',
|
696 |
-
'edit-file=' . end( $paths ),
|
697 |
-
);
|
698 |
-
|
699 |
-
switch ( true ) {
|
700 |
-
case ( in_array( 'wp-content', $paths, true ) && in_array( 'plugins', $paths, true ) ):
|
701 |
-
$file_type = 'plugin';
|
702 |
-
$content_path .= '/plugins/';
|
703 |
-
break;
|
704 |
-
case ( in_array( 'wp-content', $paths, true ) && in_array( 'themes', $paths, true ) ):
|
705 |
-
$file_type = 'theme';
|
706 |
-
$content_path .= '/themes/';
|
707 |
-
break;
|
708 |
-
}
|
709 |
-
|
710 |
-
$rel_path = str_replace( $content_path, '', $path );
|
711 |
-
$rel_paths = explode( '/', $rel_path );
|
712 |
-
|
713 |
-
if ( 'core' !== $file_type ) {
|
714 |
-
$file_slug = $rel_paths[0];
|
715 |
-
}
|
716 |
-
|
717 |
-
$url_args[] = 'file-reference=' . $file_slug;
|
718 |
-
$url_args[] = 'file-type=' . $file_type;
|
719 |
-
$url_args[] = 'string-locator-line=' . absint( $line );
|
720 |
-
$url_args[] = 'string-locator-linepos=' . absint( $linepos );
|
721 |
-
$url_args[] = 'string-locator-path=' . urlencode( str_replace( '/', DIRECTORY_SEPARATOR, $path ) );
|
722 |
-
|
723 |
-
$url = admin_url( $this->path_to_use . '?' . implode( '&', $url_args ) );
|
724 |
-
|
725 |
-
return $url;
|
726 |
-
}
|
727 |
-
|
728 |
-
/**
|
729 |
-
* Parse the search option to determine what kind of search we are performing and what directory to start in.
|
730 |
-
*
|
731 |
-
* @param string $option The search-type identifier.
|
732 |
*
|
733 |
-
* @return
|
734 |
*/
|
735 |
-
function
|
736 |
-
$
|
737 |
-
|
738 |
-
'type' => '',
|
739 |
-
'slug' => '',
|
740 |
-
);
|
741 |
-
|
742 |
-
switch ( true ) {
|
743 |
-
case ( 't--' === $option ):
|
744 |
-
$data['path'] = WP_CONTENT_DIR . '/themes/';
|
745 |
-
$data['type'] = 'theme';
|
746 |
-
break;
|
747 |
-
case ( strlen( $option ) > 3 && 't-' === substr( $option, 0, 2 ) ):
|
748 |
-
$data['path'] = WP_CONTENT_DIR . '/themes/' . substr( $option, 2 );
|
749 |
-
$data['type'] = 'theme';
|
750 |
-
$data['slug'] = substr( $option, 2 );
|
751 |
-
break;
|
752 |
-
case ( 'p--' === $option ):
|
753 |
-
$data['path'] = WP_CONTENT_DIR . '/plugins/';
|
754 |
-
$data['type'] = 'plugin';
|
755 |
-
break;
|
756 |
-
case ( 'mup--' === $option ):
|
757 |
-
$data['path'] = WP_CONTENT_DIR . '/mu-plugins/';
|
758 |
-
$data['type'] = 'mu-plugin';
|
759 |
-
break;
|
760 |
-
case ( strlen( $option ) > 3 && 'p-' === substr( $option, 0, 2 ) ):
|
761 |
-
$slug = explode( '/', substr( $option, 2 ) );
|
762 |
-
|
763 |
-
$data['path'] = WP_CONTENT_DIR . '/plugins/' . $slug[0];
|
764 |
-
$data['type'] = 'plugin';
|
765 |
-
$data['slug'] = $slug[0];
|
766 |
-
break;
|
767 |
-
case ( 'core' === $option ):
|
768 |
-
$data['path'] = ABSPATH;
|
769 |
-
$data['type'] = 'core';
|
770 |
-
break;
|
771 |
-
case ( 'wp-content' === $option ):
|
772 |
-
$data['path'] = WP_CONTENT_DIR;
|
773 |
-
$data['type'] = 'core';
|
774 |
-
break;
|
775 |
-
}
|
776 |
-
|
777 |
-
if ( empty( $data['path'] ) ) {
|
778 |
-
return false;
|
779 |
-
}
|
780 |
-
|
781 |
-
return (object) $data;
|
782 |
}
|
783 |
|
784 |
/**
|
785 |
-
*
|
786 |
*
|
787 |
-
* @param
|
788 |
*
|
789 |
* @return bool
|
790 |
*/
|
791 |
-
function
|
792 |
-
$
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
if ( empty( $path ) ) {
|
802 |
-
$valid = false;
|
803 |
-
}
|
804 |
-
if ( stristr( $path, '..' ) ) {
|
805 |
-
$valid = false;
|
806 |
-
}
|
807 |
-
if ( ! stristr( $path, $abspath ) ) {
|
808 |
-
$valid = false;
|
809 |
}
|
810 |
|
811 |
-
return $
|
812 |
-
}
|
813 |
-
|
814 |
-
/**
|
815 |
-
* Set the text domain for translated plugin content.
|
816 |
-
*
|
817 |
-
* @return void
|
818 |
-
*/
|
819 |
-
function load_i18n() {
|
820 |
-
$i18n_dir = 'string-locator/languages/';
|
821 |
-
load_plugin_textdomain( 'string-locator', false, $i18n_dir );
|
822 |
}
|
823 |
|
824 |
/**
|
@@ -832,30 +445,34 @@ class String_Locator {
|
|
832 |
return;
|
833 |
}
|
834 |
|
835 |
-
|
836 |
-
|
837 |
-
}
|
838 |
|
839 |
-
|
840 |
-
|
841 |
-
}
|
842 |
|
843 |
/**
|
844 |
* String Locator Styles
|
845 |
*/
|
846 |
-
wp_enqueue_style( 'string-locator', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . '
|
847 |
|
848 |
if ( ! isset( $_GET['edit-file'] ) || ! current_user_can( 'edit_themes' ) ) {
|
849 |
/**
|
850 |
* String Locator Scripts
|
851 |
*/
|
852 |
-
wp_enqueue_script(
|
|
|
|
|
|
|
|
|
|
|
|
|
853 |
|
854 |
wp_localize_script(
|
855 |
'string-locator-search',
|
856 |
'string_locator',
|
857 |
array(
|
858 |
-
'
|
859 |
'search_nonce' => wp_create_nonce( 'string-locator-search' ),
|
860 |
'search_current_prefix' => __( 'Next file: ', 'string-locator' ),
|
861 |
'saving_results_string' => __( 'Saving search results…', 'string-locator' ),
|
@@ -864,6 +481,11 @@ class String_Locator {
|
|
864 |
'search_error' => __( 'The above error was returned by your server, for more details please consult your servers error logs.', 'string-locator' ),
|
865 |
'search_no_results' => __( 'Your search was completed, but no results were found.', 'string-locator' ),
|
866 |
'warning_title' => __( 'Warning', 'string-locator' ),
|
|
|
|
|
|
|
|
|
|
|
867 |
)
|
868 |
);
|
869 |
|
@@ -877,7 +499,13 @@ class String_Locator {
|
|
877 |
/**
|
878 |
* String Locator Scripts
|
879 |
*/
|
880 |
-
wp_enqueue_script(
|
|
|
|
|
|
|
|
|
|
|
|
|
881 |
|
882 |
wp_localize_script(
|
883 |
'string-locator-editor',
|
@@ -886,7 +514,9 @@ class String_Locator {
|
|
886 |
'CodeMirror' => $code_mirror,
|
887 |
'goto_line' => absint( $_GET['string-locator-line'] ),
|
888 |
'goto_linepos' => absint( $_GET['string-locator-linepos'] ),
|
889 |
-
'
|
|
|
|
|
890 |
)
|
891 |
);
|
892 |
}
|
@@ -939,6 +569,8 @@ class String_Locator {
|
|
939 |
return false;
|
940 |
}
|
941 |
|
|
|
|
|
942 |
/**
|
943 |
* Show the edit page if;
|
944 |
* - The edit file path query var is set
|
@@ -946,273 +578,25 @@ class String_Locator {
|
|
946 |
* - The edit file path query var does not contains double dots (used to traverse directories)
|
947 |
* - The user is capable of editing files.
|
948 |
*/
|
949 |
-
if ( isset( $_GET['string-locator-path'] ) &&
|
950 |
-
|
951 |
} else {
|
952 |
-
|
953 |
-
}
|
954 |
-
}
|
955 |
-
|
956 |
-
function admin_body_class( $class ) {
|
957 |
-
if ( isset( $_GET['string-locator-path'] ) && $this->is_valid_location( $_GET['string-locator-path'] ) && current_user_can( 'edit_themes' ) ) {
|
958 |
-
$class .= ' file-edit-screen';
|
959 |
}
|
960 |
|
961 |
-
|
962 |
-
}
|
963 |
|
964 |
-
|
965 |
-
|
966 |
-
*
|
967 |
-
* @param string $start Start delimited.
|
968 |
-
* @param string $end End delimiter.
|
969 |
-
* @param string $string The string to scan.
|
970 |
-
*
|
971 |
-
* @return array
|
972 |
-
*/
|
973 |
-
function smart_scan( $start, $end, $string ) {
|
974 |
-
$opened = array();
|
975 |
-
|
976 |
-
$lines = explode( "\n", $string );
|
977 |
-
for ( $i = 0; $i < count( $lines ); $i ++ ) {
|
978 |
-
if ( stristr( $lines[ $i ], $start ) ) {
|
979 |
-
$opened[] = $i;
|
980 |
-
}
|
981 |
-
if ( stristr( $lines[ $i ], $end ) ) {
|
982 |
-
array_pop( $opened );
|
983 |
-
}
|
984 |
}
|
985 |
-
|
986 |
-
return $opened;
|
987 |
}
|
988 |
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
* Also runs over the Smart-Scan if enabled.
|
993 |
-
*
|
994 |
-
* @return void|array
|
995 |
-
*/
|
996 |
-
function editor_save( $request ) {
|
997 |
-
$_POST = $request->get_params();
|
998 |
-
|
999 |
-
$check_loopback = isset( $_POST['string-locator-loopback-check'] );
|
1000 |
-
$do_smart_scan = isset( $_POST['string-locator-smart-edit'] );
|
1001 |
-
|
1002 |
-
if ( $this->is_valid_location( $_POST['string-locator-path'] ) ) {
|
1003 |
-
$path = urldecode( $_POST['string-locator-path'] );
|
1004 |
-
$content = stripslashes( $_POST['string-locator-editor-content'] );
|
1005 |
-
|
1006 |
-
/**
|
1007 |
-
* Send an error notice if the file isn't writable
|
1008 |
-
*/
|
1009 |
-
if ( ! is_writeable( $path ) ) {
|
1010 |
-
$this->notice[] = array(
|
1011 |
-
'type' => 'error',
|
1012 |
-
'message' => __( 'The file could not be written to, please check file permissions or edit it manually.', 'string-locator' ),
|
1013 |
-
);
|
1014 |
-
|
1015 |
-
return array(
|
1016 |
-
'notices' => $this->notice,
|
1017 |
-
);
|
1018 |
-
}
|
1019 |
-
|
1020 |
-
/**
|
1021 |
-
* If enabled, run the Smart-Scan on the content before saving it
|
1022 |
-
*/
|
1023 |
-
if ( $do_smart_scan ) {
|
1024 |
-
$open_brace = substr_count( $content, '{' );
|
1025 |
-
$close_brace = substr_count( $content, '}' );
|
1026 |
-
if ( $open_brace !== $close_brace ) {
|
1027 |
-
$this->failed_edit = true;
|
1028 |
-
|
1029 |
-
$opened = $this->smart_scan( '{', '}', $content );
|
1030 |
-
|
1031 |
-
foreach ( $opened as $line ) {
|
1032 |
-
$this->notice[] = array(
|
1033 |
-
'type' => 'error',
|
1034 |
-
'message' => sprintf(
|
1035 |
-
// translators: 1: Line number with an error.
|
1036 |
-
__( 'There is an inconsistency in the opening and closing braces, { and }, of your file on line %s', 'string-locator' ),
|
1037 |
-
'<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
|
1038 |
-
),
|
1039 |
-
);
|
1040 |
-
}
|
1041 |
-
}
|
1042 |
-
|
1043 |
-
$open_bracket = substr_count( $content, '[' );
|
1044 |
-
$close_bracket = substr_count( $content, ']' );
|
1045 |
-
if ( $open_bracket !== $close_bracket ) {
|
1046 |
-
$this->failed_edit = true;
|
1047 |
-
|
1048 |
-
$opened = $this->smart_scan( '[', ']', $content );
|
1049 |
-
|
1050 |
-
foreach ( $opened as $line ) {
|
1051 |
-
$this->notice[] = array(
|
1052 |
-
'type' => 'error',
|
1053 |
-
'message' => sprintf(
|
1054 |
-
// translators: 1: Line number with an error.
|
1055 |
-
__( 'There is an inconsistency in the opening and closing braces, [ and ], of your file on line %s', 'string-locator' ),
|
1056 |
-
'<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
|
1057 |
-
),
|
1058 |
-
);
|
1059 |
-
}
|
1060 |
-
}
|
1061 |
-
|
1062 |
-
$open_parenthesis = substr_count( $content, '(' );
|
1063 |
-
$close_parenthesis = substr_count( $content, ')' );
|
1064 |
-
if ( $open_parenthesis !== $close_parenthesis ) {
|
1065 |
-
$this->failed_edit = true;
|
1066 |
-
|
1067 |
-
$opened = $this->smart_scan( '(', ')', $content );
|
1068 |
-
|
1069 |
-
foreach ( $opened as $line ) {
|
1070 |
-
$this->notice[] = array(
|
1071 |
-
'type' => 'error',
|
1072 |
-
'message' => sprintf(
|
1073 |
-
// translators: 1: Line number with an error.
|
1074 |
-
__( 'There is an inconsistency in the opening and closing braces, ( and ), of your file on line %s', 'string-locator' ),
|
1075 |
-
'<a href="#" class="string-locator-edit-goto" data-goto-line="' . ( $line + 1 ) . '">' . ( $line + 1 ) . '</a>'
|
1076 |
-
),
|
1077 |
-
);
|
1078 |
-
}
|
1079 |
-
}
|
1080 |
-
|
1081 |
-
if ( $this->failed_edit ) {
|
1082 |
-
return array(
|
1083 |
-
'notices' => $this->notice,
|
1084 |
-
);
|
1085 |
-
}
|
1086 |
-
}
|
1087 |
-
|
1088 |
-
$original = file_get_contents( $path );
|
1089 |
-
|
1090 |
-
$this->write_file( $path, $content );
|
1091 |
-
|
1092 |
-
/**
|
1093 |
-
* Check the status of the site after making our edits.
|
1094 |
-
* If the site fails, revert the changes to return the sites to its original state
|
1095 |
-
*/
|
1096 |
-
if ( $check_loopback ) {
|
1097 |
-
$header = wp_remote_head( site_url() );
|
1098 |
-
|
1099 |
-
if ( ! is_wp_error( $header ) && 301 === (int) $header['response']['code'] ) {
|
1100 |
-
$header = wp_remote_head( $header['headers']['location'] );
|
1101 |
-
}
|
1102 |
-
|
1103 |
-
$bad_http_check = apply_filters( 'string_locator_bad_http_codes', $this->bad_http_codes );
|
1104 |
-
}
|
1105 |
-
|
1106 |
-
if ( $check_loopback && is_wp_error( $header ) ) {
|
1107 |
-
$this->failed_edit = true;
|
1108 |
-
$this->write_file( $path, $original );
|
1109 |
-
|
1110 |
-
// Likely loopback error, so be useful in our errors.
|
1111 |
-
if ( 'http_request_failed' === $header->get_error_code() ) {
|
1112 |
-
return array(
|
1113 |
-
'notices' => array(
|
1114 |
-
array(
|
1115 |
-
'type' => 'error',
|
1116 |
-
'message' => __( 'Your changes were not saved, as a check of your site could not be completed afterwards. This may be due to a <a href="https://wordpress.org/support/article/loopbacks/">loopback</a> error.', 'string-locator' ),
|
1117 |
-
),
|
1118 |
-
),
|
1119 |
-
);
|
1120 |
-
}
|
1121 |
-
|
1122 |
-
// Fallback error message here.
|
1123 |
-
return array(
|
1124 |
-
'notices' => array(
|
1125 |
-
array(
|
1126 |
-
'type' => 'error',
|
1127 |
-
'message' => $header->get_error_message(),
|
1128 |
-
),
|
1129 |
-
),
|
1130 |
-
);
|
1131 |
-
} elseif ( $check_loopback && in_array( $header['response']['code'], $bad_http_check, true ) ) {
|
1132 |
-
$this->failed_edit = true;
|
1133 |
-
$this->write_file( $path, $original );
|
1134 |
-
|
1135 |
-
return array(
|
1136 |
-
'notices' => array(
|
1137 |
-
array(
|
1138 |
-
'type' => 'error',
|
1139 |
-
'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' ),
|
1140 |
-
),
|
1141 |
-
),
|
1142 |
-
);
|
1143 |
-
} else {
|
1144 |
-
return array(
|
1145 |
-
'notices' => array(
|
1146 |
-
array(
|
1147 |
-
'type' => 'success',
|
1148 |
-
'message' => __( 'The file has been saved', 'string-locator' ),
|
1149 |
-
),
|
1150 |
-
),
|
1151 |
-
);
|
1152 |
-
}
|
1153 |
-
} else {
|
1154 |
-
return array(
|
1155 |
-
'notices' => array(
|
1156 |
-
array(
|
1157 |
-
'type' => 'error',
|
1158 |
-
'message' => sprintf(
|
1159 |
-
// translators: %s: The file location that was sent.
|
1160 |
-
__( 'The file location provided, <strong>%s</strong>, is not valid.', 'string-locator' ),
|
1161 |
-
$_POST['string-locator-path']
|
1162 |
-
),
|
1163 |
-
),
|
1164 |
-
),
|
1165 |
-
);
|
1166 |
-
}
|
1167 |
-
}
|
1168 |
-
|
1169 |
-
/**
|
1170 |
-
* When editing a file, this is where we write all the new content.
|
1171 |
-
* We will break early if the user isn't allowed to edit files.
|
1172 |
-
*
|
1173 |
-
* @param string $path The path to the file.
|
1174 |
-
* @param string $content The content to write to the file.
|
1175 |
-
*
|
1176 |
-
* @return void
|
1177 |
-
*/
|
1178 |
-
private function write_file( $path, $content ) {
|
1179 |
-
if ( ! current_user_can( 'edit_themes' ) ) {
|
1180 |
-
return;
|
1181 |
-
}
|
1182 |
-
|
1183 |
-
// Verify the location is valid before we try using it.
|
1184 |
-
if ( ! $this->is_valid_location( $path ) ) {
|
1185 |
-
return;
|
1186 |
-
}
|
1187 |
-
|
1188 |
-
$back_compat_filter = apply_filters( 'string-locator-filter-closing-php-tags', true ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
1189 |
-
|
1190 |
-
if ( apply_filters( 'string_locator_filter_closing_php_tags', $back_compat_filter ) ) {
|
1191 |
-
$content = preg_replace( '/\?>$/si', '', trim( $content ), - 1, $replaced_strings );
|
1192 |
-
|
1193 |
-
if ( $replaced_strings >= 1 ) {
|
1194 |
-
$this->notice[] = array(
|
1195 |
-
'type' => 'error',
|
1196 |
-
'message' => __( 'We detected a PHP code tag ending, this has been automatically stripped out to help prevent errors in your code.', 'string-locator' ),
|
1197 |
-
);
|
1198 |
-
}
|
1199 |
-
}
|
1200 |
-
|
1201 |
-
$file = fopen( $path, 'w' );
|
1202 |
-
$lines = explode( "\n", str_replace( array( "\r\n", "\r" ), "\n", $content ) );
|
1203 |
-
$total_lines = count( $lines );
|
1204 |
-
|
1205 |
-
for ( $i = 0; $i < $total_lines; $i ++ ) {
|
1206 |
-
$write_line = $lines[ $i ];
|
1207 |
-
|
1208 |
-
if ( ( $i + 1 ) < $total_lines ) {
|
1209 |
-
$write_line .= PHP_EOL;
|
1210 |
-
}
|
1211 |
-
|
1212 |
-
fwrite( $file, $write_line );
|
1213 |
}
|
1214 |
|
1215 |
-
|
1216 |
}
|
1217 |
|
1218 |
/**
|
@@ -1233,196 +617,32 @@ class String_Locator {
|
|
1233 |
}
|
1234 |
|
1235 |
/**
|
1236 |
-
*
|
1237 |
*
|
1238 |
-
* @param string $
|
1239 |
-
* @param string $string The search string.
|
1240 |
-
* @param mixed $location The file location object/string.
|
1241 |
-
* @param string $type File type.
|
1242 |
-
* @param string $slug The plugin/theme slug of the file.
|
1243 |
-
* @param boolean $regex Should a regex search be performed.
|
1244 |
*
|
1245 |
-
* @return
|
1246 |
*/
|
1247 |
-
function
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
1251 |
-
$output = array();
|
1252 |
-
$linenum = 0;
|
1253 |
-
$match_count = 0;
|
1254 |
-
|
1255 |
-
if ( ! is_object( $location ) ) {
|
1256 |
-
$path = $location;
|
1257 |
-
$location = explode( DIRECTORY_SEPARATOR, $location );
|
1258 |
-
$file = end( $location );
|
1259 |
-
} else {
|
1260 |
-
$path = $location->getPathname();
|
1261 |
-
$file = $location->getFilename();
|
1262 |
-
}
|
1263 |
-
|
1264 |
-
/*
|
1265 |
-
* Check if the filename matches our search pattern
|
1266 |
-
*/
|
1267 |
-
if ( stristr( $file, $string ) || ( $regex && preg_match( $string, $file ) ) ) {
|
1268 |
-
$relativepath = str_replace(
|
1269 |
-
array(
|
1270 |
-
ABSPATH,
|
1271 |
-
'\\',
|
1272 |
-
'/',
|
1273 |
-
),
|
1274 |
-
array(
|
1275 |
-
'',
|
1276 |
-
DIRECTORY_SEPARATOR,
|
1277 |
-
DIRECTORY_SEPARATOR,
|
1278 |
-
),
|
1279 |
-
$path
|
1280 |
-
);
|
1281 |
-
$match_count ++;
|
1282 |
-
|
1283 |
-
$editurl = $this->create_edit_link( $path, $linenum );
|
1284 |
-
|
1285 |
-
$path_string = sprintf(
|
1286 |
-
'<a href="%s">%s</a>',
|
1287 |
-
esc_url( $editurl ),
|
1288 |
-
esc_html( $relativepath )
|
1289 |
-
);
|
1290 |
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
1294 |
-
'[%s]',
|
1295 |
-
esc_html__( 'Filename matches search', 'string-locator' )
|
1296 |
-
),
|
1297 |
-
'linepos' => '',
|
1298 |
-
'path' => $path,
|
1299 |
-
'filename' => $path_string,
|
1300 |
-
'filename_raw' => $relativepath,
|
1301 |
-
'editurl' => ( current_user_can( 'edit_themes' ) ? $editurl : false ),
|
1302 |
-
'stringresult' => $file,
|
1303 |
-
);
|
1304 |
}
|
1305 |
|
1306 |
-
|
1307 |
-
|
1308 |
-
while ( ( $readline = fgets( $readfile ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
|
1309 |
-
$string_preview_is_cut = false;
|
1310 |
-
$linenum ++;
|
1311 |
-
/**
|
1312 |
-
* If our string is found in this line, output the line number and other data
|
1313 |
-
*/
|
1314 |
-
if ( ( ! $regex && stristr( $readline, $string ) ) || ( $regex && preg_match( $string, $readline, $match, PREG_OFFSET_CAPTURE ) ) ) {
|
1315 |
-
/**
|
1316 |
-
* Prepare the visual path for the end user
|
1317 |
-
* Removes path leading up to WordPress root and ensures consistent directory separators
|
1318 |
-
*/
|
1319 |
-
$relativepath = str_replace(
|
1320 |
-
array(
|
1321 |
-
ABSPATH,
|
1322 |
-
'\\',
|
1323 |
-
'/',
|
1324 |
-
),
|
1325 |
-
array(
|
1326 |
-
'',
|
1327 |
-
DIRECTORY_SEPARATOR,
|
1328 |
-
DIRECTORY_SEPARATOR,
|
1329 |
-
),
|
1330 |
-
$path
|
1331 |
-
);
|
1332 |
-
$match_count ++;
|
1333 |
-
|
1334 |
-
if ( $regex ) {
|
1335 |
-
$str_pos = $match[0][1];
|
1336 |
-
} else {
|
1337 |
-
$str_pos = stripos( $readline, $string );
|
1338 |
-
}
|
1339 |
-
|
1340 |
-
/**
|
1341 |
-
* Create the URL to take the user to the editor
|
1342 |
-
*/
|
1343 |
-
$editurl = $this->create_edit_link( $path, $linenum, $str_pos );
|
1344 |
-
|
1345 |
-
$string_preview = $readline;
|
1346 |
-
if ( strlen( $string_preview ) > ( strlen( $string ) + $this->excerpt_length ) ) {
|
1347 |
-
$string_location = strpos( $string_preview, $string );
|
1348 |
-
|
1349 |
-
$string_location_start = $string_location - $this->excerpt_length;
|
1350 |
-
if ( $string_location_start < 0 ) {
|
1351 |
-
$string_location_start = 0;
|
1352 |
-
}
|
1353 |
-
|
1354 |
-
$string_location_end = ( strlen( $string ) + ( $this->excerpt_length * 2 ) );
|
1355 |
-
if ( $string_location_end > strlen( $string_preview ) ) {
|
1356 |
-
$string_location_end = strlen( $string_preview );
|
1357 |
-
}
|
1358 |
-
|
1359 |
-
$string_preview = substr( $string_preview, $string_location_start, $string_location_end );
|
1360 |
-
$string_preview_is_cut = true;
|
1361 |
-
}
|
1362 |
-
|
1363 |
-
if ( $regex ) {
|
1364 |
-
$string_preview = preg_replace( preg_replace( '/\/(.+)\//', '/($1)/', $string ), '<strong>$1</strong>', esc_html( $string_preview ) );
|
1365 |
-
} else {
|
1366 |
-
$string_preview = preg_replace( '/(' . $string . ')/i', '<strong>$1</strong>', esc_html( $string_preview ) );
|
1367 |
-
}
|
1368 |
-
if ( $string_preview_is_cut ) {
|
1369 |
-
$string_preview = sprintf(
|
1370 |
-
'…%s…',
|
1371 |
-
$string_preview
|
1372 |
-
);
|
1373 |
-
}
|
1374 |
-
|
1375 |
-
$path_string = sprintf(
|
1376 |
-
'<a href="%s">%s</a>',
|
1377 |
-
esc_url( $editurl ),
|
1378 |
-
esc_html( $relativepath )
|
1379 |
-
);
|
1380 |
-
|
1381 |
-
$output[] = array(
|
1382 |
-
'ID' => $match_count,
|
1383 |
-
'linenum' => $linenum,
|
1384 |
-
'linepos' => $str_pos,
|
1385 |
-
'path' => $path,
|
1386 |
-
'filename' => $path_string,
|
1387 |
-
'filename_raw' => $relativepath,
|
1388 |
-
'editurl' => ( current_user_can( 'edit_themes' ) ? $editurl : false ),
|
1389 |
-
'stringresult' => $string_preview,
|
1390 |
-
);
|
1391 |
-
}
|
1392 |
-
}
|
1393 |
-
|
1394 |
-
fclose( $readfile );
|
1395 |
-
} else {
|
1396 |
-
/**
|
1397 |
-
* The file was unreadable, give the user a friendly notification
|
1398 |
-
*/
|
1399 |
-
$output[] = array(
|
1400 |
-
'linenum' => '#',
|
1401 |
-
// translators: 1: Filename.
|
1402 |
-
'filename' => esc_html( sprintf( __( 'Could not read file: %s', 'string-locator' ), $filename ) ),
|
1403 |
-
'stringresult' => '',
|
1404 |
-
);
|
1405 |
}
|
1406 |
-
|
1407 |
-
|
1408 |
-
|
1409 |
-
|
1410 |
-
|
1411 |
-
$files = array();
|
1412 |
-
|
1413 |
-
$paths = new RecursiveIteratorIterator(
|
1414 |
-
new RecursiveDirectoryIterator( $path ),
|
1415 |
-
RecursiveIteratorIterator::SELF_FIRST
|
1416 |
-
);
|
1417 |
-
|
1418 |
-
foreach ( $paths as $name => $location ) {
|
1419 |
-
if ( is_dir( $location->getPathname() ) ) {
|
1420 |
-
continue;
|
1421 |
-
}
|
1422 |
-
|
1423 |
-
$files[] = $location->getPathname();
|
1424 |
}
|
1425 |
|
1426 |
-
return $
|
1427 |
}
|
1428 |
}
|
1 |
<?php
|
2 |
|
3 |
+
namespace JITS\StringLocator;
|
4 |
+
|
5 |
/**
|
6 |
* Class String_Locator
|
7 |
*/
|
8 |
class String_Locator {
|
9 |
/**
|
10 |
+
* The code language used for the editing page.
|
11 |
+
*
|
12 |
+
* @var string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
*/
|
14 |
public $string_locator_language = '';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* An array containing all notices to display.
|
18 |
+
*
|
19 |
+
* @var array
|
20 |
+
*/
|
21 |
+
public $notice = array();
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
/**
|
24 |
* Construct the plugin
|
36 |
* @return void
|
37 |
*/
|
38 |
public function init() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
|
40 |
|
41 |
add_action( 'admin_menu', array( $this, 'populate_menu' ) );
|
45 |
|
46 |
add_action( 'plugins_loaded', array( $this, 'load_i18n' ) );
|
47 |
|
|
|
|
|
|
|
|
|
48 |
add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
|
49 |
|
50 |
+
add_filter( 'string_locator_search_sources_markup', array( $this, 'add_search_options' ), 10, 2 );
|
51 |
+
add_action( 'wp_ajax_string_locator_notice_dismiss', array( $this, 'dismiss_notice' ) );
|
52 |
|
53 |
+
add_action( 'admin_notices', array( $this, 'show_sponsorship_notice' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
+
public function dismiss_notice() {
|
57 |
+
// Only users who can use the plugin should be able to dismiss the notice.
|
58 |
+
if ( ! current_user_can( 'update_core' ) || ! wp_verify_nonce( $_POST['_nonce'], 'string-locator-notice-dismiss' ) ) {
|
59 |
+
return;
|
60 |
+
}
|
61 |
+
|
62 |
+
update_option( 'string-locator-sponsorship-notice-dismissed', array( 'is_dismissed' => true ) );
|
63 |
+
}
|
|
|
64 |
|
65 |
+
public function show_sponsorship_notice() {
|
66 |
+
$screen = get_current_screen();
|
67 |
|
68 |
+
// Only show the notice on the plugins search page.
|
69 |
+
if ( 'tools_page_string-locator' !== $screen->id || isset( $_GET['edit-file'] ) ) {
|
70 |
+
return;
|
|
|
|
|
71 |
}
|
72 |
+
|
73 |
+
// Do not show the notice to users who have dismissed it.
|
74 |
+
$option = get_option( 'string-locator-sponsorship-notice-dismissed', array( 'is_dismissed' => false ) );
|
75 |
+
if ( true === $option['is_dismissed'] ) {
|
76 |
+
return;
|
77 |
}
|
78 |
+
|
79 |
+
?>
|
80 |
+
|
81 |
+
<div class="notice notice-info is-dismissible" id="string-locator-sponsorship-notice">
|
82 |
+
<p>
|
83 |
+
<?php esc_html_e( 'Thank you for trying out the String Locator plugin!', 'string-locator' ); ?>
|
84 |
+
</p>
|
85 |
+
|
86 |
+
<p>
|
87 |
+
<?php esc_html_e( 'If you like the plugin, please consider making a donation to support the plugin development.', 'string-locator' ); ?>
|
88 |
+
</p>
|
89 |
+
|
90 |
+
<p>
|
91 |
+
<a href="https://github.com/sponsors/Clorith/" target="_blank" class="button button-primary">
|
92 |
+
<?php esc_html_e( 'Donate via GitHub', 'string-locator' ); ?>
|
93 |
+
</a>
|
94 |
+
|
95 |
+
<a href="https://paypal.me/clorith" target="_blank" class="button">
|
96 |
+
<?php esc_html_e( 'Donate via PayPal', 'string-locator' ); ?>
|
97 |
+
</a>
|
98 |
+
</p>
|
99 |
+
|
100 |
+
<script>
|
101 |
+
jQuery( document ).ready( function( $ ) {
|
102 |
+
$( '#string-locator-sponsorship-notice' ).on( 'click', '.notice-dismiss', function() {
|
103 |
+
$.post( ajaxurl, {
|
104 |
+
action: 'string_locator_notice_dismiss',
|
105 |
+
_nonce: '<?php echo wp_create_nonce( 'string-locator-notice-dismiss' ); ?>'
|
106 |
+
} );
|
107 |
+
} );
|
108 |
+
} );
|
109 |
+
</script>
|
110 |
+
</div>
|
111 |
+
|
112 |
+
<?php
|
113 |
+
}
|
114 |
+
|
115 |
+
public function add_search_options( $searchers, $search_location ) {
|
116 |
+
ob_start();
|
117 |
+
?>
|
118 |
+
<optgroup label="<?php esc_attr_e( 'Core', 'string-locator' ); ?>">
|
119 |
+
<option value="core"><?php esc_html_e( 'The whole WordPress directory', 'string-locator' ); ?></option>
|
120 |
+
<option value="wp-content"><?php esc_html_e( 'Everything under wp-content', 'string-locator' ); ?></option>
|
121 |
+
</optgroup>
|
122 |
+
<optgroup label="<?php esc_attr_e( 'Themes', 'string-locator' ); ?>">
|
123 |
+
<?php echo String_Locator::get_themes_options( $search_location ); ?>
|
124 |
+
</optgroup>
|
125 |
+
<?php if ( String_Locator::has_mu_plugins() ) : ?>
|
126 |
+
<optgroup label="<?php esc_attr_e( 'Must Use Plugins', 'string-locator' ); ?>">
|
127 |
+
<?php echo String_Locator::get_mu_plugins_options( $search_location ); ?>
|
128 |
+
</optgroup>
|
129 |
+
<?php endif; ?>
|
130 |
+
<optgroup label="<?php esc_attr_e( 'Plugins', 'string-locator' ); ?>">
|
131 |
+
<?php echo String_Locator::get_plugins_options( $search_location ); ?>
|
132 |
+
</optgroup>
|
133 |
+
<?php
|
134 |
+
|
135 |
+
$searchers .= ob_get_clean();
|
136 |
+
|
137 |
+
return $searchers;
|
138 |
}
|
139 |
|
140 |
/**
|
148 |
function plugin_row_meta( $meta, $plugin_file ) {
|
149 |
if ( 'string-locator/string-locator.php' === $plugin_file ) {
|
150 |
$meta[] = sprintf(
|
151 |
+
'<a href="https://github.com/sponsors/Clorith/">%s</a>',
|
152 |
esc_html__( 'Donate to this plugin', 'string-locator' )
|
153 |
);
|
154 |
}
|
209 |
'string-locator-path' => ( isset( $_GET['string-locator-path'] ) ? $_GET['string-locator-path'] : '' ),
|
210 |
);
|
211 |
|
212 |
+
$fields = apply_filters( 'string_locator_editor_fields', $fields );
|
213 |
+
|
214 |
$field_output = array();
|
215 |
|
216 |
foreach ( $fields as $label => $value ) {
|
307 |
return false;
|
308 |
}
|
309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
/**
|
311 |
* Create a table row for insertion into the search results list.
|
312 |
*
|
376 |
|
377 |
$table_columns = sprintf(
|
378 |
'<tr>
|
379 |
+
<th scope="col" class="manage-column column-stringresult column-primary string">%s</th>
|
380 |
+
<th scope="col" class="manage-column column-filename filename">%s</th>
|
381 |
+
<th scope="col" class="manage-column column-linenum line">%s</th>
|
382 |
+
<th scope="col" class="manage-column column-linepos position">%s</th>
|
383 |
</tr>',
|
384 |
esc_html( __( 'String', 'string-locator' ) ),
|
385 |
esc_html( __( 'File', 'string-locator' ) ),
|
404 |
}
|
405 |
|
406 |
/**
|
407 |
+
* Set the text domain for translated plugin content.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
408 |
*
|
409 |
+
* @return void
|
410 |
*/
|
411 |
+
function load_i18n() {
|
412 |
+
$i18n_dir = 'string-locator/languages/';
|
413 |
+
load_plugin_textdomain( 'string-locator', false, $i18n_dir );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
414 |
}
|
415 |
|
416 |
/**
|
417 |
+
* Convert a value to its absolute boolean interpretation.
|
418 |
*
|
419 |
+
* @param $value
|
420 |
*
|
421 |
* @return bool
|
422 |
*/
|
423 |
+
public static function absbool( $value ) {
|
424 |
+
if ( is_bool( $value ) ) {
|
425 |
+
$bool = $value;
|
426 |
+
} else {
|
427 |
+
if ( 'false' === $value ) {
|
428 |
+
$bool = false;
|
429 |
+
} else {
|
430 |
+
$bool = true;
|
431 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
432 |
}
|
433 |
|
434 |
+
return $bool;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
435 |
}
|
436 |
|
437 |
/**
|
445 |
return;
|
446 |
}
|
447 |
|
448 |
+
$search = STRING_LOCATOR_PLUGIN_DIR . 'build/string-locator-search.asset.php';
|
449 |
+
$editor = STRING_LOCATOR_PLUGIN_DIR . 'build/string-locator.asset.php';
|
|
|
450 |
|
451 |
+
$search = file_exists( $search ) ? require $search : array();
|
452 |
+
$editor = file_exists( $editor ) ? require $editor : array();
|
|
|
453 |
|
454 |
/**
|
455 |
* String Locator Styles
|
456 |
*/
|
457 |
+
wp_enqueue_style( 'string-locator', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'build/string-locator.css', array(), $search['version'] );
|
458 |
|
459 |
if ( ! isset( $_GET['edit-file'] ) || ! current_user_can( 'edit_themes' ) ) {
|
460 |
/**
|
461 |
* String Locator Scripts
|
462 |
*/
|
463 |
+
wp_enqueue_script(
|
464 |
+
'string-locator-search',
|
465 |
+
trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'build/string-locator-search.js',
|
466 |
+
array( 'jquery', 'wp-util' ),
|
467 |
+
$search['version'],
|
468 |
+
true
|
469 |
+
);
|
470 |
|
471 |
wp_localize_script(
|
472 |
'string-locator-search',
|
473 |
'string_locator',
|
474 |
array(
|
475 |
+
'rest_nonce' => wp_create_nonce( 'wp_rest' ),
|
476 |
'search_nonce' => wp_create_nonce( 'string-locator-search' ),
|
477 |
'search_current_prefix' => __( 'Next file: ', 'string-locator' ),
|
478 |
'saving_results_string' => __( 'Saving search results…', 'string-locator' ),
|
481 |
'search_error' => __( 'The above error was returned by your server, for more details please consult your servers error logs.', 'string-locator' ),
|
482 |
'search_no_results' => __( 'Your search was completed, but no results were found.', 'string-locator' ),
|
483 |
'warning_title' => __( 'Warning', 'string-locator' ),
|
484 |
+
'url' => array(
|
485 |
+
'search' => get_rest_url( null, 'string-locator/v1/search' ),
|
486 |
+
'clean' => get_rest_url( null, 'string-locator/v1/clean' ),
|
487 |
+
'directory_structure' => get_rest_url( null, 'string-locator/v1/get-directory-structure' ),
|
488 |
+
),
|
489 |
)
|
490 |
);
|
491 |
|
499 |
/**
|
500 |
* String Locator Scripts
|
501 |
*/
|
502 |
+
wp_enqueue_script(
|
503 |
+
'string-locator-editor',
|
504 |
+
trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'build/string-locator.js',
|
505 |
+
array( 'jquery', 'code-editor', 'wp-util' ),
|
506 |
+
$editor['version'],
|
507 |
+
true
|
508 |
+
);
|
509 |
|
510 |
wp_localize_script(
|
511 |
'string-locator-editor',
|
514 |
'CodeMirror' => $code_mirror,
|
515 |
'goto_line' => absint( $_GET['string-locator-line'] ),
|
516 |
'goto_linepos' => absint( $_GET['string-locator-linepos'] ),
|
517 |
+
'url' => array(
|
518 |
+
'save' => get_rest_url( null, 'string-locator/v1/save' ),
|
519 |
+
),
|
520 |
)
|
521 |
);
|
522 |
}
|
569 |
return false;
|
570 |
}
|
571 |
|
572 |
+
$include_path = '';
|
573 |
+
|
574 |
/**
|
575 |
* Show the edit page if;
|
576 |
* - The edit file path query var is set
|
578 |
* - The edit file path query var does not contains double dots (used to traverse directories)
|
579 |
* - The user is capable of editing files.
|
580 |
*/
|
581 |
+
if ( isset( $_GET['string-locator-path'] ) && self::is_valid_location( $_GET['string-locator-path'] ) && current_user_can( 'edit_themes' ) ) {
|
582 |
+
$include_path = trailingslashit( STRING_LOCATOR_PLUGIN_DIR ) . 'views/editor.php';
|
583 |
} else {
|
584 |
+
$include_path = trailingslashit( STRING_LOCATOR_PLUGIN_DIR ) . 'views/search.php';
|
|
|
|
|
|
|
|
|
|
|
|
|
585 |
}
|
586 |
|
587 |
+
$include_path = apply_filters( 'string_locator_view', $include_path );
|
|
|
588 |
|
589 |
+
if ( ! empty( $include_path ) ) {
|
590 |
+
include_once $include_path;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
591 |
}
|
|
|
|
|
592 |
}
|
593 |
|
594 |
+
function admin_body_class( $class ) {
|
595 |
+
if ( isset( $_GET['string-locator-path'] ) && self::is_valid_location( $_GET['string-locator-path'] ) && current_user_can( 'edit_themes' ) ) {
|
596 |
+
$class .= ' file-edit-screen';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
597 |
}
|
598 |
|
599 |
+
return $class;
|
600 |
}
|
601 |
|
602 |
/**
|
617 |
}
|
618 |
|
619 |
/**
|
620 |
+
* Check if a file path is valid for editing.
|
621 |
*
|
622 |
+
* @param string $path Path to file.
|
|
|
|
|
|
|
|
|
|
|
623 |
*
|
624 |
+
* @return bool
|
625 |
*/
|
626 |
+
public static function is_valid_location( $path ) {
|
627 |
+
$valid = true;
|
628 |
+
$path = str_replace( array( '/' ), array( DIRECTORY_SEPARATOR ), stripslashes( $path ) );
|
629 |
+
$abspath = str_replace( array( '/' ), array( DIRECTORY_SEPARATOR ), ABSPATH );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
630 |
|
631 |
+
// Check that it is a valid file we are trying to access as well.
|
632 |
+
if ( ! file_exists( $path ) ) {
|
633 |
+
$valid = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
}
|
635 |
|
636 |
+
if ( empty( $path ) ) {
|
637 |
+
$valid = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
638 |
}
|
639 |
+
if ( stristr( $path, '..' ) ) {
|
640 |
+
$valid = false;
|
641 |
+
}
|
642 |
+
if ( ! stristr( $path, $abspath ) ) {
|
643 |
+
$valid = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
644 |
}
|
645 |
|
646 |
+
return $valid;
|
647 |
}
|
648 |
}
|
readme.txt
CHANGED
@@ -5,8 +5,8 @@ Plugin URI: http://wordpress.org/plugins/string-locator/
|
|
5 |
Donate link: https://www.paypal.me/clorith
|
6 |
Tags: text, search, find, syntax, highlight
|
7 |
Requires at least: 4.9
|
8 |
-
Tested up to: 5.
|
9 |
-
Stable tag: 2.
|
10 |
License: GPLv2 or later
|
11 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
12 |
|
@@ -45,10 +45,14 @@ When writing your search string, make sure to wrap your search in forward slashe
|
|
45 |
|
46 |
== Changelog ==
|
47 |
|
48 |
-
= 2.
|
49 |
-
* Fixed
|
50 |
-
*
|
51 |
-
*
|
|
|
|
|
|
|
|
|
52 |
|
53 |
= Older entries =
|
54 |
See changelog.txt for the version history
|
5 |
Donate link: https://www.paypal.me/clorith
|
6 |
Tags: text, search, find, syntax, highlight
|
7 |
Requires at least: 4.9
|
8 |
+
Tested up to: 5.9
|
9 |
+
Stable tag: 2.5.0
|
10 |
License: GPLv2 or later
|
11 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
12 |
|
45 |
|
46 |
== Changelog ==
|
47 |
|
48 |
+
= 2.5.0 (2022-02-27) =
|
49 |
+
* Fixed a bug where content would have slashes stripped unexpectedly.
|
50 |
+
* Improved table spacing on search results.
|
51 |
+
* Improved loopback checks to also check admin access.
|
52 |
+
* Hardened the search iterator so users can't accidentally perform unexpected directory traversal.
|
53 |
+
* Introduced actions and filters in various places to enable extenders, and future enhancements.
|
54 |
+
* Moved all ajax requests to dedicated REST endpoints.
|
55 |
+
* Refactored file structure.
|
56 |
|
57 |
= Older entries =
|
58 |
See changelog.txt for the version history
|
resources/css/string-locator.css
DELETED
@@ -1,2 +0,0 @@
|
|
1 |
-
.wrap>h1{margin-bottom:15px}.string-locator-italics{font-style:italic}.string-locator-feedback{background:#fff;display:inline-block;width:100%;text-align:center}.string-locator-feedback.hide{display:none}.string-locator-feedback progress{width:100%;height:1.5em}.string-locator-feedback #string-locator-feedback-text{display:inline-block;text-align:center;width:100%}body.tools_page_string-locator.file-edit-screen #wpcontent{padding-left:0}body.tools_page_string-locator.file-edit-screen #wpfooter{display:none}body.tools_page_string-locator.file-edit-screen #wpbody-content{padding-bottom:0}table.tools_page_string-locator{display:none}table.tools_page_string-locator.restore{display:table}.string-locator-editor-wrapper{width:100%;height:100%;display:grid;grid-gap:0;grid-template-columns:80% 20%}.string-locator-editor-wrapper .notice,.string-locator-editor-wrapper .string-locator-header{height:40px;padding:4px 2px;border-bottom:1px solid #e2e4e7;background:#fff;display:flex;flex-direction:row;align-items:stretch;justify-content:space-between;z-index:30;right:0;left:0;top:0;position:sticky}@media (min-width: 600px){.string-locator-editor-wrapper .notice,.string-locator-editor-wrapper .string-locator-header{position:fixed;padding:8px;top:46px}}@media (min-width: 782px){.string-locator-editor-wrapper .notice,.string-locator-editor-wrapper .string-locator-header{top:32px;left:160px}}.string-locator-editor-wrapper .notice .title,.string-locator-editor-wrapper .string-locator-header .title{font-size:16px}.string-locator-editor-wrapper .notice>div,.string-locator-editor-wrapper .string-locator-header>div{display:inline-flex;align-items:center}.string-locator-editor-wrapper .notice .button,.string-locator-editor-wrapper .string-locator-header .button{margin:0 3px 0 12px}.string-locator-editor-wrapper .notice{height:fit-content;margin:0;top:89px;display:block}.string-locator-editor-wrapper .notice.is-dismissible{position:sticky}.string-locator-editor-wrapper .string-locator-editor{margin-top:57px}.string-locator-editor-wrapper .string-locator-sidebar{margin-top:57px;background:#fff;border-left:1px solid #e2e4e7}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel{border-top:1px solid #e2e4e7;padding-bottom:10px}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel:first-of-type{border-top:none}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel .title{color:#191e23;border:none;box-shadow:none;font-weight:600;padding:15px;margin:0}.string-locator-editor-wrapper .string-locator-sidebar .string-locator-panel .row{padding:5px 15px}.string-locator-editor-wrapper .CodeMirror .CodeMirror-activeline .CodeMirror-activeline-background{background-color:#cfe4ff}.string-locator-editor-wrapper .CodeMirror .CodeMirror-activeline .CodeMirror-gutter-background{background-color:#cfe4ff}
|
2 |
-
|
|
|
|
resources/js/string-locator-search.js
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
!function(t){var r={};function o(e){if(r[e])return r[e].exports;var n=r[e]={i:e,l:!1,exports:{}};return t[e].call(n.exports,n,n.exports,o),n.l=!0,n.exports}o.m=t,o.c=r,o.d=function(t,r,e){o.o(t,r)||Object.defineProperty(t,r,{enumerable:!0,get:e})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,r){if(1&r&&(t=o(t)),8&r)return t;if(4&r&&"object"==typeof t&&t&&t.__esModule)return t;var e=Object.create(null);if(o.r(e),Object.defineProperty(e,"default",{enumerable:!0,value:t}),2&r&&"string"!=typeof t)for(var n in t)o.d(e,n,function(r){return t[r]}.bind(null,n));return e},o.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(r,"a",r),r},o.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},o.p="",o(o.s=3)}([,,,function(t,r,o){t.exports=o(4)},function(t,r){jQuery(document).ready((function(t){let r=!1;const o=wp.template("string-locator-search-result");function e(r,o,e){t(".notices").append('<div class="notice notice-'+e+' is-dismissible"><p><strong>'+r+"</strong><br />"+o+"</p></div>")}function n(o,n){r=!1,t(".string-locator-feedback").hide(),e(o,n,"error")}function a(s,c){if(c>=s||!r)return t("#string-locator-feedback-text").html(string_locator.saving_results_string),function(){r=!1,t("#string-locator-feedback-text").text("");const o={action:"string-locator-clean",nonce:string_locator.search_nonce};t.post(string_locator.ajax_url,o,(function(){t(".string-locator-feedback").hide(),t("tbody",".tools_page_string-locator").is(":empty")&&t("tbody",".tools_page_string-locator").html('<tr><td colspan="3">'+string_locator.search_no_results+"</td></tr>")})).fail((function(t,r,o){n(t.status+" "+o,string_locator.search_error)}))}(),!1;const i={action:"string-locator-search",filenum:c,nonce:string_locator.search_nonce};t.post(string_locator.ajax_url,i,(function(r){if(!r.success){if(!1===r.data.continue)return n(string_locator.warning_title,r.data.message),!1;e(string_locator.warning_title,r.data.message,"warning")}void 0!==r.data.search&&(t("#string-locator-search-progress").val(r.data.filenum),t("#string-locator-feedback-text").html(string_locator.search_current_prefix+r.data.next_file),function(r){t(".no-items",".tools_page_string-locator").is(":visible")&&t(".no-items",".tools_page_string-locator").hide();if(Array!==r.constructor)return!1;r.forEach((function(r){if(r)for(let e=0,n=r.length;e<n;e++){const n=r[e];void 0!==n.stringresult&&t("tbody",".tools_page_string-locator").append(o(n))}}))}(r.data.search));const c=r.data.filenum+1;a(s,c)}),"json").fail((function(t,r,o){n(t.status+" "+o,string_locator.search_error)}))}t("#string-locator-search-form").on("submit",(function(o){o.preventDefault(),t("#string-locator-feedback-text").text(string_locator.search_preparing),t(".string-locator-feedback").show(),r=!0,t(".notices").html(""),t("#string-locator-search-progress").removeAttr("value"),t("tbody",".tools_page_string-locator").html("");const s={action:"string-locator-get-directory-structure",directory:t("#string-locator-search").val(),search:t("#string-locator-string").val(),regex:t("#string-locator-regex").is(":checked"),nonce:string_locator.search_nonce};t("table.tools_page_string-locator").show(),t.post(string_locator.ajax_url,s,(function(r){r.success?(t("#string-locator-search-progress").attr("max",r.data.total).val(r.data.current),t("#string-locator-feedback-text").text(string_locator.search_started),a(r.data.total,0)):e(r.data,"alert")}),"json").fail((function(t,r,o){n(t.status+" "+o,string_locator.search_error)}))}))}))}]);
|
|
resources/js/string-locator.js
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
!function(t){var e={};function o(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,o),n.l=!0,n.exports}o.m=t,o.c=e,o.d=function(t,e,r){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(o.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)o.d(r,n,function(e){return t[e]}.bind(null,n));return r},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=0)}([function(t,e,o){o(1),t.exports=o(2)},function(t,e){jQuery(document).ready((function(t){let e;if(!1!==string_locator.CodeMirror&&""!==string_locator.CodeMirror){e=wp.codeEditor.initialize("code-editor",string_locator.CodeMirror);const r=wp.template("string-locator-alert");function o(t){const e=Math.max(document.documentElement.clientHeight,window.innerHeight||0)-89;t.setSize(null,parseInt(e))}t(".string-locator-editor").on("click",".string-locator-edit-goto",(function(o){o.preventDefault(),e.codemirror.scrollIntoView(parseInt(t(this).data("goto-line"))),e.codemirror.setCursor(parseInt(t(this).data("goto-line")-1),t(this).data("goto-linepos"))})),t("body").on("submit","#string-locator-edit-form",(function(e){const o=t("#string-locator-notices");return t.post(string_locator.save_url,t(this).serialize()).always((function(e){void 0===e.notices?o.append(r({type:"error",message:e.responseText})):t.each(e.notices,(function(){o.append(r(this))}))})),e.preventDefault(),!1})),o(e.codemirror),e.codemirror.scrollIntoView(parseInt(string_locator.goto_line)),e.codemirror.setCursor(parseInt(string_locator.goto_line-1),parseInt(string_locator.goto_linepos)),window.addEventListener("resize",o(e.codemirror))}else e=t("#code-editor"),e.css("width",t(".string-locator-edit-wrap").width()),e.css("height",parseInt(Math.max(document.documentElement.clientHeight,window.innerHeight||0)-89));t("#string-locator-notices").on("click",".notice-dismiss",(function(e){return t(this).closest(".notice").slideUp(400,"swing",(function(){t(this).remove()})),e.preventDefault(),!1}))}))},function(t,e,o){"use strict";o.r(e)}]);
|
|
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.
|
7 |
* Author: Clorith
|
8 |
* Author URI: http://www.clorith.net
|
9 |
* Text Domain: string-locator
|
@@ -25,6 +25,8 @@
|
|
25 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
26 |
*/
|
27 |
|
|
|
|
|
28 |
if ( ! defined( 'ABSPATH' ) ) {
|
29 |
die();
|
30 |
}
|
@@ -32,9 +34,30 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
32 |
define( 'STRING_LOCATOR_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
33 |
define( 'STRING_LOCATOR_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
34 |
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
/**
|
38 |
* Instantiate the plugin
|
39 |
*/
|
40 |
-
|
|
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.5.0
|
7 |
* Author: Clorith
|
8 |
* Author URI: http://www.clorith.net
|
9 |
* Text Domain: string-locator
|
25 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
26 |
*/
|
27 |
|
28 |
+
namespace JITS\StringLocator;
|
29 |
+
|
30 |
if ( ! defined( 'ABSPATH' ) ) {
|
31 |
die();
|
32 |
}
|
34 |
define( 'STRING_LOCATOR_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
35 |
define( 'STRING_LOCATOR_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
36 |
|
37 |
+
/**
|
38 |
+
* Plugin test runners
|
39 |
+
*/
|
40 |
+
require_once __DIR__ . '/includes/Tests/class-loopback.php';
|
41 |
+
require_once __DIR__ . '/includes/Tests/class-smart-scan.php';
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Plugin action classes.
|
45 |
+
*/
|
46 |
+
require_once __DIR__ . '/includes/class-save.php';
|
47 |
+
require_once __DIR__ . '/includes/class-search.php';
|
48 |
+
require_once __DIR__ . '/includes/class-directory-iterator.php';
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Prepare REST endpoints.
|
52 |
+
*/
|
53 |
+
require_once __DIR__ . '/includes/REST/class-base.php';
|
54 |
+
require_once __DIR__ . '/includes/REST/class-save.php';
|
55 |
+
require_once __DIR__ . '/includes/REST/class-clean.php';
|
56 |
+
require_once __DIR__ . '/includes/REST/class-search.php';
|
57 |
+
require_once __DIR__ . '/includes/REST/class-directory-structure.php';
|
58 |
|
59 |
/**
|
60 |
* Instantiate the plugin
|
61 |
*/
|
62 |
+
require_once __DIR__ . '/includes/class-string-locator.php';
|
63 |
+
new String_Locator();
|
editor.php → views/editor.php
RENAMED
@@ -1,9 +1,11 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
die();
|
4 |
}
|
5 |
|
6 |
-
global $string_locator;
|
7 |
$editor_content = '';
|
8 |
|
9 |
// $file is validated in String_Locator::is_valid_location() before this page can be loaded through String_Locator::options_page().
|
@@ -56,15 +58,11 @@ if ( 'core' === $_GET['file-type'] ) {
|
|
56 |
}
|
57 |
}
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
$editor_content .= $readline;
|
64 |
-
}
|
65 |
}
|
66 |
-
} else {
|
67 |
-
$editor_content = stripslashes( $_POST['string-locator-editor-content'] );
|
68 |
}
|
69 |
?>
|
70 |
<form id="string-locator-edit-form" class="string-locator-editor-wrapper">
|
@@ -73,7 +71,7 @@ if ( ! $string_locator->failed_edit ) {
|
|
73 |
<h1 class="screen-reader-text">
|
74 |
<?php
|
75 |
/* translators: Title on the editor page. */
|
76 |
-
esc_html_e( 'String Locator -
|
77 |
?>
|
78 |
</h1>
|
79 |
|
@@ -100,6 +98,12 @@ if ( ! $string_locator->failed_edit ) {
|
|
100 |
|
101 |
<div class="string-locator-editor">
|
102 |
<div id="string-locator-notices">
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
<?php if ( isset( $details['parent'] ) && ! $details['parent'] ) { ?>
|
104 |
<div class="row notice notice-warning inline below-h2 is-dismissible">
|
105 |
<p>
|
@@ -107,7 +111,7 @@ if ( ! $string_locator->failed_edit ) {
|
|
107 |
</p>
|
108 |
|
109 |
<p>
|
110 |
-
<?php _e( 'When making changes to a theme, it is recommended you make a <a href="https://
|
111 |
</p>
|
112 |
</div>
|
113 |
<?php } ?>
|
@@ -124,17 +128,25 @@ if ( ! $string_locator->failed_edit ) {
|
|
124 |
<?php } ?>
|
125 |
</div>
|
126 |
|
127 |
-
|
|
|
128 |
name="string-locator-editor-content"
|
129 |
-
class="string-locator-editor"
|
130 |
id="code-editor"
|
131 |
-
data-editor-goto-line="
|
132 |
-
data-editor-language="
|
133 |
autofocus="autofocus"
|
134 |
-
|
|
|
|
|
|
|
|
|
135 |
</div>
|
136 |
|
137 |
<div class="string-locator-sidebar">
|
|
|
|
|
|
|
138 |
<div class="string-locator-panel">
|
139 |
<h2 class="title"><?php esc_html_e( 'Details', 'string-locator' ); ?></h2>
|
140 |
<div class="string-locator-panel-body">
|
@@ -153,23 +165,7 @@ if ( ! $string_locator->failed_edit ) {
|
|
153 |
<div class="string-locator-panel">
|
154 |
<h2 class="title"><?php esc_html_e( 'Save checks', 'string-locator' ); ?></h2>
|
155 |
<div class="string-locator-panel-body">
|
156 |
-
|
157 |
-
<label>
|
158 |
-
<input type="checkbox" name="string-locator-smart-edit" checked="checked">
|
159 |
-
<?php esc_html_e( 'Enable a smart-scan of your code to help detect bracket mismatches before saving.', 'string-locator' ); ?>
|
160 |
-
</label>
|
161 |
-
</div>
|
162 |
-
|
163 |
-
<div class="row">
|
164 |
-
<label>
|
165 |
-
<input type="checkbox" name="string-locator-loopback-check" checked="checked">
|
166 |
-
<?php esc_html_e( 'Enable loopback tests after making a save.', 'string-locator' ); ?>
|
167 |
-
</label>
|
168 |
-
<br>
|
169 |
-
<em>
|
170 |
-
<?php esc_html_e( 'This feature is highly recommended, and is what WordPress does when using the plugin- or theme-editor.', 'string-locator' ); ?>
|
171 |
-
</em>
|
172 |
-
</div>
|
173 |
</div>
|
174 |
</div>
|
175 |
|
@@ -209,7 +205,7 @@ if ( ! $string_locator->failed_edit ) {
|
|
209 |
|
210 |
foreach ( $function_info['user'] as $user_func ) {
|
211 |
if ( strstr( $editor_content, $user_func . '(' ) ) {
|
212 |
-
$function_object = new ReflectionFunction( $user_func );
|
213 |
$attrs = $function_object->getParameters();
|
214 |
|
215 |
$attr_strings = array();
|
@@ -256,6 +252,8 @@ if ( ! $string_locator->failed_edit ) {
|
|
256 |
</div>
|
257 |
<?php endif; ?>
|
258 |
|
|
|
|
|
259 |
</div>
|
260 |
|
261 |
</div>
|
1 |
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator;
|
4 |
+
|
5 |
if ( ! defined( 'ABSPATH' ) ) {
|
6 |
die();
|
7 |
}
|
8 |
|
|
|
9 |
$editor_content = '';
|
10 |
|
11 |
// $file is validated in String_Locator::is_valid_location() before this page can be loaded through String_Locator::options_page().
|
58 |
}
|
59 |
}
|
60 |
|
61 |
+
$readfile = fopen( $file, 'r' );
|
62 |
+
if ( $readfile ) {
|
63 |
+
while ( ( $readline = fgets( $readfile ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
|
64 |
+
$editor_content .= $readline;
|
|
|
|
|
65 |
}
|
|
|
|
|
66 |
}
|
67 |
?>
|
68 |
<form id="string-locator-edit-form" class="string-locator-editor-wrapper">
|
71 |
<h1 class="screen-reader-text">
|
72 |
<?php
|
73 |
/* translators: Title on the editor page. */
|
74 |
+
esc_html_e( 'String Locator - Editor', 'string-locator' );
|
75 |
?>
|
76 |
</h1>
|
77 |
|
98 |
|
99 |
<div class="string-locator-editor">
|
100 |
<div id="string-locator-notices">
|
101 |
+
<div class="row notice notice-error inline below-h2 hide-if-js">
|
102 |
+
<p>
|
103 |
+
Something about JS being required!
|
104 |
+
</p>
|
105 |
+
</div>
|
106 |
+
|
107 |
<?php if ( isset( $details['parent'] ) && ! $details['parent'] ) { ?>
|
108 |
<div class="row notice notice-warning inline below-h2 is-dismissible">
|
109 |
<p>
|
111 |
</p>
|
112 |
|
113 |
<p>
|
114 |
+
<?php _e( 'When making changes to a theme, it is recommended you make a <a href="https://developer.wordpress.org/themes/advanced-topics/child-themes/">Child Theme</a>.', 'string-locator' ); ?>
|
115 |
</p>
|
116 |
</div>
|
117 |
<?php } ?>
|
128 |
<?php } ?>
|
129 |
</div>
|
130 |
|
131 |
+
<?php
|
132 |
+
$editor = '<textarea
|
133 |
name="string-locator-editor-content"
|
134 |
+
class="string-locator-editor hide-if-no-js"
|
135 |
id="code-editor"
|
136 |
+
data-editor-goto-line="' . esc_attr( $_GET['string-locator-line'] ) . ':' . esc_attr( $_GET['string-locator-linepos'] ) . '"
|
137 |
+
data-editor-language=""
|
138 |
autofocus="autofocus"
|
139 |
+
>' . esc_html( $editor_content ) . '</textarea>';
|
140 |
+
|
141 |
+
echo apply_filters( 'string_locator_editor_markup', $editor );
|
142 |
+
|
143 |
+
?>
|
144 |
</div>
|
145 |
|
146 |
<div class="string-locator-sidebar">
|
147 |
+
|
148 |
+
<?php do_action( 'string_locator_editor_sidebar_start' ); ?>
|
149 |
+
|
150 |
<div class="string-locator-panel">
|
151 |
<h2 class="title"><?php esc_html_e( 'Details', 'string-locator' ); ?></h2>
|
152 |
<div class="string-locator-panel-body">
|
165 |
<div class="string-locator-panel">
|
166 |
<h2 class="title"><?php esc_html_e( 'Save checks', 'string-locator' ); ?></h2>
|
167 |
<div class="string-locator-panel-body">
|
168 |
+
<?php do_action( 'string_locator_editor_checks' ); ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
</div>
|
170 |
</div>
|
171 |
|
205 |
|
206 |
foreach ( $function_info['user'] as $user_func ) {
|
207 |
if ( strstr( $editor_content, $user_func . '(' ) ) {
|
208 |
+
$function_object = new \ReflectionFunction( $user_func );
|
209 |
$attrs = $function_object->getParameters();
|
210 |
|
211 |
$attr_strings = array();
|
252 |
</div>
|
253 |
<?php endif; ?>
|
254 |
|
255 |
+
<?php do_action( 'string_locator_editor_sidebar_end' ); ?>
|
256 |
+
|
257 |
</div>
|
258 |
|
259 |
</div>
|
search.php → views/search.php
RENAMED
@@ -1,4 +1,7 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
2 |
if ( ! defined( 'ABSPATH' ) ) {
|
3 |
die();
|
4 |
}
|
@@ -35,6 +38,8 @@ if ( isset( $_GET['restore'] ) ) {
|
|
35 |
<?php esc_html_e( 'String Locator', 'string-locator' ); ?>
|
36 |
</h1>
|
37 |
|
|
|
|
|
38 |
<?php if ( ! current_user_can( 'edit_themes' ) ) : ?>
|
39 |
<div class="notice notice-warning inline">
|
40 |
<p>
|
@@ -51,21 +56,11 @@ if ( isset( $_GET['restore'] ) ) {
|
|
51 |
<form action="<?php echo esc_url( $this_url ); ?>" method="post" id="string-locator-search-form">
|
52 |
<label for="string-locator-search"><?php esc_html_e( 'Search through', 'string-locator' ); ?></label>
|
53 |
<select name="string-locator-search" id="string-locator-search">
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
<?php echo String_Locator::get_themes_options( $search_location ); ?>
|
60 |
-
</optgroup>
|
61 |
-
<?php if ( String_Locator::has_mu_plugins() ) : ?>
|
62 |
-
<optgroup label="<?php esc_attr_e( 'Must Use Plugins', 'string-locator' ); ?>">
|
63 |
-
<?php echo String_Locator::get_mu_plugins_options( $search_location ); ?>
|
64 |
-
</optgroup>
|
65 |
-
<?php endif; ?>
|
66 |
-
<optgroup label="<?php esc_attr_e( 'Plugins', 'string-locator' ); ?>">
|
67 |
-
<?php echo String_Locator::get_plugins_options( $search_location ); ?>
|
68 |
-
</optgroup>
|
69 |
</select>
|
70 |
|
71 |
<label for="string-locator-string"><?php esc_html_e( 'Search string', 'string-locator' ); ?></label>
|
1 |
<?php
|
2 |
+
|
3 |
+
namespace JITS\StringLocator;
|
4 |
+
|
5 |
if ( ! defined( 'ABSPATH' ) ) {
|
6 |
die();
|
7 |
}
|
38 |
<?php esc_html_e( 'String Locator', 'string-locator' ); ?>
|
39 |
</h1>
|
40 |
|
41 |
+
<?php do_action( 'string_locator_view_search_pre_form' ); ?>
|
42 |
+
|
43 |
<?php if ( ! current_user_can( 'edit_themes' ) ) : ?>
|
44 |
<div class="notice notice-warning inline">
|
45 |
<p>
|
56 |
<form action="<?php echo esc_url( $this_url ); ?>" method="post" id="string-locator-search-form">
|
57 |
<label for="string-locator-search"><?php esc_html_e( 'Search through', 'string-locator' ); ?></label>
|
58 |
<select name="string-locator-search" id="string-locator-search">
|
59 |
+
<?php
|
60 |
+
$searchers = apply_filters( 'string_locator_search_sources_markup', '', $search_location );
|
61 |
+
|
62 |
+
echo $searchers;
|
63 |
+
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
</select>
|
65 |
|
66 |
<label for="string-locator-string"><?php esc_html_e( 'Search string', 'string-locator' ); ?></label>
|