=== Flexa Media Folders ===
Contributors: flexatech
Tags: media library, folders, media, organize, attachments
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 8.2
Stable tag: 1.3.2
License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Organize the WordPress Media Library into drag-and-drop folders with a fast React tree, color labels, and a REST API.

== Description ==

Flexa Media Folders adds a hierarchical, drag-and-drop folder tree to the WordPress Media Library. Group images, videos, documents and any other attachment into nested folders, then filter the library to a single folder with one click - in the grid view, the list view, and the media picker inside the post editor.

Folders are stored in two dedicated, indexed database tables, so your `wp_term*` tables stay clean and the tree loads fast even with thousands of files.

= Features =

* **Hierarchical folder tree** - create unlimited nested folders, rename them, and reorder them by drag-and-drop.
* **Drag attachments into folders** - drag a grid thumbnail or a list-view row straight onto a folder. Select several items first and the whole selection moves at once.
* **Works everywhere the Media Library does** - the folder sidebar appears on the Media grid, the list view (`upload.php`), and the media modal in the block editor, the Classic Editor and on taxonomy term screens.
* **One-click filtering** - click a folder to filter the library to it, plus built-in "All files" and "Uncategorized" views. On the list view the active folder is kept in the URL, so it survives paging and reloads.
* **Keyboard control** - the tree is a real ARIA tree: arrow keys walk it, Left/Right collapse and expand, Home/End jump to the ends, and typing a few letters jumps to a folder by name. F2 renames, Cmd/Ctrl+X cuts, Cmd/Ctrl+V pastes, and Cmd/Ctrl+Backspace opens the delete confirmation.
* **Inline rename** - click the name of an already-selected folder (Finder style) and edit it in place. Enter saves, Esc cancels.
* **Color labels** - assign one of 18 colors to any folder from the right-click menu to spot it at a glance.
* **Full context menu** - New Folder, Rename, Cut, Paste, Delete and Change Color, all from a right-click.
* **Bulk folder creation** - paste an indented list of names and get the whole subtree in one step.
* **Import from another folder plugin** - bring an existing tree over from FileBird, Real Media Library, CatFolders, Folders (Premio) or Media Library Organizer, including the attachment assignments. The source plugin's own data is left untouched.
* **Pick the upload target** - choose which folder new uploads land in from the uploader itself, without leaving the screen.
* **File counts** - each folder can show how many attachments it contains, counting its subfolders too if you prefer (both are Settings toggles).
* **Folder search** - filter the folder tree as you type.
* **Settings page** - choose a default folder for new uploads (none / last used / a specific folder), set the folder sort order, toggle file counts, exclude specific post types or taxonomies from showing the sidebar, and tune the sidebar's icon size, text size and corner radii.
* **Safe SVG uploads** - optional, off by default. When you switch it on, every uploaded SVG is sanitized (scripts, event handlers and remote references stripped) before it is stored, and the library renders the SVG itself instead of a generic file icon.
* **Multisite ready** - each site in a network gets its own folder tree. Tables are provisioned when a site is created and removed when it is deleted.
* **Dark mode** - the tree automatically follows your WordPress admin color scheme.
* **Accessible** - full ARIA tree semantics, keyboard navigation, and visible focus styles.
* **Translation ready** - every string is internationalized, 29 locales ship with the plugin, and a `.pot` template is bundled.
* **REST API** - all screens are powered by a REST API under `/wp-json/flexa-mf/v1/`, so external integrations and your own code can read and manage folders too.
* **WP-CLI** - manage folders, run an import, and reset plugin data from the command line with `wp flexa-mf`.
* **Clean uninstall** - a Settings danger zone (and `wp flexa-mf reset`) wipes all plugin data on demand; uninstalling the plugin drops its tables. Your media files are never touched.

= Source code for compiled JavaScript and CSS =

The plugin ships with minified/compiled JavaScript and CSS in `assets/dist/`. The human-readable source code for these assets is publicly available and maintained at:

https://github.com/flexatech/flexa-media-folders

The assets are built with pnpm and Vite. To build them from source:

1. `pnpm install`
2. `pnpm build` (production build; the Vite config lives at `apps/admin/vite.config.ts`)

