soderlind / virtual-media-folders
Virtual folder organization and smart management for the WordPress Media Library.
Fund package maintenance!
paypal.me/PerSoderlind
Installs: 58
Dependents: 0
Suggesters: 0
Security: 0
Stars: 8
Watchers: 0
Forks: 0
Open Issues: 1
Language:JavaScript
Type:wordpress-plugin
pkg:composer/soderlind/virtual-media-folders
Requires
- php: >=8.3
- yahnis-elsts/plugin-update-checker: ^5.6
Requires (Dev)
- brain/monkey: ^2.6
- phpunit/phpunit: ^11.0
- dev-main
- 1.2.2
- 1.2.1
- 1.2.0
- 1.1.7
- 1.1.6
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.1.17
- 0.1.16
- 0.1.15
- 0.1.14
- 0.1.13
- 0.1.12
- 0.1.11
- 0.1.10
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1.0
- dev-fix/get-folders
- dev-translate/a11y
- dev-a11y
- dev-add/plugin-assets
- dev-last/file
- dev-bulk/optimistic
- dev-optimistic
- dev-dependabot/npm_and_yarn/express-4.22.1
- dev-refactor
- dev-modal/folders
- dev-uncategorized/as-default
- dev-folders/drag-drop
- dev-check/updater
- dev-add/phpcsignore
- dev-settings/all-media
- dev-plugin/housekeeping
This package is auto-updated.
Last update: 2025-12-12 09:39:59 UTC
README
Virtual folder organization and smart management for the WordPress Media Library.
Way back in 2006 (almost 20 years ago!), I released ImageManager 2.0, a popular WordPress plugin for image management and editing. Virtual Media Folders is my modern take on media organization for WordPress, built with React and modern tooling.
Description
Virtual Media Folders brings virtual folder organization to your WordPress Media Library. Organize your media files into hierarchical folders without moving files on disk—folders are virtual, so your URLs never change.
Features
- Virtual Folders: Create hierarchical folder structures to organize media
- Drag & Drop: Easily move media between folders with drag and drop
- Sticky Sidebar: Folder navigation stays visible while scrolling through media
- Gutenberg Integration: Filter media by folder directly in the block editor
- Bulk Actions: Move multiple media items at once
- Keyboard Accessible: Full keyboard navigation support
- Internationalized: Ready for translation (Norwegian Bokmål included)
Accessibility
Virtual Media Folders is built with accessibility in mind, following WCAG 2.1 guidelines:
Keyboard Navigation
The folder tree supports complete keyboard navigation:
| Key | Action |
|---|---|
↓ / ↑ |
Move between folders |
→ |
Expand folder or move to first child |
← |
Collapse folder or move to parent |
Enter / Space |
Select folder |
Home |
Jump to first folder |
End |
Jump to last folder |
Accessible Drag and Drop
Reordering folders is fully keyboard accessible using the drag handle (⋮⋮):
- Tab to the drag handle next to a folder name
- Press Enter or Space to pick up the folder
- Use Arrow keys to move the folder up or down
- Press Enter or Space to drop, or Escape to cancel
Screen readers announce each step:
- On focus: "Reorder, button, sortable. Press Enter to start dragging..."
- After drop: "Photos moved to position 3"
Moving media to folders can be done three ways:
-
Mouse/Touch: Drag media items onto a folder in the sidebar
-
Keyboard Move Mode:
- Focus a media item in the grid (use Tab or arrow keys)
- Press M to pick up the item (or select multiple first, then press M)
- Tab to the folder sidebar
- Navigate to target folder with Arrow keys
- Press Enter to drop, or M / Escape to cancel
-
Bulk Action Dropdown:
- Click "Bulk select" in the media library toolbar
- Select media items using Space or Enter
- Use the "Move to folder" dropdown and click "Apply"
Screen reader announcements guide you through keyboard move mode:
- On pick up: "Photo.jpg picked up. Navigate to a folder and press Enter to drop..."
- On drop: "Moved Photo.jpg to Events"
- On cancel: "Move cancelled"
ARIA Support
- Full ARIA tree pattern (
role="tree",role="treeitem") aria-expandedfor collapsible foldersaria-selectedfor current selectionaria-level,aria-setsize,aria-posinsetfor position context- Live region announcements for drag-drop operations
Screen Reader Support
- Announces folder reordering and deletion
- Hidden instructions for drag-and-drop operations
- Item counts announced with folder names
Visual Accessibility
- Focus indicators: Enhanced
:focus-visiblestyles with visible outlines - Reduced motion: Respects
prefers-reduced-motionpreference - High contrast: Supports
forced-colorsmode for Windows High Contrast - Color contrast: Meets WCAG AA contrast requirements
Requirements
- WordPress 6.8 or higher
- PHP 8.3 or higher
Installation
- Download
virtual-media-folders.zip - Upload via Plugins > Add New > Upload Plugin
- Activate the plugin.
Plugin updates are handled automatically via GitHub. No need to manually download and install updates.
Development
# Add via Composer composer require soderlind/virtual-media-folders # Install dependencies composer install npm install # Start development build with watch npm run start # Build for production npm run build # Run PHP tests composer test # Run JavaScript tests npm test
Usage
Organizing Media
- Go to Media > Library in your WordPress admin
- Click the folder icon next to the view switcher to show the folder sidebar
- Use the + button to create new folders
- Drag and drop media items onto folders to organize them
- Click a folder to filter the media library view
Settings
Go to Media > Folder Settings to configure:
- Show "All Media" – Display the "All Media" option in the folder sidebar [I prefer to keep this disabled]
- Show "Uncategorized" – Display the "Uncategorized" folder for media without a folder
- Jump to folder after move – Automatically navigate to the target folder after moving media
- Default folder for uploads – Automatically assign new uploads to a specific folder
Gutenberg Block Editor
When inserting media in the block editor:
- Open the Media Library modal from a block (e.g., Image or Gallery block etc.)
- Use the folder sidebar to filter by folder
- Select your media as usual
Folder Structure
virtual-media-folders/
├── build/ # Compiled assets
├── docs/ # Documentation
├── languages/ # Translation files
├── src/
│ ├── Admin.php # Media Library integration
│ ├── Editor.php # Gutenberg integration
│ ├── RestApi.php # REST API endpoints
│ ├── Settings.php # Settings page
│ ├── Suggestions.php # Smart suggestions
│ ├── Taxonomy.php # Custom taxonomy
│ ├── admin/ # Media Library UI
│ │ ├── components/ # React components
│ │ └── styles/ # CSS
│ ├── editor/ # Gutenberg integration
│ └── shared/ # Shared components & hooks
├── tests/
│ ├── js/ # Vitest tests
│ └── php/ # PHPUnit tests
├── uninstall.php # Cleanup on uninstall
└── virtual-media-folders.php # Main plugin file
REST API
The plugin provides REST API endpoints under /wp-json/vmf/v1:
Folders
GET /folders- List all foldersPOST /folders- Create a folderGET /folders/{id}- Get a folderPUT /folders/{id}- Update a folderDELETE /folders/{id}- Delete a folderPOST /folders/{id}/media- Add media to folderDELETE /folders/{id}/media- Remove media from folder
Example: Create a Folder and Add Media
Authentication requires Application Passwords (WordPress 5.6+). Generate one at Users > Profile > Application Passwords. The password format is xxxx xxxx xxxx xxxx xxxx xxxx (with spaces).
Note: Replace
usernamewith your actual WordPress username andxxxx xxxx xxxx xxxx xxxx xxxxwith your Application Password.
# Create a new folder curl -X POST "https://example.com/wp-json/vmf/v1/folders" \ -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \ -H "Content-Type: application/json" \ -d '{"name": "Photos", "parent": 0}' # Response: {"id": 5, "name": "Photos", "slug": "photos", "parent": 0, "count": 0} # Add media (ID 123) to the folder (ID 5) curl -X POST "https://example.com/wp-json/vmf/v1/folders/5/media" \ -u "username:xxxx xxxx xxxx xxxx xxxx xxxx" \ -H "Content-Type: application/json" \ -d '{"media_id": 123}' # List all folders curl "https://example.com/wp-json/vmf/v1/folders" \ -u "username:xxxx xxxx xxxx xxxx xxxx xxxx"
Hooks & Filters
Actions
vmf_folder_created- Fired when a folder is createdvmf_folder_deleted- Fired when a folder is deletedvmf_media_moved- Fired when media is moved to a folder
Settings Filters
vmf_default_settings
Filter the default settings values.
add_filter( 'vmf_default_settings', function( $defaults ) { // Change default values $defaults['show_all_media'] = true; $defaults['show_uncategorized'] = true; $defaults['jump_to_folder_after_move'] = false; $defaults['default_folder'] = 0; return $defaults; } );
vmf_settings
Filter all settings at once after loading from the database.
add_filter( 'vmf_settings', function( $options ) { // Force jump to folder after move for all users $options['jump_to_folder_after_move'] = true; return $options; } );
vmf_setting_{$key}
Filter a specific setting value. Available keys:
show_all_media- Show "All Media" in sidebarshow_uncategorized- Show "Uncategorized" in sidebarjump_to_folder_after_move- Navigate to folder after moving filesdefault_folder- Default folder for new uploads (0 = none)
// Hide "All Media" option for non-administrators add_filter( 'vmf_setting_show_all_media', function( $value, $key, $options ) { if ( ! current_user_can( 'manage_options' ) ) { return false; } return $value; }, 10, 3 ); // Always show uncategorized for editors add_filter( 'vmf_setting_show_uncategorized', function( $value ) { if ( current_user_can( 'edit_others_posts' ) ) { return true; } return $value; } );
Note: At least one of
show_all_mediaorshow_uncategorizedmust betrue. If both are set tofalsevia filters,show_all_mediawill automatically be set totrue.
Preconfiguring Folders
You can programmatically create folders using the WordPress taxonomy API. Use the after_setup_theme or init hook with a one-time check to avoid creating duplicates:
add_action( 'init', function() { // Only run once - use an option flag if ( get_option( 'my_theme_vmf_folders_created' ) ) { return; } // Make sure the taxonomy exists if ( ! taxonomy_exists( 'media_folder' ) ) { return; } // Define your folder structure $folders = [ 'Photos' => [ 'Events', 'Products', 'Team', ], 'Documents' => [ 'Reports', 'Presentations', ], 'Videos', 'Logos', ]; // Create folders foreach ( $folders as $parent => $children ) { if ( is_array( $children ) ) { // Parent folder with children $parent_term = wp_insert_term( $parent, 'media_folder' ); if ( ! is_wp_error( $parent_term ) ) { foreach ( $children as $child ) { wp_insert_term( $child, 'media_folder', [ 'parent' => $parent_term['term_id'], ] ); } } } else { // Top-level folder (no children) wp_insert_term( $children, 'media_folder' ); } } // Mark as done so it only runs once update_option( 'my_theme_vmf_folders_created', true ); }, 20 ); // Priority 20 to run after taxonomy registration
You can also set the custom folder order using term meta:
// Set custom order for folders (lower numbers appear first) update_term_meta( $term_id, 'vmf_order', 0 ); // First position update_term_meta( $term_id, 'vmf_order', 1 ); // Second position
Other Filters
vmf_suggestion_matchers- Customize suggestion matching logicvmf_folder_capabilities- Modify capability requirements
Translation
Generate translation files:
# Generate POT file npm run i18n:make-pot # Generate JSON files for JavaScript npm run i18n:make-json # Generate PHP files for faster loading npm run i18n:make-php
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all tests pass
- Submit a pull request
Copyright and License
Virtual Media Folders is copyright 2025 Per Soderlind
Virtual Media Folders is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
Virtual Media Folders is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with the Extension. If not, see http://www.gnu.org/licenses/.
