A minimal, dependency-free, drag & drop upload UI that mirrors the native <input type="file"> so Elementor can serialize files correctly (AJAX + submissions + email). Includes hardened server-side validation hooks and post-submit reset. Built as Must-Use plugin with external, versioned assets and optional cache-busting for test environments.
- CodeCorn™ DZ Lite (MU) — Dropzone-like upload for Elementor Forms
- Pure JS/CSS: no Dropzone.js or other vendors.
- Native-first: keeps the hidden file input in sync (
DataTransfer), so Elementor sees exactly what’s selected. - Multiple files: robust add/remove/re-add cycle without losing selections.
- No layout shift: anti-flicker boot flags + minimal fade-in.
- Post-submit reset: clears UI + native files after successful Elementor submission (multiple event paths covered).
- Cache-busting in test: automatic
filemtime()versioning when debug is on. - Hardened validation hooks (PHP) to block double extensions, bad MIME, oversize files.
- WordPress 5.8+ (tested up to 6.7)
- PHP 7.4+
- Elementor 3.18+ and Elementor Pro (for Forms) 3.18+
- jQuery present on front-end (standard in WP themes)
- Clone or copy into
wp-content/mu-plugins/:
wp-content/
└─ mu-plugins/
├─ mu-cc-dz-loader.php
└─ codecorn/
└─ dz-lite/
├─ assets/
│ ├─ css/critical.css
│ └─ js/{first.js,core.js,init.js}
└─ vendors/ (not used)
- (Optional) Enable debug / cache-bust in
wp-config.php:
define('CC_DZ_DEBUG', true);- Load any page with an Elementor Form upload field — the custom dropzone renders automatically.
mu-cc-dz-loader.php # MU loader class (CC_DZ): enqueue, preload, flags, cache-bust
codecorn/dz-lite/
assets/css/critical.css # Skin + anti-flicker rules
assets/js/first.js # Bootstrap + logger
assets/js/core.js # Dropzone logic (UI + queue + DT sync + reset)
assets/js/init.js # Elementor hooks + boot complete
vendors/ # (empty) no third-party deps
- Replaces the visual of Elementor’s upload field with a custom wrapper, without removing the native input.
- Maintains a logical queue and mirrors it into
input.filesviaDataTransfer, so submissions & emails are correct. - On successful submit, listens to multiple Elementor hooks (
form:submit:success,submit_success, AJAX fallback) and resets UI +input.files.
CC_DZ_DEBUG(bool): whentrue, assets are versioned withfilemtime()→ instant cache invalidation.
-
Set these in Elementor’s upload field (UI). The dropzone reads:
acceptattributemultiple- data attributes (if present):
data-maxfiles,data-maxsize(MB)
- When
multipleis enabled, the loader enforcesname="…[]"to match Elementor’s serializer.
-
Auto-init on
elementor/frontend/init→frontend/element_ready/form.default. -
No action required in the form widget besides standard field settings.
-
Post-submit reset is triggered on:
- Instance event:
form:submit:success - Global event:
submit_success - Fallback:
ajaxComplete(elementor_pro_forms_send_form) - Native
submit(last resort)
- Instance event:
Add to a MU plugin (or theme functions.php) to harden validation:
add_filter('elementor_pro/forms/validation/file', function ($record, $field_id) {
$allowed_ext = ['jpg','jpeg','png','gif','webp','heic','heif','mp4','mov','avi','mkv','webm'];
$max_mb = 30; $max_bytes = $max_mb * 1024 * 1024;
if (empty($record['files'])) return;
foreach ($record['files'] as $file) {
if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
throw new \Exception('Upload non valido.');
}
if (preg_match('/\.(php\d*|phtml|phar|pl|py|cgi|asp|js|sh|bin|exe|dll)(\.|$)/i', $file['name'])) {
throw new \Exception('Formato non consentito.');
}
if (!empty($file['size']) && (int)$file['size'] > $max_bytes) {
throw new \Exception('File troppo grande. Max '.$max_mb.'MB.');
}
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_ext, true)) {
throw new \Exception('Carica solo foto o video.');
}
$check = wp_check_filetype_and_ext($file['tmp_name'], $file['name']);
$real_ext = strtolower($check['ext'] ?? '');
$real_mime = strtolower($check['type'] ?? '');
if ($real_ext !== '' && !in_array($real_ext, $allowed_ext, true)) {
throw new \Exception('Tipo di file non consentito.');
}
$is_img = $real_mime ? str_starts_with($real_mime, 'image/') : in_array($ext, ['jpg','jpeg','png','gif','webp','heic','heif'], true);
$is_vid = $real_mime ? str_starts_with($real_mime, 'video/') : in_array($ext, ['mp4','mov','avi','mkv','webm'], true);
if (!$is_img && !$is_vid) {
throw new \Exception('Carica solo foto o video.');
}
}
}, 10, 2);- Wrapper is focusable (
role="button",tabindex="0"). - Keyboard activation via Enter/Space opens the file picker.
aria-live="polite"on previews list for incremental updates.
No build tool is required. Plain ES5-ish JS for maximum WP compatibility.
Local toggles
-
Enable debug logs + cache-busting:
define('CC_DZ_DEBUG', true);
Coding standards
- PHP: PSR-12-ish, strict types not enforced (MU context).
- JS: no external dependencies; keep browser support aligned with WordPress core.
Where to edit
- UI/logic:
assets/js/core.js - Elementor bootstrap:
assets/js/init.js - Minimal helpers/logger:
assets/js/first.js - Styles/skin/anti-flicker:
assets/css/critical.css
- Semantic Versioning: MAJOR.MINOR.PATCH
- Update
CC_DZ::VERinmu-cc-dz-loader.phpfor each release. - Prod builds: set
CC_DZ_DEBUGtofalse(or remove) so versions are static and cache-friendly. - Test builds: set
CC_DZ_DEBUGtotrueto bypass caches usingfilemtime().
-
Issues: use GitHub Issues. Provide:
- WP/Elementor versions
- Theme + list of active plugins
- Browser/OS
- Steps to reproduce + console/network logs
-
Security: for any vulnerability or suspicion, contact us privately. Do not open public issues with exploit details.
MIT © CodeCorn™ Technology. See LICENSE.
CodeCorn™ Technology — boutique engineering for WordPress/Elementor stacks.
- Focus: front-end UX without dependencies, robust server-side hardening, perf & DX discipline.
- GitHub:
@CodeCornTech