Use `pnpm dev` for a watched development build.

The only bundled third-party PHP library is `enshrined/svg-sanitize` (GPL-2.0-or-later), in `vendor/`, used to sanitize SVG uploads. It is installed with Composer from https://github.com/darylldoyle/svg-sanitizer.

== External services ==

All of your folders and media stay on your own site. The plugin connects to one external service, only in the admin area, for the single reason below.

= Deactivation feedback (Flexa Product Intelligence) =

When you go to deactivate Flexa Media Folders on the Plugins screen, a short optional survey asks why. It is served by Flexa's product intelligence service at `https://product-intelligence.flexacommerce.com`. It runs only in the admin, on `wp-admin/plugins.php`, never on the front end, and never blocks or delays deactivation. What is sent, and when:

* On opening the Plugins screen: a request to `/api/v1/config` (product slug and tier) to load the survey configuration. The response is cached for 6 hours.
* When you deactivate or interact with the survey: the reason you pick and any optional message you type, sent to `/api/v1/deactivations`, `/api/v1/events`, `/api/v1/feedback`, `/api/v1/feature-requests` and `/api/v1/recovery-events`.

Every request includes an anonymous per-site identifier (a random UUID), the plugin version and tier, and by default your WordPress version, PHP version and locale. No email, site domain, user identity or raw IP is collected. To stop sending environment data, use `add_filter( 'flexa_mf/deactivation_survey/config', function ( $c ) { return array( 'collect_environment' => false ) + $c; } );`. To disable the survey entirely, use `add_filter( 'flexa_mf/deactivation_survey/enabled', '__return_false' );`.
Terms of Service: https://flexacommerce.com/pages/terms
Privacy Policy: https://flexacommerce.com/pages/privacy

== Installation ==

1. Upload the plugin files to `/wp-content/plugins/flexa-media-folders`, or install it through the WordPress **Plugins** screen.
2. Activate the plugin through the **Plugins** screen.
3. Open **Media** in the admin menu - the folder sidebar appears next to your library. Visit **Media → Media Folders** to configure defaults.

== Frequently Asked Questions ==

= Does this move or rename my media files? =

No. Folders are an organizational layer stored separately in the database. Your files stay exactly where WordPress put them, with the same URLs.

= What happens to my folders if I deactivate or delete the plugin? =

Deactivating keeps all folder data. Deleting (uninstalling) the plugin drops its database tables and removes its settings - but your media files are never deleted. You can also wipe folder data on demand from the Settings danger zone.

= Does it use WordPress taxonomies? =

No. Folders live in two dedicated, indexed tables. This keeps your `wp_term*` tables clean and lets the tree load in a single query.

= I already use another folder plugin. Can I bring my tree over? =

Yes. The Settings page has an Import panel that detects FileBird, Real Media Library, CatFolders, Folders (Premio) and Media Library Organizer, and copies their folders and attachment assignments across. The other plugin's data is only read, never modified, so you can compare the result before deactivating it. The same thing is available as `wp flexa-mf import`.

= Can I filter the Media Library by folder inside the post editor? =

Yes. The folder sidebar is available in the media modal in both the block editor and the Classic Editor.

= Can I use the folder tree without a mouse? =

Yes. Tab into the tree and the arrow keys walk it, Left and Right collapse and expand, Home and End jump to the first and last folder, and typing a few letters jumps to a folder by name. F2 renames the focused folder, Cmd/Ctrl+X and Cmd/Ctrl+V cut and paste it, and Cmd/Ctrl+Backspace opens the delete confirmation.

= Is enabling SVG uploads safe? =

SVG uploads are off by default because an SVG is an XML document that can carry scripts. When you turn the setting on, the plugin sanitizes every uploaded SVG before it is stored, stripping `<script>` elements, `on*` event handlers and remote references. Sanitization is not optional and cannot be switched off from the UI; it only runs for files uploaded after the setting is enabled, so audit any SVGs that were already in your library.

= Does it work on multisite? =

Yes. Every site in a network keeps its own folder tree in its own set of tables. Network activation provisions the existing sites, a newly created site is provisioned automatically, and deleting a site removes its tables.

