├── .github └── CODEOWNERS ├── .gitignore ├── .scrutinizer.yml ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE.txt ├── Makefile ├── README.txt ├── assets ├── css │ ├── better-search-replace.css │ ├── images │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ └── ui-icons_ffffff_256x240.png │ └── jquery-ui.min.css ├── img │ ├── bsr-logo-white.svg │ ├── phil-gravatar.jpeg │ └── sidebar-upgrade.png ├── index.php ├── js │ └── better-search-replace.js └── svg │ ├── icon-arrow.svg │ ├── icon-checkmark.svg │ ├── icon-help.svg │ ├── icon-upgrade.svg │ ├── logo-bsr.svg │ └── mdb-birds.svg ├── better-search-replace.php ├── build-cfg ├── better-search-replace │ ├── config.php │ ├── filter │ ├── filter.php │ ├── pre-zip.php │ └── version-check.php ├── build-plugin.sh └── utils │ ├── create-json.php │ ├── get_plugin_version.php │ └── get_plugin_zip_name.php ├── ext ├── bsr-ext-functions.php └── class-bsr-plugin-updater.php ├── includes ├── class-bsr-admin.php ├── class-bsr-ajax.php ├── class-bsr-compatibility.php ├── class-bsr-db.php ├── class-bsr-i18n.php ├── class-bsr-loader.php ├── class-bsr-main.php ├── class-bsr-plugin-footer.php ├── class-bsr-templates-helper.php ├── class-bsr-utils.php └── index.php ├── index.php ├── languages ├── better-search-replace-de_DE.mo ├── better-search-replace-de_DE.po ├── better-search-replace-es_ES.mo ├── better-search-replace-es_ES.po ├── better-search-replace-fr_FR.mo ├── better-search-replace-fr_FR.po └── better-search-replace.pot ├── package.json ├── templates ├── bsr-dashboard.php ├── bsr-help.php ├── bsr-search-replace.php ├── bsr-settings.php └── sidebar.php ├── vendor └── brumann │ └── polyfill-unserialize │ ├── LICENSE │ └── src │ ├── DisallowedClassesSubstitutor.php │ └── Unserialize.php └── yarn.lock /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @deliciousbrains/deli-eng 2 | 3 | #jira:DELI is where issues related to this repository should be ticketed] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Operating Systems 2 | Thumbs.db 3 | ehthumbs.db 4 | Desktop.ini 5 | $RECYCLE.BIN/ 6 | .DS_Store 7 | *~ 8 | 9 | # IDEs 10 | *.sublime-project 11 | *.sublime-workspace 12 | .idea 13 | *.pydevproject 14 | .project 15 | .metadata 16 | build/ 17 | tmp/ 18 | tests/clover.xml 19 | 20 | # Node 21 | node_modules 22 | 23 | # Artifacts 24 | builds/ 25 | assets/css/better-search-replace.min.css 26 | assets/js/better-search-replace.min.* 27 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | # .scrutinizer.yml 2 | tools: 3 | external_code_coverage: true 4 | sensiolabs_security_checker: true 5 | php_mess_detector: true 6 | php_code_sniffer: true 7 | php_code_coverage: true 8 | php_sim: true 9 | php_analyzer: true 10 | php_pdepend: true 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | #Contributing to Better Search Replace 2 | 3 | Better Search Replace is open source to encourage feedback and community patches. 4 | 5 | Please check the guidelines below before contributing to make sure that everything stays in order. 6 | 7 | __Please Note:__ GitHub is for bug reports and contributions only - if you have a support question please don't post here, go to the [Support Forum](https://wordpress.org/support/plugin/better-search-replace) instead. 8 | 9 | ## Getting Started 10 | 11 | * Submit a ticket for your issue, assuming one does not already exist. 12 | * Raise it on the [Issue Tracker](https://github.com/deliciousbrains/Better-Search-Replace/issues) 13 | * Clearly describe the issue including steps to reproduce the bug. 14 | * Make sure you fill in the earliest version that you know has the issue as well as the version of WordPress you're using. 15 | 16 | ## Making Changes 17 | 18 | * Fork the repository on GitHub 19 | * Make the changes to your forked repository 20 | * Ensure you stick to the [WordPress Coding Standards](https://codex.wordpress.org/WordPress_Coding_Standards) 21 | * When committing, reference your issue (if present) and include a note about the fix 22 | * If possible, and if applicable, please also add/update unit tests for your changes 23 | * Push the changes to your fork and submit a pull request to the 'master' branch of the repository 24 | 25 | ## Code Documentation 26 | 27 | * Please make sure that every function is documented! 28 | * If you're adding/editing a function in a class, make sure to add `@access {private|public|protected}` 29 | * Finally, please use tabs and not spaces. The tab indent size should be 4 for all code. 30 | 31 | Once you've submitted your pull request, it will be reviewed and merged in if all looks good. 32 | 33 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | makepot: { 6 | target: { 7 | options: { 8 | domainPath: '/languages/', 9 | mainFile: 'better-search-replace.php', 10 | potFilename: 'better-search-replace.pot', 11 | potHeaders: { 12 | poedit: true, 13 | 'report-msgid-bugs-to': 'http://wordpress.org/support/plugin/better-search-replace', 14 | 'last-translator': 'Delicious Brains ', 15 | 'language-team': 'Delicious Brains ' 16 | }, 17 | type: 'wp-plugin', 18 | updateTimestamp: true, 19 | } 20 | } 21 | }, 22 | cssmin: { 23 | target: { 24 | files: [{ 25 | expand: true, 26 | cwd: 'assets/css', 27 | src: ['*.css', '!*.min.css'], 28 | dest: 'assets/css', 29 | ext: '.min.css' 30 | }] 31 | } 32 | }, 33 | uglify: { 34 | my_target: { 35 | options: { 36 | sourceMap: true 37 | }, 38 | files: { 39 | 'assets/js/better-search-replace.min.js': ['assets/js/better-search-replace.js'] 40 | } 41 | } 42 | }, 43 | watch: { 44 | css: { 45 | files: [ 46 | 'assets/css/*.css', 47 | '!assets/css/*.min.css' 48 | ], 49 | tasks: [ 50 | 'cssmin' 51 | ] 52 | }, 53 | js: { 54 | files: [ 55 | 'assets/js/*.js', 56 | '!assets/js/*.min.js', 57 | '!assets/js/*.min.js.map' 58 | ], 59 | tasks: [ 60 | 'uglify' 61 | ] 62 | } 63 | } 64 | }); 65 | 66 | grunt.loadNpmTasks('grunt-wp-i18n'); 67 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 68 | grunt.loadNpmTasks('grunt-contrib-uglify'); 69 | grunt.loadNpmTasks('grunt-contrib-watch'); 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PHP_SRC := better-search-replace.php $(wildcard includes/*.php) $(wildcard ext/*.php) 2 | POT_OBJ := languages/better-search-replace.pot 3 | CSS_DIR := assets/css 4 | CSS_ALL := $(wildcard $(CSS_DIR)/*.css) 5 | CSS_SRC := $(filter-out %.min.css,$(CSS_ALL)) 6 | CSS_OBJ := $(CSS_SRC:.css=.min.css) 7 | JS_DIR := assets/js 8 | JS_ALL := $(wildcard $(JS_DIR)/*.js) 9 | JS_SRC := $(filter-out %.min.js,$(JS_ALL)) 10 | JS_OBJ := $(JS_SRC:.js=.min.js) 11 | JS_MAP := $(JS_SRC:.js=.min.js.map) 12 | 13 | .PHONY: all 14 | all: build-translations build-css uglify 15 | 16 | .PHONY: build-translations 17 | build-translations: $(POT_OBJ) 18 | 19 | $(POT_OBJ): $(PHP_SRC) | node_modules 20 | grunt makepot 21 | 22 | .PHONY: build-css 23 | build-css: $(CSS_OBJ) 24 | 25 | $(CSS_OBJ): %.min.css: %.css | node_modules 26 | grunt cssmin 27 | 28 | .PHONY: uglify 29 | uglify: $(JS_OBJ) 30 | 31 | $(JS_OBJ): %.min.js: %.js | node_modules 32 | grunt uglify 33 | 34 | .PHONY: zip 35 | zip: all 36 | ./build-cfg/build-plugin.sh 37 | 38 | .PHONY: package 39 | package: zip 40 | 41 | .PHONY: install 42 | install: node_modules 43 | 44 | node_modules: package.json 45 | yarn install 46 | 47 | .PHONY: update-deps 48 | update-deps: 49 | yarn upgrade 50 | 51 | .PHONY: product-info 52 | product-info: 53 | php build-cfg/utils/create-json.php README.txt better-search-replace.php > builds/info.json 54 | 55 | .PHONY: clean 56 | clean: 57 | rm -rf builds 58 | rm -f $(CSS_OBJ) 59 | rm -f $(JS_OBJ) $(JS_MAP) 60 | 61 | .PHONY: clean-all 62 | clean-all: clean 63 | rm -rf node_modules 64 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | === Better Search Replace === 2 | Contributors: wpengine, deliciousbrains, mattshaw 3 | Tags: search replace, search and replace, search replace database, update database urls, update live url 4 | Requires at least: 3.0.1 5 | Tested up to: 6.7 6 | Stable tag: 1.4.11-dev 7 | License: GPLv3 or later 8 | License URI: http://www.gnu.org/licenses/gpl-3.0.html 9 | 10 | A simple plugin to update URLs or other text in a database. 11 | 12 | == Description == 13 | 14 | When moving your WordPress site to a new domain or server, you will likely run into a need to run a search/replace on the database for everything to work correctly. Fortunately, there are several plugins available for this task, however, all have a different approach to a few key features. This plugin consolidates the best features from these plugins, incorporating the following features in one simple plugin: 15 | 16 | * Serialization support for all tables 17 | * The ability to select specific tables 18 | * The ability to run a "dry run" to see how many fields will be updated 19 | * No server requirements aside from a running installation of WordPress 20 | * WordPress Multisite support 21 | 22 | > **Time-saving features available in the Pro version:** 23 | > 24 | > * View exactly what changed during a search/replace 25 | > * Backup and import the database while running a search/replace 26 | > * Priority email support from the developer of the plugin 27 | > * Save or load custom profiles for quickly repeating a search/replace in the future 28 | > * Support and updates for 1 year 29 | > 30 | > **[Learn more about Better Search Replace Pro](https://bettersearchreplace.com/)** 31 | 32 | The search and replace functionality is heavily based on interconnect/it's great and open-source Search Replace DB script, modified to use WordPress native database functions to ensure compatibility. 33 | 34 | **Supported Languages** 35 | 36 | * English 37 | * French 38 | * German 39 | * Spanish 40 | 41 | **Want to contribute?** 42 | 43 | Feel free to open an issue or submit a pull request on [GitHub](https://github.com/deliciousbrains/better-search-replace/). 44 | 45 | == Installation == 46 | 47 | Install Better Search Replace like you would install any other WordPress plugin. 48 | 49 | Dashboard Method: 50 | 51 | 1. Login to your WordPress admin and go to Plugins -> Add New 52 | 2. Type "Better Search Replace" in the search bar and select this plugin 53 | 3. Click "Install", and then "Activate Plugin" 54 | 55 | 56 | Upload Method: 57 | 58 | 1. Unzip the plugin and upload the "better-search-replace" folder to your 'wp-content/plugins' directory 59 | 2. Activate the plugin through the Plugins menu in WordPress 60 | 61 | == Frequently Asked Questions == 62 | 63 | = Using Better Search Replace = 64 | 65 | Once activated, Better Search Replace will add a page under the "Tools" menu page in your WordPress admin. 66 | 67 | = Is my host supported? = 68 | 69 | Yes! This plugin should be compatible with any host. 70 | 71 | = Can I damage my site with this plugin? = 72 | 73 | Yes! Entering a wrong search or replace string could damage your database. Because of this, it is always advisable to have a backup of your database before using this plugin. 74 | 75 | = How does this work on WordPress Multisite? = 76 | 77 | When this plugin is installed on a WordPress multisite network: 78 | 79 | * Subsite administrators can only search and replace within tables that belong to that subsite by visiting Dashboard > Tools > Better Search Replace from WP Admin of the subsite. 80 | * Network administrators (i.e. Super Admins) and administrators of the primary site can search and replace across all tables in the multisite network by visiting Dashboard > Tools > Better Search Replace from WP Admin of the primary site. 81 | 82 | To change which users have access to the plugin, the user capability can be modified via code using the `bsr_capability` filter. 83 | 84 | = How can I use this plugin when changing URLs? = 85 | 86 | If you're moving your site from one server to another and changing the URL of your WordPress installation, the approach below allows you to do so easily without affecting the old site: 87 | 88 | 1. Backup the database on your current site 89 | 2. Install the database on your new host 90 | 3. On the new host, define the new site URL in the `wp-config.php` file, as shown [here](http://codex.wordpress.org/Changing_The_Site_URL#Edit_wp-config.php) 91 | 4. Log in at your new admin URL and run Better Search Replace on the old site URL for the new site URL 92 | 5. Delete the site_url constant you added to `wp-config.php`. You may also need to regenerate your .htaccess by going to Settings -> Permalinks and saving the settings. 93 | 94 | More information on moving WordPress can be found [here](http://codex.wordpress.org/Moving_WordPress). 95 | 96 | == Screenshots == 97 | 98 | 1. The Better Search Replace page added to the "Tools" menu 99 | 2. After running a search/replace dry-run. 100 | 101 | == Changelog == 102 | 103 | = Unreleased = 104 | 105 | = 1.4.10 - January 14, 2025 = 106 | * Fix: Improved security and stability 107 | 108 | = 1.4.9 - October 4, 2024 = 109 | * Security: The plugin now uses its own update mechanism from WP Engine servers 110 | * New: Dependencies have been updated 111 | 112 | = 1.4.8 - September 3, 2024 = 113 | * No changes as this was a pro-only release for Better Search Replace Pro 114 | 115 | = 1.4.7 - May 30, 2024 = 116 | * Fix: The case-insensitive setting once again allows case-insensitive strings to be matched within serialized data, fixing a regression introduced in version 1.4.6 117 | 118 | = 1.4.6 - April 17, 2024 = 119 | * Changed: Serialized text strings are now only deserialized when containing a match, resulting in faster performance 120 | * Security: Table names are now escaped when displaying search results 121 | 122 | = 1.4.5 - January 18, 2024 = 123 | * Security: Unserializing an object during search and replace operations now passes `'allowed_classes' => false` to avoid instantiating the object and potentially running malicious code stored in the database (thanks to Wordfence for responsible disclosure on December 18, 2023 followed by development and testing of the fix by WP Engine) 124 | * Fix: A regression in version 1.4.4 which caused some search results to be skipped has been fixed to ensure only numeric keyed objects are skipped 125 | 126 | = 1.4.4 - December 14, 2023 = 127 | * Fix: Objects with numerical properties are now skipped to avoid causing errors 128 | 129 | = 1.4.3 - September 5, 2023 = 130 | * New: Links to plugin documentation, support, feedback, and changelog are now available in the footer of WP Admin 131 | * Improvement: PHP 8.2 and Better Search Replace are now compatible 132 | 133 | = 1.4.2 - January 11, 2023 = 134 | * Security: Arbitrary tab templates in the `templates` directory can no longer be loaded using a query parameter. 135 | 136 | = 1.4.1 - July 25, 2022 = 137 | * Security: Selected tables are now confirmed to exist before processing the request 138 | 139 | = 1.4 - April 7, 2022 = 140 | * New: Better Search Replace has a brand new user interface 141 | * Improvement: Default capability required to use the plugin has changed from "install_plugins" to "manage_options" for compatibility with DISALLOW_FILE_MODS 142 | 143 | = 1.3.4 - December 7, 2020 = 144 | * Improvement: WordPress 5.6 and PHP 8 compatible 145 | * Fix: Strings that have been serialized twice showing up as false-positives 146 | 147 | = 1.3.3 - February 26, 2019 = 148 | * Fix: Some special characters interfering with search/replace 149 | * Security: Pass template filenames through `sanitize_file_name()` 150 | * Security: Verify nonce when downloading diagnostic info 151 | 152 | = 1.3.2 - January 3, 2018 = 153 | * Fix: Only one table searched on some environments (props @Ov3rfly) 154 | * Tweak: Update text in sidebar 155 | 156 | = 1.3.1 - September 14, 2017 = 157 | * Security: Check if data is serialized before unserializing it 158 | * Improvement: Increased size of table select 159 | 160 | = 1.3 - November 10, 2016 = 161 | * Improvement: Updated sidebar and added pro version discount 162 | * Fix: Outdated links to old website 163 | * Fix: Prevent requests to invalid tabs 164 | 165 | = 1.2.10 - June 2, 2016 = 166 | * Fix: CSS not loaded on details page 167 | 168 | = 1.2.9 - December 8, 2015 = 169 | * Fix: Bug with case-insensitive searches in serialized objects 170 | * Fix: Bug with early skip due to lack of primary key 171 | 172 | = 1.2.8 - November 25, 2015 = 173 | * Fix: Bug with report details 174 | 175 | = 1.2.7 - November 24, 2015 = 176 | * Fix: Untranslateable string 177 | * Tweak: Check BSR_PATH instead of ABSPATH to be consistent 178 | * Tested with 4.4 179 | 180 | = 1.2.6 = 181 | * Removed unused code/small cleanup 182 | 183 | = 1.2.5 = 184 | * Improved progress bar info and styles 185 | * Small cleanup 186 | 187 | = 1.2.4 = 188 | * Added "Settings saved" notice when saving settings 189 | * Fixed bug with wp_magic_quotes interfering with some search strings 190 | 191 | = 1.2.3 = 192 | * Fixed bug with searching for backslashes 193 | * Fixed potential bug with getting tables in large multisites 194 | * Fixed potential notice in append_report 195 | * Improved handling of missing primary keys 196 | 197 | = 1.2.2 = 198 | * Fixed AJAX conflict with WooCommerce 199 | * Fixed a few issues with translations 200 | * Tweaked "System Info" to use get_locale() instead of WP_LANG constant 201 | * Updated German translation (props @Linus Ziegenhagen) 202 | 203 | = 1.2.1 = 204 | * Fixed minor issue with display of progress bar 205 | * Updated translation file 206 | 207 | = 1.2 = 208 | * Switched to AJAX bulk processing for search/replaces 209 | * Decreased minimum "Max Page Size" to 1000 210 | * Added "Help" tab with system info for easier troubleshooting 211 | 212 | = 1.1.1 = 213 | * Added ability to change max page size 214 | * Decreased default page size to prevent white screen issue on some environments 215 | 216 | = 1.1 = 217 | * Added ability to change capability required to use plugin 218 | * Small bugfixes and translation fixes 219 | 220 | = 1.0.6 = 221 | * Added table sizes to the database table listing 222 | * Added French translation (props @Jean Philippe) 223 | 224 | = 1.0.5 = 225 | * Added support for case-insensitive searches 226 | * Added German translation (props @Linus Ziegenhagen) 227 | 228 | = 1.0.4 = 229 | * Potential security fixes 230 | 231 | = 1.0.3 = 232 | * Fixed issue with searching for special characters like '\' 233 | * Fixed bug with replacing some objects 234 | 235 | = 1.0.2 = 236 | * Fixed untranslateable strings on submit button and submenu page. 237 | 238 | = 1.0.1 = 239 | * Fixed issue with loading translations and added Spanish translation (props Eduardo Larequi) 240 | * Fixed bug with reporting timing 241 | * Updated to use "Dry Run" as default 242 | * Added support for WordPress Multisite (see FAQs for more info) 243 | 244 | = 1.0 = 245 | * Initial release 246 | -------------------------------------------------------------------------------- /assets/css/better-search-replace.css: -------------------------------------------------------------------------------- 1 | /*OLD STYLING----------------------------*/ 2 | 3 | #bsr-results-table th { font-weight: bold; } 4 | #bsr-results-table tbody tr:nth-child(odd) { background-color: #F9F9F9; } 5 | .bsr-second, .bsr-third, .bsr-fourth { text-align: center !important; } 6 | #bsr-details-view-wrap { margin: 0 10px; } 7 | #bsr-details-view { table-layout: fixed; padding-top: 40px; } 8 | .bsr-old-val { background-color: #fdd; } 9 | .bsr-new-val { background-color: #cfc; } 10 | .bsr-change { width: 400px !important; } 11 | .bsr-slider { width: 23em; } 12 | .bsr-progress-wrap { width: 95%; height: 12px; background-color: #ddd; } 13 | .bsr-progress { width: 0%; height: 100%; background-color: #0073aa; } 14 | #bsr-search-replace-wrap {/* margin-top: 10px; */} 15 | .bsr-description { display: block; margin-top: 6px !important; color: #444 !important; } 16 | #bsr-help-heading { font-size: 1.3em !important; padding: 0 !important; } 17 | .bsr-processing-wrap { background: #f4f4f4; padding: 16px 16px 32px; overflow: auto; border-top: 1px solid #eae9e9; margin: 20px -12px -23px -12px; } 18 | .bsr-spinner { margin: -3px 0 0 0; } 19 | .bsr-license-status { float: left; margin-right: 15px; height: 28px; font-size: 13px; line-height: 26px; color: var(--color-white); text-align: center; padding: 0 10px; border-radius: 4px; } 20 | #bsr-license-active { background: green; } 21 | #bsr-license-inactive { background: gray; } 22 | .bsr-no-profiles { vertical-align: middle; } 23 | #bsr-back-to-overview { width: 100%; position: fixed; margin-top: 0; background: #f8f8f8; border-bottom: 1px solid #ddd; padding: 8px 10px; } 24 | #bsr-table-select { width:25em; height: 180px; } 25 | 26 | 27 | /*CSS VARIABLES----------------------------*/ 28 | :root { 29 | /*colors*/ 30 | --color-primary: #0073AA; 31 | --color-accent: #27CC87; 32 | --color-white: #fff; 33 | --color-black: 000; 34 | --color-header: #383A46; 35 | --color-nav: #262932; 36 | --color-body-text: #3C3B59; 37 | --color-desc-text: #6E6D99; 38 | --color-panel-heading: #708AA4; 39 | --color-secondary-hover: rgba(0, 115, 170, .15); 40 | --color-secondary-focus: rgba(0, 115, 170, .4); 41 | --color-divider: #CAD8EC; 42 | --color-divider-light: rgba(202, 216, 236, .5); 43 | --color-progress-bg: #57645E; 44 | --color-modal-background: rgba(47, 58, 74, .8); 45 | 46 | /*spacers*/ 47 | --spacer-xs: 8px; 48 | --spacer-sm: 16px; 49 | --spacer-md: 24px; 50 | --spacer-lg: 32px; 51 | --spacer-xl: 40px; 52 | --spacer-2xl: 48px; 53 | --spacer-3xl: 56px; 54 | --spacer-4xl: 64px; 55 | --spacer-5xl: 72px; 56 | --spacer-6xl: 80px; 57 | 58 | /*layout*/ 59 | --panel-border-radius: 6px; 60 | 61 | /*shadows*/ 62 | --panel-shadow: 0px 2px 1px rgba(39, 45, 77, 0.05), 0px 2px 8px rgba(187, 187, 187, 0.20); 63 | --input-shadow: 0px 2px 2px rgba(182, 181, 204, 0.25); 64 | } 65 | 66 | 67 | /*WP OVERRIDES & WRAPPER STYLING----------------------------*/ 68 | 69 | .wrap { 70 | margin: 0; 71 | padding: 0; 72 | overflow-x: auto!important; 73 | } 74 | 75 | #wpcontent { 76 | padding: 0!important; 77 | } 78 | 79 | #wpbody-content { 80 | background: var(--color-header); 81 | padding-bottom: 0!important; 82 | margin: 0 !important; 83 | float: none; 84 | } 85 | 86 | .form-table { 87 | margin-top: 0; 88 | } 89 | 90 | .wp-core-ui select { 91 | background: #fff url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat right 12px top 55%; 92 | background-size: 18px 18px; 93 | } 94 | 95 | .metabox-holder { 96 | padding: 0!important; 97 | } 98 | 99 | 100 | /*MODAL OVERRIDES----------------------------*/ 101 | 102 | #TB_overlay { 103 | background: var(--color-modal-background)!important; 104 | } 105 | 106 | #TB_window { 107 | background: var(--color-white); 108 | box-shadow: var(--panel-shadow); 109 | border-radius: var(--panel-border-radius); 110 | overflow: hidden; 111 | } 112 | 113 | #TB_title { 114 | display: flex; 115 | align-items: center; 116 | padding: 0 var(--spacer-lg); 117 | height: var(--spacer-3xl)!important; 118 | font-weight: 500!important; 119 | } 120 | 121 | #TB_ajaxWindowTitle { 122 | font-weight: 500; 123 | font-size: 14px; 124 | letter-spacing: 0.6px; 125 | text-transform: uppercase; 126 | color: var(--color-panel-heading); 127 | line-height: 1; 128 | padding:0!important; 129 | } 130 | 131 | .tb-close-icon { 132 | color: var(--color-panel-heading)!important; 133 | top: -14px!important; 134 | right: 12px!important; 135 | } 136 | 137 | #bsr-results-table { 138 | border-radius: var(--panel-border-radius)!important; 139 | } 140 | 141 | #bsr-results-table th { 142 | line-height: 2rem!important; 143 | } 144 | 145 | #bsr-results-table tbody tr:last-child td:first-of-type { 146 | border-bottom-left-radius: var(--panel-border-radius); 147 | } 148 | 149 | #bsr-results-table tbody tr:last-child td:last-of-type { 150 | border-bottom-right-radius: var(--panel-border-radius); 151 | } 152 | 153 | #bsr-results-table th { 154 | color: var(--color-header)!important; 155 | } 156 | 157 | #bsr-results-table td .tooltip { 158 | font-weight: bold; 159 | cursor: pointer; 160 | text-decoration: underline; 161 | } 162 | 163 | #bsr-results-table td .helper-message { 164 | max-width: 220px; 165 | } 166 | 167 | #bsr-results-table td .helper-message a { 168 | font-weight: bold; 169 | } 170 | 171 | #bsr-back-to-overview { 172 | padding: 16px!important; 173 | } 174 | 175 | #bsr-details-view { 176 | padding-top:48px; 177 | } 178 | 179 | .bsr-change { 180 | border-bottom: 1px solid var(--color-divider); 181 | padding-bottom: 16px!important; 182 | } 183 | 184 | .widefat td { 185 | padding: var(--spacer-sm); 186 | } 187 | 188 | .bsr-row-desc td { 189 | line-height: 2rem!important; 190 | padding-bottom: 0; 191 | font-size: 14px; 192 | } 193 | 194 | 195 | /*LAYOUT STYLING----------------------------*/ 196 | 197 | .row { 198 | display: flex; 199 | column-gap: var(--spacer-lg); 200 | } 201 | 202 | .col { 203 | display: flex; 204 | flex-direction: column; 205 | justify-content: center; 206 | } 207 | 208 | .hidden { 209 | display: none; 210 | } 211 | 212 | .full-width { 213 | width: 100%; 214 | } 215 | 216 | .row p { 217 | margin: 0; 218 | } 219 | 220 | 221 | /*HEADER STYLING----------------------------*/ 222 | 223 | 224 | .header { 225 | background: var(--color-header); 226 | display:flex; 227 | height: 96px; 228 | padding: 0 var(--spacer-lg); 229 | } 230 | 231 | .header a { 232 | display: flex; 233 | } 234 | 235 | .logo { 236 | width: 72px; 237 | } 238 | 239 | .header .content { 240 | display: flex; 241 | justify-content: space-between; 242 | align-items: center; 243 | max-width: 1280px; 244 | width: 100%; 245 | } 246 | 247 | .upgrade-notice { 248 | color: var(--color-accent); 249 | cursor: pointer; 250 | line-height: 1.3; 251 | font-size: 13.5px; 252 | font-weight: 500; 253 | } 254 | 255 | .upgrade-notice:hover { 256 | text-decoration: underline; 257 | color: var(--color-accent); 258 | } 259 | 260 | .upgrade-notice img { 261 | width: 16px; 262 | height: 16px; 263 | margin-right: var(--spacer-xs); 264 | } 265 | 266 | .update-nag, notice-warning-inline { 267 | margin: var(--spacer-md) var(--spacer-lg) 0 var(--spacer-lg); 268 | } 269 | 270 | .bsr-notice-container { 271 | padding: var(--spacer-sm) var(--spacer-lg) 0 var(--spacer-lg); 272 | max-width: 900px; 273 | } 274 | 275 | #setting-error-settings_updated, 276 | .bsr-updated { 277 | display: none; 278 | } 279 | 280 | /*NAV STYLING----------------------------*/ 281 | 282 | .nav-tab-wrapper { 283 | font-size: 23px; 284 | height: var(--spacer-3xl); 285 | background: var(--color-nav); 286 | display: flex; 287 | padding: 0; 288 | align-items: center; 289 | margin: 0; 290 | } 291 | 292 | .nav-tab { 293 | color: var(--color-white); 294 | opacity: .5; 295 | border-top: 4px solid transparent !important; 296 | border-bottom: 4px solid transparent !important; 297 | } 298 | 299 | .nav-tab:hover { 300 | opacity: 1; 301 | background: none; 302 | color: var(--color-white); 303 | } 304 | 305 | .nav-tab:focus { 306 | background: none; 307 | color: var(--color-white); 308 | opacity: inherit; 309 | } 310 | 311 | .nav-tab-active { 312 | color: var(--color-white)! important; 313 | opacity: 1!important; 314 | border-bottom: 4px solid var(--color-accent) !important; 315 | } 316 | 317 | .nav-tab-wrapper ul { 318 | padding: 0 var(--spacer-lg); 319 | display: flex; 320 | column-gap: var(--spacer-lg); 321 | height: 100%; 322 | margin: 0; 323 | } 324 | 325 | .nav-tab-wrapper li { 326 | margin: 0; 327 | padding: 0; 328 | } 329 | 330 | .nav-tab-wrapper a { 331 | margin: 0; 332 | background: none; 333 | border: none; 334 | padding: 0; 335 | font-weight: 500 !important; 336 | align-items: center; 337 | display: inline-flex; 338 | height: 100%; 339 | box-sizing: border-box; 340 | } 341 | 342 | 343 | /*BUTTON STYLING----------------------------*/ 344 | 345 | .button { 346 | font-size: 15px !important; 347 | letter-spacing: 0.4px !important; 348 | border-radius: var(--panel-border-radius) !important; 349 | font-weight: 500 !important; 350 | display: inline-flex !important; 351 | align-items: center !important; 352 | padding: 0 var(--spacer-md) !important; 353 | column-gap: 8px; 354 | margin: 0!important; 355 | } 356 | 357 | .button-lg { 358 | height: var(--spacer-3xl) !important; 359 | } 360 | 361 | .button-md { 362 | height: var(--spacer-2xl) !important; 363 | } 364 | 365 | .button-sm { 366 | height: var(--spacer-xl) !important; 367 | padding: 0 var(--spacer-sm) !important; 368 | font-size: 14px !important; 369 | border-radius: 4px!important; 370 | } 371 | 372 | .button-primary { 373 | color: var(--color-white) !important; 374 | background: var(--color-primary) !important; 375 | border: 1px solid !important; 376 | } 377 | 378 | .button-secondary { 379 | color: var(--color-primary) !important; 380 | background: transparent !important; 381 | border: 1px solid var(--color-primary) !important; 382 | } 383 | 384 | .button-primary:hover { 385 | filter: brightness(0.95) !important; 386 | } 387 | 388 | .button-primary:active { 389 | filter: brightness(0.92) !important; 390 | } 391 | 392 | .button-primary:focus { 393 | outline: none !important; 394 | box-shadow: 0 0 0 4px var(--color-secondary-focus) !important; 395 | } 396 | 397 | .button-secondary:hover { 398 | background: var(--color-secondary-hover) !important; 399 | } 400 | 401 | .button-active:active { 402 | outline: none !important; 403 | box-shadow: 0 0 0 4px var(--color-secondary-focus) !important; 404 | } 405 | 406 | .button-secondary:focus { 407 | outline: none !important; 408 | box-shadow: 0 0 0 4px var(--color-secondary-focus) !important; 409 | } 410 | 411 | .button-link { 412 | color: #2271b1; 413 | } 414 | 415 | .button svg { 416 | width: 16px; 417 | } 418 | 419 | #bsr-submit.bsr-disabled, #bsr-submit.button-disabled, #bsr-backup-submit.bsr-disabled, #bsr-backup-submit.button-disabled, #bsr-import-submit.bsr-disabled, #bsr-import-submit.button-disabled { 420 | background: var(--color-primary)!important; 421 | color: var(--color-white) !important; 422 | opacity: .4; 423 | } 424 | 425 | 426 | /*PLUGIN UI----------------------------*/ 427 | 428 | .bsr-action-form { 429 | margin: 0!important; 430 | border: 0!important; 431 | background-color: rgb(240, 240, 241); 432 | box-shadow: none; 433 | padding: var(--spacer-2xl) var(--spacer-lg); 434 | padding-bottom: 120px; 435 | } 436 | 437 | .ui-sidebar-wrapper { 438 | max-width: 1280px; 439 | display: flex; 440 | flex-direction: row; 441 | column-gap: 48px; 442 | } 443 | 444 | #bsr-search-replace-wrap { 445 | background: transparent; 446 | border: 0; 447 | padding: 0; 448 | box-shadow: none; 449 | } 450 | 451 | #bsr-search-replace-form { 452 | display: flex; 453 | flex-direction: column; 454 | row-gap: var(--spacer-lg); 455 | } 456 | 457 | #bsr-search-replace-form .updated { 458 | margin-top: 0; 459 | margin-bottom: 0; 460 | display: block !important; 461 | } 462 | 463 | .inside { 464 | margin: 0!important; 465 | padding: 0!important; 466 | width: 100%; 467 | min-width: 640px; 468 | display: flex; 469 | flex-direction: column; 470 | row-gap: var(--spacer-lg); 471 | } 472 | 473 | .panel { 474 | background: var(--color-white); 475 | box-shadow: var(--panel-shadow); 476 | border-radius: var(--panel-border-radius); 477 | } 478 | 479 | .panel-header { 480 | height: 56px; 481 | padding: 0 var(--spacer-lg) !important; 482 | border-bottom: 1px solid var( --color-divider); 483 | display: flex; 484 | align-items: center; 485 | justify-content: space-between; 486 | } 487 | 488 | .panel-header a { 489 | display: inline-flex; 490 | } 491 | 492 | .panel-header h3 { 493 | font-weight: 500; 494 | font-size: 14px; 495 | letter-spacing: 0.6px; 496 | text-transform: uppercase; 497 | color: var(--color-panel-heading); 498 | line-height: 1; 499 | padding: 0!important; 500 | } 501 | 502 | .panel-footer { 503 | padding: var(--spacer-md) var(--spacer-lg); 504 | border-top: 1px solid var( --color-divider); 505 | } 506 | 507 | #bsr-error-wrap { 508 | display: none; 509 | } 510 | 511 | #bsr-error-wrap .error { 512 | margin: 0; 513 | } 514 | 515 | .bsr-processing-wrap { 516 | background: var(--color-white); 517 | box-shadow: var(--panel-shadow); 518 | border-radius: var(--panel-border-radius); 519 | margin: 0; 520 | padding: var(--spacer-lg); 521 | } 522 | 523 | .bsr-progress { 524 | height: var(--spacer-xs); 525 | border-radius: 20px; 526 | background: var(--color-accent); 527 | } 528 | 529 | .bsr-progress-wrap { 530 | height: var(--spacer-xs); 531 | } 532 | 533 | .panel-content { 534 | padding: var(--spacer-lg); 535 | display: flex; 536 | flex-direction: column; 537 | row-gap: var(--spacer-lg); 538 | } 539 | 540 | .settings { 541 | padding: 0!important; 542 | row-gap: 0; 543 | } 544 | 545 | .settings .row { 546 | padding: var(--spacer-lg) !important; 547 | border-bottom: 1px solid var(--color-divider-light); 548 | } 549 | 550 | .last-row { 551 | border: none!important; 552 | } 553 | 554 | .additional-settings .row { 555 | padding: var(--spacer-sm) var(--spacer-lg) !important; 556 | } 557 | 558 | .settings .row .col { 559 | row-gap: 4px; 560 | } 561 | 562 | .settings-header { 563 | display: inline-flex; 564 | column-gap: var(--spacer-xs); 565 | } 566 | 567 | .settings-header span { 568 | font-size: 14px; 569 | font-weight: 600; 570 | color: var(--color-panel-heading); 571 | } 572 | 573 | .slider-wrapper { 574 | padding: var(--spacer-xs) 0 0 0; 575 | } 576 | 577 | .bsr-slider { 578 | border: none!important; 579 | background: var(--color-divider)!important; 580 | } 581 | 582 | .ui-widget-header { 583 | background: var(--color-accent)!important; 584 | } 585 | 586 | .ui-slider-handle { 587 | top: -6px!important; 588 | cursor: pointer!important; 589 | box-shadow: var(--input-shadow); 590 | } 591 | 592 | .ui-state-hover, .ui-state-focus { 593 | border-color: var(--color-panel-heading)!important; 594 | background: var(--color-white)!important; 595 | } 596 | 597 | .tables { 598 | row-gap: var(--spacer-xs); 599 | } 600 | 601 | .import-file { 602 | border-radius: var(--panel-border-radius); 603 | padding: var(--spacer-sm); 604 | border: 1px solid var( --color-divider); 605 | } 606 | 607 | .checkbox { 608 | justify-content: center; 609 | } 610 | 611 | label.replace_guids { 612 | column-gap: var(--spacer-xs); 613 | align-items: center; 614 | display: flex; 615 | } 616 | 617 | label.replace_guids a { 618 | display: inline-flex; 619 | } 620 | 621 | label.replace_guids img { 622 | width: 16px; 623 | margin-top: -2px; 624 | } 625 | 626 | #search_for, #replace_with { 627 | width: 100%; 628 | display: inline-flex; 629 | align-content: center; 630 | line-height: 1; 631 | } 632 | 633 | #profile-name { 634 | padding-left: 80px!important; 635 | } 636 | 637 | .regular-text { 638 | background: var(--color-white)!important; 639 | border: 1px solid var(--color-divider)!important; 640 | box-shadow: var(--input-shadow)!important; 641 | border-radius: var(--panel-border-radius); 642 | height: var(--spacer-xl); 643 | } 644 | 645 | #bsr-table-select { 646 | width: 100% !important; 647 | max-width: 100%; 648 | height: 200px; 649 | max-height: 200px; 650 | padding: var(--spacer-xs)!important; 651 | } 652 | 653 | .description { 654 | color: var(--color-desc-text)!important; 655 | } 656 | 657 | p.submit { 658 | margin: 0; 659 | padding: 0; 660 | } 661 | 662 | .helper-message { 663 | font-size: 14px; 664 | color: var(--color-body-text); 665 | line-height: 1.4em; 666 | padding: 10px 16px; 667 | border-radius: var(--panel-border-radius); 668 | background-color: #fff; 669 | position: absolute; 670 | max-width: 500px; 671 | z-index: 9999; 672 | display: none; 673 | box-shadow: 4px 0px 20px rgba(0,0,0,0.20); 674 | text-align: left; 675 | } 676 | 677 | .helper-message.right:after { 678 | content: ''; 679 | border-bottom: 8px solid transparent; 680 | border-right: 8px solid #fff; 681 | border-top: 8px solid transparent; 682 | display: block; 683 | height: 0; 684 | left: -7px; 685 | position: absolute; 686 | top: 11px; 687 | width: 0; 688 | } 689 | 690 | .helper-message.left:before { 691 | content: ''; 692 | border-bottom: 8px solid transparent; 693 | border-left: 8px solid #fff; 694 | border-top: 8px solid transparent; 695 | display: block; 696 | height: 0; 697 | right: -7px; 698 | position: absolute; 699 | top: 11px; 700 | width: 0; 701 | } 702 | 703 | .helper-message.bottom:before { 704 | content: ''; 705 | border-left: 8px solid transparent; 706 | border-right: 8px solid transparent; 707 | border-bottom: 8px solid #aaa; 708 | border-top: 0; 709 | display: block; 710 | height: 0; 711 | left: 50%; 712 | margin-left: -8px; 713 | position: absolute; 714 | top: -9px; 715 | width: 0; 716 | } 717 | 718 | .helper-message.bottom:after { 719 | content: ''; 720 | border-left: 8px solid transparent; 721 | border-right: 8px solid transparent; 722 | border-bottom: 8px solid #fff; 723 | border-top: 0; 724 | display: block; 725 | height: 0; 726 | left: 50%; 727 | margin-left: -8px; 728 | position: absolute; 729 | top: -8px; 730 | width: 0; 731 | } 732 | 733 | /*INPUT STYLING----------------------------*/ 734 | 735 | input#submit { 736 | min-height: 40px!important; 737 | } 738 | 739 | label { 740 | font-size: 14px; 741 | } 742 | .input-text { 743 | display: flex; 744 | flex-direction: column; 745 | row-gap: var(--spacer-xs); 746 | } 747 | 748 | input[type="text"] { 749 | padding: 0 var(--spacer-sm)!important; 750 | } 751 | 752 | input[type="text"]:focus, select:focus { 753 | border-color: var(--color-primary) !important; 754 | outline: 0; 755 | box-shadow: 0 0 0 1px var(--color-primary) !important; 756 | } 757 | 758 | input[type="checkbox"] { 759 | margin: 0; 760 | } 761 | 762 | /*checkbox input*/ 763 | input[type="checkbox"] { 764 | /* margin: 0 0.5rem; */ 765 | width: 18px !important; 766 | height: 18px !important; 767 | box-shadow: none !important; 768 | } 769 | 770 | input[type=checkbox]:checked { 771 | border: none; 772 | background: var(--color-primary); 773 | content: url("data:image/svg+xml,%3Csvg width='6' height='4' viewBox='3 -4 3 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3.95048 6.24692C3.55481 6.64497 2.91278 6.64497 2.5173 6.24692L0.296759 4.01311C-0.0989197 3.61525 -0.0989197 2.96939 0.296759 2.57154C0.692247 2.17349 1.33427 2.17349 1.72995 2.57154L3.05295 3.90226C3.15283 4.00254 3.31495 4.00254 3.41502 3.90226L6.99732 0.298534C7.39281 -0.0995112 8.03483 -0.0995112 8.43051 0.298534C8.62052 0.48968 8.72727 0.749023 8.72727 1.01932C8.72727 1.28961 8.62052 1.54896 8.43051 1.7401L3.95048 6.24692Z' fill='white'/%3E%3C/svg%3E%0A"); 774 | border-color: var(--color-primary); 775 | } 776 | 777 | input[type=checkbox]:checked::before { 778 | border: none; 779 | color: #fff; 780 | background-image: url("data:image/svg+xml,%3Csvg width='9' height='7' viewBox='0 0 9 7' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3.95048 6.24692C3.55481 6.64497 2.91278 6.64497 2.5173 6.24692L0.296759 4.01311C-0.0989197 3.61525 -0.0989197 2.96939 0.296759 2.57154C0.692247 2.17349 1.33427 2.17349 1.72995 2.57154L3.05295 3.90226C3.15283 4.00254 3.31495 4.00254 3.41502 3.90226L6.99732 0.298534C7.39281 -0.0995112 8.03483 -0.0995112 8.43051 0.298534C8.62052 0.48968 8.72727 0.749023 8.72727 1.01932C8.72727 1.28961 8.62052 1.54896 8.43051 1.7401L3.95048 6.24692Z' fill='white'/%3E%3C/svg%3E%0A"); 781 | } 782 | 783 | input[type="checkbox"]:focus { 784 | outline: 0.15rem solid rgba(35, 109, 231, .4); 785 | outline-offset: 1px; 786 | box-shadow: none; 787 | } 788 | 789 | input[type="file" i] { 790 | margin-inline-end: 16px!important; 791 | } 792 | 793 | input[type="file" i]::-webkit-file-upload-button { 794 | margin-inline-end: 16px!important; 795 | } 796 | 797 | textarea[readonly] { 798 | width: 100%; 799 | height: 480px; 800 | font-family: Menlo,Monaco,monospace; 801 | font-size: 12px; 802 | margin: 0; 803 | background: var(--color-header); 804 | color: var(--color-white); 805 | padding: var(--spacer-sm); 806 | border-radius: var(--panel-border-radius); 807 | border: none; 808 | box-shadow: var(--input-shadow) 809 | } 810 | 811 | select { 812 | border-radius: var(--panel-border-radius)!important; 813 | border: 1px solid var(--color-divider)!important; 814 | box-shadow: var(--input-shadow)!important; 815 | height: var(--spacer-xl); 816 | } 817 | 818 | .select { 819 | width: 320px; 820 | max-width: 320px; 821 | line-height: 1; 822 | padding: 0 var(--spacer-sm)!important; 823 | } 824 | 825 | select option { 826 | font-size: 13px!important; 827 | } 828 | 829 | .saved-profiles .panel-content { 830 | row-gap: var(--spacer-md)!important; 831 | } 832 | 833 | .profile-select { 834 | align-items: flex-end; 835 | column-gap: var(--spacer-xs); 836 | } 837 | 838 | .license-field { 839 | display: flex; 840 | column-gap: var(--spacer-xs); 841 | } 842 | 843 | #bsr-license-active, #bsr-license-inactive { 844 | display:none; 845 | } 846 | 847 | #wpfooter p { 848 | font-style: italic; 849 | } 850 | 851 | /*BREAKPOINT STYLING----------------------------*/ 852 | 853 | @media only screen and (max-width: 800px) { 854 | #bsr_license_key { 855 | width: 100%; 856 | } 857 | .license-field { 858 | width: 100%; 859 | } 860 | } 861 | 862 | @media only screen and (max-width: 640px) { 863 | .search-replace { 864 | flex-direction: column; 865 | row-gap: var(--spacer-md); 866 | } 867 | .profile-select { 868 | flex-direction: column; 869 | row-gap: var(--spacer-sm); 870 | } 871 | .profile-select .input-text, #bsr-profile { 872 | width: 100%!important; 873 | max-width: 100%!important; 874 | } 875 | .license-field { 876 | flex-direction: column; 877 | row-gap: var(--spacer-sm); 878 | } 879 | .license-field input[type="submit"] { 880 | width: max-content; 881 | } 882 | } 883 | 884 | @media only screen and (max-width: 480px) { 885 | .select { 886 | width: 100%!important; 887 | } 888 | } 889 | 890 | 891 | /*SIDEBAR STYLING----------------------------*/ 892 | 893 | .upgrade-sidebar { 894 | max-width: 290px; 895 | height: fit-content; 896 | background: linear-gradient(176.44deg, #0F5858 2.92%, #10585C 23.02%, #145469 42.98%, #0D3458 71.92%, #062645 99.66%); 897 | border-radius: var(--panel-border-radius); 898 | } 899 | 900 | .upgrade-sidebar .content { 901 | padding: 0 var(--spacer-md); 902 | } 903 | 904 | .upgrade-sidebar .content h3 { 905 | color: var(--color-white); 906 | font-size: 24px; 907 | margin-top: 16px; 908 | } 909 | 910 | .upgrade-sidebar .content p { 911 | line-height: 1.6; 912 | color: var(--color-white); 913 | font-size: 16px; 914 | } 915 | 916 | .upgrade-sidebar ul { 917 | padding: var(--spacer-md) 0; 918 | display: flex; 919 | flex-direction: column; 920 | row-gap: 16px; 921 | } 922 | 923 | .upgrade-sidebar ul li { 924 | display: flex; 925 | column-gap: var(--spacer-sm); 926 | align-items: flex-start; 927 | list-style: none; 928 | padding-left: 28px; 929 | background: url("data:image/svg+xml,%3Csvg width='19' height='19' viewBox='0 0 19 19' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M10.6568 17.1461C15.008 16.3789 17.9133 12.2296 17.1461 7.87846C16.3789 3.5273 12.2296 0.621957 7.87846 1.38919C3.5273 2.15641 0.621957 6.30567 1.38919 10.6568C2.15641 15.008 6.30567 17.9133 10.6568 17.1461ZM14.3711 7.55993L8.43564 13.4954C8.23406 13.697 7.90729 13.697 7.70571 13.4954L4.35087 10.1405C4.14932 9.93899 4.14932 9.61218 4.35087 9.41064L5.08077 8.68073C5.28235 8.47918 5.60916 8.47918 5.81071 8.68073L8.07067 10.9407L12.9113 6.10012C13.1129 5.89854 13.4397 5.89854 13.6412 6.10012L14.3711 6.83002C14.5727 7.03157 14.5727 7.35838 14.3711 7.55993Z' fill='%2324CE87'/%3E%3C/svg%3E%0A") 0 2px no-repeat; 930 | } 931 | 932 | .upgrade-sidebar ul li p { 933 | font-size: 13.5px!important; 934 | opacity: .8; 935 | margin: 0; 936 | } 937 | 938 | .upgrade-offer-text { 939 | text-align: center; 940 | font-weight: bold; 941 | font-style: italic; 942 | font-size: 14.5px!important; 943 | } 944 | 945 | .upgrade-offer-text span { 946 | color: var(--color-accent); 947 | } 948 | 949 | .button-row { 950 | width: 100%; 951 | margin: 0 auto; 952 | padding: var(--spacer-sm) 0 var(--spacer-lg) 0; 953 | text-align: center; 954 | } 955 | 956 | .button-row a { 957 | font-size: 13.5px!important; 958 | font-weight: 600!important; 959 | text-transform: uppercase; 960 | letter-spacing: 2px!important; 961 | border: 0!important; 962 | padding: 0 var(--spacer-lg)!important; 963 | box-shadow: 0 4px 4px 0 rgba(0,0,0,.25); 964 | } 965 | -------------------------------------------------------------------------------- /assets/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /assets/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /assets/css/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /assets/css/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /assets/css/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /assets/css/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /assets/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /assets/img/bsr-logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 11 | 12 | 15 | 17 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /assets/img/phil-gravatar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/img/phil-gravatar.jpeg -------------------------------------------------------------------------------- /assets/img/sidebar-upgrade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/assets/img/sidebar-upgrade.png -------------------------------------------------------------------------------- /assets/index.php: -------------------------------------------------------------------------------- 1 | ' + response.message + '

