# contact-forms/2.2.4/classes/Element/File.php

Contact Forms by Cimatti, version 2.2.4. 288 lines.

- Page: https://pluginprobe.com/plugins/contact-forms/2.2.4/code/classes/Element/File.php
- Raw: https://pluginprobe.com/plugins/contact-forms/2.2.4/raw/classes/Element/File.php
- Modified: 2026-04-08T09:49:58+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/contact-forms/2.2.4/code/classes/Element/File.php#L10-L20`.

```php
<?php
class AccuaForm_Element_File extends Element_File {
  protected $destPath;
  protected $alreadySubmittedText;
  // Modern default extensions: documents, images, archives
  protected $validExtensions = array(
    // Documents
    'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'ods', 'odp', 'txt', 'rtf', 'csv',
    // Images
    'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'heic',
    // Archives
    'zip', 'rar', '7z', 'tar', 'gz'
  );
  protected $maxSize;  protected $limitsText;
  protected $errorsText;
    public function __construct($label, $name, ?array $properties = null) {
    $this->alreadySubmittedText = __( "File already submitted.", 'contact-forms' );
    // translators: %size% and %extensions% are custom placeholders replaced via str_replace(), not sprintf().
    $this->limitsText = __( 'Maximum filesize: %size%. Allowed extensions: %extensions%.', 'contact-forms' ); // phpcs:ignore WordPress.WP.I18n.UnorderedPlaceholdersText -- Custom placeholders used with str_replace(), not sprintf()
    $this->errorsText = array(
      'upload' => __( 'Upload failed, please retry. If the problem persists please contact us', 'contact-forms' ),
      'size' => __( 'Uploaded file is too big', 'contact-forms' ),
      'empty' => __( 'Uploaded file is empty', 'contact-forms' ),
      'name' => __( 'File name is not valid. Please rename it avoiding unusual characters', 'contact-forms' ),
      // translators: %extensions% is a custom placeholder replaced via str_replace().
      'ext' => __( 'File extension not allowed. Allowed extensions are %extensions%', 'contact-forms' ),
    );
    
    // Use WordPress upload directory function instead of hardcoded path
    $wp_upload_dir = wp_upload_dir();
    $accua_upload_dir = $wp_upload_dir['basedir'] . '/accua-forms';
    
    // Create directory if it doesn't exist using WordPress function
    if (!file_exists($accua_upload_dir)) {
      wp_mkdir_p($accua_upload_dir);
    }
    
    // Use trailingslashit to ensure path ends with a slash
    $this->destPath = trailingslashit($accua_upload_dir);
    $this->maxSize = self::file_upload_max_size();
    parent::__construct($label, $name, $properties);
  }
  
  public function __sleep() {
    return array("attributes", "label", "validation", 'alreadySubmittedText', 'validExtensions', 'maxSize', 'limitsText', 'errorsText', 'destPath');
  }
  
  public function render() {
    if(empty($this->attributes["value"])){
      // Build help text for limits
      $helpText = '';
      if ($this->limitsText !== ''){
        if ($this->validExtensions) {
          $validExtensions = implode(', ', $this->validExtensions);
        } else {
          $validExtensions = '*';
        }
        $search = array('%size%', '%extensions%');
        $replace = array(self::format_size($this->maxSize), $validExtensions);
        $helpText = str_replace($search, $replace, $this->limitsText);
      }

      // Build accept attribute for file extensions
      $acceptAttr = '';
      if ($this->validExtensions && is_array($this->validExtensions)) {
        $acceptAttr = '.' . implode(',.', $this->validExtensions);
      }

      // Generate unique IDs for ARIA associations
      $inputId = esc_attr($this->getID() ?: 'accua-file-' . $this->getName());
      $helpId = $inputId . '-help';
      $announceId = $inputId . '-announce';

      // Open wrapper with data attributes for JS
      echo '<div class="accua-file-upload-wrapper" data-max-size="' . esc_attr($this->maxSize) . '" data-accept="' . esc_attr($acceptAttr) . '">';
      
      // Dropzone container
      echo '<div class="accua-file-dropzone" role="button" tabindex="0" ';
      echo 'aria-describedby="' . esc_attr($helpId) . '" ';
      echo 'aria-dropeffect="none">';
      
      // Visual content for dropzone
      echo '<span class="accua-file-dropzone-text">';
      echo '<span class="accua-file-dropzone-icon" aria-hidden="true">📁</span> ';
      echo esc_html__('Drag & drop file here or', 'contact-forms') . ' ';
      echo '<span class="accua-file-browse-btn">' . esc_html__('browse', 'contact-forms') . '</span>';
      echo '</span>';
      
      // Set accept attribute on native file input for browser file picker filtering
      if ($acceptAttr !== '') {
        $this->attributes['accept'] = $acceptAttr;
      }

      // Hidden native file input (for accessibility and form submission)
      parent::render();
      
      echo '</div>'; // .accua-file-dropzone
      
      // Help text
      if ($helpText !== '') {
        echo '<p class="pfbc-help" id="' . esc_attr($helpId) . '">' . esc_html($helpText) . '</p>';
      }
      
      // File list container (populated by JS)
      echo '<div class="accua-file-list" hidden aria-live="polite" aria-relevant="additions removals"></div>';
      
      // Screen reader announcement region
      echo '<div class="accua-file-sr-announcement" id="' . esc_attr($announceId) . '" aria-live="polite" aria-atomic="true" class="screen-reader-text"></div>';
      
      echo '</div>'; // .accua-file-upload-wrapper
      
    } else {
      echo esc_html($this->alreadySubmittedText);
    }
  }
  
  public function getAlreadySubmittedText() {
    return $this->alreadySubmittedText;
  }
  
  public function hasHelpText() {
    return !empty($this->limitsText);
  }
  
  public function appendToPostHTML($html) {
    $this->postHTML = ($this->postHTML ?? '') . $html;
  }
  
  public static function format_size($size) {
    if ($size >= 1073741824) {
      return round($size/1073741824, 2).' GB';
    } else if ($size >= 1048576) {
      return round($size/1048576, 2).' MB';
    } else if ($size >= 1024) {
      return round($size/1024, 2).' KB';
    } else if ($size > 0) {
      return round($size).' B';
    } else {
      return '-';
    }
  }
  
  public static function parse_size($value) {
    $value = strtolower( trim( $value ) );
    $bytes = (float) $value;

    if ( false !== strpos( $value, 'g' ) ) {
      $bytes *= 1073741824; // 1024 * 1024 * 1024
    } elseif ( false !== strpos( $value, 'm' ) ) {
      $bytes *= 1048576; // 1024 * 1024
    } elseif ( false !== strpos( $value, 'k' ) ) {
      $bytes *= 1024;
    }

    return min( $bytes, PHP_INT_MAX );
  }
  
  public static function file_upload_max_size() {
    static $max_size = -1;

    if ($max_size < 0) {
      $max_size = wp_max_upload_size();
    }
    return $max_size;
  }
  
  public function setMaxSize($size) {
    $phpMaxSize = self::file_upload_max_size();
    $size = self::parse_size($size);
    return $this->maxSize = ($phpMaxSize < $size) ? $phpMaxSize : $size;
  }
  
  public function setErrorsText($errors){
    if(is_array($errors)) {
      $this->errorsText = $errors + $this->errorsText;
    }
  }
    public function setDestPath($path) {
    // Check if path is absolute BEFORE normalizing (path_is_absolute handles both Unix and Windows)
    $is_absolute = path_is_absolute($path);
    
    // Normalize path separators for consistent handling
    $path = wp_normalize_path($path);
    
    // Handle relative paths by prepending ABSPATH
    if (!$is_absolute) {
      $path = path_join(ABSPATH, $path);
    }
    
    // Create directory with proper WordPress function and secure permissions
    if (!file_exists($path)) {
      wp_mkdir_p($path);
    }
    
    // Verify the directory exists and is writable
    if (!is_dir($path) || !wp_is_writable($path)) {
      // Fall back to WordPress uploads directory if target path is not writable
      $upload_dir = wp_upload_dir();
      $path = $upload_dir['basedir'] . '/accua-forms';
      wp_mkdir_p($path);
    }
    
    return $this->destPath = trailingslashit($path);
  }
  
  public function handle_upload($filedata){
    $valid = true;
    $file = array(
      'dest_path' => $this->destPath,
    );

    if (!empty($filedata['error'])){
      $valid = false;
      switch($filedata['error']){
        case UPLOAD_ERR_INI_SIZE:
        case UPLOAD_ERR_FORM_SIZE:
          $file['errors'][] = $this->errorsText['size'];
        break;
        default:
          $file['errors'][] = $this->errorsText['upload'];
      }
    } else if ($filedata['size'] <= 0) {
      $valid = false;
      $file['errors'][] = $this->errorsText['empty']; 
    } else if ($this->maxSize > 0 && $filedata['size'] > $this->maxSize) {
      $valid = false;
      $file['errors'][] = $this->errorsText['size'];
    } else {
      $file['size'] = $filedata['size'];
    }
    
    if (isset($filedata['name']) && $filedata['name'] !== ''){
      $file['name'] = $filedata['name'];
      if ((strpos($file['name'], "\0") !== false) || (strpbrk($file['name'], "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177\\/:*?\"<>|") !== false) ) {
        $valid = false;
        $file['errors'][] = $this->errorsText['name'];
      } else if ($this->validExtensions) {
        $ext = strrchr($file['name'], '.');
        $ext = ($ext === false) ? '' : strtolower(ltrim($ext, '.'));
        $valid_ext_lower = array_map('strtolower', $this->validExtensions);
        if (!in_array($ext, $valid_ext_lower, true)) {
          $valid = false;
          $file['errors'][] = str_replace('%extensions%', implode(', ',$this->validExtensions), $this->errorsText['ext']);
        }
      }
    } else {
      $file['name'] = '';
      if ($valid) {
        $valid = false;
        $file['errors'][] = $this->errorsText['name'];
      }
    }
      if ($valid) {
      // Additional security check - validate MIME type
      $file_info = wp_check_filetype_and_ext($filedata['tmp_name'], $file['name']);
      if (empty($file_info['type'])) {
        $valid = false;
        $file['errors'][] = __('Invalid file type detected', 'contact-forms');
      } else {
        // Make sure destination directory exists
        if (!is_dir($this->destPath)) {
          wp_mkdir_p($this->destPath);
        }
        
        // Generate a unique filename with sanitization
        do {
          $tmpname = 'tmp' . wp_generate_password(8, false) . '_' . sanitize_file_name($file['name']);
        } while (is_file($this->destPath.$tmpname));
        
        // Move uploaded file
        // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- move_uploaded_file is required for file uploads, no WP alternative
        $valid = move_uploaded_file($filedata['tmp_name'], $this->destPath.$tmpname);
        if ($valid) {
          $file['tmp_name'] = $tmpname;
          // Set proper file permissions
          // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- Direct chmod needed for upload permissions
          chmod($this->destPath.$tmpname, 0644);
        } else {
          $file['errors'][] = $this->errorsText['upload'];
        }
      }
      
    }
    
    return $file;
  }
}

```