= Is there a REST API? =

Yes - all folder data is exposed under `/wp-json/flexa-mf/v1/`. The endpoints require the same capabilities as the Media Library itself.

= How does the plugin handle authentication and permissions? =

Every state-changing request goes through the WordPress REST API and is gated by two checks: a capability check and the standard WordPress REST nonce (`X-WP-Nonce`). There are three capability gates, each filterable:

* `flexa_mf/capabilities/edit` (default `manage_categories`, so Editors and Administrators) for changing the folder structure itself: create, rename, move, delete, reorder, bulk create and import. The tree is shared site-wide, so reshaping it is deliberately a higher bar than uploading.
* `flexa_mf/capabilities/manage` (default `upload_files`) for reading the tree and filing media into an existing folder.
* `flexa_mf/capabilities/settings` (default `manage_options`) for the settings and data-reset endpoints.

On top of that, assigning or detaching media is verified per attachment with the `edit_post` capability, and the attachment listing is scoped to media the current user can manage. Users without the folder-edit capability also get the tree-editing actions hidden in the UI, so they are not offered buttons the server would reject. The plugin does not register any custom admin-ajax endpoints.

= Can I stop the sidebar from showing on a particular post type? =

Yes. The plugin's Settings page has a per-post-type exclusion list, and a matching one for taxonomy term screens.

== Screenshots ==

1. The folder tree sidebar in the Media Library grid view.
2. Dragging selected attachments onto a folder.
3. The folder right-click context menu with the color palette.
4. The folder sidebar inside the block editor media modal.
5. The Flexa Media Folders settings page.

== Changelog ==

= 1.3.2 =
* Fixed: `wp flexa-mf folder create --color=#rrggbb` never set the folder's color. WP-CLI keeps `--color` for its own output colorization and strips it before a command runs, so the flag is now `--folder-color`. The old spelling still works and prints a notice pointing at the new one. A hex value the plugin cannot parse now stops the command instead of creating a folder with no color.

= 1.3.1 =
* Fixed: folders could not be clicked in the block editor's media modal on WordPress 7.1. Core wraps the attachment grid in a new container that leaves the grid itself positioned against the whole browser pane, so the list stretched across the folder sidebar as an invisible layer and absorbed the clicks.

= 1.3.0 =
* New: full keyboard control of the folder tree. Arrow keys walk it, Left/Right collapse and expand, Home/End jump to the ends, and typing a few letters jumps to a folder by name. F2 renames, Cmd/Ctrl+X cuts, Cmd/Ctrl+V pastes, and Cmd/Ctrl+Backspace opens the delete confirmation.
* New: import an existing folder tree, with its attachment assignments, from FileBird, Real Media Library, CatFolders, Folders (Premio) or Media Library Organizer. Available from an Import panel on the Settings page and as `wp flexa-mf import`. The source plugin's data is only read, never changed.
* New: optional SVG uploads. Off by default; when enabled, every uploaded SVG is sanitized before it is stored and the library renders the SVG itself instead of a generic file icon.
* New: pick the folder new uploads land in from the uploader, without leaving the screen.
* New: multisite support. Each site in a network keeps its own folder tree. Tables are provisioned on network activation and when a site is created, and removed when a site is deleted.
* New: 29 translations now ship with the plugin.
* New: Settings additions - count the files in subfolders towards a folder's badge, exclude individual taxonomies from showing the sidebar (to match the existing post-type list), and tune the sidebar's folder-icon size, text size and corner radii.
* New: a "Reset sidebar size" entry in the sidebar menu, for when a drag-resize has left the panel somewhere awkward.
* Change: renaming now happens inline in the tree instead of in a side panel, and it is armed by clicking the name of an already-selected folder (Finder style) rather than by double-clicking. F2 and the right-click Rename option work as before.
* Removed: the "Download folder" context-menu entry, which only ever showed a notice saying the feature was coming.

= 1.2.8 =
* Added an optional, admin-only deactivation feedback survey so we can learn why the plugin is being removed. It runs only on the Plugins screen, never blocks or delays deactivation, and can be turned off with a filter. See the "External services" section for exactly what is sent.

