File "FrmProFieldFile.php"

Full path: /home/bud/public_html/swamp/wp-admin/wp-content/plugins/formidable-pro/classes/models/fields/FrmProFieldFile.php
File size: 18.15 KB
MIME-type: text/x-php
Charset: utf-8

<?php

if ( ! defined( 'ABSPATH' ) ) {
	die( 'You are not allowed to call this page directly.' );
}

/**
 * @since 3.0
 */
class FrmProFieldFile extends FrmFieldType {

	/**
	 * @var string
	 * @since 3.0
	 */
	protected $type = 'file';

	private $entry_id;

	protected function include_form_builder_file() {
		return FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/field-' . $this->type . '.php';
	}

	protected function field_settings_for_type() {
		$settings = array(
			'invalid'       => true,
			'read_only'     => true,
		);

		FrmProFieldsHelper::fill_default_field_display( $settings );
		return $settings;
	}

	/**
	 * @return array
	 */
	protected function extra_field_opts() {
		return array(
			'ftypes'     => array(),
			'attach'     => false,
			'delete'     => false,
			'restrict'   => 0,
			'resize'     => false,
			'new_size'   => '600',
			'resize_dir' => 'width',
			'drop_msg'   => __( 'Drop a file here or click to upload', 'formidable-pro' ),
			'choose_msg' => __( 'Choose File', 'formidable-pro' ),
		);
	}

	/**
	 * @since 4.0
	 *
	 * @param array $args
	 * @return void
	 */
	public function show_primary_options( $args ) {
		$field   = $args['field'];
		$form_id = absint( $field['form_id'] );
		$mimes   = $this->get_mime_options( $field );

		$public_files_tooltip = self::maybe_get_public_files_tooltip( $form_id );
		if ( $public_files_tooltip ) {
			$settings_url = admin_url( 'admin.php?page=formidable&frm_action=settings&id=' . $form_id . '&t=permissions_settings_settings' );
		}

		include FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/back-end/file-options.php';

		parent::show_primary_options( $args );
	}

	/**
	 * @param int $form_id
	 * @return string
	 */
	private static function maybe_get_public_files_tooltip( $form_id ) {
		$form_is_protected = FrmProFileField::get_option( $form_id, 'protect_files', 0 );

		if ( $form_is_protected ) {
			$protect_files_roles = FrmProFileField::get_option( $form_id, 'protect_files_role', 0 );
			$uploads_are_public  = ! $protect_files_roles || in_array( '', $protect_files_roles, true );
		} else {
			$uploads_are_public = true;
		}

		if ( ! $uploads_are_public ) {
			return false;
		}

		$form_is_indexed = ! FrmProFileField::get_option( $form_id, 'noindex_files', 0 );
		return self::get_public_files_tooltip( $form_is_protected, $form_is_indexed );
	}

	/**
	 * @param bool $form_is_protected
	 * @param bool $form_is_indexed
	 * @return string
	 */
	private static function get_public_files_tooltip( $form_is_protected, $form_is_indexed ) {
		$tooltip = sprintf(
			/* translators: %s a conditional additional string (and could be indexed by search engines) if indexing is not turned off. */
			__( 'Files uploaded with this field can be viewed by anyone with access to a link%s.', 'formidable-pro' ),
			$form_is_indexed ? ' and could be indexed by search engines' : ''
		);

		$recommendation = $form_is_protected ? __( 'changing who can access the file', 'formidable-pro' ) : __( 'enabling file protection', 'formidable-pro' );
		if ( $form_is_indexed ) {
			$recommendation .= __( ' and turning off indexing', 'formidable-pro' );
		}

		/* translators: %s recommendation. Can be a few things (changing who can access the file, enabling file protection and turning off indexing) */
		$recommendation = sprintf( __( ' If this is a concern, we recommend %s.' ), $recommendation );

		return $tooltip . $recommendation;
	}

	/**
	 * @since 3.06.01
	 */
	public function translatable_strings() {
		$strings   = parent::translatable_strings();
		$strings[] = 'drop_msg';
		$strings[] = 'choose_msg';
		return $strings;
	}

