=== Version Locker ===
Contributors: alphadev01
Tags: disable updates, lock plugins, lock themes, update manager, version control
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 7.0
Stable tag: 1.2.3
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Lock plugin and theme updates to prevent accidental or automatic updates. Simple, secure update control for WordPress.

== Description ==

Version Locker lets you lock specific plugins and themes to their current version. Once locked, they won't update automatically or manually until you unlock them.

Useful for keeping your site stable when you have customized plugins, client sites, or production environments where you need control over when things update.

### What It Does

* Lock individual plugins to prevent updates
* Lock individual themes to prevent updates
* Block all plugin updates at once (global killswitch)
* Block all theme updates at once (global killswitch)
* Block WordPress core updates (global killswitch)
* Quick toggle locks on/off without page reload
* Bulk lock or unlock multiple items
* "Update available" indicator on locked items (without enabling the update)
* See the pinned version and how far behind a locked item is (major / minor / patch)
* Add a note to any lock, and optionally set it to auto-unlock on a date
* Search and filter your plugins and themes
* Activity log showing who locked what and when
* Compatible with Multisite (each site keeps its own locks)

### How It Works

The plugin uses WordPress filters to hide update notifications and block update attempts. It doesn't modify any plugin or theme files.

== Installation ==

1. Install through WordPress Plugins screen or upload to `/wp-content/plugins/`
2. Activate the plugin
3. Go to **Tools → Version Locker**
4. Toggle locks on or off for plugins and themes

== Frequently Asked Questions ==

= Does this modify plugin or theme files? =
No. It only uses WordPress filters to control update behavior.

= Will locked items still show update available? =
Yes. The Plugins and Themes tabs show an "update available" indicator with the new version number for locked items, but the update is held and the update button won't appear until you unlock.

= Can I still update a locked item? =
Yes, just unlock it first. Updates work normally once unlocked.

= What's the difference between locking one item vs using the killswitch? =
Locking targets specific items. The killswitch disables ALL plugin updates, ALL theme updates, or core updates site-wide.

= Does "Block all plugin updates" stop someone uploading a plugin ZIP by hand? =
No. The global update switches only stop WordPress update checks (update notices, one-click updates, and auto-updates). A plugin uploaded by hand from Plugins > Add New > Upload Plugin is treated by WordPress as an install, so an update or downgrade ZIP will still install. To stop manual uploads, lock that specific plugin or enable "Block Plugin Installs". Files changed directly on the server over FTP, SSH, or cPanel cannot be blocked by any plugin, because WordPress does not run during a direct file copy.

= Does this work on Multisite? =
Yes. Each site has its own independent locks.

= What happens if I delete the plugin? =
By default, all locks and settings are preserved on uninstall. To delete all data on removal, enable "Delete all data on uninstall" in the Settings tab before uninstalling.

= What changed from 1.2.1? =
Theme locking, global killswitches, quick toggles, bulk actions, Multisite support, and a rebuilt codebase. All existing locks carry over automatically.

== Screenshots ==

1. Plugins tab with lock toggles and filters
2. Themes tab with lock controls
3. Settings page with killswitches and privacy controls
4. Audit log showing who locked what and when

== Changelog ==

= 1.2.3 – 2026-06-10 =

**Compatibility**

* Tested up to WordPress 7.0

**Fixed**

* The "update available" indicator now works for locked plugins and themes. Previously the update payload was discarded, so locked items never showed a pending update. Update data is now preserved in a private bucket so the UI can display it while WordPress still withholds the update.
* Audit log "Role" column and deleted-user names now display correctly (they were read from the wrong field and always showed blank).
* Filter tabs (All / Locked / Unlocked / Updates) now work — they were bound to a selector that did not exist in the markup.
* Success and error toasts now render (the notice container was missing from the page).
* Themes-tab pagination buttons now work.
* Confirmation prompts now fire for all dangerous toggles via a single `data-confirm` handler.
* Reconciled the "Orphan Locks" auto-clean default so the Settings UI and the cleanup logic agree (locks are preserved by default).
* "Auto-Lock Uploads" now works for both plugins and themes. It previously never locked an uploaded item because it relied on an identifier WordPress omits on install; the installed item is now resolved from the upgrader instance.