= 1.2.7 =
* Compatibility: tested up to WordPress 7.1. Fixed the folder sidebar in the media modal (block editor "Select or Upload Media"), where WordPress 7.1 wraps the attachment grid in an absolutely-positioned container that overlapped the folder tree.

= 1.2.6 =
* Security: creating, renaming, moving, deleting, reordering and bulk-creating folders now require the `manage_categories` capability (Editors and Administrators) instead of `upload_files`. Folders are a shared, site-wide structure, so lower-privileged users could previously reorganize the tree for everyone. Reading folders and assigning your own media to them still only needs `upload_files`. The gate is filterable via `flexa_mf/capabilities/edit`.

= 1.2.5 =
* Fix: the i18n build helper script is no longer included in the release package, so no unguarded PHP file ships to production.

= 1.2.4 =
* New: translation template (.pot) covering all plugin strings, so the plugin can now be translated into other languages.

= 1.2.3 =
* New: a Settings link on the Plugins page now takes you straight to the Media Folders settings.

= 1.2.2 =
* Fix: development files are no longer included in the release package.

= 1.2.1 =
* Fix: the "Download folder" notice now inserts the folder name correctly in translated languages, so translators can reposition the placeholder.

= 1.2.0 =
* New: after moving media into a folder, the confirmation toast now offers an Undo button for 5 seconds - click it to send every item back to its previous folder (including Uncategorized) with folder counts and the media grid updated automatically.

= 1.1.0 =
* New: rename a folder inline by double-clicking its name in the sidebar - press Enter or click away to save, Esc to cancel. Empty names are ignored and the right-click Rename option still works as before.

= 1.0.2 =
* Security: the folder assign/detach REST endpoints now verify per-attachment edit permission (`edit_post`) for each item, so a user can only organize media they are allowed to edit instead of any attachment in the library.
* Security: the attachment-listing REST endpoint now scopes results to media the caller can manage - users who cannot edit others' posts see only their own uploads.

= 1.0.1 =
* Security: bind every custom-table identifier through `wpdb::prepare()`'s `%i` placeholder so no SQL string interpolates a table name outside `prepare()`.
* Security: gate the Media Library query-filter superglobal reads behind an explicit `current_user_can()` check (defense in depth on top of WordPress's upstream cap enforcement).
* Fix: the admin UI now loads correctly on the Settings page.
* Fix: dark-mode styling on the folder tree, drag tooltip, and toast.
* Compatibility: minimum WordPress version bumped from 5.8 to 6.2 (required for the `%i` placeholder).

= 1.0.0 =
* Initial release.
* Hierarchical, drag-and-drop folder tree for the Media Library.
* Drag attachments into folders.
* Folder sidebar in the Media grid, the list view, and the editor media modal.
* One-click folder filtering with built-in "All files" and "Uncategorized" views.
* Folder color labels (18-color palette) and a full right-click context menu.
* Per-folder file counts, folder-tree search, and a Settings page (default upload folder, sort order, count display, post-type exclusions).
* Dark mode that follows the WordPress admin color scheme; full keyboard and ARIA accessibility.
* REST API under `/wp-json/flexa-mf/v1/` and `wp flexa-mf` WP-CLI commands.
* Translation ready with a bundled `.pot` template.

== Upgrade Notice ==

= 1.3.2 =
The WP-CLI flag that sets a new folder's color is now `--folder-color`. `--color` belongs to WP-CLI itself and was being swallowed before the plugin saw it; the old spelling still works.

= 1.3.1 =
Fixes folders being unclickable in the block editor media modal on WordPress 7.1.

= 1.3.0 =
Adds keyboard control of the folder tree, import from five other folder plugins, optional sanitized SVG uploads, an upload-target picker, multisite support and 29 translations. One behavior change: renaming is now inline in the tree and is armed by clicking an already-selected folder's name rather than by double-clicking.

= 1.0.2 =
Security hardening: REST endpoints now enforce per-attachment edit permissions and scope media listings to what each user can manage.

= 1.0.1 =
Security hardening for SQL identifier binding and admin capability checks; dark-mode and settings-page UI fixes. Now requires WordPress 6.2 or later.

= 1.0.0 =
Initial release of Flexa Media Folders.
