PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.3
Tutor LMS – eLearning and online course solution v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / components / FileUploader.php
tutor / components Last commit date
Constants 3 weeks ago Accordion.php 1 week ago Alert.php 1 week ago AttachmentCard.php 1 week ago Avatar.php 1 week ago Badge.php 1 week ago BaseComponent.php 3 weeks ago Button.php 1 week ago ConfirmationModal.php 1 week ago CourseFilter.php 1 week ago DateFilter.php 1 week ago DropdownFilter.php 1 week ago EmptyState.php 1 week ago FileUploader.php 1 week ago InputField.php 1 week ago Modal.php 1 week ago Nav.php 1 week ago Pagination.php 1 week ago Popover.php 1 week ago PreviewTrigger.php 1 week ago Progress.php 1 week ago SearchFilter.php 1 week ago Sorting.php 1 week ago StarRating.php 1 week ago StarRatingInput.php 1 week ago StatusSelect.php 1 week ago SvgIcon.php 1 week ago Table.php 1 week ago Tabs.php 1 week ago Tooltip.php 1 week ago WPEditor.php 1 week ago
FileUploader.php
689 lines
1 <?php
2 /**
3 * FileUploader Component Class.
4 *
5 * @package Tutor\Components
6 * @author Themeum
7 * @link https://themeum.com
8 * @since 4.0.0
9 */
10
11 namespace Tutor\Components;
12
13 defined( 'ABSPATH' ) || exit;
14
15 use Tutor\Components\Constants\Size;
16 use Tutor\Components\Constants\Variant;
17 use TUTOR\Icon;
18 use Tutor\Components\Button;
19 use Tutor\Components\AttachmentCard;
20
21
22 /**
23 * Class FileUploader
24 *
25 * Example Usage:
26 * ```php
27 * // Native single-file upload
28 * FileUploader::make()
29 * ->name( 'resume' )
30 * ->accept( '.pdf,.doc' )
31 * ->render();
32 *
33 * // Native multiple-file upload with size/count limits
34 * FileUploader::make()
35 * ->name( 'course_attachments' )
36 * ->uploader_title( __( 'Upload Assignments', 'tutor' ) )
37 * ->uploader_subtitle( __( 'Supports PDF, DOCX — max 20 MB each', 'tutor' ) )
38 * ->accept( '.pdf,.docx' )
39 * ->multiple( true )
40 * ->max_files( 3 )
41 * ->max_size( 20 * 1024 * 1024 )
42 * ->render();
43 *
44 * // Image uploader (native file input, image preview variant)
45 * FileUploader::make()
46 * ->variant( FileUploader::IMAGE_UPLOADER )
47 * ->name( 'cover_photo' )
48 * ->accept( '.jpg,.jpeg,.png,.webp' )
49 * ->render();
50 *
51 * // WP Media Library — single image
52 * FileUploader::make()
53 * ->variant( FileUploader::IMAGE_UPLOADER )
54 * ->use_wp_media( true )
55 * ->wp_media_library_type( 'image' )
56 * ->wp_media_title( __( 'Select Profile Photo', 'tutor' ) )
57 * ->wp_media_button_text( __( 'Use This Photo', 'tutor' ) )
58 * ->name( 'profile_photo' )
59 * ->render();
60 *
61 * // WP Media Library — multiple documents
62 * FileUploader::make()
63 * ->use_wp_media( true )
64 * ->multiple( true )
65 * ->wp_media_library_type( 'application/pdf,application/msword' )
66 * ->name( 'shared_files' )
67 * ->render();
68 *
69 * // Custom icon, title, subtitle and button text
70 * FileUploader::make()
71 * ->name( 'resources' )
72 * ->uploader_icon( Icon::RESOURCES )
73 * ->uploader_icon_size( 32 )
74 * ->uploader_title( __( 'Resource Center', 'tutor' ) )
75 * ->uploader_subtitle( __( 'Add any relevant documents here', 'tutor' ) )
76 * ->uploader_button_text( __( 'Choose Resources', 'tutor' ) )
77 * ->render();
78 *
79 * // Required upload with an ID and Alpine.js callbacks
80 * FileUploader::make()
81 * ->name( 'assignment_file' )
82 * ->id( 'assignment-upload' )
83 * ->required( true )
84 * ->accept( '.pdf' )
85 * ->attr( 'onFileSelect', 'handleFileSelect' )
86 * ->attr( 'onError', 'handleUploadError' )
87 * ->render();
88 *
89 * // Retrieve HTML without echoing
90 * $html = FileUploader::make()->name( 'attachment' )->get();
91 * ```
92 *
93 * @since 4.0.0
94 */
95 class FileUploader extends BaseComponent {
96
97 /**
98 * Variant constants.
99 *
100 * @since 4.0.0
101 */
102 public const FILE_UPLOADER = 'file-uploader';
103 public const IMAGE_UPLOADER = 'image-uploader';
104
105
106 /**
107 * File uploader accept attribute.
108 *
109 * @since 4.0.0
110 *
111 * @var string
112 */
113 protected $accept = '.pdf,.doc,.docx,.jpg,.jpeg,.png';
114
115 /**
116 * File uploader max size.
117 *
118 * @since 4.0.0
119 *
120 * @var int
121 */
122 protected $max_size = null;
123
124 /**
125 * File uploader max files.
126 *
127 * @since 4.0.0
128 *
129 * @var int|null
130 */
131 protected $max_files = null;
132
133 /**
134 * File uploader icon.
135 *
136 * @since 4.0.0
137 *
138 * @var string
139 */
140 protected $uploader_icon = Icon::UPLOAD_FILE;
141
142 /**
143 * File uploader title.
144 *
145 * @since 4.0.0
146 *
147 * @var string
148 */
149 protected $uploader_title = '';
150
151 /**
152 * File uploader subtitle.
153 *
154 * @since 4.0.0
155 *
156 * @var string
157 */
158 protected $uploader_subtitle = '';
159
160 /**
161 * File uploader button text.
162 *
163 * @since 4.0.0
164 *
165 * @var string
166 */
167 protected $uploader_button_text = '';
168
169 /**
170 * Component variant.
171 *
172 * @since 4.0.0
173 *
174 * @var string
175 */
176 protected $variant = self::FILE_UPLOADER;
177
178 /**
179 * Whether to use WordPress media library instead of native file input.
180 *
181 * @since 4.0.0
182 *
183 * @var bool
184 */
185 protected $use_wp_media = false;
186
187 /**
188 * Title for WordPress media modal.
189 *
190 * @since 4.0.0
191 *
192 * @var string
193 */
194 protected $wp_media_title = '';
195
196 /**
197 * Button text for WordPress media modal.
198 *
199 * @since 4.0.0
200 *
201 * @var string
202 */
203 protected $wp_media_button_text = '';
204
205 /**
206 * Library type filter for WordPress media (image, video, audio, application).
207 *
208 * @since 4.0.0
209 *
210 * @var string
211 */
212 protected $wp_media_library_type = '';
213
214 /**
215 * Whether multiple option can be selected in input.
216 *
217 * @since 4.0.0
218 *
219 * @var boolean
220 */
221 protected $multiple = false;
222
223 /**
224 * InputField name attribute.
225 *
226 * @since 4.0.0
227 *
228 * @var string
229 */
230 protected $name = '';
231
232 /**
233 * InputField ID attribute.
234 *
235 * @since 4.0.0
236 *
237 * @var string
238 */
239 protected $id = '';
240
241 /**
242 * Whether input is required.
243 *
244 * @since 4.0.0
245 *
246 * @var bool
247 */
248 protected $required = false;
249
250 /**
251 * InputField value.
252 *
253 * @since 4.0.0
254 *
255 * @var string
256 */
257 protected $value = '';
258
259 /**
260 * Upload icon size.
261 *
262 * @since 4.0.0
263 *
264 * @var integer
265 */
266 protected $upload_icon_size = 24;
267
268 /**
269 * Set uploader accept attribute.
270 *
271 * Common types: .pdf, .doc, .docx, .jpg, .jpeg, .png
272 * For WP Media: image, video, audio, application
273 *
274 * @since 4.0.0
275 *
276 * @param string $accept Accept attribute.
277 *
278 * @return $this
279 */
280 public function accept( $accept ) {
281 $this->accept = $accept;
282
283 return $this;
284 }
285
286 /**
287 * Set uploader max size.
288 *
289 * @since 4.0.0
290 *
291 * @param int $max_size Max size in bytes.
292 *
293 * @return $this
294 */
295 public function max_size( $max_size = null ) {
296 if ( null === $max_size ) {
297 $max_size = wp_max_upload_size();
298 }
299 $this->max_size = $max_size;
300
301 return $this;
302 }
303
304 /**
305 * Set uploader max files.
306 *
307 * @since 4.0.0
308 *
309 * @param int|null $max_files Maximum number of selectable files.
310 *
311 * @return $this
312 */
313 public function max_files( $max_files = null ) {
314 $this->max_files = null !== $max_files ? (int) $max_files : null;
315
316 return $this;
317 }
318
319 /**
320 * Set uploader icon.
321 *
322 * @since 4.0.0
323 *
324 * @param string $icon Icon name.
325 *
326 * @return $this
327 */
328 public function uploader_icon( $icon ) {
329 $this->uploader_icon = $icon;
330
331 return $this;
332 }
333
334 /**
335 * Set uploader icon size.
336 *
337 * @since 4.0.0
338 *
339 * @param int $size Icon size.
340 *
341 * @return $this
342 */
343 public function uploader_icon_size( $size ) {
344 $this->upload_icon_size = $size;
345
346 return $this;
347 }
348
349 /**
350 * Set uploader title.
351 *
352 * @since 4.0.0
353 *
354 * @param string $title Title text.
355 *
356 * @return $this
357 */
358 public function uploader_title( $title ) {
359 $this->uploader_title = $title;
360
361 return $this;
362 }
363
364 /**
365 * Set uploader subtitle.
366 *
367 * @since 4.0.0
368 *
369 * @param string $subtitle Subtitle text.
370 *
371 * @return $this
372 */
373 public function uploader_subtitle( $subtitle ) {
374 $this->uploader_subtitle = $subtitle;
375
376 return $this;
377 }
378
379 /**
380 * Set uploader button text.
381 *
382 * @since 4.0.0
383 *
384 * @param string $button_text Button text.
385 *
386 * @return $this
387 */
388 public function uploader_button_text( $button_text ) {
389 $this->uploader_button_text = $button_text;
390
391 return $this;
392 }
393
394 /**
395 * Set component variant.
396 *
397 * @since 4.0.0
398 *
399 * @param string $variant Component variant (file-uploader|image-uploader).
400 *
401 * @return $this
402 */
403 public function variant( $variant ) {
404 $this->variant = $variant;
405
406 return $this;
407 }
408
409 /**
410 * Set use WordPress media library.
411 *
412 * @since 4.0.0
413 *
414 * @param bool $use_wp_media Use WP media library.
415 *
416 * @return $this
417 */
418 public function use_wp_media( $use_wp_media = true ) {
419 $this->use_wp_media = (bool) $use_wp_media;
420
421 return $this;
422 }
423
424 /**
425 * Set WP media title.
426 *
427 * @since 4.0.0
428 *
429 * @param string $wp_media_title WP media title.
430 *
431 * @return $this
432 */
433 public function wp_media_title( $wp_media_title ) {
434 $this->wp_media_title = $wp_media_title;
435
436 return $this;
437 }
438
439 /**
440 * Set WP media button text.
441 *
442 * @since 4.0.0
443 *
444 * @param string $wp_media_button_text WP media button text.
445 *
446 * @return $this
447 */
448 public function wp_media_button_text( $wp_media_button_text ) {
449 $this->wp_media_button_text = $wp_media_button_text;
450
451 return $this;
452 }
453
454 /**
455 * Set WP media library type.
456 *
457 * Common types: image, video, audio, application, text
458 *
459 * @since 4.0.0
460 *
461 * @param string $wp_media_library_type WP media library type.
462 *
463 * @return $this
464 */
465 public function wp_media_library_type( $wp_media_library_type ) {
466 $this->wp_media_library_type = $wp_media_library_type;
467
468 return $this;
469 }
470
471 /**
472 * Set multiple.
473 *
474 * @since 4.0.0
475 *
476 * @param bool $multiple Multiple.
477 *
478 * @return $this
479 */
480 public function multiple( $multiple = true ) {
481 $this->multiple = (bool) $multiple;
482
483 return $this;
484 }
485
486 /**
487 * Set input name.
488 *
489 * @since 4.0.0
490 *
491 * @param string $name Name.
492 *
493 * @return $this
494 */
495 public function name( $name ) {
496 $this->name = sanitize_key( $name );
497
498 return $this;
499 }
500
501 /**
502 * Set input ID.
503 *
504 * @since 4.0.0
505 *
506 * @param string $id InputField ID.
507 *
508 * @return $this
509 */
510 public function id( $id ) {
511 $this->id = sanitize_key( $id );
512
513 return $this;
514 }
515
516 /**
517 * Set required.
518 *
519 * @since 4.0.0
520 *
521 * @param bool $required Required.
522 *
523 * @return $this
524 */
525 public function required( $required = true ) {
526 $this->required = $required;
527
528 return $this;
529 }
530
531 /**
532 * Set value.
533 *
534 * @since 4.0.0
535 *
536 * @param string $value Value.
537 *
538 * @return $this
539 */
540 public function value( $value ) {
541 $this->value = $value;
542
543 return $this;
544 }
545
546 /**
547 * Get component content
548 *
549 * @return string
550 */
551 public function get(): string {
552 $multiple = $this->multiple;
553 $accept = $this->accept;
554 $max_size = $this->max_size ?? wp_max_upload_size();
555 $max_files = $this->max_files;
556 $icon = $this->uploader_icon;
557 $title = ! empty( $this->uploader_title ) ? $this->uploader_title : __( 'Drop files here or click to upload', 'tutor' );
558 $subtitle = ! empty( $this->uploader_subtitle ) ? $this->uploader_subtitle : '';
559 $button_text = ! empty( $this->uploader_button_text ) ? $this->uploader_button_text : __( 'Select Files', 'tutor' );
560 $on_file_select = $this->attributes['onFileSelect'] ?? 'null';
561 $on_error = $this->attributes['onError'] ?? 'null';
562 $on_file_remove = $this->attributes['onFileRemove'] ?? 'null';
563 $variant = $this->variant;
564 $uploader_attributes = $this->attributes;
565
566 // Remove Alpine specific attributes from HTML attributes.
567 unset( $uploader_attributes['onFileSelect'] );
568 unset( $uploader_attributes['onError'] );
569
570 $this->attributes = $uploader_attributes;
571
572 ob_start();
573 ?>
574 <div
575 class="tutor-file-uploader-wrapper"
576 x-data="tutorFileUploader({
577 multiple: <?php echo $multiple ? 'true' : 'false'; ?>,
578 maxFiles: <?php echo null !== $max_files ? (int) $max_files : 'undefined'; ?>,
579 accept: '<?php echo esc_attr( $accept ); ?>',
580 maxSize: <?php echo (int) $max_size; ?>,
581 onFileSelect: <?php echo esc_js( $on_file_select ); ?>,
582 onFileRemove: <?php echo esc_js( $on_file_remove ); ?>,
583 onError: <?php echo esc_js( $on_error ); ?>,
584 variant: '<?php echo esc_attr( $variant ); ?>',
585 value: values.<?php echo esc_attr( $this->name ); ?>,
586 name: '<?php echo esc_attr( $this->name ); ?>',
587 required: <?php echo is_bool( $this->required ) ? ( $this->required ? 'true' : 'false' ) : "'" . esc_js( $this->required ) . "'"; ?>,
588 useWPMedia: <?php echo $this->use_wp_media ? 'true' : 'false'; ?>,
589 wpMediaTitle: '<?php echo esc_js( $this->wp_media_title ); ?>',
590 wpMediaButtonText: '<?php echo esc_js( $this->wp_media_button_text ); ?>',
591 wpMediaLibraryType: '<?php echo esc_js( $this->wp_media_library_type ); ?>',
592 })"
593 >
594 <template x-if="imagePreview">
595 <div class="tutor-file-preview">
596 <img :src="imagePreview" alt="Preview">
597 <div class="tutor-file-preview-overlay">
598 <div class="tutor-file-preview-actions">
599 <?php
600 Button::make()
601 ->label( __( 'Delete', 'tutor' ) )
602 ->variant( Variant::DESTRUCTIVE )
603 ->size( Size::SMALL )
604 ->attr( 'type', 'button' )
605 ->attr( '@click.stop', 'removeFile()' )
606 ->render();
607
608 Button::make()
609 ->label( __( 'Upload New', 'tutor' ) )
610 ->variant( Variant::PRIMARY )
611 ->size( Size::SMALL )
612 ->attr( 'type', 'button' )
613 ->attr( '@click.stop', 'openFileDialog()' )
614 ->render();
615 ?>
616 </div>
617 </div>
618 </div>
619 </template>
620
621 <template x-if="variant === 'file-uploader' && selectedFiles.length > 0">
622 <div class="tutor-flex tutor-flex-column tutor-gap-5">
623 <div class="tutor-grid tutor-grid-cols-2 tutor-sm-grid-cols-1 tutor-gap-5">
624 <template x-for="(file, index) in selectedFiles" :key="index">
625 <div class="tutor-attachment-card-wrapper">
626 <?php
627 AttachmentCard::make()
628 ->title_attr( 'x-text', 'file.name' )
629 ->meta_attr( 'x-text', 'formatBytes(file.size)' )
630 ->action_attr( '@click.stop', 'removeFile(index)' )
631 ->render();
632 ?>
633 </div>
634 </template>
635 </div>
636 <div x-show="maxFiles > selectedFiles.length" class="tutor-mt-1">
637 <button type="button" class="tutor-btn tutor-btn-primary-soft tutor-sm-w-full" @click="openFileDialog()">
638 <?php $this->multiple ? esc_html_e( 'Upload More Files', 'tutor' ) : esc_html_e( 'Upload Again', 'tutor' ); ?>
639 </button>
640 </div>
641 </div>
642 </template>
643
644 <!-- Upload Area -->
645 <div
646 x-show="!imagePreview && (variant !== 'file-uploader' || selectedFiles.length === 0)"
647 class="tutor-file-uploader"
648 :class="{
649 'tutor-file-uploader-drag-over': isDragOver,
650 'tutor-file-uploader-disabled': isDisabled
651 }"
652 @click="openFileDialog()"
653 @dragover.prevent="handleDragOver($event)"
654 @dragleave.prevent="handleDragLeave($event)"
655 @drop.prevent="handleDrop($event)"
656 <?php echo self::IMAGE_UPLOADER === $variant ? 'data-image-uploader' : ''; ?>
657 >
658 <input
659 type="file"
660 class="tutor-file-uploader-input"
661 :multiple="multiple"
662 :accept="accept"
663 :disabled="isDisabled"
664 x-ref="fileInput"
665 @change="handleFileSelect($event)"
666 name="<?php echo esc_attr( $this->name ); ?>"
667 id="<?php echo esc_attr( ! empty( $this->id ) ? $this->id : $this->name ); ?>"
668 <?php echo $this->get_attributes_string(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
669 >
670 <div class="tutor-file-uploader-icon">
671 <?php SvgIcon::make()->name( $icon )->size( $this->upload_icon_size )->render(); ?>
672 </div>
673 <div class="tutor-file-uploader-content">
674 <p class="tutor-file-uploader-title"><?php echo esc_html( $title ); ?></p>
675 <?php if ( ! empty( $subtitle ) ) : ?>
676 <p class="tutor-file-uploader-subtitle"><?php echo esc_html( $subtitle ); ?></p>
677 <?php endif; ?>
678 </div>
679 <button type="button" class="tutor-btn tutor-btn-primary-soft" :disabled="isDisabled">
680 <?php echo esc_html( $button_text ); ?>
681 </button>
682 </div>
683 </div>
684 <?php
685 $this->component_string = ob_get_clean();
686 return $this->component_string;
687 }
688 }
689