	/**
	 * @since 3.01.01
	 */
	private function get_mime_options( $field ) {
		$mimes = get_allowed_mime_types();
		ksort( $mimes );
		$selected_mimes = $field['ftypes'];

		$ordered = array();
		foreach ( (array) $selected_mimes as $mime ) {
			$key = array_search( $mime, $mimes );
			if ( $key !== false ) {
				$ordered[ $key ] = $mimes[ $key ];
				unset( $mimes[ $key ] );
			}
		}

		$mimes = $ordered + $mimes;
		return $mimes;
	}

	public function validate( $args ) {
		return FrmProFileField::no_js_validate( array(), $this->field, $args['value'], $args );
	}

	/**
	 * Upload new files, delete removed files
	 *
	 * @since 3.0
	 * @param array|string $value (the posted value)
	 * @param array $atts
	 *
	 * @return array|string $value
	 */
	public function get_value_to_save( $value, $atts ) {
		// Upload files and get new meta value for file upload fields
		$value = FrmProFileField::prepare_file_upload_meta( $value, $this->field, $atts['entry_id'] );

		if ( is_array( $value ) ) {
			$value = array_map( 'intval', array_filter( $value ) );
		}

		return $value;
	}

	protected function prepare_display_value( $value, $atts ) {
		if ( ! is_numeric( $value ) && ! is_array( $value ) ) {
			return $value;
		}

		$showing_image = ( isset( $atts['html'] ) && $atts['html'] ) || ( isset( $atts['show_image'] ) && $atts['show_image'] );
		$default_sep = $showing_image ? ' ' : ', ';
		$atts['sep'] = isset( $atts['sep'] ) ? $atts['sep'] : $default_sep;

		$this->get_file_html_from_atts( $atts, $value );

		$return_array = isset( $atts['return_array'] ) && $atts['return_array'];
		if ( is_array( $value ) && ! $return_array ) {
			$value = implode( $atts['sep'], $value );

			$show_text_only = isset( $atts['show'] ) && 'id' === $atts['show'];
			if ( $showing_image && ! $show_text_only ) {
				$value = '<div class="frm_file_container">' . $value . '</div>';
			}
		}

		return $value;
	}

	protected function fill_default_atts( &$atts ) {
		// don't add default separator
	}

	/**
	 * Get the HTML for a file upload field depending on the $atts
	 *
	 * @since 3.0
	 *
	 * @param array $atts
	 * @param string|array|int $replace_with
	 */
	private function get_file_html_from_atts( $atts, &$replace_with ) {
		$show_id = isset( $atts['show'] ) && $atts['show'] == 'id';
		if ( ! $show_id && ! empty( $replace_with ) ) {
			//size options are thumbnail, medium, large, or full
			$size = $this->set_size( $atts );

			$new_atts = $this->set_file_atts( $atts );

			$this->modify_atts_for_reverse_compatibility( $atts, $new_atts );

			$ids = (array) $replace_with;
			$replace_with = $this->get_displayed_file_html( $ids, $size, $new_atts );
		}

		if ( is_array( $replace_with ) ) {
			$replace_with = array_filter( $replace_with );
		}
	}

	/**
	 * @param array $atts
	 * @return array
	 */
	public function set_file_atts( $atts ) {
		$new_atts = array(
			'show_filename' => ! empty( $atts['show_filename'] ),
			'show_image'    => ! empty( $atts['show_image'] ),
			'add_link'      => ! empty( $atts['add_link'] ),
			'new_tab'       => ! empty( $atts['new_tab'] ),
		);
		return array_merge( $atts, $new_atts );
	}

	/**
	 * Check the 'size' first, and fallback to 'show' for reverse compatibility
	 * Set the default size for showing images
	 *
	 * @since 3.0
	 */
	public function set_size( $atts ) {
		if ( isset( $atts['size'] ) ) {
			$size = $atts['size'];
		} elseif ( isset( $atts['show'] ) ) {
			$size = $atts['show'];
		} elseif ( isset( $atts['source'] ) && $atts['source'] == 'entry_formatter' ) {
			$size = 'full';
		} else {
			$size = 'thumbnail';
		}
		return $size;
	}

