=== Bulk Content Toolkit === Contributors: tlloancy Donate link: https://donorbox.org/wordpress-plugins Tags: bulk edit, content management, custom fields, quick edit Requires at least: 5.0 Tested up to: 6.9 Requires PHP: 7.4 Stable Tag: 1.3.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Add custom meta fields to the WordPress Quick Edit and Bulk Edit panels — no coding required. == Description == Bulk Content Toolkit lets you pick any custom meta field from your post types and expose it directly in the native WordPress **Quick Edit** row and **Bulk Edit** panel. **How it works** 1. Go to **Settings → Bulk Edit**. 2. Pick a post type. 3. Move the fields you want to edit into the "Selected Fields" column. 4. Save — those fields now appear in Quick Edit and Bulk Edit for that post type. **Supported field types** The plugin auto-detects the correct input type (checkbox, select, text, number, datetime, email, URL, color, range, textarea) from your existing data, with a built-in override list for common WooCommerce and LearnPress meta keys. **Compatibility** - All registered public post types (posts, pages, WooCommerce products, LearnPress courses, custom types, etc.) - WordPress 5.0+ / PHP 7.4+ == Installation == 1. Upload the plugin folder to `/wp-content/plugins/` or install via **Plugins → Add New**. 2. Activate the plugin. 3. Go to **Settings → Bulk Edit** and configure your fields. == Frequently Asked Questions == = Does this plugin work with custom post types? = Yes, all registered public post types are supported. = Can I undo bulk actions? = The plugin does not store undo history. Use the Bulk Edit panel carefully, or combine with a revision/backup plugin. = Is there a limit to how many fields I can add? = No hard limit, but adding many fields will add columns to the list view. We recommend selecting only the fields you actively need to bulk-edit. = How are field types detected? = On first save, the plugin samples existing meta values and infers the type (checkbox, select, number, etc.). Common WooCommerce and LearnPress fields are hardcoded to the correct type. If a field type is wrong, remove it from the settings and re-add it after adding more data. == Changelog == = 1.3.0 = **Security** - Replaced all wp_redirect() with wp_safe_redirect() + exit (5 occurrences). - Added isset() + wp_unslash() on all $_POST/$_REQUEST accesses before sanitization. - Removed unauthenticated (nopriv) AJAX hook on get_bulk_edit_statuses — meta values should not be exposed to unauthenticated users. - Fixed SQL injection risk: $table_name now passed through esc_sql() before interpolation in direct DB queries. - Added explicit nonce verification on the inline-save (quick edit) path in save_post hook. - Fixed missing nonce check on bulk edit save — phpcs:disable with justification (WP core verifies the bulk-edit form nonce upstream). **Bug fixes** - Fixed fatal PHP error in checkbox type detection: missing $ on $found_values variable name — checkbox auto-detection for 0/1 pairs was completely broken. - Fixed double-prefix bug on checkbox field key in bulk save: custom_custom_{field} was never found, so unchecking a field in bulk edit had no effect. - Fixed checkbox bulk edit model: hidden input and checkbox no longer share the same name attribute. The hidden carries the real submitted value (yes/no/indeterminate); the checkbox is visual-only and drives the hidden via JS. - Fixed select "no change" logic: empty string now correctly skips the field in PHP for all select fields, without depending on a change_field_key that does not exist for selects. - Fixed date() replaced with gmdate() (8 occurrences) to avoid runtime timezone drift. - Fixed i18n placeholders in _n() call: %s, %s → %1$s, %2$s, with missing translators comment added. - Fixed output escaping on $count in bulk action admin notice. **Improvements** - Bulk edit mixed state: checkbox now initializes to indeterminate (orange, centered knob) when selected posts have different values. Clicking the switch exits indeterminate and commits a value. Saving without touching it leaves all posts unchanged. - Bulk edit mixed state: select fields initialize to empty "— No change —" when values differ. Saving without changing the select leaves all posts unchanged. - Bulk edit mixed state: text/number/datetime/etc fields leave the companion "Change to" select at "— No change —" when values differ, preventing accidental overwrites. - Quick edit: all field types (text, number, datetime-local, email, url, color, textarea) now pre-populate with the post's current value when the row opens. Previously only checkbox and select were pre-populated. - Bulk edit JS now uses MutationObserver on #the-list instead of a fragile 500ms setTimeout. - Column display: checkbox fields now show a dashicon instead of "Yes"/"No" text. Dates use the WP date format from Settings. Long text fields are truncated at 40 characters. URLs display domain only. - Columns now respect Screen Options: each BCT column can be individually shown/hidden per user via the native WP Screen Options panel (top-right of the list view). - 14 near-identical HTML generator functions merged into one generic bulkedittoolkit_generate_input_html() — ~200 lines removed. - All script/style enqueues now pass BULKEDITTOOLKIT_VERSION for proper browser cache busting. - Settings cache added via wp_cache_get/set (5 min TTL) — the settings table is no longer queried on every page load. - Direct DB queries now use esc_sql() on table names and $wpdb->prepare() with %s placeholders where applicable. **Field type system** - New file includes/utils/field-types.php centralizes two registries: known_types (hardcoded map of well-known meta keys to their correct input type) and blocked_keys (WP/plugin internals that must never appear in the field picker, including _edit_last, _edit_lock, _wp_page_template, etc.). - Type sampler rewritten: now uses an 80% vote threshold across sampled values instead of first-match. A single outlier value no longer causes an entire field to be mistyped. - Checkbox detection now requires values to form a known pair (yes/no, 1/0, true/false, on/off) — no longer confused by fields that happen to have two distinct values. - Field discovery query rewritten: single SQL JOIN with DISTINCT instead of WP_Query loop over all posts — dramatically faster on large catalogs. **i18n** - All hardcoded French strings replaced with English base strings wrapped in __() / esc_html_e(). - JS feedback strings (save confirmation, error messages) now passed through wp_localize_script() instead of being hardcoded in French in the JS file. - All die() direct-access guards updated to standard English: die('No direct access.'). - French code comments translated to English throughout. = 1.2.9 = - Bumped: compatibility with WordPress 6.9. = 1.2.8 = - Fixed: select tag gathered with wrong method in bulk edit. = 1.2.7 = - Fixed: select and switch definitively handled in bulk edit. = 1.2.5 = - Fixed: CSS was overstepping its own scope due to a bad condition. = 1.2.4 = - Fixed: "No Change" option for select fields in bulk edit now correctly prevents unintended updates. = 1.2.3 = - Reverted: new user JS was not an improvement. = 1.2.2 = - Improved: clearer bulk edit interface. = 1.2.1 = - Fixed: select fields now require at least two values to display. = 1.2 = - New: Known Types System for WooCommerce and LearnPress fields. - New: Reset and Rebuild field types from settings. - Improved: AJAX handling for Quick Edit. - Fixed: input type detection edge cases. = 1.1 = - Improved: styling for switches and select fields. - Fixed: rendering of select fields in quick edit. = 1.0.5 = - Verified compatibility with WordPress 6.8.1. = 1.0.0 = - Initial release. == Upgrade Notice == = 1.3.0 = Security release. Update immediately. Fixes unsafe redirect, SQL injection risk, missing nonce checks, and a fatal PHP error in field type detection.