' ); 34 | } 35 | 36 | if ( 'done' == response.step ) { 37 | 38 | bsr_update_progress_bar( '100%' ); 39 | 40 | // Maybe run another action. 41 | if ( typeof response.next_action != 'undefined' ) { 42 | bsr_update_progress_bar( '0%', 0 ); 43 | bsr_process_step( response.next_action, 0, 0, response.bsr_data ); 44 | } else { 45 | $('.bsr-processing-wrap').remove(); 46 | $('.bsr-disabled').removeClass('bsr-disabled button-disabled' ); 47 | window.location = response.url; 48 | } 49 | 50 | } else { 51 | bsr_update_progress_bar( response.percentage ); 52 | bsr_process_step( action, response.step, response.page, response.bsr_data ); 53 | } 54 | 55 | } 56 | }).fail(function (response) { 57 | $('.bsr-processing-wrap').remove(); 58 | $('.bsr-disabled').removeClass('bsr-disabled button-disabled' ); 59 | $('#bsr-error-wrap').html( '

' + bsr_object_vars.unknown + '

' ).show(); 60 | if ( window.console && window.console.log ) { 61 | console.log(response); 62 | } 63 | }); 64 | 65 | } 66 | 67 | /** 68 | * Initializes a search/replace. 69 | */ 70 | function bsr_search_replace() { 71 | 72 | var search_replace_submit = $( '#bsr-submit' ); 73 | var bsr_error_wrap = $( '#bsr-error-wrap' ); 74 | search_replace_submit.on( 'click', function( e ) { 75 | 76 | e.preventDefault(); 77 | 78 | if ( ! search_replace_submit.hasClass( 'button-disabled' ) ) { 79 | 80 | if ( ! $( '#search_for' ).val() ) { 81 | bsr_error_wrap.html( '