**Removed**

* Email notifications have been removed entirely (service, settings, test-email tool, and related options). They depended on server SMTP, fired on update availability rather than on real attempts, and added maintenance weight. Legacy email options are cleaned up on uninstall.
* Removed dead code: an unused delete-confirmation modal, an unreachable GET unlock handler, and the empty audit "Hash" column.
* Replaced the custom global error/exception handler with lightweight per-callback guards, relying on WordPress core fatal-error protection. Same crash safety, far less code.

**Added**

* Locks now store the version they were pinned at, who locked them and when, an optional note, and an optional auto-unlock date. The Plugins and Themes tabs show the pinned version alongside any available update with a major/minor/patch drift badge, and a per-lock editor lets you record a note and an expiry.
* Auto-expiring locks: set an "auto-unlock on" date and the lock is released automatically (enforced immediately, cleaned up daily, and recorded in the audit log). Expiry dates are stored in UTC so they behave consistently across timezones.

**Changed**

* The audit log is now stored in a dedicated database table instead of a single option. Writes are atomic, so concurrent admin actions can no longer drop entries. Existing logs are migrated automatically. Timestamps are stored in UTC and shown in the site's timezone.
* Lock storage moved from a flat list of slugs to a per-item record. Existing locks are migrated automatically on upgrade.

**Security**

* Nonce verification now runs before the capability check when saving settings.
* Authentication/authorization now run before any rate-limit state changes.

**Performance**

* Locked-item lists and killswitch options are now autoloaded (they are read on every update check), avoiding extra per-request queries.

**Other**

* Admin UI now follows the active admin color scheme via `--wp-admin-theme-color`, with inline colors moved into the stylesheet for WordPress 7.0's refreshed admin.
* Additional i18n coverage for JavaScript and audit strings.

= 1.2.2 – 2026-01-24 =

**New**

* Theme locking — lock individual themes just like plugins
* Global killswitches — disable all plugin, all theme, or core updates with a single toggle
* Email notifications — get alerted when a locked item tries to update
* Quick toggle — lock or unlock without a page reload
* Bulk lock/unlock — select multiple items and toggle them at once
* Auto-lock for newly installed plugins (optional)
* Live search and filter on plugins and themes tabs
* Multisite support — each site manages its own locks independently
* Audit log now available on all sites (previously multi-admin only)
* Audit log CSV export
* Rate limiting on AJAX actions to prevent abuse

**Improvements**

* Rebuilt with a proper class-based structure (autoloader, services, failsafe layer)
* Error handling now catches bootstrap failures and shows a graceful admin notice instead of crashing
* Improved input validation across all form submissions and AJAX handlers
* Added `VLOCKER_DISABLE_ALL` constant as an emergency kill switch (define in wp-config.php)
* Better performance through caching
* PHP requirement lowered from 7.4 to 7.0
* Uninstall behavior changed to safe-by-default — data is now preserved on removal unless you opt in to deletion from the Settings tab

**Security**

* Separate capability checks per tab (plugins, themes, settings)
* AJAX nonce verification on all endpoints
* CSV injection protection on audit log export
* Transient-based rate limiting on quick toggle and test email

= 1.2.1 =
* Initial release
* Plugin locking only (no theme support)
* Audit log for multi-admin sites only

== Upgrade Notice ==

= 1.2.3 =
Tested with WordPress 7.0. Fixes the "update available" indicator for locked items, the audit Role column, filter tabs, and on-screen feedback. Email notifications have been removed. Your locks and settings are preserved on upgrade.

= 1.2.2 =
Major update. Adds theme locking, global killswitches, email notifications, quick toggles, and Multisite support. All existing plugin locks are preserved on upgrade. Note: uninstall behavior has changed — data is now kept by default unless you enable deletion in Settings.

== Credits ==

Thanks to [Nemai Naskar](https://profiles.wordpress.org/nemai/) for testing and feedback on 1.2.2.