	/**
	 * Maintain reverse compatibility for html=1, links=1, and show=label
	 *
	 * @since 3.0
	 *
	 * @param array $atts
	 * @param array $new_atts
	 */
	private function modify_atts_for_reverse_compatibility( $atts, &$new_atts ) {
		// For show=label
		if ( ! $new_atts['show_filename'] && isset( $atts['show'] ) && $atts['show'] == 'label' ) {
			$new_atts['show_filename'] = true;
		}

		// For html=1
		$inc_html = ( isset( $atts['html'] ) && $atts['html'] );
		if ( $inc_html && ! $new_atts['show_image'] ) {

			if ( $new_atts['show_filename'] ) {
				// For show_filename with html=1
				$new_atts['show_image'] = false;
				$new_atts['add_link'] = true;
			} else {
				// html=1 without show_filename=1
				$new_atts['show_image'] = true;
				$new_atts['add_link_for_non_image'] = true;
			}
		}

		// For links=1
		$show_links = ( isset( $atts['links'] ) && $atts['links'] );
		if ( $show_links && ! $new_atts['add_link'] ) {
			$new_atts['add_link'] = true;
		}
	}

	/**
	 * Get HTML for a file upload field depending on atts and file type
	 *
	 * @since 3.0
	 *
	 * @param array $ids
	 * @param string $size
	 * @param array $atts
	 * @return array|string
	 */
	public function get_displayed_file_html( $ids, $size = 'thumbnail', $atts = array() ) {
		$defaults = array(
			'class'                  => '',
			'show_filename'          => false,
			'show_image'             => false,
			'add_link'               => false,
			'add_link_for_non_image' => false,
		);
		$atts = wp_parse_args( $atts, $defaults );
		$atts['size'] = $size;

		$img_html = array();
		foreach ( (array) $ids as $id ) {
			if ( ! is_numeric( $id ) ) {
				if ( ! empty( $id ) ) {
					// If a custom value was set with a hook, don't remove it
					$img_html[] = $id;
				}
				continue;
			}

			$img = $this->get_file_display( $id, $atts );
			if ( $img ) {
				$img_html[] = $img;
			}
		}
		unset( $img, $id );

		if ( count( $img_html ) == 1 ) {
			$img_html = reset( $img_html );
		}

		return $img_html;
	}

	/**
	 * Get the HTML to display an upload in a File Upload field
	 *
	 * @since 3.0
	 *
	 * @param int $id
	 * @param array $atts
	 * @return string $html
	 */
	public function get_file_display( $id, $atts ) {
		if ( ! $id || ! $this->file_exists_by_id( $id ) ) {
			return '';
		}

		$is_image = wp_attachment_is_image( $id );
		$url      = FrmProFileField::get_file_url( $id, $is_image ? $atts['size'] : false );

		if ( ! FrmProFileField::user_has_permission( $id ) ) {
			$html = $this->get_display_html_for_inaccessible_file( $id, $atts );
		} else {
			$html = $atts['show_image'] ? wp_get_attachment_image( $id, $atts['size'], ! $is_image ) : '';

			// If show_filename=1 is included
			if ( $atts['show_filename'] ) {
				$label = $this->get_single_file_name( $id );
				if ( $atts['show_image'] ) {
					$html .= ' <span id="frm_media_' . absint( $id ) . '" class="frm_upload_label">' . esc_html( $label ) . '</span>';
				} else {
					$html .= esc_html( $label );
				}
			}

			// If neither show_image or show_filename are included, get file URL
			if ( ! $html ) {
				$html = $url;
			}

			// If add_link=1 is included
			if ( $atts['add_link'] || ( ! $is_image && $atts['add_link_for_non_image'] ) ) {
				$href   = $is_image ? FrmProFileField::get_file_url( $id ) : $url;
				$target = ! empty( $atts['new_tab'] ) ? ' target="_blank"' : '';
				$html   = '<a href="' . esc_attr( $href ) . '" class="frm_file_link"' . $target . '>' . $html . '</a>';
			}

			if ( ! empty( $atts['class'] ) ) {
				$html = str_replace( ' class="', ' class="' . esc_attr( $atts['class'] . ' ' ), $html );
			}
		}

		$atts['media_id'] = $id;
		return apply_filters( 'frm_image_html_array', $html, $atts );
	}