' + bsr_object_vars.no_search + '

' ).show(); 82 | } else if ( ! $( '#bsr-table-select' ).val() ) { 83 | bsr_error_wrap.html( '

' + bsr_object_vars.no_tables + '

' ).show(); 84 | } else { 85 | var str = $( '.bsr-action-form' ).serialize(); 86 | var data = str.replace(/%5C/g, "#BSR_BACKSLASH#" ); 87 | 88 | bsr_error_wrap.html('').hide(); 89 | search_replace_submit.addClass( 'bsr-disabled button-disabled' ); 90 | $( '#bsr-submit-wrap' ).before('
'); 91 | $('.bsr-progress-wrap').append( '

' + bsr_object_vars.processing + '

' ); 92 | bsr_process_step( 'process_search_replace', 0, 0, data ); 93 | } 94 | 95 | } 96 | 97 | }); 98 | 99 | } 100 | 101 | /** 102 | * Updates the progress bar for AJAX bulk actions. 103 | */ 104 | function bsr_update_progress_bar( percentage, speed ) { 105 | if ( typeof speed == 'undefined' ) { 106 | speed = 150; 107 | } 108 | $( '.bsr-progress' ).animate({ 109 | width: percentage 110 | }, speed ); 111 | } 112 | 113 | /** 114 | * Updates the "Max Page Size" slider. 115 | */ 116 | function bsr_update_sliders( percentage ) { 117 | $('#bsr-page-size-slider').slider({ 118 | value: bsr_object_vars.page_size, 119 | range: "min", 120 | min: 1000, 121 | max: 50000, 122 | step: 1000, 123 | slide: function( event, ui ) { 124 | $('#bsr-page-size-value').text( ui.value ); 125 | $('#bsr_page_size').val( ui.value ); 126 | } 127 | }); 128 | } 129 | 130 | bsr_init(); 131 | 132 | function toggle_tooltip( icon ) { 133 | var icon = $( icon ); 134 | var bubble = icon.next(); 135 | 136 | // Close any that are already open 137 | $( '.helper-message' ).not( bubble ).hide(); 138 | 139 | var position = icon.position(); 140 | 141 | if ( icon.parent()[0].nodeName === 'TD' ) { 142 | position = icon.offset(); 143 | } 144 | 145 | if ( bubble.hasClass( 'left' ) ) { 146 | bubble.css({ 147 | 'left': ( position.left - bubble.width() - icon.width() - 29 ) + 'px', 148 | 'top': ( position.top + icon.height() / 2 - 18 ) + 'px' 149 | }) 150 | } else if ( bubble.hasClass( 'bottom' ) ) { 151 | bubble.css( { 152 | 'left': ( ( position.left - bubble.width() / 2 ) - 5 ) + 'px', 153 | 'top': ( position.top + icon.height() + 19 ) + 'px' 154 | } ); 155 | } else { 156 | bubble.css( { 157 | 'left': ( position.left + icon.width() + 19 ) + 'px', 158 | 'top': ( position.top + icon.height() / 2 - 18 ) + 'px' 159 | } ); 160 | } 161 | 162 | bubble.toggle(); 163 | } 164 | 165 | $('body').on('thickbox:iframe:loaded', function(){ 166 | var $iframeBody = $( '#TB_window iframe' ).contents().find( 'body' ); 167 | 168 | $iframeBody.on( 'mouseover', '.tooltip', function( e ) { 169 | e.preventDefault(); 170 | $iframeBody.find( '.helper-message' ).hide(); 171 | toggle_tooltip( this ); 172 | e.stopPropagation(); 173 | }); 174 | 175 | $iframeBody.on( 'mouseleave', 'td', function( e ) { 176 | $iframeBody.find( '.helper-message' ).hide(); 177 | }); 178 | }); 179 | 180 | $( 'body' ).on( 'mouseover', '.tooltip', function( e ) { 181 | toggle_tooltip( this ); 182 | } ); 183 | 184 | $( 'body' ).on( 'mouseleave', '.tooltip', function( e ) { 185 | $( '.helper-message' ).hide(); 186 | } ); 187 | 188 | $( '.notice.inline' ) 189 | .appendTo('.bsr-notice-container' ) 190 | .css( 'display', 'block' ); 191 | 192 | setTimeout(function() { 193 | const $settings_saved_notice = $( '#setting-error-settings_updated' ); 194 | const $bsr_notices = $( '.bsr-updated' ); 195 | 196 | if ( $settings_saved_notice.length || $bsr_notices.length ) { 197 | $( '
' ).prependTo( '.inside' ); 198 | $settings_saved_notice.prependTo( '.bsr-inner-notice-container' ).css( 'display', 'block' ); 199 | $bsr_notices.prependTo( '.bsr-inner-notice-container' ).css( 'display', 'block' ); 200 | } 201 | 202 | $( '.bsr-inner-notice-container .notice-dismiss' ).on( 'click', function ( e ) { 203 | if ( ! $bsr_notices.length ) { 204 | $( '.bsr-inner-notice-container' ).remove(); 205 | } 206 | }); 207 | }, 75); 208 | 209 | 210 | 211 | })( jQuery ); 212 | -------------------------------------------------------------------------------- /assets/svg/icon-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/icon-checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/icon-help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/icon-upgrade.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/logo-bsr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/svg/mdb-birds.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /better-search-replace.php: -------------------------------------------------------------------------------- 1 | . 38 | */ 39 | 40 | // If this file was called directly, abort. 41 | if ( ! defined( 'WPINC' ) ) { 42 | die; 43 | } 44 | 45 | if ( ! function_exists( 'run_better_search_replace' ) ) { 46 | // Defines the path to the main plugin file. 47 | define( 'BSR_FILE', __FILE__ ); 48 | 49 | // Defines the path to be used for includes. 50 | define( 'BSR_PATH', plugin_dir_path( BSR_FILE ) ); 51 | 52 | // Defines the URL to the plugin. 53 | define( 'BSR_URL', plugin_dir_url( BSR_FILE ) ); 54 | 55 | // Defines the current version of the plugin. 56 | define( 'BSR_VERSION', '1.4.11-dev' ); 57 | 58 | // Defines the name of the plugin. 59 | define( 'BSR_NAME', 'Better Search Replace' ); 60 | 61 | /** 62 | * Begins execution of the plugin. 63 | * 64 | * Since everything within the plugin is registered via hooks, 65 | * then kicking off the plugin from this point in the file does 66 | * not affect the page life cycle. 67 | * 68 | * @since 1.0.0 69 | */ 70 | function run_better_search_replace() { 71 | if ( bsr_enabled_for_user() ) { 72 | /** 73 | * The core plugin class that is used to define internationalization, 74 | * dashboard-specific hooks, and public-facing site hooks. 75 | */ 76 | require BSR_PATH . 'includes/class-bsr-main.php'; 77 | $plugin = new Better_Search_Replace(); 78 | $plugin->run(); 79 | } 80 | } 81 | 82 | add_action( 'after_setup_theme', 'run_better_search_replace' ); 83 | } 84 | 85 | if ( ! function_exists( 'bsr_enabled_for_user' ) ) { 86 | /** 87 | * Is the current user allowed to use BSR? 88 | * 89 | * @return bool 90 | */ 91 | function bsr_enabled_for_user() { 92 | // Allows for overriding the capability required to run the plugin. 93 | $cap = apply_filters( 'bsr_capability', 'manage_options' ); 94 | 95 | return current_user_can( $cap ); 96 | } 97 | } 98 | 99 | if ( file_exists( BSR_PATH . 'ext/bsr-ext-functions.php' ) ) { 100 | require_once BSR_PATH . 'ext/bsr-ext-functions.php'; 101 | } 102 | -------------------------------------------------------------------------------- /build-cfg/better-search-replace/config.php: -------------------------------------------------------------------------------- 1 | svn/trunk/better-search-replace.php.tmp" ); 17 | system( 'mv svn/trunk/better-search-replace.php.tmp svn/trunk/better-search-replace.php' ); 18 | system( 'svn stat svn/ | grep \'^\?\' | awk \'{print $2}\' | xargs -I x svn add x@' ); 19 | system( 'svn stat svn/ | grep \'^\!\' | awk \'{print $2}\' | xargs -I x svn rm --force x@' ); 20 | system( "svn cp svn/trunk svn/tags/$version" ); 21 | system( 'svn stat svn/' ); 22 | 23 | echo 'Commit to WP.org? (Y/n)? '; 24 | if ( 'Y' == strtoupper( trim( fgets( STDIN ) ) ) ) { 25 | system( "svn ci --username deliciousbrains svn/ -m 'Deploy version $version'" ); 26 | } 27 | 28 | system( 'rm -fR svn' ); // All done 29 | } 30 | -------------------------------------------------------------------------------- /build-cfg/better-search-replace/version-check.php: -------------------------------------------------------------------------------- 1 | array( 4 | '@Version:\s+(.*)\n@' => 'header', 5 | "/define\(\s*'BSR_VERSION',\s*'([^']+)'\s*\);/" => 'constant', 6 | ), 7 | ); -------------------------------------------------------------------------------- /build-cfg/build-plugin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function usage() { 4 | echo "Usage: $0 [ slug1 slug2 ... ]" 5 | exit 1 6 | } 7 | 8 | SCRIPT_ARGS="$@" 9 | PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" 10 | PLUGIN_SRC_PATH="$PROJECT_ROOT" 11 | PLUGIN_BUILDS_PATH="$PROJECT_ROOT/builds" 12 | PLUGIN_BUILD_CONFIG_PATH="$PROJECT_ROOT/build-cfg" 13 | PUBLISH="" 14 | if [[ $* == *--publish* ]] 15 | then 16 | PUBLISH="-p" 17 | fi 18 | 19 | function echog() { 20 | echo "$(tput setaf 2)$1$(tput sgr 0)" 21 | } 22 | function echor() { 23 | echo "$(tput setaf 1)$1$(tput sgr 0)" 24 | } 25 | 26 | function build_plugin() { 27 | PLUGIN_DIR="$PLUGIN_BUILD_CONFIG_PATH/$PLUGIN" 28 | 29 | if [[ -d "$PLUGIN_DIR" && ! -L "$PLUGIN_DIR" ]]; then 30 | if [ "utils" != "$PLUGIN" ] && [ "common" != "$PLUGIN" ]; 31 | then 32 | VERSION=$(php -f "$PLUGIN_BUILD_CONFIG_PATH/utils/get_plugin_version.php" "$PROJECT_ROOT" $PLUGIN) 33 | ZIP_NAME=$(php -f "$PLUGIN_BUILD_CONFIG_PATH/utils/get_plugin_zip_name.php" "$PROJECT_ROOT" $PLUGIN) 34 | BUILD_ZIP="$PLUGIN_BUILDS_PATH/$ZIP_NAME-$VERSION.zip"; 35 | 36 | if [ -f "$BUILD_ZIP" ] 37 | then 38 | rm "$BUILD_ZIP" 39 | fi 40 | 41 | echog "Building $PLUGIN v$VERSION..." 42 | cd "$PLUGIN_BUILD_CONFIG_PATH/$PLUGIN/" 43 | "$PLUGIN_BUILDS_PATH/plugin-build" "$VERSION" "$PUBLISH" 44 | echog "Plugin built: $BUILD_ZIP" 45 | echo "--------------------------" 46 | fi 47 | else 48 | usage 49 | fi 50 | } 51 | 52 | cd "$PROJECT_ROOT"; 53 | 54 | if [[ $SCRIPT_ARGS == *--pro-version* ]] 55 | then 56 | echo $(php -f "$PLUGIN_BUILD_CONFIG_PATH/utils/get_plugin_version.php" "$PROJECT_ROOT" wp-offload-ses) 57 | exit; 58 | fi 59 | 60 | if [ -z "${SCRIPT_ARGS}" ] 61 | then 62 | echog "Clearing previously built plugins..." 63 | rm -rf "$PLUGIN_BUILDS_PATH" 64 | fi 65 | 66 | if [ ! -d "$PLUGIN_BUILDS_PATH" ] 67 | then 68 | mkdir -p "$PLUGIN_BUILDS_PATH" 69 | fi 70 | 71 | if [ ! -f "$PLUGIN_BUILDS_PATH/plugin-build" -o ! -x "$PLUGIN_BUILDS_PATH/plugin-build" ] 72 | then 73 | echog "Downloading plugin-build script..." 74 | curl -sSL https://raw.githubusercontent.com/deliciousbrains/wp-plugin-build/582fdeb3f6d19ae0b1f2bd0da9b48f45c131ac34/plugin-build -o "$PLUGIN_BUILDS_PATH/plugin-build" 75 | chmod +x "$PLUGIN_BUILDS_PATH/plugin-build" 76 | fi 77 | 78 | echog "Building plugins..." 79 | if [ -z "${SCRIPT_ARGS}" ] 80 | then 81 | for PLUGIN_DIR in "$PLUGIN_BUILD_CONFIG_PATH"/*/ 82 | do 83 | PLUGIN=$(basename "$PLUGIN_DIR"); 84 | 85 | build_plugin 86 | done 87 | else 88 | for PLUGIN in ${SCRIPT_ARGS} 89 | do 90 | build_plugin 91 | done 92 | fi -------------------------------------------------------------------------------- /build-cfg/utils/create-json.php: -------------------------------------------------------------------------------- 1 | get_readme_meta( $readme ), 19 | $this->get_php_file_meta( $plugin ) 20 | ); 21 | 22 | return json_encode( $plugin_info, JSON_PRETTY_PRINT ); 23 | } 24 | 25 | /** 26 | * Get the meta data from the readme.txt file. 27 | * 28 | * @param string $readme The contents of the readme.txt file. 29 | * 30 | * @return array 31 | */ 32 | public function get_readme_meta( $readme ) { 33 | $readme_headers = array( 34 | 'contributors' => 'contributors', 35 | 'tags' => 'Tags', 36 | 'requires_at_least' => 'Requires at least', 37 | 'tested_up_to' => 'Tested up to', 38 | 'stable_tag' => 'Stable tag', 39 | ); 40 | 41 | $meta = $this->get_file_meta( $readme, $readme_headers ); 42 | 43 | $section_names = array( 44 | 'description' => 'Description', 45 | 'changelog' => 'Changelog', 46 | 'faq' => 'Frequently Asked Questions', 47 | ); 48 | 49 | $sections = $this->get_readme_sections( $readme, $section_names ); 50 | if ( ! empty( $sections ) ) { 51 | $meta['sections'] = $sections; 52 | } 53 | 54 | return $meta; 55 | } 56 | 57 | /** 58 | * Get the meta data from the main plugin file. 59 | * 60 | * @param string $plugin The contents of the main plugin file. 61 | * 62 | * @return array 63 | */ 64 | public function get_php_file_meta( $plugin ) { 65 | $plugin_headers = array( 66 | 'name' => 'Plugin Name', 67 | 'pluginURI' => 'Plugin URI', 68 | 'version' => 'Version', 69 | 'description' => 'Description', 70 | 'author' => 'Author', 71 | 'authorURI' => 'Author URI', 72 | 'textDomain' => 'Text Domain', 73 | 'domainPath' => 'Domain Path', 74 | 'network' => 'Network', 75 | ); 76 | 77 | return $this->get_file_meta( $plugin, $plugin_headers ); 78 | } 79 | 80 | /** 81 | * Retrieve metadata from a file. 82 | * 83 | * Searches for metadata in the first 8kiB of a file, such as a plugin or theme. 84 | * Each piece of metadata must be on its own line. Fields can not span multiple 85 | * lines, the value will get cut at the end of the first line. 86 | * 87 | * 88 | * @param string $content File contents. 89 | * @param array $headers List of headers, in the format array('HeaderKey' => 'Header Name'). 90 | * 91 | * @return array Array of file headers in `HeaderKey => Header Value` format. 92 | */ 93 | protected function get_file_meta( $content, $headers ) { 94 | // Make sure we catch CR-only line endings. 95 | $content = str_replace( "\r", "\n", $content ); 96 | $all_headers = array(); 97 | 98 | foreach ( $headers as $field => $regex ) { 99 | $pattern = '/^[ \t\/*#@]*' . preg_quote($regex, '/' ) . ':(.*)$/mi'; 100 | 101 | if ( preg_match( $pattern , $content, $match ) && $match[1] ) { 102 | $all_headers[ $field ] = $this->clean_header_comment( $match[1] ); 103 | } else { 104 | $all_headers[ $field ] = ''; 105 | } 106 | } 107 | 108 | return $all_headers; 109 | } 110 | 111 | protected function get_readme_sections( $str, $defined_sections ) { 112 | $parts = preg_split( 113 | '/^\s*==\s*(.+?)\s*==/m', 114 | $str, 115 | -1, 116 | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY 117 | ); 118 | 119 | $sections = array(); 120 | for ( $i=1; $i <= count( $parts ); $i +=2 ) { 121 | $title = $this->sanitize_text( $parts[$i-1] ); 122 | if ( ! in_array( $title, $defined_sections ) ) { 123 | continue; 124 | } 125 | 126 | $content = $this->sanitize_text( $parts[ $i ] ); 127 | $key = array_flip( $defined_sections )[ $title ]; 128 | $sections[ $key ] = $content; 129 | } 130 | 131 | return $sections; 132 | } 133 | 134 | /** 135 | * Strip close comment and close php tags from file headers used by WP. 136 | * 137 | * @see https://core.trac.wordpress.org/ticket/8497 138 | * 139 | * @param string $str Header comment to clean up. 140 | * 141 | * @return string 142 | */ 143 | protected function clean_header_comment( $str ) { 144 | return trim( preg_replace( "/\s*(?:\*\/|\?>).*/", '', $str ) ); 145 | } 146 | 147 | /** 148 | * Non fancy text sanitization. 149 | * 150 | * @param string $text 151 | * 152 | * @return string 153 | */ 154 | private function sanitize_text( $text ) { 155 | $text = trim($text); 156 | 157 | return $text; 158 | } 159 | 160 | } 161 | 162 | $plugin_meta = new Plugin_Meta(); 163 | echo $plugin_meta->get_plugin_meta( $argv[1], $argv[2] ); 164 | -------------------------------------------------------------------------------- /build-cfg/utils/get_plugin_version.php: -------------------------------------------------------------------------------- 1 | $regexes ) { 17 | $file = "$src_dir/$file"; 18 | 19 | if ( ! file_exists( $file ) ) { 20 | $messages .= "Whoa! Couldn't find $file\n"; 21 | continue; 22 | } 23 | 24 | $file_content = file_get_contents( $file ); 25 | 26 | if ( ! $file_content ) { 27 | $messages .= "Whoa! Could not read contents of $file\n"; 28 | continue; 29 | } 30 | 31 | foreach ( $regexes as $regex => $context ) { 32 | if ( ! preg_match( $regex, $file_content, $matches ) ) { 33 | $messages .= "Whoa! Couldn't find $context version number in $file\n"; 34 | continue; 35 | } 36 | 37 | if ( isset( $matches[1] ) ) { 38 | return $matches[1]; 39 | } 40 | } 41 | } 42 | } 43 | 44 | echo $messages; 45 | 46 | return false; 47 | } -------------------------------------------------------------------------------- /build-cfg/utils/get_plugin_zip_name.php: -------------------------------------------------------------------------------- 1 | 'better-search-replace', 10 | 'plugin_basename' => plugin_basename( BSR_FILE ), 11 | ); 12 | 13 | require_once BSR_PATH . 'ext/class-bsr-plugin-updater.php'; 14 | new DeliciousBrains\Better_Search_Replace\BSR_Plugin_Updater( $properties ); 15 | } 16 | 17 | add_action( 'admin_init', 'bsr_check_for_upgrades' ); 18 | } 19 | -------------------------------------------------------------------------------- /ext/class-bsr-plugin-updater.php: -------------------------------------------------------------------------------- 1 | api_url = 'https://wpe-plugin-updates.wpengine.com/'; 59 | 60 | $this->cache_time = time() + HOUR_IN_SECONDS * 5; 61 | 62 | $this->properties = $this->get_full_plugin_properties( $properties, $this->api_url ); 63 | 64 | if ( ! $this->properties ) { 65 | return; 66 | } 67 | 68 | $this->register(); 69 | } 70 | 71 | /** 72 | * Get the full plugin properties, including the directory name, version, basename, and add a transient name. 73 | * 74 | * @param array $properties These properties are passed in when instantiating to identify the plugin and it's update location. 75 | * @param string $api_url The URL where the api is located. 76 | * 77 | * @return array|null 78 | */ 79 | public function get_full_plugin_properties( $properties, $api_url ) { 80 | $plugins = \get_plugins(); 81 | 82 | // Scan through all plugins installed and find the one which matches this one in question. 83 | foreach ( $plugins as $plugin_basename => $plugin_data ) { 84 | // Match using the passed-in plugin's basename. 85 | if ( $plugin_basename === $properties['plugin_basename'] ) { 86 | // Add the values we need to the properties. 87 | $properties['plugin_dirname'] = dirname( $plugin_basename ); 88 | $properties['plugin_version'] = $plugin_data['Version']; 89 | $properties['plugin_update_transient_name'] = 'wpesu-plugin-' . sanitize_title( $properties['plugin_dirname'] ); 90 | $properties['plugin_update_transient_exp_name'] = 'wpesu-plugin-' . sanitize_title( $properties['plugin_dirname'] ) . '-expiry'; 91 | $properties['plugin_manifest_url'] = trailingslashit( $api_url ) . trailingslashit( $properties['plugin_slug'] ) . 'info.json'; 92 | 93 | return $properties; 94 | } 95 | } 96 | 97 | // No matching plugin was found installed. 98 | return null; 99 | } 100 | 101 | /** 102 | * Register hooks. 103 | * 104 | * @return void 105 | */ 106 | public function register() { 107 | add_filter( 'plugins_api', array( $this, 'filter_plugin_update_info' ), 20, 3 ); 108 | add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'filter_plugin_update_transient' ) ); 109 | } 110 | 111 | /** 112 | * Filter the plugin update transient to take over update notifications. 113 | * 114 | * @param ?object $transient_value The value of the `site_transient_update_plugins` transient. 115 | * 116 | * @handles site_transient_update_plugins 117 | * @return object|null 118 | */ 119 | public function filter_plugin_update_transient( $transient_value ) { 120 | // No update object exists. Return early. 121 | if ( empty( $transient_value ) ) { 122 | return $transient_value; 123 | } 124 | 125 | $result = $this->fetch_plugin_info(); 126 | 127 | if ( false === $result ) { 128 | return $transient_value; 129 | } 130 | 131 | $res = $this->parse_plugin_info( $result ); 132 | 133 | if ( version_compare( $this->properties['plugin_version'], $result->version, '<' ) ) { 134 | $transient_value->response[ $res->plugin ] = $res; 135 | $transient_value->checked[ $res->plugin ] = $result->version; 136 | } else { 137 | $transient_value->no_update[ $res->plugin ] = $res; 138 | } 139 | 140 | return $transient_value; 141 | } 142 | 143 | /** 144 | * Filters the plugin update information. 145 | * 146 | * @param object $res The response to be modified for the plugin in question. 147 | * @param string $action The action in question. 148 | * @param object $args The arguments for the plugin in question. 149 | * 150 | * @handles plugins_api 151 | * @return object 152 | */ 153 | public function filter_plugin_update_info( $res, $action, $args ) { 154 | // Do nothing if this is not about getting plugin information. 155 | if ( 'plugin_information' !== $action ) { 156 | return $res; 157 | } 158 | 159 | // Do nothing if it is not our plugin. 160 | if ( $this->properties['plugin_dirname'] !== $args->slug ) { 161 | return $res; 162 | } 163 | 164 | $result = $this->fetch_plugin_info(); 165 | 166 | // Do nothing if we don't get the correct response from the server. 167 | if ( false === $result ) { 168 | return $res; 169 | } 170 | 171 | return $this->parse_plugin_info( $result ); 172 | } 173 | 174 | /** 175 | * Fetches the plugin update object from the WP Product Info API. 176 | * 177 | * @return object|false 178 | */ 179 | private function fetch_plugin_info() { 180 | // Fetch cache first. 181 | $expiry = get_option( $this->properties['plugin_update_transient_exp_name'], 0 ); 182 | $response = get_option( $this->properties['plugin_update_transient_name'] ); 183 | 184 | if ( empty( $expiry ) || time() > $expiry || empty( $response ) ) { 185 | $response = wp_remote_get( 186 | $this->properties['plugin_manifest_url'], 187 | array( 188 | 'timeout' => 10, 189 | 'headers' => array( 190 | 'Accept' => 'application/json', 191 | ), 192 | ) 193 | ); 194 | 195 | if ( 196 | is_wp_error( $response ) || 197 | 200 !== wp_remote_retrieve_response_code( $response ) || 198 | empty( wp_remote_retrieve_body( $response ) ) 199 | ) { 200 | return false; 201 | } 202 | 203 | $response = wp_remote_retrieve_body( $response ); 204 | 205 | // Cache the response. 206 | update_option( $this->properties['plugin_update_transient_exp_name'], $this->cache_time, false ); 207 | update_option( $this->properties['plugin_update_transient_name'], $response, false ); 208 | } 209 | 210 | $decoded_response = json_decode( $response ); 211 | 212 | if ( json_last_error() !== JSON_ERROR_NONE ) { 213 | return false; 214 | } 215 | 216 | return $decoded_response; 217 | } 218 | 219 | /** 220 | * Parses the product info response into an object that WordPress would be able to understand. 221 | * 222 | * @param object $response The response object. 223 | * 224 | * @return stdClass 225 | */ 226 | private function parse_plugin_info( $response ) { 227 | global $wp_version; 228 | 229 | $res = new stdClass(); 230 | $res->name = $response->name; 231 | $res->slug = $response->slug; 232 | $res->version = $response->version; 233 | $res->requires = $response->requires; 234 | $res->download_link = $response->download_link; 235 | $res->trunk = $response->download_link; 236 | $res->new_version = $response->version; 237 | $res->plugin = $this->properties['plugin_basename']; 238 | $res->package = $response->download_link; 239 | 240 | // Plugin information modal and core update table use a strict version comparison, which is weird. 241 | // If we're genuinely not compatible with the point release, use our WP tested up to version. 242 | // otherwise use exact same version as WP to avoid false positive. 243 | $res->tested = 1 === version_compare( substr( $wp_version, 0, 3 ), $response->tested ) 244 | ? $response->tested 245 | : $wp_version; 246 | 247 | $res->sections = array( 248 | 'description' => $response->sections->description, 249 | 'changelog' => $response->sections->changelog, 250 | ); 251 | 252 | return $res; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /includes/class-bsr-admin.php: -------------------------------------------------------------------------------- 1 | better_search_replace = $better_search_replace; 48 | $this->version = $version; 49 | } 50 | 51 | /** 52 | * Register any CSS and JS used by the plugin. 53 | * @since 1.0.0 54 | * @access public 55 | * @param string $hook Used for determining which page(s) to load our scripts. 56 | */ 57 | public function enqueue_scripts( $hook ) { 58 | if ( 'tools_page_better-search-replace' === $hook ) { 59 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 60 | 61 | wp_enqueue_style( 'better-search-replace', BSR_URL . "assets/css/better-search-replace$min.css", array(), $this->version, 'all' ); 62 | wp_enqueue_style( 'jquery-style', BSR_URL . 'assets/css/jquery-ui.min.css', array(), $this->version, 'all' ); 63 | wp_enqueue_script( 'jquery-ui-slider' ); 64 | wp_enqueue_script( 'better-search-replace', BSR_URL . "assets/js/better-search-replace$min.js", array( 'jquery' ), $this->version, true ); 65 | wp_enqueue_style( 'thickbox' ); 66 | wp_enqueue_script( 'thickbox' ); 67 | 68 | wp_localize_script( 'better-search-replace', 'bsr_object_vars', array( 69 | 'page_size' => get_option( 'bsr_page_size' ) ? absint( get_option( 'bsr_page_size' ) ) : 20000, 70 | 'endpoint' => BSR_AJAX::get_endpoint(), 71 | 'ajax_nonce' => wp_create_nonce( 'bsr_ajax_nonce' ), 72 | 'no_search' => __( 'No search string was defined, please enter a URL or string to search for.', 'better-search-replace' ), 73 | 'no_tables' => __( 'Please select the tables that you want to update.', 'better-search-replace' ), 74 | 'unknown' => __( 'An error occurred processing your request. Try decreasing the "Max Page Size", or contact support.', 'better-search-replace' ), 75 | 'processing' => __( 'Processing...', 'better-search-replace' ) 76 | ) ); 77 | } 78 | } 79 | 80 | /** 81 | * Register any menu pages used by the plugin. 82 | * @since 1.0.0 83 | * @access public 84 | */ 85 | public function bsr_menu_pages() { 86 | $cap = apply_filters( 'bsr_capability', 'manage_options' ); 87 | add_submenu_page( 'tools.php', __( 'Better Search Replace', 'better-search-replace' ), __( 'Better Search Replace', 'better-search-replace' ), $cap, 'better-search-replace', array( $this, 'bsr_menu_pages_callback' ) ); 88 | } 89 | 90 | /** 91 | * The callback for creating a new submenu page under the "Tools" menu. 92 | * @access public 93 | */ 94 | public function bsr_menu_pages_callback() { 95 | require_once BSR_PATH . 'includes/class-bsr-templates-helper.php'; 96 | require_once BSR_PATH . 'templates/bsr-dashboard.php'; 97 | } 98 | 99 | /** 100 | * Renders the result or error onto the better-search-replace admin page. 101 | */ 102 | public static function render_result() { 103 | // Trying to show results? 104 | if ( ! isset( $_GET['result'] ) ) { 105 | return; 106 | } 107 | 108 | $result = get_transient( 'bsr_results' ); 109 | 110 | // Have results with required fields set with correctly typed data? 111 | if ( 112 | empty( $result ) || 113 | ! isset( $result['tables'] ) || 114 | ! is_int( $result['tables'] ) || 115 | ! isset( $result['change'] ) || 116 | ! is_int( $result['change'] ) || 117 | ! isset( $result['updates'] ) || 118 | ! is_int( $result['updates'] ) 119 | ) { 120 | return; 121 | } 122 | 123 | if ( isset( $result['dry_run'] ) && $result['dry_run'] === 'on' ) { 124 | $msg = sprintf( 125 | __( 126 | '

