Changeset 2685592
- Timestamp:
- 02/27/2022 01:44:33 PM (3 years ago)
- Location:
- string-locator
- Files:
-
- 38 added
- 4 edited
-
tags/2.5.0 (added)
-
tags/2.5.0/LICENSE (added)
-
tags/2.5.0/build (added)
-
tags/2.5.0/build/string-locator-search.asset.php (added)
-
tags/2.5.0/build/string-locator-search.js (added)
-
tags/2.5.0/build/string-locator.asset.php (added)
-
tags/2.5.0/build/string-locator.css (added)
-
tags/2.5.0/build/string-locator.js (added)
-
tags/2.5.0/changelog.txt (added)
-
tags/2.5.0/includes (added)
-
tags/2.5.0/includes/REST (added)
-
tags/2.5.0/includes/REST/class-base.php (added)
-
tags/2.5.0/includes/REST/class-clean.php (added)
-
tags/2.5.0/includes/REST/class-directory-structure.php (added)
-
tags/2.5.0/includes/REST/class-save.php (added)
-
tags/2.5.0/includes/REST/class-search.php (added)
-
tags/2.5.0/includes/Tests (added)
-
tags/2.5.0/includes/Tests/class-loopback.php (added)
-
tags/2.5.0/includes/Tests/class-smart-scan.php (added)
-
tags/2.5.0/includes/class-directory-iterator.php (added)
-
tags/2.5.0/includes/class-save.php (added)
-
tags/2.5.0/includes/class-search.php (added)
-
tags/2.5.0/includes/class-string-locator.php (added)
-
tags/2.5.0/readme.txt (added)
-
tags/2.5.0/string-locator.php (added)
-
tags/2.5.0/uninstall.php (added)
-
tags/2.5.0/views (added)
-
tags/2.5.0/views/editor.php (added)
-
tags/2.5.0/views/search.php (added)
-
trunk/build (added)
-
trunk/build/string-locator-search.asset.php (added)
-
trunk/build/string-locator-search.js (added)
-
trunk/build/string-locator.asset.php (added)
-
trunk/build/string-locator.css (added)
-
trunk/build/string-locator.js (added)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/includes/class-string-locator.php (modified) (19 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/string-locator.php (modified) (3 diffs)
-
trunk/views (added)
-
trunk/views/editor.php (added)
-
trunk/views/search.php (added)
Legend:
- Unmodified
- Added
- Removed
-
string-locator/trunk/changelog.txt
r2432238 r2685592 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 1 6 = 2.4.1 = 2 7 * Fixed case-sensitive class call, apparently not all PHP versions are equal in how this is treated. -
string-locator/trunk/includes/class-string-locator.php
r2432238 r2685592 1 1 <?php 2 3 namespace JITS\StringLocator; 2 4 3 5 /** … … 6 8 class String_Locator { 7 9 /** 8 * @var string $string_locator_language The code language used for the editing page. 9 * @var string $version String Locator version number. 10 * @var array $notice An array containing all notices to display. 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. 10 * The code language used for the editing page. 11 * 12 * @var string 19 13 */ 20 14 public $string_locator_language = ''; 21 public $version = '2.4.2'; 22 public $notice = array(); 23 public $failed_edit = false; 24 private $path_to_use = ''; 25 private $bad_http_codes = array( '500' ); 26 private $bad_file_types = array( 'rar', '7z', 'zip', 'tar', 'gz', 'jpg', 'jpeg', 'png', 'gif', 'mp3', 'mp4', 'avi', 'wmv' ); 27 private $excerpt_length = 25; 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'; 15 16 /** 17 * An array containing all notices to display. 18 * 19 * @var array 20 */ 21 public $notice = array(); 33 22 34 23 /** … … 48 37 */ 49 38 public function init() { 50 /**51 * Define class variables requiring expressions52 */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 39 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); 66 40 … … 72 46 add_action( 'plugins_loaded', array( $this, 'load_i18n' ) ); 73 47 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 48 add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 ); 79 49 80 add_action( 'rest_api_init', array( $this, 'add_rest_route' ) ); 81 } 82 83 public function add_rest_route() { 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 * Sets up the memory limit variables. 102 * 103 * @since 2.0.0 104 * 105 * @return void 106 */ 107 function set_memory_limit() { 108 $memory_limit = ini_get( 'memory_limit' ); 109 110 $this->max_memory_consumption = absint( $memory_limit ); 111 112 if ( strstr( $memory_limit, 'k' ) ) { 113 $this->max_memory_consumption = ( str_replace( 'k', '', $memory_limit ) * 1000 ); 114 } 115 if ( strstr( $memory_limit, 'M' ) ) { 116 $this->max_memory_consumption = ( str_replace( 'M', '', $memory_limit ) * 1000000 ); 117 } 118 if ( strstr( $memory_limit, 'G' ) ) { 119 $this->max_memory_consumption = ( str_replace( 'G', '', $memory_limit ) * 1000000000 ); 120 } 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; 121 138 } 122 139 … … 132 149 if ( 'string-locator/string-locator.php' === $plugin_file ) { 133 150 $meta[] = sprintf( 134 '<a href="https:// www.paypal.me/clorith">%s</a>',151 '<a href="https://github.com/sponsors/Clorith/">%s</a>', 135 152 esc_html__( 'Donate to this plugin', 'string-locator' ) 136 153 ); … … 193 210 ); 194 211 212 $fields = apply_filters( 'string_locator_editor_fields', $fields ); 213 195 214 $field_output = array(); 196 215 … … 287 306 288 307 return false; 289 }290 291 /**292 * Handles the AJAX request to prepare the search hierarchy.293 *294 * @return void295 */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 files310 * This is to prevent the SQL string from being too large and crashing everything311 */312 $back_compat_filter = apply_filters( 'string-locator-files-per-array', 500 ); //phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores313 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.0346 *347 * @return bool348 */349 function nearing_execution_limit() {350 // Max execution time is 0 or -1 (infinite) in server config351 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.UseUnderscores356 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.0371 *372 * @return bool373 */374 function nearing_memory_limit() {375 // Check if the memory limit is set t o0 or -1 (infinite) in server config376 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 well381 $back_compat_filter = apply_filters( 'string-locator-extra-memory-buffer', 256000 ); //phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores382 $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.0410 *411 * @return void412 */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.UseUnderscores419 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 $filenum449 ),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_consumption476 ),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.Discouraged488 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 it526 */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 extension532 */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 void567 */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 308 } 580 309 … … 648 377 $table_columns = sprintf( 649 378 '<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>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> 654 383 </tr>', 655 384 esc_html( __( 'String', 'string-locator' ) ), … … 676 405 677 406 /** 678 * Create an admin edit link for the supplied path.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 string685 */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 bool|object734 */735 function prepare_scan_path( $option ) {736 $data = array(737 'path' => '',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 * Check if a file path is valid for editing.786 *787 * @param string $path Path to file.788 *789 * @return bool790 */791 function is_valid_location( $path ) {792 $valid = true;793 $path = str_replace( array( '/' ), array( DIRECTORY_SEPARATOR ), stripslashes( $path ) );794 $abspath = str_replace( array( '/' ), array( DIRECTORY_SEPARATOR ), ABSPATH );795 796 // Check that it is a valid file we are trying to access as well.797 if ( ! file_exists( $path ) ) {798 $valid = false;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 $valid;812 }813 814 /**815 407 * Set the text domain for translated plugin content. 816 408 * … … 820 412 $i18n_dir = 'string-locator/languages/'; 821 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; 822 435 } 823 436 … … 833 446 } 834 447 835 if ( ! wp_script_is( 'react', 'registered' ) ) { 836 wp_register_script( 'react', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'resources/js/react.js', array() ); 837 } 838 839 if ( ! wp_script_is( 'react-dom', 'registered' ) ) { 840 wp_register_script( 'react-dom', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'resources/js/react-dom.js', array() ); 841 } 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(); 842 453 843 454 /** 844 455 * String Locator Styles 845 456 */ 846 wp_enqueue_style( 'string-locator', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . ' resources/css/string-locator.css', array(), $this->version);457 wp_enqueue_style( 'string-locator', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'build/string-locator.css', array(), $search['version'] ); 847 458 848 459 if ( ! isset( $_GET['edit-file'] ) || ! current_user_can( 'edit_themes' ) ) { … … 850 461 * String Locator Scripts 851 462 */ 852 wp_enqueue_script( 'string-locator-search', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'resources/js/string-locator-search.js', array( 'jquery', 'wp-util' ), $this->version, true ); 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 ); 853 470 854 471 wp_localize_script( … … 856 473 'string_locator', 857 474 array( 858 ' ajax_url' => admin_url( 'admin-ajax.php' ),475 'rest_nonce' => wp_create_nonce( 'wp_rest' ), 859 476 'search_nonce' => wp_create_nonce( 'string-locator-search' ), 860 477 'search_current_prefix' => __( 'Next file: ', 'string-locator' ), … … 865 482 'search_no_results' => __( 'Your search was completed, but no results were found.', 'string-locator' ), 866 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 ), 867 489 ) 868 490 ); … … 878 500 * String Locator Scripts 879 501 */ 880 wp_enqueue_script( 'string-locator-editor', trailingslashit( STRING_LOCATOR_PLUGIN_URL ) . 'resources/js/string-locator.js', array( 'jquery', 'code-editor', 'wp-util' ), $this->version, true ); 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 ); 881 509 882 510 wp_localize_script( … … 887 515 'goto_line' => absint( $_GET['string-locator-line'] ), 888 516 'goto_linepos' => absint( $_GET['string-locator-linepos'] ), 889 'save_url' => get_rest_url( null, 'string-locator/v1/save' ), 517 'url' => array( 518 'save' => get_rest_url( null, 'string-locator/v1/save' ), 519 ), 890 520 ) 891 521 ); … … 939 569 return false; 940 570 } 571 572 $include_path = ''; 941 573 942 574 /** … … 947 579 * - The user is capable of editing files. 948 580 */ 949 if ( isset( $_GET['string-locator-path'] ) && $this->is_valid_location( $_GET['string-locator-path'] ) && current_user_can( 'edit_themes' ) ) {950 include_once( dirname( __FILE__ ) . '/../editor.php' );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'; 951 583 } else { 952 include_once( dirname( __FILE__ ) . '/../search.php' ); 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; 953 591 } 954 592 } 955 593 956 594 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' ) ) {595 if ( isset( $_GET['string-locator-path'] ) && self::is_valid_location( $_GET['string-locator-path'] ) && current_user_can( 'edit_themes' ) ) { 958 596 $class .= ' file-edit-screen'; 959 597 } 960 598 961 599 return $class; 962 }963 964 /**965 * Check for inconsistencies in brackets and similar.966 *967 * @param string $start Start delimited.968 * @param string $end End delimiter.969 * @param string $string The string to scan.970 *971 * @return array972 */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 * Handler for storing the content of the code editor.991 *992 * Also runs over the Smart-Scan if enabled.993 *994 * @return void|array995 */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 writable1008 */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 it1022 */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 state1095 */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 void1177 */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.UseUnderscores1189 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 fclose( $file );1216 600 } 1217 601 … … 1234 618 1235 619 /** 1236 * Scan through an individual file to look for occurrences of £string. 1237 * 1238 * @param string $filename The path to the file. 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 array 1246 */ 1247 function scan_file( $filename, $string, $location, $type, $slug, $regex = false ) { 1248 if ( empty( $string ) || ! is_file( $filename ) ) { 1249 return array(); 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 $output[] = array( 1292 'ID' => $match_count, 1293 'linenum' => sprintf( 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 $readfile = @fopen( $filename, 'r' ); 1307 if ( $readfile ) { 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 return $output; 1408 } 1409 1410 function ajax_scan_path( $path ) { 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 $files; 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; 1427 647 } 1428 648 } -
string-locator/trunk/readme.txt
r2566661 r2685592 6 6 Tags: text, search, find, syntax, highlight 7 7 Requires at least: 4.9 8 Tested up to: 5. 89 Stable tag: 2. 4.28 Tested up to: 5.9 9 Stable tag: 2.5.0 10 10 License: GPLv2 or later 11 11 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 46 46 == Changelog == 47 47 48 = 2.4.2 = 49 * Fixed the option to restore previous search. 50 * Fixed respecting text capitalization in previews when doing a non-regex search. 51 * Changed capability checks, now works on hosts that maintain updates for their users. 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. 52 56 53 57 = Older entries = -
string-locator/trunk/string-locator.php
r2432238 r2685592 4 4 * Plugin URI: http://www.clorith.net/wordpress-string-locator/ 5 5 * Description: Scan through theme and plugin files looking for text strings 6 * Version: 2. 4.26 * Version: 2.5.0 7 7 * Author: Clorith 8 8 * Author URI: http://www.clorith.net … … 26 26 */ 27 27 28 namespace JITS\StringLocator; 29 28 30 if ( ! defined( 'ABSPATH' ) ) { 29 31 die(); … … 33 35 define( 'STRING_LOCATOR_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 34 36 35 require_once( __DIR__ . '/includes/class-string-locator.php' ); 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'; 36 58 37 59 /** 38 60 * Instantiate the plugin 39 61 */ 40 $string_locator = new String_Locator(); 62 require_once __DIR__ . '/includes/class-string-locator.php'; 63 new String_Locator();
Note: See TracChangeset
for help on using the changeset viewer.