	/**
	 * @since 5.4.1
	 *
	 * @param int   $id File id.
	 * @param array $atts
	 * @return string
	 */
	private function get_display_html_for_inaccessible_file( $id, $atts ) {
		if ( ! FrmProFileField::file_is_temporary( $id ) || ! $atts['show_image'] ) {
			$frm_settings = FrmAppHelper::get_settings();
			return esc_html( $frm_settings->admin_permission );
		}

		$file = FrmProFileField::get_mock_file( $id );
		$html = '<img src="' . esc_url( FrmProFileField::get_safe_file_icon( $file ) ) . '" alt="' . esc_attr( $file['name'] ) . '" />';
		return $html;
	}

	/**
	 * Check if a file exists on the site
	 *
	 * @since 3.0
	 * @param $id
	 * @return bool true if a post exists with the specified id that has the 'attachment' post_type
	 */
	private function file_exists_by_id( $id ) {
		global $wpdb;
		return 'attachment' === FrmDb::get_var( $wpdb->posts, array( 'ID' => $id ), 'post_type' );
	}

	/**
	 * Get the file name for a single media ID
	 *
	 * @since 3.0
	 *
	 * @param int $id
	 * @return boolean|string $filename
	 */
	private function get_single_file_name( $id ) {
		$filepath = get_attached_file( $id, true );
		if ( ! is_string( $filepath ) ) {
			return false;
		}
		return basename( $filepath );
	}

	public function front_field_input( $args, $shortcode_atts ) {
		$field = $this->field;
		$html_id = $args['html_id'];
		$field_name = $args['field_name'];

		$file_name = str_replace( 'item_meta[' . $field['id'] . ']', 'file' . $field['id'], $field_name );
		if ( $file_name == $field_name ) {
			// this is a repeating field
			$repeat_meta = explode( '-', $html_id );
			$repeat_meta = end( $repeat_meta );
			$file_name = 'file' . $field['id'] . '-' . $repeat_meta;
			unset( $repeat_meta );
		}

		$aria = '';
		$this->add_aria_description( $args, $aria );

		ob_start();
		include( FrmProAppHelper::plugin_path() . '/classes/views/frmpro-fields/front-end/file.php' );
		$input_html = ob_get_contents();
		ob_end_clean();

		return $input_html;
	}

	/**
	 * Add extra classes on front-end input
	 *
	 * @since 3.01.04
	 */
	protected function get_input_class() {
		$class = '';
		if ( FrmField::is_option_true( $this->field, 'multiple' ) ) {
			$class = 'frm_multiple_file';
		}

		// Hide the "No files selected" text if files are selected
		if ( ! FrmField::is_option_empty( $this->field, 'value' ) ) {
			$class .= ' frm_transparent';
		}

		return $class;
	}

	protected function prepare_import_value( $value, $atts ) {
		$value = $this->get_file_id( $value );
		// If single file upload field, reset array
		if ( ! FrmField::is_option_true( $this->field, 'multiple' ) ) {
			$value = reset( $value );
		}
		return $value;
	}

	public function get_file_id( $value ) {
		global $wpdb;

		if ( ! is_array($value ) ) {
			$value = explode(',', $value);
		}

		foreach ( (array) $value as $pos => $m ) {
			$m = trim( $m );
			if ( empty( $m ) ) {
				continue;
			}

			if ( ! is_numeric( $m ) ) {
				//get the ID from the URL if on this site
				$m = FrmDb::get_var( $wpdb->posts, array( 'guid' => $m ), 'ID' );
			}

			if ( ! is_numeric( $m ) ) {
				unset( $value[ $pos ] );
			} else {
				$value[ $pos ] = $m;
			}

			unset( $pos, $m );
		}

		return $value;
	}

	/**
	 * After sanitizing our data, ensure it comes back in the same format it went in as
	 *
	 * @param mixed $value
	 * @param array $new_value
	 * @param bool $was_array
	 */
	private function adjust_value( &$value, $new_value, $was_array ) {
		$value = $new_value;
		if ( ! $was_array ) {
			if ( $value ) {
				$value = reset( $value );
			} else {
				$value = '';
			}
		}
	}