DRY RUN: %1$d tables were searched, %2$d cells were found that need to be updated, and %3$d changes were made.

Click here for more details, or use the form below to run the search/replace.

', 127 | 'better-search-replace' 128 | ), 129 | $result['tables'], 130 | $result['change'], 131 | $result['updates'], 132 | get_admin_url() . 'admin-post.php?action=bsr_view_details&TB_iframe=true&width=800&height=500' 133 | ); 134 | } else { 135 | $msg = sprintf( 136 | __( 137 | '

During the search/replace, %1$d tables were searched, with %2$d cells changed in %3$d updates.

Click here for more details.

', 138 | 'better-search-replace' 139 | ), 140 | $result['tables'], 141 | $result['change'], 142 | $result['updates'], 143 | get_admin_url() . 'admin-post.php?action=bsr_view_details&TB_iframe=true&width=800&height=500' 144 | ); 145 | } 146 | 147 | echo ''; 148 | } 149 | 150 | /** 151 | * Prefills the given value on the search/replace page (dry run, live run, from profile). 152 | * @access public 153 | * @param string $value The value to check for. 154 | * @param string $type The type of the value we're filling. 155 | */ 156 | public static function prefill_value( $value, $type = 'text' ) { 157 | 158 | // Grab the correct data to prefill. 159 | if ( isset( $_GET['result'] ) && get_transient( 'bsr_results' ) ) { 160 | $values = get_transient( 'bsr_results' ); 161 | } else { 162 | $values = array(); 163 | } 164 | 165 | // Prefill the value. 166 | if ( isset( $values[$value] ) ) { 167 | 168 | if ( 'checkbox' === $type && 'on' === $values[$value] ) { 169 | echo 'checked'; 170 | } else { 171 | echo str_replace( '#BSR_BACKSLASH#', '\\', esc_attr( htmlentities( $values[$value] ) ) ); 172 | } 173 | 174 | } 175 | 176 | } 177 | 178 | /** 179 | * Loads the tables available to run a search replace, prefilling if already 180 | * selected the tables. 181 | * @access public 182 | */ 183 | public static function load_tables() { 184 | 185 | // Get the tables and their sizes. 186 | $tables = BSR_DB::get_tables(); 187 | $sizes = BSR_DB::get_sizes(); 188 | 189 | echo ''; 213 | 214 | } 215 | 216 | /** 217 | * Loads the result details (via Thickbox). 218 | * @access public 219 | */ 220 | public function load_details() { 221 | 222 | if ( get_transient( 'bsr_results' ) ) { 223 | $results = get_transient( 'bsr_results' ); 224 | $min = ( defined( 'SCRIPT_DEBUG' ) && true === SCRIPT_DEBUG ) ? '' : '.min'; 225 | $bsr_styles = BSR_URL . 'assets/css/better-search-replace.css?v=' . BSR_VERSION; 226 | 227 | ?> 228 | 229 | 230 | 231 |
232 | 233 | 234 | 235 | 236 | 237 | $report ) { 239 | $time = $report['end'] - $report['start']; 240 | 241 | if ( $report['change'] != 0 ) { 242 | $report['change'] = '' . esc_html( $report['change'] ). ''; 243 | 244 | $upgrade_link = sprintf( 245 | __( 'UPGRADE to view details on the exact changes that will be made.', 'better-search-replace'), 246 | 'https://deliciousbrains.com/better-search-replace/upgrade/?utm_source=insideplugin&utm_medium=web&utm_content=tooltip&utm_campaign=bsr-to-migrate' 247 | ); 248 | 249 | $report['change'] .= '' . $upgrade_link . ''; 250 | } 251 | 252 | if ( $report['updates'] != 0 ) { 253 | $report['updates'] = '' . esc_html( $report['updates'] ) . ''; 254 | } 255 | 256 | echo ''; 257 | } 258 | ?> 259 | 260 |
' . esc_html( $table_name ) . '' . $report['change'] . '' . $report['updates'] . '' . round( $time, 3 ) . __( ' seconds', 'better-search-replace' ) . '
261 |
262 | ' . __( 'Upgrade to Pro', 'better-search-replace' ) . '' ) 304 | ); 305 | } 306 | 307 | return $links; 308 | } 309 | 310 | } 311 | -------------------------------------------------------------------------------- /includes/class-bsr-ajax.php: -------------------------------------------------------------------------------- 1 | add_ajax_actions(); 26 | } 27 | 28 | /** 29 | * Gets our custom endpoint. 30 | * @access public 31 | * @return string 32 | */ 33 | public static function get_endpoint() { 34 | return esc_url_raw( get_admin_url() . 'tools.php?page=better-search-replace&bsr-ajax=' ); 35 | } 36 | 37 | /** 38 | * Set BSR AJAX constant and headers. 39 | * @access public 40 | */ 41 | public function define_ajax() { 42 | 43 | if ( isset( $_GET['bsr-ajax'] ) && ! empty( $_GET['bsr-ajax'] ) ) { 44 | 45 | // Define the WordPress "DOING_AJAX" constant. 46 | if ( ! defined( 'DOING_AJAX' ) ) { 47 | define( 'DOING_AJAX', true ); 48 | } 49 | 50 | // Prevent notices from breaking AJAX functionality. 51 | if ( ! WP_DEBUG || ( WP_DEBUG && ! WP_DEBUG_DISPLAY ) ) { 52 | @ini_set( 'display_errors', 0 ); 53 | } 54 | 55 | // Send the headers. 56 | send_origin_headers(); 57 | @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) ); 58 | @header( 'X-Robots-Tag: noindex' ); 59 | send_nosniff_header(); 60 | nocache_headers(); 61 | 62 | } 63 | 64 | } 65 | 66 | /** 67 | * Check if we're doing AJAX and fire the related action. 68 | * @access public 69 | */ 70 | public function do_bsr_ajax() { 71 | global $wp_query; 72 | 73 | if ( isset( $_GET['bsr-ajax'] ) && ! empty( $_GET['bsr-ajax'] ) ) { 74 | $wp_query->set( 'bsr-ajax', sanitize_text_field( $_GET['bsr-ajax'] ) ); 75 | } 76 | 77 | if ( $action = $wp_query->get( 'bsr-ajax' ) ) { 78 | do_action( 'bsr_ajax_' . sanitize_text_field( $action ) ); 79 | die(); 80 | } 81 | } 82 | 83 | /** 84 | * Adds any AJAX-related actions. 85 | * @access public 86 | */ 87 | public function add_ajax_actions() { 88 | $actions = array( 89 | 'process_search_replace', 90 | ); 91 | 92 | foreach ( $actions as $action ) { 93 | add_action( 'bsr_ajax_' . $action, array( $this, $action ) ); 94 | } 95 | } 96 | 97 | /** 98 | * Processes the search/replace form submitted by the user. 99 | * @access public 100 | */ 101 | public function process_search_replace() { 102 | // Bail if not authorized. 103 | if ( ! BSR_Utils::check_admin_referer( 'bsr_ajax_nonce', 'bsr_ajax_nonce' ) ) { 104 | return; 105 | } 106 | 107 | // Initialize the DB class. 108 | $db = new BSR_DB(); 109 | $step = isset( $_REQUEST['bsr_step' ] ) ? absint( $_REQUEST['bsr_step'] ) : 0; 110 | $page = isset( $_REQUEST['bsr_page'] ) ? absint( $_REQUEST['bsr_page'] ) : 0; 111 | 112 | // Any operations that should only be performed at the beginning. 113 | if ( $step === 0 && $page === 0 ) { 114 | $args = array(); 115 | parse_str( $_POST['bsr_data'], $args ); 116 | 117 | // Build the arguments for this run. 118 | if ( ! isset( $args['select_tables'] ) || ! is_array( $args['select_tables'] ) ) { 119 | $args['select_tables'] = array(); 120 | } 121 | 122 | $args = array( 123 | 'select_tables' => array_map( 'trim', $args['select_tables'] ), 124 | 'case_insensitive' => isset( $args['case_insensitive'] ) ? $args['case_insensitive'] : 'off', 125 | 'replace_guids' => isset( $args['replace_guids'] ) ? $args['replace_guids'] : 'off', 126 | 'dry_run' => isset( $args['dry_run'] ) ? $args['dry_run'] : 'off', 127 | 'search_for' => isset( $args['search_for'] ) ? stripslashes( $args['search_for'] ) : '', 128 | 'replace_with' => isset( $args['replace_with'] ) ? stripslashes( $args['replace_with'] ) : '', 129 | 'completed_pages' => isset( $args['completed_pages'] ) ? absint( $args['completed_pages'] ) : 0, 130 | ); 131 | 132 | $args['total_pages'] = isset( $args['total_pages'] ) ? absint( $args['total_pages'] ) : $db->get_total_pages( $args['select_tables'] ); 133 | 134 | // Clear the results of the last run. 135 | delete_transient( 'bsr_results' ); 136 | delete_option( 'bsr_data' ); 137 | } else { 138 | $args = get_option( 'bsr_data' ); 139 | } 140 | 141 | // Start processing data. 142 | if ( isset( $args['select_tables'][$step] ) ) { 143 | 144 | $result = $db->srdb( $args['select_tables'][$step], $page, $args ); 145 | $this->append_report( $args['select_tables'][$step], $result['table_report'], $args ); 146 | 147 | if ( false === $result['table_complete'] ) { 148 | $page++; 149 | } else { 150 | $step++; 151 | $page = 0; 152 | } 153 | 154 | // Check if isset() again as the step may have changed since last check. 155 | if ( isset( $args['select_tables'][$step] ) ) { 156 | $msg_tbl = esc_html( $args['select_tables'][$step] ); 157 | 158 | $message = sprintf( 159 | __( 'Processing table %d of %d: %s', 'better-search-replace' ), 160 | $step + 1, 161 | count( $args['select_tables'] ), 162 | $msg_tbl 163 | ); 164 | } 165 | 166 | $args['completed_pages']++; 167 | $percentage = $args['completed_pages'] / $args['total_pages'] * 100 . '%'; 168 | 169 | } else { 170 | $db->maybe_update_site_url(); 171 | $step = 'done'; 172 | $percentage = '100%'; 173 | } 174 | 175 | update_option( 'bsr_data', $args ); 176 | 177 | // Store results in an array. 178 | $result = array( 179 | 'step' => $step, 180 | 'page' => $page, 181 | 'percentage' => $percentage, 182 | 'url' => get_admin_url() . 'tools.php?page=better-search-replace&tab=bsr_search_replace&result=true', 183 | 'bsr_data' => build_query( $args ) 184 | ); 185 | 186 | if ( isset( $message ) ) { 187 | $result['message'] = $message; 188 | } 189 | 190 | // Send output as JSON for processing via AJAX. 191 | echo json_encode( $result ); 192 | exit; 193 | 194 | } 195 | 196 | /** 197 | * Helper function for assembling the BSR Results. 198 | * @access public 199 | * @param string $table The name of the table to append to. 200 | * @param array $report The report for that table. 201 | * @param array $args An array of arguements used for this run. 202 | * @return boolean 203 | */ 204 | public function append_report( $table, $report, $args ) { 205 | 206 | // Bail if not authorized. 207 | if ( ! BSR_Utils::check_admin_referer( 'bsr_ajax_nonce', 'bsr_ajax_nonce' ) ) { 208 | return false; 209 | } 210 | 211 | // Retrieve the existing transient. 212 | $results = get_transient( 'bsr_results' ) ? get_transient( 'bsr_results') : array(); 213 | 214 | // Grab any values from the run args. 215 | $results['search_for'] = isset( $args['search_for'] ) ? $args['search_for'] : ''; 216 | $results['replace_with'] = isset( $args['replace_with'] ) ? $args['replace_with'] : ''; 217 | $results['dry_run'] = isset( $args['dry_run'] ) ? $args['dry_run'] : 'off'; 218 | $results['case_insensitive'] = isset( $args['case_insensitive'] ) ? $args['case_insensitive'] : 'off'; 219 | $results['replace_guids'] = isset( $args['replace_guids'] ) ? $args['replace_guids'] : 'off'; 220 | 221 | // Sum the values of the new and existing reports. 222 | $results['change'] = isset( $results['change'] ) ? $results['change'] + $report['change'] : $report['change']; 223 | $results['updates'] = isset( $results['updates'] ) ? $results['updates'] + $report['updates'] : $report['updates']; 224 | 225 | // Append the table report, or create a new one if necessary. 226 | if ( isset( $results['table_reports'] ) && isset( $results['table_reports'][$table] ) ) { 227 | $results['table_reports'][$table]['change'] = $results['table_reports'][$table]['change'] + $report['change']; 228 | $results['table_reports'][$table]['updates'] = $results['table_reports'][$table]['updates'] + $report['updates']; 229 | $results['table_reports'][$table]['end'] = $report['end']; 230 | } else { 231 | $results['table_reports'][$table] = $report; 232 | } 233 | 234 | // Count the number of tables. 235 | $results['tables'] = count( $results['table_reports'] ); 236 | 237 | // Update the transient. 238 | if ( ! set_transient( 'bsr_results', $results, DAY_IN_SECONDS ) ) { 239 | return false; 240 | } 241 | 242 | return true; 243 | 244 | } 245 | 246 | } 247 | 248 | $bsr_ajax = new BSR_AJAX; 249 | $bsr_ajax->init(); 250 | -------------------------------------------------------------------------------- /includes/class-bsr-compatibility.php: -------------------------------------------------------------------------------- 1 | prefix ) . "\n"; 35 | $return .= 'WP_DEBUG: ' . ( defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set' ) . "\n"; 36 | $return .= 'Memory Limit: ' . WP_MEMORY_LIMIT . "\n"; 37 | 38 | // Plugin Configuration 39 | $return .= "\n" . '-- Better Search Replace Configuration' . "\n\n"; 40 | $return .= 'Plugin Version: ' . BSR_VERSION . "\n"; 41 | $db = new BSR_DB(); 42 | $return .= 'Max Page Size: ' . $db->get_page_size() . "\n"; 43 | 44 | // Server Configuration 45 | $return .= "\n" . '-- Server Configuration' . "\n\n"; 46 | $os = self::get_os(); 47 | $return .= 'Operating System: ' . $os['name'] . "\n"; 48 | $return .= 'PHP Version: ' . PHP_VERSION . "\n"; 49 | $return .= 'MySQL Version: ' . $wpdb->db_version() . "\n"; 50 | 51 | $return .= 'Server Software: ' . $_SERVER['SERVER_SOFTWARE'] . "\n"; 52 | 53 | // PHP configs... now we're getting to the important stuff 54 | $return .= "\n" . '-- PHP Configuration' . "\n\n"; 55 | $return .= 'Memory Limit: ' . ini_get( 'memory_limit' ) . "\n"; 56 | $return .= 'Post Max Size: ' . ini_get( 'post_max_size' ) . "\n"; 57 | $return .= 'Upload Max Filesize: ' . ini_get( 'upload_max_filesize' ) . "\n"; 58 | $return .= 'Time Limit: ' . ini_get( 'max_execution_time' ) . "\n"; 59 | $return .= 'Max Input Vars: ' . ini_get( 'max_input_vars' ) . "\n"; 60 | $return .= 'Display Errors: ' . ( ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A' ) . "\n"; 61 | 62 | // WordPress active plugins 63 | $return .= "\n" . '-- WordPress Active Plugins' . "\n\n"; 64 | $plugins = get_plugins(); 65 | $active_plugins = get_option( 'active_plugins', array() ); 66 | foreach( $plugins as $plugin_path => $plugin ) { 67 | if( !in_array( $plugin_path, $active_plugins ) ) 68 | continue; 69 | $return .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n"; 70 | } 71 | 72 | // WordPress inactive plugins 73 | $return .= "\n" . '-- WordPress Inactive Plugins' . "\n\n"; 74 | foreach( $plugins as $plugin_path => $plugin ) { 75 | if( in_array( $plugin_path, $active_plugins ) ) 76 | continue; 77 | $return .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n"; 78 | } 79 | 80 | if( is_multisite() ) { 81 | // WordPress Multisite active plugins 82 | $return .= "\n" . '-- Network Active Plugins' . "\n\n"; 83 | $plugins = wp_get_active_network_plugins(); 84 | $active_plugins = get_site_option( 'active_sitewide_plugins', array() ); 85 | foreach( $plugins as $plugin_path ) { 86 | $plugin_base = plugin_basename( $plugin_path ); 87 | if( !array_key_exists( $plugin_base, $active_plugins ) ) 88 | continue; 89 | $plugin = get_plugin_data( $plugin_path ); 90 | $return .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n"; 91 | } 92 | } 93 | 94 | $return .= "\n" . '### End System Info ###'; 95 | return $return; 96 | } 97 | 98 | /** 99 | * Determines the current operating system. 100 | * @access public 101 | * @return array 102 | */ 103 | public static function get_os() { 104 | $os = array(); 105 | $uname = php_uname( 's' ); 106 | $os['code'] = strtoupper( substr( $uname, 0, 3 ) ); 107 | $os['name'] = $uname; 108 | return $os; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /includes/class-bsr-db.php: -------------------------------------------------------------------------------- 1 | wpdb = $wpdb; 42 | 43 | $this->page_size = $this->get_page_size(); 44 | } 45 | 46 | /** 47 | * Returns an array of tables in the database. 48 | * @access public 49 | * @return array 50 | */ 51 | public static function get_tables() { 52 | global $wpdb; 53 | 54 | if ( function_exists( 'is_multisite' ) && is_multisite() ) { 55 | 56 | if ( is_main_site() ) { 57 | $tables = $wpdb->get_col( 'SHOW TABLES' ); 58 | } else { 59 | $blog_id = get_current_blog_id(); 60 | $tables = $wpdb->get_col( "SHOW TABLES LIKE '" . $wpdb->base_prefix . absint( $blog_id ) . "\_%'" ); 61 | } 62 | 63 | } else { 64 | $tables = $wpdb->get_col( 'SHOW TABLES' ); 65 | } 66 | 67 | return $tables; 68 | } 69 | 70 | /** 71 | * Returns an array containing the size of each database table. 72 | * @access public 73 | * @return array 74 | */ 75 | public static function get_sizes() { 76 | global $wpdb; 77 | 78 | $sizes = array(); 79 | $tables = $wpdb->get_results( 'SHOW TABLE STATUS', ARRAY_A ); 80 | 81 | if ( is_array( $tables ) && ! empty( $tables ) ) { 82 | 83 | foreach ( $tables as $table ) { 84 | $size = round( $table['Data_length'] / 1024 / 1024, 2 ); 85 | $sizes[$table['Name']] = sprintf( __( '(%s MB)', 'better-search-replace' ), $size ); 86 | } 87 | 88 | } 89 | 90 | return $sizes; 91 | } 92 | 93 | /** 94 | * Returns the current page size. 95 | * @access public 96 | * @return int 97 | */ 98 | public function get_page_size() { 99 | $page_size = get_option( 'bsr_page_size' ) ? get_option( 'bsr_page_size' ) : 20000; 100 | return absint( $page_size ); 101 | } 102 | 103 | /** 104 | * Returns the number of pages in a table. 105 | * @access public 106 | * @return int 107 | */ 108 | public function get_pages_in_table( $table ) { 109 | if ( false === $this->table_exists( $table ) ) { 110 | return 0; 111 | } 112 | 113 | $table = esc_sql( $table ); 114 | $rows = $this->wpdb->get_var( "SELECT COUNT(*) FROM `$table`" ); 115 | $pages = ceil( $rows / $this->page_size ); 116 | 117 | return absint( $pages ); 118 | } 119 | 120 | /** 121 | * Gets the total number of pages in the DB. 122 | * @access public 123 | * @return int 124 | */ 125 | public function get_total_pages( $tables ) { 126 | $total_pages = 0; 127 | 128 | foreach ( $tables as $table ) { 129 | 130 | // Get the number of rows & pages in the table. 131 | $pages = $this->get_pages_in_table( $table ); 132 | 133 | // Always include 1 page in case we have to create schemas, etc. 134 | if ( $pages == 0 ) { 135 | $pages = 1; 136 | } 137 | 138 | $total_pages += $pages; 139 | } 140 | 141 | return absint( $total_pages ); 142 | } 143 | 144 | /** 145 | * Gets the columns in a table. 146 | * @access public 147 | * @param string $table The table to check. 148 | * @return array 149 | */ 150 | public function get_columns( $table ) { 151 | $primary_key = null; 152 | $columns = array(); 153 | 154 | if ( false === $this->table_exists( $table ) ) { 155 | return array( $primary_key, $columns ); 156 | } 157 | 158 | $fields = $this->wpdb->get_results( 'DESCRIBE ' . $table ); 159 | 160 | if ( is_array( $fields ) ) { 161 | foreach ( $fields as $column ) { 162 | $columns[] = $column->Field; 163 | if ( $column->Key == 'PRI' ) { 164 | $primary_key = $column->Field; 165 | } 166 | } 167 | } 168 | 169 | return array( $primary_key, $columns ); 170 | } 171 | 172 | /** 173 | * Adapated from interconnect/it's search/replace script. 174 | * 175 | * Modified to use WordPress wpdb functions instead of PHP's native mysql/pdo functions, 176 | * and to be compatible with batch processing via AJAX. 177 | * 178 | * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/ 179 | * 180 | * @access public 181 | * @param string $table The table to run the replacement on. 182 | * @param int $page The page/block to begin the query on. 183 | * @param array $args An associative array containing arguements for this run. 184 | * @return array 185 | */ 186 | public function srdb( $table, $page, $args ) { 187 | 188 | // Load up the default settings for this chunk. 189 | $table = esc_sql( $table ); 190 | $current_page = absint( $page ); 191 | $pages = $this->get_pages_in_table( $table ); 192 | $done = false; 193 | 194 | $args['search_for'] = str_replace( '#BSR_BACKSLASH#', '\\', $args['search_for'] ); 195 | $args['replace_with'] = str_replace( '#BSR_BACKSLASH#', '\\', $args['replace_with'] ); 196 | 197 | $table_report = array( 198 | 'change' => 0, 199 | 'updates' => 0, 200 | 'start' => microtime( true ), 201 | 'end' => microtime( true ), 202 | 'errors' => array(), 203 | 'skipped' => false 204 | ); 205 | 206 | // Get a list of columns in this table. 207 | list( $primary_key, $columns ) = $this->get_columns( $table ); 208 | 209 | // Bail out early if there isn't a primary key. 210 | if ( null === $primary_key ) { 211 | $table_report['skipped'] = true; 212 | return array( 'table_complete' => true, 'table_report' => $table_report ); 213 | } 214 | 215 | $current_row = 0; 216 | $start = $page * $this->page_size; 217 | $end = $this->page_size; 218 | 219 | // Grab the content of the table. 220 | $data = $this->wpdb->get_results( "SELECT * FROM `$table` LIMIT $start, $end", ARRAY_A ); 221 | 222 | // Loop through the data. 223 | foreach ( $data as $row ) { 224 | $current_row++; 225 | $update_sql = array(); 226 | $where_sql = array(); 227 | $upd = false; 228 | 229 | foreach( $columns as $column ) { 230 | 231 | $data_to_fix = $row[ $column ]; 232 | 233 | if ( $column == $primary_key ) { 234 | $where_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $data_to_fix ) . '"'; 235 | continue; 236 | } 237 | 238 | // Skip GUIDs by default. 239 | if ( 'on' !== $args['replace_guids'] && 'guid' == $column ) { 240 | continue; 241 | } 242 | 243 | if ( $this->wpdb->options === $table ) { 244 | 245 | // Skip any BSR options as they may contain the search field. 246 | if ( isset( $should_skip ) && true === $should_skip ) { 247 | $should_skip = false; 248 | continue; 249 | } 250 | 251 | // If the Site URL needs to be updated, let's do that last. 252 | if ( isset( $update_later ) && true === $update_later ) { 253 | $update_later = false; 254 | $edited_data = $this->recursive_unserialize_replace( $args['search_for'], $args['replace_with'], $data_to_fix, false, $args['case_insensitive'] ); 255 | 256 | if ( $edited_data != $data_to_fix ) { 257 | $table_report['change']++; 258 | $table_report['updates']++; 259 | update_option( 'bsr_update_site_url', $edited_data ); 260 | continue; 261 | } 262 | } 263 | 264 | if ( '_transient_bsr_results' === $data_to_fix || 'bsr_profiles' === $data_to_fix || 'bsr_update_site_url' === $data_to_fix || 'bsr_data' === $data_to_fix ) { 265 | $should_skip = true; 266 | } 267 | 268 | if ( 'siteurl' === $data_to_fix && $args['dry_run'] !== 'on' ) { 269 | $update_later = true; 270 | } 271 | 272 | } 273 | 274 | // Run a search replace on the data that'll respect the serialisation. 275 | $edited_data = $this->recursive_unserialize_replace( $args['search_for'], $args['replace_with'], $data_to_fix, false, $args['case_insensitive'] ); 276 | 277 | // Something was changed 278 | if ( $edited_data != $data_to_fix ) { 279 | $update_sql[] = $column . ' = "' . $this->mysql_escape_mimic( $edited_data ) . '"'; 280 | $upd = true; 281 | $table_report['change']++; 282 | } 283 | 284 | } 285 | 286 | // Determine what to do with updates. 287 | if ( $args['dry_run'] === 'on' ) { 288 | // Don't do anything if a dry run 289 | } elseif ( $upd && ! empty( $where_sql ) ) { 290 | // If there are changes to make, run the query. 291 | $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) ); 292 | $result = $this->wpdb->query( $sql ); 293 | 294 | if ( ! $result ) { 295 | $table_report['errors'][] = sprintf( __( 'Error updating row: %d.', 'better-search-replace' ), $current_row ); 296 | } else { 297 | $table_report['updates']++; 298 | } 299 | 300 | } 301 | 302 | } // end row loop 303 | 304 | if ( $current_page >= $pages - 1 ) { 305 | $done = true; 306 | } 307 | 308 | // Flush the results and return the report. 309 | $table_report['end'] = microtime( true ); 310 | $this->wpdb->flush(); 311 | return array( 'table_complete' => $done, 'table_report' => $table_report ); 312 | } 313 | 314 | /** 315 | * Adapated from interconnect/it's search/replace script. 316 | * 317 | * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/ 318 | * 319 | * Take a serialised array and unserialise it replacing elements as needed and 320 | * unserialising any subordinate arrays and performing the replace on those too. 321 | * 322 | * @access private 323 | * @param string $from String we're looking to replace. 324 | * @param string $to What we want it to be replaced with 325 | * @param array $data Used to pass any subordinate arrays back to in. 326 | * @param boolean $serialised Does the array passed via $data need serialising. 327 | * @param sting|boolean $case_insensitive Set to 'on' if we should ignore case, false otherwise. 328 | * 329 | * @return string|array The original array with all elements replaced as needed. 330 | */ 331 | public function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = false, $case_insensitive = false ) { 332 | try { 333 | // Exit early if $data is a string but has no search matches. 334 | if ( is_string( $data ) ) { 335 | $has_match = $case_insensitive ? false !== stripos( $data, $from ) : false !== strpos( $data, $from ); 336 | if ( ! $has_match ) { 337 | return $data; 338 | } 339 | } 340 | 341 | if ( is_string( $data ) && ! is_serialized_string( $data ) && ( $unserialized = $this->unserialize( $data ) ) !== false ) { 342 | $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive ); 343 | } 344 | 345 | elseif ( is_array( $data ) ) { 346 | $_tmp = array( ); 347 | foreach ( $data as $key => $value ) { 348 | $_tmp[ $key ] = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive ); 349 | } 350 | 351 | $data = $_tmp; 352 | unset( $_tmp ); 353 | } 354 | 355 | // Submitted by Tina Matter 356 | elseif ( 'object' == gettype( $data ) ) { 357 | if($this->is_object_cloneable($data)) { 358 | $_tmp = clone $data; 359 | $props = get_object_vars( $data ); 360 | foreach ( $props as $key => $value ) { 361 | // Integer properties are crazy and the best thing we can do is to just ignore them. 362 | // see http://stackoverflow.com/a/10333200 363 | if ( is_int( $key ) ) { 364 | continue; 365 | } 366 | 367 | // Skip any representation of a protected property 368 | // https://github.com/deliciousbrains/better-search-replace/issues/71#issuecomment-1369195244 369 | if ( is_string( $key ) && 1 === preg_match( "/^(\\\\0).+/im", preg_quote( $key ) ) ) { 370 | continue; 371 | } 372 | 373 | $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false, $case_insensitive ); 374 | } 375 | 376 | $data = $_tmp; 377 | unset( $_tmp ); 378 | } 379 | } 380 | 381 | elseif ( is_serialized_string( $data ) ) { 382 | $unserialized = $this->unserialize( $data ); 383 | 384 | if ( $unserialized !== false ) { 385 | $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true, $case_insensitive ); 386 | } 387 | } 388 | 389 | else { 390 | if ( is_string( $data ) ) { 391 | $data = $this->str_replace( $from, $to, $data, $case_insensitive ); 392 | } 393 | } 394 | 395 | if ( $serialised ) { 396 | return serialize( $data ); 397 | } 398 | 399 | } catch( Exception $error ) { 400 | 401 | } 402 | 403 | return $data; 404 | } 405 | 406 | /** 407 | * Updates the Site URL if necessary. 408 | * @access public 409 | * @return boolean 410 | */ 411 | public function maybe_update_site_url() { 412 | $option = get_option( 'bsr_update_site_url' ); 413 | 414 | if ( $option ) { 415 | update_option( 'siteurl', $option ); 416 | delete_option( 'bsr_update_site_url' ); 417 | return true; 418 | } 419 | 420 | return false; 421 | } 422 | 423 | /** 424 | * Mimics the mysql_real_escape_string function. Adapted from a post by 'feedr' on php.net. 425 | * @link http://php.net/manual/en/function.mysql-real-escape-string.php#101248 426 | * @access public 427 | * @param string $input The string to escape. 428 | * @return string 429 | */ 430 | public function mysql_escape_mimic( $input ) { 431 | if ( is_array( $input ) ) { 432 | return array_map( __METHOD__, $input ); 433 | } 434 | if ( ! empty( $input ) && is_string( $input ) ) { 435 | return str_replace( array( '\\', "\0", "\n", "\r", "'", '"', "\x1a" ), array( '\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z' ), $input ); 436 | } 437 | 438 | return $input; 439 | } 440 | 441 | /** 442 | * Return unserialized object or array 443 | * 444 | * @param string $serialized_string Serialized string. 445 | * @param string $method The name of the caller method. 446 | * 447 | * @return mixed, false on failure 448 | */ 449 | public static function unserialize( $serialized_string ) { 450 | if ( ! is_serialized( $serialized_string ) ) { 451 | return false; 452 | } 453 | 454 | $serialized_string = trim( $serialized_string ); 455 | 456 | if ( PHP_VERSION_ID >= 70000 ) { 457 | $unserialized_string = @unserialize( $serialized_string, array('allowed_classes' => false ) ); 458 | } else { 459 | $unserialized_string = @BSR\Brumann\Polyfill\Unserialize::unserialize( $serialized_string, array( 'allowed_classes' => false ) ); 460 | } 461 | 462 | return $unserialized_string; 463 | } 464 | 465 | /** 466 | * Wrapper for str_replace 467 | * 468 | * @param string $from 469 | * @param string $to 470 | * @param string $data 471 | * @param string|bool $case_insensitive 472 | * 473 | * @return string 474 | */ 475 | public function str_replace( $from, $to, $data, $case_insensitive = false ) { 476 | if ( 'on' === $case_insensitive ) { 477 | $data = str_ireplace( $from, $to, $data ); 478 | } else { 479 | $data = str_replace( $from, $to, $data ); 480 | } 481 | 482 | return $data; 483 | } 484 | 485 | /** 486 | * Checks whether a table exists in DB. 487 | * 488 | * @param $table 489 | * 490 | * @return bool 491 | */ 492 | private function table_exists( $table ) { 493 | return in_array( $table, $this->get_tables() ); 494 | } 495 | 496 | /** 497 | * Check if a given object can be cloned. 498 | * 499 | * @param object $object 500 | * 501 | * @return bool 502 | */ 503 | private function is_object_cloneable( $object ) { 504 | return ( new \ReflectionClass( get_class( $object ) ) )->isCloneable(); 505 | } 506 | } 507 | -------------------------------------------------------------------------------- /includes/class-bsr-i18n.php: -------------------------------------------------------------------------------- 1 | domain, 37 | false, 38 | dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/' 39 | ); 40 | 41 | } 42 | 43 | /** 44 | * Set the domain equal to that of the specified domain. 45 | * 46 | * @since 1.0.0 47 | * @param string $domain The domain that represents the locale of this plugin. 48 | */ 49 | public function set_domain( $domain ) { 50 | $this->domain = $domain; 51 | $this->load_plugin_textdomain(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /includes/class-bsr-loader.php: -------------------------------------------------------------------------------- 1 | actions = array(); 54 | $this->filters = array(); 55 | 56 | } 57 | 58 | /** 59 | * Add a new action to the collection to be registered with WordPress. 60 | * 61 | * @since 1.0.0 62 | * @var string $hook The name of the WordPress action that is being registered. 63 | * @var object $component A reference to the instance of the object on which the action is defined. 64 | * @var string $callback The name of the function definition on the $component. 65 | * @var int Optional $priority The priority at which the function should be fired. 66 | * @var int Optional $accepted_args The number of arguments that should be passed to the $callback. 67 | */ 68 | public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { 69 | $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); 70 | } 71 | 72 | /** 73 | * Add a new filter to the collection to be registered with WordPress. 74 | * 75 | * @since 1.0.0 76 | * @var string $hook The name of the WordPress filter that is being registered. 77 | * @var object $component A reference to the instance of the object on which the filter is defined. 78 | * @var string $callback The name of the function definition on the $component. 79 | * @var int Optional $priority The priority at which the function should be fired. 80 | * @var int Optional $accepted_args The number of arguments that should be passed to the $callback. 81 | */ 82 | public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { 83 | $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); 84 | } 85 | 86 | /** 87 | * A utility function that is used to register the actions and hooks into a single 88 | * collection. 89 | * 90 | * @since 1.0.0 91 | * @access private 92 | * @var array $hooks The collection of hooks that is being registered (that is, actions or filters). 93 | * @var string $hook The name of the WordPress filter that is being registered. 94 | * @var object $component A reference to the instance of the object on which the filter is defined. 95 | * @var string $callback The name of the function definition on the $component. 96 | * @var int Optional $priority The priority at which the function should be fired. 97 | * @var int Optional $accepted_args The number of arguments that should be passed to the $callback. 98 | * @return type The collection of actions and filters registered with WordPress. 99 | */ 100 | private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { 101 | 102 | $hooks[] = array( 103 | 'hook' => $hook, 104 | 'component' => $component, 105 | 'callback' => $callback, 106 | 'priority' => $priority, 107 | 'accepted_args' => $accepted_args 108 | ); 109 | 110 | return $hooks; 111 | 112 | } 113 | 114 | /** 115 | * Register the filters and actions with WordPress. 116 | * 117 | * @since 1.0.0 118 | */ 119 | public function run() { 120 | 121 | foreach ( $this->filters as $hook ) { 122 | add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); 123 | } 124 | 125 | foreach ( $this->actions as $hook ) { 126 | add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); 127 | } 128 | 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /includes/class-bsr-main.php: -------------------------------------------------------------------------------- 1 | plugin_name = 'better-search-replace'; 61 | $this->version = BSR_VERSION; 62 | $this->load_dependencies(); 63 | $this->set_locale(); 64 | $this->define_admin_hooks(); 65 | } 66 | 67 | /** 68 | * Load the required dependencies for this plugin. 69 | * 70 | * Create an instance of the loader which will be used to register the hooks 71 | * with WordPress. 72 | * 73 | * @since 1.0 74 | * @access private 75 | */ 76 | private function load_dependencies() { 77 | require_once BSR_PATH . 'includes/class-bsr-loader.php'; 78 | require_once BSR_PATH . 'includes/class-bsr-i18n.php'; 79 | require_once BSR_PATH . 'includes/class-bsr-admin.php'; 80 | require_once BSR_PATH . 'includes/class-bsr-ajax.php'; 81 | require_once BSR_PATH . 'includes/class-bsr-db.php'; 82 | require_once BSR_PATH . 'includes/class-bsr-compatibility.php'; 83 | require_once BSR_PATH . 'includes/class-bsr-plugin-footer.php'; 84 | require_once BSR_PATH . 'includes/class-bsr-utils.php'; 85 | 86 | if ( PHP_VERSION_ID < 70000 ) { 87 | require_once BSR_PATH . 'vendor/brumann/polyfill-unserialize/src/Unserialize.php'; 88 | require_once BSR_PATH . 'vendor/brumann/polyfill-unserialize/src/DisallowedClassesSubstitutor.php'; 89 | } 90 | 91 | $this->loader = new BSR_Loader(); 92 | } 93 | 94 | /** 95 | * Define the locale for this plugin for internationalization. 96 | * 97 | * Uses the BSR_i18n class in order to set the domain and to register the hook 98 | * with WordPress. 99 | * 100 | * @since 1.0 101 | * @access private 102 | */ 103 | private function set_locale() { 104 | $plugin_i18n = new BSR_i18n(); 105 | $plugin_i18n->set_domain( $this->get_plugin_name() ); 106 | } 107 | 108 | /** 109 | * Register all of the hooks related to the dashboard functionality 110 | * of the plugin. 111 | * 112 | * @since 1.0 113 | * @access private 114 | */ 115 | private function define_admin_hooks() { 116 | 117 | // Initialize the admin class. 118 | $plugin_admin = new BSR_Admin( $this->get_plugin_name(), $this->get_version() ); 119 | $plugin_footer = new BSR_Plugin_Footer(); 120 | 121 | /// Register the admin pages and scripts. 122 | $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' ); 123 | $this->loader->add_action( 'admin_menu', $plugin_admin, 'bsr_menu_pages' ); 124 | 125 | // Other admin actions. 126 | $this->loader->add_action( 'admin_init', $plugin_admin, 'register_option' ); 127 | $this->loader->add_action( 'admin_post_bsr_view_details', $plugin_admin, 'load_details' ); 128 | $this->loader->add_action( 'admin_post_bsr_download_sysinfo', $plugin_admin, 'download_sysinfo' ); 129 | $this->loader->add_action( 'plugin_row_meta', $plugin_admin, 'meta_upgrade_link', 10, 2 ); 130 | 131 | // Footer Actions 132 | $this->loader->add_filter( 'update_footer', $plugin_footer, 'update_footer', 20); 133 | $this->loader->add_filter( 'admin_footer_text', $plugin_footer, 'admin_footer_text', 20); 134 | } 135 | 136 | /** 137 | * Run the loader to execute all of the hooks with WordPress. 138 | * 139 | * @since 1.0 140 | */ 141 | public function run() { 142 | $this->loader->run(); 143 | } 144 | 145 | /** 146 | * The name of the plugin used to uniquely identify it within the context of 147 | * WordPress and to define internationalization functionality. 148 | * 149 | * @since 1.0 150 | * @return string The name of the plugin. 151 | */ 152 | public function get_plugin_name() { 153 | return $this->plugin_name; 154 | } 155 | 156 | /** 157 | * The reference to the class that orchestrates the hooks with the plugin. 158 | * 159 | * @since 1.0 160 | * @return Better_Search_Replace_Loader Orchestrates the hooks of the plugin. 161 | */ 162 | public function get_loader() { 163 | return $this->loader; 164 | } 165 | 166 | /** 167 | * Retrieve the version number of the plugin. 168 | * 169 | * @since 1.0 170 | * @return string The version number of the plugin. 171 | */ 172 | public function get_version() { 173 | return $this->version; 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /includes/class-bsr-plugin-footer.php: -------------------------------------------------------------------------------- 1 | 'bsr_free', 32 | 'utm_medium' => 'insideplugin', 33 | 'utm_campaign' => 'plugin_footer', 34 | 'utm_content' => 'footer_colophon' 35 | ] 36 | ), 37 | BSR_NAME 38 | ); 39 | $wpe_link = BSR_Utils::external_link( 40 | BSR_Utils::wpe_url( 41 | '', 42 | [ 43 | 'utm_source' => 'bsr_plugin', 44 | 'utm_content' => 'bsr_free_plugin_footer_text' 45 | ] 46 | ), 47 | 'WP Engine' 48 | ); 49 | return sprintf( 50 | /* translators: %1$s is a link to BSR's website, and %2$s is a link to WP Engine's website. */ 51 | __( '%1$s is developed and maintained by %2$s.', 'better-search-replace' ), 52 | $product_link, 53 | $wpe_link 54 | ); 55 | } 56 | 57 | /** 58 | * Filter update footer text for BSR pages 59 | * 60 | * @param string $content 61 | * @return string 62 | * @handles update_footer 63 | **/ 64 | public function update_footer( $content ) { 65 | if ( ! BSR_Utils::is_bsr_screen() ) { 66 | return $content; 67 | } 68 | $utm_params = [ 69 | 'utm_source' => 'bsr_free', 70 | 'utm_campaign' => 'plugin_footer', 71 | 'utm_content' => 'footer_navigation' 72 | ]; 73 | 74 | $links[] = BSR_Utils::external_link( 75 | BSR_Utils::bsr_url( 76 | '/docs/', 77 | $utm_params 78 | ), 79 | __( 'Documentation', 'better-search-replace' ) 80 | ); 81 | 82 | $links[] = '' . __( 'Support', 'better-search-replace' ) . ''; 83 | 84 | $links[] = BSR_Utils::external_link( 85 | BSR_Utils::bsr_url( 86 | '/feedback/', 87 | $utm_params 88 | ), 89 | __( 'Feedback', 'better-search-replace' ) 90 | ); 91 | if ( defined( 'BSR_NAME' ) && defined( 'BSR_VERSION' ) ) { 92 | $links[] = BSR_NAME . ' ' . BSR_VERSION; 93 | } 94 | 95 | return join( ' ∙ ', $links ); 96 | } 97 | } -------------------------------------------------------------------------------- /includes/class-bsr-templates-helper.php: -------------------------------------------------------------------------------- 1 | %s', esc_url( $url ), esc_html( $text ) ); 30 | } 31 | 32 | /** 33 | * Generate Better Search Replace site URL with correct UTM tags. 34 | * 35 | * @param string $path 36 | * @param array $args 37 | * @param string $hash 38 | * 39 | * @return string 40 | */ 41 | public static function bsr_url( $path, $args = array(), $hash = '' ) { 42 | $args = wp_parse_args( 43 | $args, 44 | array( 'utm_medium' => 'insideplugin' ) 45 | ); 46 | $args = array_map( 'urlencode', $args ); 47 | $url = trailingslashit( self::BSR_URL ) . ltrim( $path, '/' ); 48 | $url = add_query_arg( $args, $url ); 49 | if ( $hash ) { 50 | $url .= '#' . $hash; 51 | } 52 | 53 | return $url; 54 | } 55 | 56 | /** 57 | * Generate WP Engine site URL with correct UTM tags. 58 | * 59 | * @param string $path 60 | * @param array $args 61 | * @param string $hash 62 | * 63 | * @return string 64 | */ 65 | public static function wpe_url( $path = '', $args = array(), $hash = '' ) { 66 | $args = wp_parse_args( 67 | $args, 68 | [ 69 | 'utm_medium' => 'referral', 70 | 'utm_campaign' => 'bx_prod_referral', 71 | ] 72 | ); 73 | $args = array_map( 'urlencode', $args ); 74 | $url = trailingslashit( self::WPE_URL ) . ltrim( $path, '/' ); 75 | $url = add_query_arg( $args, $url ); 76 | 77 | if ( $hash ) { 78 | $url .= '#' . $hash; 79 | } 80 | 81 | return $url; 82 | } 83 | 84 | /** 85 | * Get the plugin page url 86 | * 87 | * @return string 88 | */ 89 | public static function plugin_page_url() { 90 | return menu_page_url( 'better-search-replace', false ); 91 | } 92 | 93 | /** 94 | * Is current admin screen for bsr. 95 | * 96 | * @return bool 97 | */ 98 | public static function is_bsr_screen() { 99 | $screen = get_current_screen(); 100 | 101 | return $screen->base === 'tools_page_better-search-replace'; 102 | } 103 | 104 | /** 105 | * Ensures intent by verifying that a user was referred from another admin 106 | * page with the correct security nonce, and that user has the capability 107 | * level to use the plugin. 108 | * 109 | * @param int|string $action The nonce action. 110 | * @param string $query_arg Key to check for nonce in `$_REQUEST`. 111 | * 112 | * @return bool 113 | */ 114 | public static function check_admin_referer( $action, $query_arg ) { 115 | return check_admin_referer( $action, $query_arg ) && bsr_enabled_for_user(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /includes/index.php: -------------------------------------------------------------------------------- 1 | \n" 10 | "Language-Team: PNTE \n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: 8bit\n" 14 | "Language: es\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Generator: Poedit 1.6.3\n" 17 | "X-Poedit-Basepath: ..\n" 18 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 19 | "X-Poedit-SourceCharset: UTF-8\n" 20 | "X-Poedit-SearchPath-0: .\n" 21 | 22 | #: includes/class-bsr-admin.php:71 23 | msgid "No search string was defined, please enter a URL or string to search for." 24 | msgstr "Bitte gib ein, wonach die Datenbank durchsucht werden soll (z. B. eine URL)." 25 | 26 | #: includes/class-bsr-admin.php:72 27 | msgid "Please select the tables that you want to update." 28 | msgstr "Bitte wähle die Tabellen aus, die durchsucht werden sollen." 29 | 30 | #: includes/class-bsr-admin.php:73 31 | msgid "An error occurred processing your request. Try decreasing the \"Max Page Size\", or contact support." 32 | msgstr "Ein Fehler ist aufgetreten. Versuche entweder die \"Max. Seiten-Anzahl\" zu verringern, oder wende dich an unseren Support." 33 | 34 | #. #-#-#-#-# plugin.pot (Better Search Replace 1.2.2) #-#-#-#-# 35 | #. Plugin Name of the plugin/theme 36 | #: includes/class-bsr-admin.php:85 37 | #: templates/bsr-dashboard.php:29 38 | msgid "Better Search Replace" 39 | msgstr "Better Search Replace " 40 | 41 | #: includes/class-bsr-admin.php:105 42 | msgid "

DRY RUN: %d tables were searched, %d cells were found that need to be updated, and %d changes were made.

Click here for more details, or use the form below to run the search/replace.

" 43 | msgstr "

TESTLAUF: %d Tabellen wurden durchsucht, %d Tabellenzellen wurden gefunden, die aktualisiert werden sollen. %d Änderungen wurden vorgenommen.

Für mehr Details zum Testlauf hier klicken.

" 44 | 45 | #: includes/class-bsr-admin.php:112 46 | msgid "

During the search/replace, %d tables were searched, with %d cells changed in %d updates.

Click here for more details.

" 47 | msgstr "

Beim Suchen/Ersetzen wurden %d Tabellen mit insgesamt %d Zellen durchsucht. %d Aktualisierungen wurden vorgenommen.

Für mehr Details zur Aktualisierung hier klicken.

" 48 | 49 | #: includes/class-bsr-admin.php:212 50 | msgid "Table" 51 | msgstr "Tabelle" 52 | 53 | #: includes/class-bsr-admin.php:212 54 | msgid "Changes Found" 55 | msgstr "Änderungen gefunden" 56 | 57 | #: includes/class-bsr-admin.php:212 58 | msgid "Rows Updated" 59 | msgstr "Zeilen aktualisiert" 60 | 61 | #: includes/class-bsr-admin.php:212 62 | msgid "Time" 63 | msgstr "Zeit" 64 | 65 | #: includes/class-bsr-admin.php:233 66 | msgid "Want even more details, easy database migrations, and saved search/replace profiles?" 67 | msgstr "Du möchtest noch mehr Einstellungsmöglichkeiten, eine einfache Datenbank-Migration und die Möglichkeit Suchen/Ersetzen-Profile anzulegen?" 68 | 69 | #: includes/class-bsr-admin.php:234 70 | msgid "Learn more about the pro version" 71 | msgstr "Lerne die Pro-Version kennen" 72 | 73 | #: includes/class-bsr-admin.php:279 74 | msgid "Upgrade to Pro" 75 | msgstr "Upgrade auf die Pro-Version" 76 | 77 | #: includes/class-bsr-db.php:81 78 | msgid "(%s MB)" 79 | msgstr "(%s MB)" 80 | 81 | #: includes/class-bsr-db.php:251 82 | msgid "Error updating row: %d." 83 | msgstr "Fehler beim Aktualisieren in Zeile: %d." 84 | 85 | #: includes/class-bsr-db.php:257 86 | msgid "Row %d has no primary key, manual change needed." 87 | msgstr "Zeile %d hat keinen Primärschlüssel. Du musst sie manuell ändern." 88 | 89 | #: templates/bsr-dashboard.php:36 90 | msgid "Search/Replace" 91 | msgstr "Suchen/Ersetzen" 92 | 93 | #: templates/bsr-dashboard.php:37 94 | msgid "Settings" 95 | msgstr "Einstellungen" 96 | 97 | #: templates/bsr-dashboard.php:38 98 | msgid "Help" 99 | msgstr "Hilfe" 100 | 101 | #: templates/bsr-help.php:17 102 | msgid "Help & Troubleshooting" 103 | msgstr "Hilfe & Fehlerbehebung" 104 | 105 | #: templates/bsr-help.php:19 106 | msgid "Free support is available on the plugin support forums." 107 | msgstr "Kostenlosen Support gibt es im Plugin Support-Forum." 108 | 109 | #: templates/bsr-help.php:21 110 | msgid "For premium features and priority email support, upgrade to pro." 111 | msgstr "Weitere Premium-Features und schneller Support per E-Mail: Upgrade auf die Pro-Version." 112 | 113 | #: templates/bsr-help.php:23 114 | msgid "Found a bug or have a feature request? Please submit an issue on GitHub!" 115 | msgstr "Du hast einen Fehler gefunden? Du wünschst dir eine neue Funktion? Schreib uns auf GitHub!" 116 | 117 | #: templates/bsr-help.php:29 118 | msgid "Download System Info" 119 | msgstr "System-Info herunterladen" 120 | 121 | #: templates/bsr-search-replace.php:21 122 | msgid "This tool allows you to search and replace text in your database (supports serialized arrays and objects)." 123 | msgstr "Mit diesem Tool kannst du Zeichenketten in der Datenbank suchen und durch andere ersetzen (serialisierte Arrays und Objekte werden unterstützt)." 124 | 125 | #: templates/bsr-search-replace.php:22 126 | msgid "To get started, use the form below to enter the text to be replaced and select the tables to update." 127 | msgstr "Gib im Formular an, welche Zeichenkette gesucht und ersetzt werden soll. Wähle aus, welche Tabellen der Datenbank durchsucht werden sollen." 128 | 129 | #: templates/bsr-search-replace.php:23 130 | msgid "WARNING: Make sure you backup your database before using this plugin!" 131 | msgstr "Achtung: Mach' bitte ein Backup von deiner Datenbank bevor du dieses Plugin benutzt!" 132 | 133 | #: templates/bsr-search-replace.php:28 134 | msgid "Search for" 135 | msgstr "Suchen nach:" 136 | 137 | #: templates/bsr-search-replace.php:33 138 | msgid "Replace with" 139 | msgstr "Ersetzen durch:" 140 | 141 | #: templates/bsr-search-replace.php:38 142 | msgid "Select tables" 143 | msgstr "Tabellen auswählen:" 144 | 145 | #: templates/bsr-search-replace.php:41 146 | msgid "Select multiple tables with Ctrl-Click for Windows or Cmd-Click for Mac." 147 | msgstr "Steurungs-/Befehlstaste (Win/Mac) oder SHIFT gedrückt halten, um mehrere Tabellen auszuwählen." 148 | 149 | #: templates/bsr-search-replace.php:46 150 | msgid "Case-Insensitive?" 151 | msgstr "Groß -und Kleinschreibung ignorieren?" 152 | 153 | #: templates/bsr-search-replace.php:49 154 | msgid "Searches are case-sensitive by default." 155 | msgstr "Groß- und Kleinschreibung wird bei der Suche standardmäßig beachtet" 156 | 157 | #: templates/bsr-search-replace.php:54 158 | msgid "Replace GUIDs?" 159 | msgstr "Auch GUIDs ersetzen?" 160 | 161 | #: templates/bsr-search-replace.php:57 162 | msgid "If left unchecked, all database columns titled 'guid' will be skipped." 163 | msgstr "Lasse das Feld frei, um alle Datenbank-Spalten mit dem Titel 'guid' beim Suchen/Ersetzen zu überspringen (empfohlen)." 164 | 165 | #: templates/bsr-search-replace.php:62 166 | msgid "Run as dry run?" 167 | msgstr "Testlauf?" 168 | 169 | #: templates/bsr-search-replace.php:65 170 | msgid "If checked, no changes will be made to the database, allowing you to check the results beforehand." 171 | msgstr "Beim Testlauf wird die Datenbank nicht verändert. So kannst du vorher prüfen, welche Ersetzungen vorgenommen werden." 172 | 173 | #: templates/bsr-search-replace.php:76 174 | msgid "Run Search/Replace" 175 | msgstr "Suchen/Ersetzen starten" 176 | 177 | #: templates/bsr-settings.php:27 178 | msgid "Max Page Size" 179 | msgstr "Max. Seiten-Anzahl" 180 | 181 | #: templates/bsr-settings.php:31 182 | msgid "Current Setting: " 183 | msgstr "Aktuelle Einstellung: " 184 | 185 | #: templates/bsr-settings.php:33 186 | msgid "If you're noticing timeouts or getting a white screen while running a search replace, try decreasing this value." 187 | msgstr "Verringere diesen Wert, wenn dein Datenbank-Server ein Timeout zurückmeldet oder du nur einen weißen Screen siehst, während das Suchen/Ersetzen läuft. " 188 | 189 | #. Plugin URI of the plugin/theme 190 | msgid "http://expandedfronts.com/better-search-replace" 191 | msgstr "http://expandedfronts.com/better-search-replace" 192 | 193 | #. Description of the plugin/theme 194 | msgid "A small plugin for running a search/replace on your WordPress database." 195 | msgstr "Ein Plugin mit dem Zeichenketten in deiner WordPress-Datenbank gesucht und ersetzt werden können." 196 | 197 | #. Author of the plugin/theme 198 | msgid "Expanded Fronts" 199 | msgstr "Expanded Fronts" 200 | 201 | #. Author URI of the plugin/theme 202 | msgid "http://expandedfronts.com" 203 | msgstr "http://expandedfronts.com" 204 | 205 | -------------------------------------------------------------------------------- /languages/better-search-replace-es_ES.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/languages/better-search-replace-es_ES.mo -------------------------------------------------------------------------------- /languages/better-search-replace-es_ES.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014 2 | # This file is distributed under the same license as the plugin package. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Better Search Replace en español\n" 6 | "Report-Msgid-Bugs-To: http://wordpress.org/plugins/better-search-replace\n" 7 | "POT-Creation-Date: 2015-01-09 12:49+0100\n" 8 | "PO-Revision-Date: 2015-01-09 12:59+0100\n" 9 | "Last-Translator: Eduardo Larequi \n" 10 | "Language-Team: PNTE \n" 11 | "Language: es\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Generator: Poedit 1.6.3\n" 17 | "X-Poedit-Basepath: ..\n" 18 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;" 19 | "_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;" 20 | "esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 21 | "X-Poedit-SourceCharset: UTF-8\n" 22 | "X-Poedit-SearchPath-0: .\n" 23 | 24 | #: includes/class-better-search-replace-admin.php:141 25 | msgid "" 26 | "No search string was defined, please enter a URL or string to search for." 27 | msgstr "" 28 | "No se ha definido ninguna cadena de búsqueda; por favor, escribe una URL o " 29 | "cadena que buscar." 30 | 31 | #: includes/class-better-search-replace-admin.php:144 32 | msgid "Please select the tables that you want to update." 33 | msgstr "Por favor, selecciona las tablas que quieres actualizar." 34 | 35 | #: includes/class-better-search-replace-admin.php:153 36 | #, php-format 37 | msgid "" 38 | "

DRY RUN: %d tables were searched, " 39 | "%d cells were found that need to be updated, and " 40 | "%d changes were made.

Click here for more details, or click the submit " 42 | "button below to run the search/replace.

" 43 | msgstr "" 44 | "

EJECUCIÓN EN SECO: se hn realizado búsquedas en " 45 | "%d tablas, se han encontrado %d celdas que deben " 46 | "ser actualizadas, y se han realizado %d cambios.

Haz clic aquí para ver más detalles, o haz clic en el botón \"Enviar" 49 | "\" que tienes a continuación para ejecutar la búsqueda y sustitución.

" 50 | 51 | #: includes/class-better-search-replace-admin.php:160 52 | #, php-format 53 | msgid "" 54 | "

During the search/replace, %d tables were searched, with " 55 | "%d cells changed in %d updates.

Click here for more details.

" 58 | msgstr "" 59 | "

Durante la búsqueda y sustitución, se han realizado búsquedas en " 60 | "%d tablas, y se han modificado %d celdas en " 61 | "%d actualizaciones.

Haz clic aquí para " 63 | "ver más detalles.

" 64 | 65 | #: templates/bsr-dashboard.php:17 66 | msgid "Better Search Replace" 67 | msgstr "Better Search Replace" 68 | 69 | #: templates/bsr-dashboard.php:19 70 | msgid "" 71 | "This tool allows you to search and replace text in your database (supports " 72 | "serialized arrays and objects)." 73 | msgstr "" 74 | "Esta herramienta permite buscar y sustituir texto en la base de datos; " 75 | "soporta matrices y objetos serializados." 76 | 77 | #: templates/bsr-dashboard.php:20 78 | msgid "" 79 | "To get started, use the form below to enter the text to be replaced and " 80 | "select the tables to update." 81 | msgstr "" 82 | "Para comenzar, utiliza el formulario que tienes a continuación para escribir " 83 | "el texto que será sustituido y selecciona las tablas que se actualizarán." 84 | 85 | #: templates/bsr-dashboard.php:21 86 | msgid "" 87 | "WARNING: Make sure you backup your database before using " 88 | "this plugin!" 89 | msgstr "" 90 | "ATENCIÓN: asegúrate de hacer una copia de seguridad de tu " 91 | "base de datos antes de utilizar este plugin." 92 | 93 | #: templates/bsr-dashboard.php:28 94 | msgid "Search for" 95 | msgstr "Buscar" 96 | 97 | #: templates/bsr-dashboard.php:33 98 | msgid "Replace with" 99 | msgstr "Sustituir con" 100 | 101 | #: templates/bsr-dashboard.php:38 102 | msgid "Select tables" 103 | msgstr "Seleccionar tablas" 104 | 105 | #: templates/bsr-dashboard.php:41 106 | msgid "" 107 | "Select multiple tables with Ctrl-Click for Windows or Cmd-Click for Mac." 108 | msgstr "" 109 | "Selecciona múltiples tablas con Ctrl+Clic (en Windows) o Cmd+Clic (en Mac)." 110 | 111 | #: templates/bsr-dashboard.php:46 112 | msgid "" 113 | "Replace GUIDs?" 115 | msgstr "" 116 | "¿Quieres " 118 | "sustituir los GUIDs ?" 119 | 120 | #: templates/bsr-dashboard.php:49 121 | msgid "If left unchecked, all database columns titled 'guid' will be skipped." 122 | msgstr "" 123 | "Si no activas esta opción, se omitirán todas las columnas de las bases de " 124 | "datos tituladas 'guid'." 125 | 126 | #: templates/bsr-dashboard.php:54 127 | msgid "Run as dry run?" 128 | msgstr "¿Quieres ejecutar \"en seco\"?" 129 | 130 | #: templates/bsr-dashboard.php:57 131 | msgid "" 132 | "If checked, no changes will be made to the database, allowing you to check " 133 | "the results beforehand." 134 | msgstr "" 135 | "Si activas esta opción, no se realizará ningún cambio en la base de datos, " 136 | "lo cual te permite verificar los resultados de antemano." 137 | -------------------------------------------------------------------------------- /languages/better-search-replace-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deliciousbrains/better-search-replace/8d4bce24c96f431f5ec8abb4232c0481862c9119/languages/better-search-replace-fr_FR.mo -------------------------------------------------------------------------------- /languages/better-search-replace-fr_FR.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 Better Search Replace 2 | # This file is distributed under the same license as the Better Search Replace package. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Better Search Replace 1.0.2\n" 6 | "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/better-search-replace\n" 7 | "POT-Creation-Date: 2015-02-05 04:52:47+00:00\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2015-05-12 10:08-0500\n" 12 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 13 | "Last-Translator: TWF \n" 14 | "Language-Team: TWF \n" 15 | "Language: fr\n" 16 | "X-Generator: Poedit 1.7.6\n" 17 | "X-Poedit-SourceCharset: UTF-8\n" 18 | "X-Poedit-KeywordsList: __;_e\n" 19 | "X-Poedit-Basepath: .\n" 20 | "X-Poedit-SearchPath-0: ..\n" 21 | 22 | #. Plugin Name of the plugin/theme 23 | #: includes/class-better-search-replace-admin.php:69 templates/bsr-dashboard.php:17 24 | msgid "Better Search Replace" 25 | msgstr "Better Search Replace" 26 | 27 | #: includes/class-better-search-replace-admin.php:138 28 | msgid "No search string was defined, please enter a URL or string to search for." 29 | msgstr "" 30 | "Aucune chaîne de recherche a été définie, entrez une URL ou une chaîne à rechercher." 31 | 32 | #: includes/class-better-search-replace-admin.php:141 33 | msgid "Please select the tables that you want to update." 34 | msgstr "Sélectionnez les tables que vous souhaitez mettre à jour." 35 | 36 | #: includes/class-better-search-replace-admin.php:150 37 | msgid "" 38 | "

DRY RUN: %d tables were searched, %d cells were found that need to be updated, and %d changes " 40 | "were made.

Click " 41 | "here for more details, or use the form below to run the search/replace.

" 42 | msgstr "" 43 | "

Résultat de l'essai : %d tables ont été " 44 | "parcourues, %d chaînes trouvées qui peuvent être mises à jour et " 45 | "%d modifications auraient pu être apportées.

Cliquez ici pour plus de détails, " 47 | "ou utilisez le formulaire ci-dessous pour exécuter la recherche/remplace.

" 48 | 49 | #: includes/class-better-search-replace-admin.php:157 50 | msgid "" 51 | "

During the search/replace, %d tables were searched, with " 52 | "%d cells changed in %d updates.

Click here for more details.

" 54 | msgstr "" 55 | "

Pendant la recherche/remplace, %d tables ont été fouillés, avec " 56 | "%d cellules changées dans %d mises à jour.

Cliquez ici " 58 | "pour plus de détails.

" 59 | 60 | #: templates/bsr-dashboard.php:19 61 | msgid "" 62 | "This tool allows you to search and replace text in your database (supports " 63 | "serialized arrays and objects)." 64 | msgstr "" 65 | "Cet outil vous permet de rechercher et remplacer du texte dans votre base de données " 66 | "(prend en charge la sérialisation des tables et objets)." 67 | 68 | #: templates/bsr-dashboard.php:20 69 | msgid "" 70 | "To get started, use the form below to enter the text to be replaced and select the " 71 | "tables to update." 72 | msgstr "" 73 | "Pour commencer, utiliser le formulaire ci-dessous pour entrer dans le texte pour le " 74 | "remplacement et sélectionnez les tables à mettre à jour." 75 | 76 | #: templates/bsr-dashboard.php:21 77 | msgid "" 78 | "WARNING: Make sure you backup your database before using this " 79 | "plugin!" 80 | msgstr "" 81 | "Avertissement : Assurez-vous que vous avez sauvegardé votre base de " 82 | "données avant d'utiliser ce plugin !" 83 | 84 | #: templates/bsr-dashboard.php:28 85 | msgid "Search for" 86 | msgstr "Rechercher" 87 | 88 | #: templates/bsr-dashboard.php:33 89 | msgid "Replace with" 90 | msgstr "Remplacer avec" 91 | 92 | #: templates/bsr-dashboard.php:38 93 | msgid "Select tables" 94 | msgstr "Dans les tables" 95 | 96 | #: templates/bsr-dashboard.php:41 97 | msgid "Select multiple tables with Ctrl-Click for Windows or Cmd-Click for Mac." 98 | msgstr "Sélectionnez plusieurs tables avec la touche CTRL+clic ou CMD+clic pour Mac" 99 | 100 | #: templates/bsr-dashboard.php:46 101 | msgid "" 102 | "Replace GUIDs?" 104 | msgstr "" 105 | "Remplacez les GUID ? En savoir plus sur " 107 | "les GUID" 108 | 109 | #: templates/bsr-dashboard.php:49 110 | msgid "If left unchecked, all database columns titled 'guid' will be skipped." 111 | msgstr "Si décoché, toutes les colonnes 'guid' de la base de données seront igniorés." 112 | 113 | #: templates/bsr-dashboard.php:54 114 | msgid "Run as dry run?" 115 | msgstr "Juste faire un test ?" 116 | 117 | #: templates/bsr-dashboard.php:57 118 | msgid "" 119 | "If checked, no changes will be made to the database, allowing you to check the " 120 | "results beforehand." 121 | msgstr "" 122 | "Si coché, aucun changement ne sera apporté à la base de données, vous aurez un bilan " 123 | "des résultats possibles" 124 | 125 | #: templates/bsr-dashboard.php:66 126 | msgid "Run Search/Replace" 127 | msgstr "Rechercher/Remplacer" 128 | 129 | #. Plugin URI of the plugin/theme 130 | msgid "http://expandedfronts.com/better-search-replace" 131 | msgstr "http://expandedfronts.com/better-search-replace" 132 | 133 | #. Description of the plugin/theme 134 | msgid "A small plugin for running a search/replace on your WordPress database." 135 | msgstr "" 136 | "Un petit plugin pour rechercher et remplacer des cabine dans votre base de données " 137 | "WordPress." 138 | 139 | #. Author of the plugin/theme 140 | msgid "Expanded Fronts" 141 | msgstr "Expanded Fronts" 142 | 143 | #. Author URI of the plugin/theme 144 | msgid "http://expandedfronts.com" 145 | msgstr "http://expandedfronts.com" 146 | -------------------------------------------------------------------------------- /languages/better-search-replace.pot: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2025 WP Engine 2 | # This file is distributed under the GPL-3.0. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: Better Search Replace 1.4.10\n" 6 | "Report-Msgid-Bugs-To: " 7 | "http://wordpress.org/support/plugin/better-search-replace\n" 8 | "POT-Creation-Date: 2025-01-09 17:40:23+00:00\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=utf-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "PO-Revision-Date: 2025-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Delicious Brains \n" 14 | "Language-Team: Delicious Brains \n" 15 | "Language: en\n" 16 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 17 | "X-Poedit-Country: United States\n" 18 | "X-Poedit-SourceCharset: UTF-8\n" 19 | "X-Poedit-KeywordsList: " 20 | "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_" 21 | "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n" 22 | "X-Poedit-Basepath: ../\n" 23 | "X-Poedit-SearchPath-0: .\n" 24 | "X-Poedit-Bookmarks: \n" 25 | "X-Textdomain-Support: yes\n" 26 | "X-Generator: grunt-wp-i18n 1.0.3\n" 27 | 28 | #: includes/class-bsr-admin.php:72 29 | msgid "No search string was defined, please enter a URL or string to search for." 30 | msgstr "" 31 | 32 | #: includes/class-bsr-admin.php:73 33 | msgid "Please select the tables that you want to update." 34 | msgstr "" 35 | 36 | #: includes/class-bsr-admin.php:74 37 | msgid "" 38 | "An error occurred processing your request. Try decreasing the \"Max Page " 39 | "Size\", or contact support." 40 | msgstr "" 41 | 42 | #: includes/class-bsr-admin.php:75 43 | msgid "Processing..." 44 | msgstr "" 45 | 46 | #. Plugin Name of the plugin/theme 47 | msgid "Better Search Replace" 48 | msgstr "" 49 | 50 | #: includes/class-bsr-admin.php:125 51 | msgid "" 52 | "

DRY RUN: %1$d tables were searched, " 53 | "%2$d cells were found that need to be updated, and " 54 | "%3$d changes were made.

Click here for more " 56 | "details, or use the form below to run the search/replace.

" 57 | msgstr "" 58 | 59 | #: includes/class-bsr-admin.php:136 60 | msgid "" 61 | "

During the search/replace, %1$d tables were searched, " 62 | "with %2$d cells changed in %3$d " 63 | "updates.

Click here for more details.

" 65 | msgstr "" 66 | 67 | #: includes/class-bsr-admin.php:234 68 | msgid "Table" 69 | msgstr "" 70 | 71 | #: includes/class-bsr-admin.php:234 72 | msgid "Changes Found" 73 | msgstr "" 74 | 75 | #: includes/class-bsr-admin.php:234 76 | msgid "Rows Updated" 77 | msgstr "" 78 | 79 | #: includes/class-bsr-admin.php:234 80 | msgid "Time" 81 | msgstr "" 82 | 83 | #: includes/class-bsr-admin.php:245 84 | msgid "" 85 | "UPGRADE to view details on the exact " 86 | "changes that will be made." 87 | msgstr "" 88 | 89 | #: includes/class-bsr-admin.php:256 90 | msgid " seconds" 91 | msgstr "" 92 | 93 | #: includes/class-bsr-admin.php:303 94 | msgid "Upgrade to Pro" 95 | msgstr "" 96 | 97 | #: includes/class-bsr-ajax.php:159 98 | msgid "Processing table %d of %d: %s" 99 | msgstr "" 100 | 101 | #: includes/class-bsr-db.php:85 102 | msgid "(%s MB)" 103 | msgstr "" 104 | 105 | #: includes/class-bsr-db.php:295 106 | msgid "Error updating row: %d." 107 | msgstr "" 108 | 109 | #: includes/class-bsr-plugin-footer.php:51 110 | #. translators: %1$s is a link to BSR's website, and %2$s is a link to WP 111 | #. Engine's website. 112 | msgid "%1$s is developed and maintained by %2$s." 113 | msgstr "" 114 | 115 | #: includes/class-bsr-plugin-footer.php:79 116 | msgid "Documentation" 117 | msgstr "" 118 | 119 | #: includes/class-bsr-plugin-footer.php:82 120 | msgid "Support" 121 | msgstr "" 122 | 123 | #: includes/class-bsr-plugin-footer.php:89 124 | msgid "Feedback" 125 | msgstr "" 126 | 127 | #: templates/bsr-dashboard.php:53 128 | msgid "Upgrade now and get 50% off" 129 | msgstr "" 130 | 131 | #: templates/bsr-dashboard.php:65 templates/bsr-search-replace.php:32 132 | msgid "Search/Replace" 133 | msgstr "" 134 | 135 | #: templates/bsr-dashboard.php:66 templates/bsr-settings.php:30 136 | msgid "Settings" 137 | msgstr "" 138 | 139 | #: templates/bsr-dashboard.php:67 140 | msgid "Help" 141 | msgstr "" 142 | 143 | #: templates/bsr-help.php:26 144 | msgid "Help & Troubleshooting" 145 | msgstr "" 146 | 147 | #: templates/bsr-help.php:35 148 | msgid "Free support is available on the plugin support forums." 149 | msgstr "" 150 | 151 | #: templates/bsr-help.php:43 152 | msgid "" 153 | "Upgrade to " 154 | "gain access to premium features and priority email support." 155 | msgstr "" 156 | 157 | #: templates/bsr-help.php:51 158 | msgid "" 159 | "Found a bug or have a feature request? Please submit an issue on GitHub!" 161 | msgstr "" 162 | 163 | #: templates/bsr-help.php:61 164 | msgid "System Info" 165 | msgstr "" 166 | 167 | #: templates/bsr-search-replace.php:37 168 | msgid "" 169 | "Search and replace text in the database, including serialized arrays and " 170 | "objects. Be sure to back up your database before running this process." 171 | msgstr "" 172 | 173 | #: templates/bsr-search-replace.php:46 174 | msgid "Search for" 175 | msgstr "" 176 | 177 | #: templates/bsr-search-replace.php:51 178 | msgid "Replace with" 179 | msgstr "" 180 | 181 | #: templates/bsr-search-replace.php:59 182 | msgid "Select tables" 183 | msgstr "" 184 | 185 | #: templates/bsr-search-replace.php:61 186 | msgid "Select multiple tables with Ctrl-Click for Windows or Cmd-Click for Mac." 187 | msgstr "" 188 | 189 | #: templates/bsr-search-replace.php:73 190 | msgid "Additional Settings" 191 | msgstr "" 192 | 193 | #: templates/bsr-search-replace.php:84 194 | msgid "Case-Insensitive" 195 | msgstr "" 196 | 197 | #: templates/bsr-search-replace.php:85 198 | msgid "Searches are case-sensitive by default." 199 | msgstr "" 200 | 201 | #: templates/bsr-search-replace.php:95 202 | msgid "Replace GUIDs" 203 | msgstr "" 204 | 205 | #: templates/bsr-search-replace.php:96 206 | msgid "If left unchecked, all database columns titled 'guid' will be skipped." 207 | msgstr "" 208 | 209 | #: templates/bsr-search-replace.php:106 210 | msgid "Run as dry run" 211 | msgstr "" 212 | 213 | #: templates/bsr-search-replace.php:107 214 | msgid "" 215 | "If checked, no changes will be made to the database, allowing you to check " 216 | "the results beforehand." 217 | msgstr "" 218 | 219 | #: templates/bsr-search-replace.php:118 220 | msgid "Run Search/Replace" 221 | msgstr "" 222 | 223 | #: templates/bsr-settings.php:39 224 | msgid "Max Page Size" 225 | msgstr "" 226 | 227 | #: templates/bsr-settings.php:43 228 | msgid "" 229 | "If you notice timeouts or are unable to backup/import the database, try " 230 | "decreasing this value." 231 | msgstr "" 232 | 233 | #: templates/sidebar.php:4 234 | msgid "Upgrade" 235 | msgstr "" 236 | 237 | #: templates/sidebar.php:5 238 | msgid "Gain access to more database and migration features" 239 | msgstr "" 240 | 241 | #: templates/sidebar.php:9 242 | msgid "Preview database changes before they are saved" 243 | msgstr "" 244 | 245 | #: templates/sidebar.php:12 246 | msgid "Use regular expressions for complex string replacements" 247 | msgstr "" 248 | 249 | #: templates/sidebar.php:15 250 | msgid "Migrate full sites including themes, plugins, media, and database" 251 | msgstr "" 252 | 253 | #: templates/sidebar.php:18 254 | msgid "Export and import WordPress databases" 255 | msgstr "" 256 | 257 | #: templates/sidebar.php:21 258 | msgid "Email support" 259 | msgstr "" 260 | 261 | #: templates/sidebar.php:25 262 | msgid "Get up to 50% off your first year!" 263 | msgstr "" 264 | 265 | #: templates/sidebar.php:29 266 | msgid "Upgrade Now" 267 | msgstr "" 268 | 269 | #. Author URI of the plugin/theme 270 | msgid "https://bettersearchreplace.com" 271 | msgstr "" 272 | 273 | #. Description of the plugin/theme 274 | msgid "A small plugin for running a search/replace on your WordPress database." 275 | msgstr "" 276 | 277 | #. Author of the plugin/theme 278 | msgid "WP Engine" 279 | msgstr "" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "better-search-replace", 3 | "version": "1.4.11-dev", 4 | "description": "A simple plugin for updating URLs or other text in a database.", 5 | "main": "index.js", 6 | "repository": "https://github.com/deliciousbrains/better-search-replace", 7 | "author": "Delicious Brains ", 8 | "license": "GPL-3.0-only", 9 | "devDependencies": { 10 | "grunt": "^1.6.1", 11 | "grunt-contrib-cssmin": "^4.0.0", 12 | "grunt-contrib-uglify": "^5.0.1", 13 | "grunt-contrib-watch": "^1.1.0", 14 | "grunt-wp-i18n": "^1.0.3" 15 | }, 16 | "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" 17 | } 18 | -------------------------------------------------------------------------------- /templates/bsr-dashboard.php: -------------------------------------------------------------------------------- 1 | Better Search Replace. 5 | * 6 | * @link https://bettersearchreplace.com 7 | * @since 1.0.0 8 | * 9 | * @package Better_Search_Replace 10 | * @subpackage Better_Search_Replace/templates 11 | */ 12 | 13 | // Prevent direct access. 14 | if ( ! defined( 'BSR_PATH' ) ) exit; 15 | 16 | // Determines which tab to display. 17 | $active_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'bsr_search_replace'; 18 | 19 | switch( $active_tab ) { 20 | case 'bsr_settings': 21 | $action = 'action="' . get_admin_url() . 'options.php' . '"'; 22 | break; 23 | case 'bsr_help': 24 | $action = 'action="' . get_admin_url() . 'admin-post.php' . '"'; 25 | break; 26 | default: 27 | $action = ''; 28 | } 29 | 30 | if ( 'bsr_settings' === $active_tab ) { 31 | $action = get_admin_url() . 'options.php'; 32 | } else { 33 | $action = get_admin_url() . 'admin-post.php'; 34 | } 35 | 36 | ?> 37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 |
45 | 46 | 56 | 57 | 58 | 59 | 60 | 61 |
62 | 63 | 70 | 71 |
72 | 73 | 78 | 79 |
80 | 81 |
82 | -------------------------------------------------------------------------------- /templates/bsr-help.php: -------------------------------------------------------------------------------- 1 | 18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 |

27 |
28 | 29 |
30 | 31 |
32 |

33 | plugin support forums.', 'better-search-replace' ), 36 | $bsr_support_url 37 | ) 38 | ?> 39 |

40 |

41 | Upgrade to gain access to premium features and priority email support.', 'better-search-replace' ), 44 | 'https://deliciousbrains.com/better-search-replace/upgrade/?utm_source=insideplugin&utm_medium=web&utm_content=help-tab&utm_campaign=bsr-to-migrate' 45 | ); 46 | ?> 47 |

48 |

49 | GitHub!', 'better-search-replace' ), 52 | 'https://github.com/deliciousbrains/better-search-replace' 53 | ); 54 | ?> 55 |

56 |
57 | 58 | 59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |

69 | 70 | 71 | 72 |

73 |
74 | 75 |
76 |
77 |
78 | 79 | 84 | 85 |
86 | -------------------------------------------------------------------------------- /templates/bsr-search-replace.php: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
32 |

33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 | 41 |
42 | 43 | 44 |
45 |
46 | 47 | 48 |
49 | 50 |
51 | 52 | 53 |
54 |
55 | 56 | 57 |
58 |
59 | 60 | 61 |

62 |
63 |
64 | 65 |
66 |
67 | 68 | 69 | 70 |
71 | 72 |
73 |

74 |
75 | 76 |
77 | 78 | 79 | 88 | 89 | 90 | 99 | 100 | 101 | 110 | 111 |
112 |
113 |
114 | 115 |
116 | 117 | 118 | 121 |
122 |
123 | 124 |
125 | 126 | 131 | 132 | 133 |
134 | 135 |
136 | -------------------------------------------------------------------------------- /templates/bsr-settings.php: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 |

31 |
32 | 33 |
34 | 35 | 36 |
37 |
38 |
39 | 40 | 41 |
42 | 43 |

44 |
45 |
46 |
47 |
48 |
49 | 50 | 51 | 54 | 55 |
56 |
57 |
58 | 59 | 64 | 65 |
66 | -------------------------------------------------------------------------------- /templates/sidebar.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

5 |

6 | 7 |
    8 |
  • 9 |

    10 |
  • 11 |
  • 12 |

    13 |
  • 14 |
  • 15 |

    16 |
  • 17 |
  • 18 |

    19 |
  • 20 |
  • 21 |

    22 |
  • 23 |
24 | 25 |

50% off your first year!', 'better-search-replace' ); ?>

26 | 27 |
28 | 30 |
31 |
32 |
-------------------------------------------------------------------------------- /vendor/brumann/polyfill-unserialize/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2019 Denis Brumann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/brumann/polyfill-unserialize/src/DisallowedClassesSubstitutor.php: -------------------------------------------------------------------------------- 1 | , ]` and 27 | * marks start and end positions of items to be ignored. 28 | * 29 | * @var array[] 30 | */ 31 | private $ignoreItems = array(); 32 | 33 | /** 34 | * @param string $serialized 35 | * @param string[] $allowedClasses 36 | */ 37 | public function __construct($serialized, array $allowedClasses) 38 | { 39 | $this->serialized = $serialized; 40 | $this->allowedClasses = $allowedClasses; 41 | 42 | $this->buildIgnoreItems(); 43 | $this->substituteObjects(); 44 | } 45 | 46 | /** 47 | * @return string 48 | */ 49 | public function getSubstitutedSerialized() 50 | { 51 | return $this->serialized; 52 | } 53 | 54 | /** 55 | * Identifies items to be ignored - like nested serializations in string literals. 56 | */ 57 | private function buildIgnoreItems() 58 | { 59 | $offset = 0; 60 | while (preg_match(self::PATTERN_STRING, $this->serialized, $matches, PREG_OFFSET_CAPTURE, $offset)) { 61 | $length = (int)$matches[1][0]; // given length in serialized data (e.g. `s:123:"` --> 123) 62 | $start = $matches[2][1]; // offset position of quote character 63 | $end = $start + $length + 1; 64 | $offset = $end + 1; 65 | 66 | // serialized string nested in outer serialized string 67 | if ($this->ignore($start, $end)) { 68 | continue; 69 | } 70 | 71 | $this->ignoreItems[] = array($start, $end); 72 | } 73 | } 74 | 75 | /** 76 | * Substitutes disallowed object class names and respects items to be ignored. 77 | */ 78 | private function substituteObjects() 79 | { 80 | $offset = 0; 81 | while (preg_match(self::PATTERN_OBJECT, $this->serialized, $matches, PREG_OFFSET_CAPTURE, $offset)) { 82 | $completeMatch = (string)$matches[0][0]; 83 | $completeLength = strlen($completeMatch); 84 | $start = $matches[0][1]; 85 | $end = $start + $completeLength; 86 | $leftBorder = (string)$matches[1][0]; 87 | $className = (string)$matches[2][0]; 88 | $objectSize = (int)$matches[3][0]; 89 | $offset = $end + 1; 90 | 91 | // class name is actually allowed - skip this item 92 | if (in_array($className, $this->allowedClasses, true)) { 93 | continue; 94 | } 95 | // serialized object nested in outer serialized string 96 | if ($this->ignore($start, $end)) { 97 | continue; 98 | } 99 | 100 | $incompleteItem = $this->sanitizeItem($className, $leftBorder, $objectSize); 101 | $incompleteItemLength = strlen($incompleteItem); 102 | $offset = $start + $incompleteItemLength + 1; 103 | 104 | $this->replace($incompleteItem, $start, $end); 105 | $this->shift($end, $incompleteItemLength - $completeLength); 106 | } 107 | } 108 | 109 | /** 110 | * Replaces sanitized object class names in serialized data. 111 | * 112 | * @param string $replacement Sanitized object data 113 | * @param int $start Start offset in serialized data 114 | * @param int $end End offset in serialized data 115 | */ 116 | private function replace($replacement, $start, $end) 117 | { 118 | $this->serialized = substr($this->serialized, 0, $start) 119 | . $replacement . substr($this->serialized, $end); 120 | } 121 | 122 | /** 123 | * Whether given offset positions should be ignored. 124 | * 125 | * @param int $start 126 | * @param int $end 127 | * @return bool 128 | */ 129 | private function ignore($start, $end) 130 | { 131 | foreach ($this->ignoreItems as $ignoreItem) { 132 | if ($ignoreItem[0] <= $start && $ignoreItem[1] >= $end) { 133 | return true; 134 | } 135 | } 136 | 137 | return false; 138 | } 139 | 140 | /** 141 | * Shifts offset positions of ignore items by `$size`. 142 | * This is necessary whenever object class names have been 143 | * substituted which have a different length than before. 144 | * 145 | * @param int $offset 146 | * @param int $size 147 | */ 148 | private function shift($offset, $size) 149 | { 150 | foreach ($this->ignoreItems as &$ignoreItem) { 151 | // only focus on items starting after given offset 152 | if ($ignoreItem[0] < $offset) { 153 | continue; 154 | } 155 | $ignoreItem[0] += $size; 156 | $ignoreItem[1] += $size; 157 | } 158 | } 159 | 160 | /** 161 | * Sanitizes object class item. 162 | * 163 | * @param string $className 164 | * @param int $leftBorder 165 | * @param int $objectSize 166 | * @return string 167 | */ 168 | private function sanitizeItem($className, $leftBorder, $objectSize) 169 | { 170 | return sprintf( 171 | '%sO:22:"__PHP_Incomplete_Class":%d:{s:27:"__PHP_Incomplete_Class_Name";%s', 172 | $leftBorder, 173 | $objectSize + 1, // size of object + 1 for added string 174 | \serialize($className) 175 | ); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /vendor/brumann/polyfill-unserialize/src/Unserialize.php: -------------------------------------------------------------------------------- 1 | = 70000) { 18 | return \unserialize($serialized, $options); 19 | } 20 | if (!array_key_exists('allowed_classes', $options) || true === $options['allowed_classes']) { 21 | return \unserialize($serialized); 22 | } 23 | $allowedClasses = $options['allowed_classes']; 24 | if (false === $allowedClasses) { 25 | $allowedClasses = array(); 26 | } 27 | if (!is_array($allowedClasses)) { 28 | $allowedClasses = array(); 29 | trigger_error( 30 | 'unserialize(): allowed_classes option should be array or boolean', 31 | E_USER_WARNING 32 | ); 33 | } 34 | 35 | $worker = new DisallowedClassesSubstitutor($serialized, $allowedClasses); 36 | 37 | return \unserialize($worker->getSubstitutedSerialized()); 38 | } 39 | } 40 | --------------------------------------------------------------------------------