feat(media-folder-pro): add virtual folder system for WordPress media library
Custom WordPress plugin that replaces the default flat media library with a structured folder view. Features: hierarchical folders via custom taxonomy, sidebar folder tree, drag & drop, modal integration with Elementor/builders, bulk assign, upload auto-assign, toast notifications. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
218
wp-content/plugins/media-folder-pro/media-folder-pro.php
Normal file
218
wp-content/plugins/media-folder-pro/media-folder-pro.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Media Folder Pro
|
||||
* Plugin URI: https://www.project-pro.pl
|
||||
* Description: Strukturyzowana biblioteka mediów z wirtualnymi folderami (podkatalogami).
|
||||
* Version: 0.1.0
|
||||
* Author: Project Pro
|
||||
* Author URI: https://www.project-pro.pl
|
||||
* License: GPL-2.0-or-later
|
||||
* Text Domain: media-folder-pro
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 8.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
define( 'MFP_VERSION', '0.1.0' );
|
||||
define( 'MFP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'MFP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
|
||||
require_once MFP_PLUGIN_DIR . 'includes/class-taxonomy.php';
|
||||
require_once MFP_PLUGIN_DIR . 'includes/class-ajax-handler.php';
|
||||
require_once MFP_PLUGIN_DIR . 'includes/class-media-query.php';
|
||||
|
||||
final class Media_Folder_Pro {
|
||||
|
||||
private static ?self $instance = null;
|
||||
private MFP_Taxonomy $taxonomy;
|
||||
private MFP_Ajax_Handler $ajax;
|
||||
private MFP_Media_Query $media_query;
|
||||
|
||||
public static function instance(): self {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct() {
|
||||
$this->taxonomy = new MFP_Taxonomy();
|
||||
$this->ajax = new MFP_Ajax_Handler( $this->taxonomy );
|
||||
$this->media_query = new MFP_Media_Query();
|
||||
|
||||
add_action( 'init', [ $this->taxonomy, 'register' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_modal_assets' ], 20 );
|
||||
add_action( 'wp_enqueue_media', [ $this, 'enqueue_modal_on_media_load' ] );
|
||||
add_action( 'admin_footer-upload.php', [ $this, 'render_folder_tree_container' ] );
|
||||
|
||||
$this->ajax->register_hooks();
|
||||
$this->media_query->register();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared localization data for all JS scripts.
|
||||
*/
|
||||
private function get_mfp_data(): array {
|
||||
return [
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'mfp_nonce' ),
|
||||
'i18n' => [
|
||||
'allMedia' => __( 'Wszystkie media', 'media-folder-pro' ),
|
||||
'newFolder' => __( 'Nowy folder', 'media-folder-pro' ),
|
||||
'folderName' => __( 'Nazwa folderu:', 'media-folder-pro' ),
|
||||
'rename' => __( 'Zmień nazwę', 'media-folder-pro' ),
|
||||
'delete' => __( 'Usuń', 'media-folder-pro' ),
|
||||
'newSubfolder' => __( 'Nowy podfolder', 'media-folder-pro' ),
|
||||
'confirmDelete' => __( 'Czy na pewno usunąć ten folder?', 'media-folder-pro' ),
|
||||
'folderNotEmpty' => __( 'Folder nie jest pusty. Usuń najpierw zawartość.', 'media-folder-pro' ),
|
||||
'error' => __( 'Wystąpił błąd. Spróbuj ponownie.', 'media-folder-pro' ),
|
||||
'assignSuccess' => __( 'Media przypisane do folderu.', 'media-folder-pro' ),
|
||||
'assignError' => __( 'Nie udało się przypisać mediów.', 'media-folder-pro' ),
|
||||
'dropHere' => __( 'Upuść tutaj', 'media-folder-pro' ),
|
||||
'uncategorized' => __( 'Bez folderu', 'media-folder-pro' ),
|
||||
'moveToFolder' => __( 'Przenieś do folderu', 'media-folder-pro' ),
|
||||
'removeFromFolder' => __( 'Usuń z folderu', 'media-folder-pro' ),
|
||||
'noSelection' => __( 'Zaznacz media do przeniesienia', 'media-folder-pro' ),
|
||||
'folderCreated' => __( 'Folder utworzony', 'media-folder-pro' ),
|
||||
'folderRenamed' => __( 'Nazwa zmieniona', 'media-folder-pro' ),
|
||||
'folderDeleted' => __( 'Folder usunięty', 'media-folder-pro' ),
|
||||
'folderMoved' => __( 'Folder przeniesiony', 'media-folder-pro' ),
|
||||
'emptyState' => __( 'Brak folderów', 'media-folder-pro' ),
|
||||
'createFirst' => __( 'Utwórz pierwszy folder', 'media-folder-pro' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure mfpData is localized (registers inline script if tree JS not loaded).
|
||||
*/
|
||||
private function ensure_mfp_data(): void {
|
||||
if ( wp_script_is( 'media-folder-pro-tree', 'enqueued' ) ) {
|
||||
return;
|
||||
}
|
||||
wp_register_script( 'media-folder-pro-tree', false );
|
||||
wp_enqueue_script( 'media-folder-pro-tree' );
|
||||
wp_localize_script( 'media-folder-pro-tree', 'mfpData', $this->get_mfp_data() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Full assets for Media Library pages (upload.php, media-new.php).
|
||||
*/
|
||||
public function enqueue_admin_assets( string $hook ): void {
|
||||
if ( ! in_array( $hook, [ 'upload.php', 'media-new.php' ], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'media-folder-pro-admin',
|
||||
MFP_PLUGIN_URL . 'assets/css/admin.css',
|
||||
[],
|
||||
MFP_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'media-folder-pro-tree',
|
||||
MFP_PLUGIN_URL . 'assets/js/folder-tree.js',
|
||||
[],
|
||||
MFP_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'media-folder-pro-filter',
|
||||
MFP_PLUGIN_URL . 'assets/js/media-filter.js',
|
||||
[ 'media-views', 'media-folder-pro-tree' ],
|
||||
MFP_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'media-folder-pro-modal',
|
||||
MFP_PLUGIN_URL . 'assets/js/modal-integration.js',
|
||||
[ 'media-views', 'media-folder-pro-tree' ],
|
||||
MFP_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script( 'media-folder-pro-tree', 'mfpData', $this->get_mfp_data() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal integration for standard admin pages (post editor, etc.).
|
||||
*/
|
||||
public function enqueue_modal_assets( string $hook ): void {
|
||||
if ( $hook === 'upload.php' || $hook === 'media-new.php' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! did_action( 'wp_enqueue_media' ) && ! wp_script_is( 'media-views', 'enqueued' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wp_script_is( 'media-folder-pro-modal', 'enqueued' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'media-folder-pro-admin',
|
||||
MFP_PLUGIN_URL . 'assets/css/admin.css',
|
||||
[],
|
||||
MFP_VERSION
|
||||
);
|
||||
|
||||
$this->ensure_mfp_data();
|
||||
|
||||
wp_enqueue_script(
|
||||
'media-folder-pro-modal',
|
||||
MFP_PLUGIN_URL . 'assets/js/modal-integration.js',
|
||||
[ 'media-views', 'media-folder-pro-tree' ],
|
||||
MFP_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into wp_enqueue_media — fires whenever any plugin/theme calls wp_enqueue_media().
|
||||
* Covers: Elementor, WPBakery, Divi, ACF, and any builder using wp.media.
|
||||
*/
|
||||
public function enqueue_modal_on_media_load(): void {
|
||||
if ( wp_script_is( 'media-folder-pro-modal', 'enqueued' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip pages handled by enqueue_admin_assets — wp_enqueue_media() fires
|
||||
// BEFORE admin_enqueue_scripts on upload.php, which would poison the
|
||||
// tree script handle with a false (empty) registration.
|
||||
$screen = get_current_screen();
|
||||
if ( $screen && in_array( $screen->base, [ 'upload', 'media' ], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'media-folder-pro-admin',
|
||||
MFP_PLUGIN_URL . 'assets/css/admin.css',
|
||||
[],
|
||||
MFP_VERSION
|
||||
);
|
||||
|
||||
$this->ensure_mfp_data();
|
||||
|
||||
wp_enqueue_script(
|
||||
'media-folder-pro-modal',
|
||||
MFP_PLUGIN_URL . 'assets/js/modal-integration.js',
|
||||
[ 'media-views', 'media-folder-pro-tree' ],
|
||||
MFP_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public function render_folder_tree_container(): void {
|
||||
echo '<div id="mfp-folder-root"></div>';
|
||||
}
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', [ 'Media_Folder_Pro', 'instance' ] );
|
||||
Reference in New Issue
Block a user