	/**
	 * Filter out attachments to only include temporary formidable files
	 *
	 * @since 4.0.04
	 * @param mixed $value
	 */
	public function sanitize_value( &$value ) {
		$form              = FrmForm::getOne( $this->field->form_id );
		$form_is_protected = FrmProFileField::get_option( $form->parent_form_id ? $form->parent_form_id : $form->id, 'protect_files', 0 );

		$stop_sanitizing = ! $form_is_protected;
		$stop_sanitizing = apply_filters( 'frm_stop_file_switching', $stop_sanitizing, array( 'form_id' => $this->field->form_id, 'field_id' => $this->field->id ) );

		if ( $stop_sanitizing ) {
			return;
		}

		$this->entry_id  = FrmAppHelper::get_param( 'id', '', 'post', 'absint' );
		$was_array       = is_array( $value );
		$assigned_ids    = array();
		$unsafe_file_ids = array_filter( array_map( 'absint', (array) $value ) );

		if ( ! $unsafe_file_ids ) {
			$this->adjust_value( $value, array(), $was_array );
			return;
		}

		if ( $this->entry_id ) {
			$assigned_ids              = $this->get_assigned_file_ids( $unsafe_file_ids );
			$all_ids_have_been_matched = count( $assigned_ids ) === count( $unsafe_file_ids );
			if ( $all_ids_have_been_matched ) {
				$this->adjust_value( $value, $assigned_ids, $was_array );
				return;
			}
		}

		$default_ids   = $this->get_default_file_ids( $unsafe_file_ids );
		$temporary_ids = $this->get_temporary_ids( $unsafe_file_ids );

		$this->adjust_value( $value, array_merge( $assigned_ids, $default_ids, $temporary_ids ), $was_array );
	}

	/**
	 * Given a set of file ids, check field default_valies for ids that actually match this field
	 *
	 * @param array $unsafe_file_ids
	 * @return array
	 */
	private function get_default_file_ids( $unsafe_file_ids ) {
		$default_value = maybe_unserialize( $this->field->default_value );
		if ( is_string( $default_value ) ) {
			$default_value = do_shortcode( $default_value );
		}
		$default_ids = array_filter( array_map( 'absint', (array) $default_value ) );
		if ( $default_ids ) {
			$default_ids = array_intersect( $default_ids, $unsafe_file_ids );
		}
		return $default_ids;
	}

	/**
	 * Given a set of file ids, check frm_item_metas for ids that actually match this field
	 *
	 * @param array $unsafe_file_ids
	 * @return array
	 */
	private function get_assigned_file_ids( $unsafe_file_ids ) {
		$item_ids   = FrmDb::get_col( 'frm_items', array( 'parent_item_id' => $this->entry_id ) );
		$item_ids[] = $this->entry_id;

		$metas = FrmProEntryMeta::get_all_metas_for_field(
			$this->field,
			array(
				'entry_ids' => $item_ids,
				'is_draft'  => 'both',
			)
		);

		// Flatten meta, convert to integers, with unique values, and only include intersecting "unsafe_file_ids" values.
		return array_values(
			array_reduce(
				$metas,
				function( $total, $meta ) use ( $unsafe_file_ids ) {
					if ( is_numeric( $meta ) ) {
						$meta_file_ids = array( (int) $meta );
					} elseif ( is_array( $meta ) ) {
						$meta_file_ids = array_map( 'absint', $meta );
					} else {
						return $total;
					}

					foreach ( $meta_file_ids as $file_id ) {
						if ( in_array( $file_id, $unsafe_file_ids, true ) ) {
							$total[ $file_id ] = $file_id;
						}
					}

					return $total;
				},
				array()
			)
		);
	}

	/**
	 * Given a set of file ids, check wp_postmeta for ids that actually match this field
	 *
	 * @param array $unsafe_file_ids
	 * @return array
	 */
	private function get_temporary_ids( $unsafe_file_ids ) {
		global $wpdb;

		// only include posts with a user id match
		// if a user is not logged in, user id is 0 (and will only match with guest files)
		$where         = array(
			'ID'          => $unsafe_file_ids,
			'post_author' => get_current_user_id(),
		);
		$temporary_ids = FrmDb::get_col( $wpdb->posts, $where, 'ID' );

		if ( ! $temporary_ids ) {
			return array();
		}

		return FrmDb::get_col(
			$wpdb->postmeta,
			array(
				'post_id'    => $temporary_ids,
				'meta_key'   => '_frm_temporary',
				'meta_value' => $this->field->id,
			),
			'post_id'
		);
	}
}