# double-opt-in/3.0.3/compatibility/OptInFrontend.class.php

Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification, version 3.0.3. 532 lines.

- Page: https://pluginprobe.com/plugins/double-opt-in/3.0.3/code/compatibility/OptInFrontend.class.php
- Raw: https://pluginprobe.com/plugins/double-opt-in/3.0.3/raw/compatibility/OptInFrontend.class.php
- Modified: 2024-08-13T13:52:36+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/double-opt-in/3.0.3/code/compatibility/OptInFrontend.class.php#L10-L20`.

```php
<?php

namespace forge12\contactform7\CF7DoubleOptIn;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

abstract class OptInFrontend {
	/**
	 * The Type of the OptIn Form System
	 *
	 * @var string
	 */
	protected string $type = '';

	/**
	 * Constructor for the class.
	 *
	 * This constructor registers the necessary actions to be performed,
	 * by hooking methods of the current class, for the following events:
	 * - 'f12_cf7_doubleoptin_before_send_default_mail'
	 * - 'f12_cf7_doubleoptin_after_send_default_mail'
	 * - 'f12_cf7_doubleoptin_trigger_default_mail'
	 * - 'shutdown'
	 *
	 * @return void
	 */
	public function __construct( string $type ) {
		$this->type = $type;
		add_action( 'f12_cf7_doubleoptin_before_send_default_mail', [ $this, 'beforeSendDefaultMail' ], 10, 1 );
		add_action( 'f12_cf7_doubleoptin_after_send_default_mail', [ $this, 'afterSendDefaultMail' ], 10, 1 );
		add_action( 'f12_cf7_doubleoptin_trigger_default_mail', [ $this, 'sendDefaultMail' ], 10, 1 );
		add_action( 'shutdown', [ $this, 'removeFiles' ] );
		add_action( 'init', [ $this, 'validateOptIn' ] );
		add_filter( 'f12_cf7_doubleoptin_get_recipient_' . $this->type, [ $this, 'getRecipient' ], 10, 3 );
	}

	/**
	 * Retrieves the recipient for a given recipient name, form parameters, and post parameters.
	 *
	 * @param string $recipient     The name of the recipient to retrieve.
	 * @param array  $formParameter The array of form parameters.
	 * @param array  $postParameter The array of post parameters.
	 *
	 * @return string The recipient for the given parameters.
	 */
	abstract public function getRecipient( string $recipient, array $formParameter, array $postParameter ): string;

	/**
	 * Sends a default mail for the given OptIn object.
	 *
	 * This method is declared as abstract, meaning that it must be implemented
	 * by any child class that extends the current class. The method takes
	 * a single parameter, $OptIn, of type OptIn. This parameter represents
	 * the OptIn object for which the default mail needs to be sent.
	 *
	 * This method should be overridden by child classes to define the specific
	 * logic for sending the default mail for the given OptIn object.
	 *
	 * Note that the implementation details of this method vary depending on the
	 * specific subclass. Therefore, the implementation code is not provided here.
	 *
	 * @param OptIn $OptIn The OptIn object for which the default mail needs to be sent.
	 *
	 * @return void
	 */
	abstract public function sendDefaultMail( OptIn $OptIn ): void;

	/**
	 * This method is used to perform necessary actions before sending the default mail.
	 *
	 * This method removes the filter for the forge12 spam captcha if the class
	 * '\forge12\contactform7\CF7Captcha\TimerValidatorCF7' exists. It removes the filters 'wpcf7_spam' for the methods
	 * '\forge12\contactform7\CF7Captcha::isSpam' and
	 * '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam', and also removes the action 'wpcf7_mail_sent' for the
	 * method
	 * '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP', if these filters and action exist.
	 *
	 * Additionally, this method removes the filter 'wpcf7_spam' for the method 'wpcf7_recaptcha_verify_response' with
	 * a priority of 9.
	 *
	 * @return void
	 */
	public function beforeSendDefaultMail() {
		// Remove the filter for the forge12 spam captcha
		if ( class_exists( '\forge12\contactform7\CF7Captcha\TimerValidatorCF7' ) ) {
			remove_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha::isSpam' );
			remove_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam' );
			remove_action( 'wpcf7_mail_sent', '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP' );
		}

		// Remove the filter for the google repatcha validation
		remove_filter( 'wpcf7_spam', 'wpcf7_recaptcha_verify_response', 9 );
	}

	/**
	 * Performs actions after sending the default mail.
	 *
	 * In this method, two filters and an action are added to the WordPress hooks system.
	 *
	 * The first filter is added with the hook name 'wpcf7_spam' and the callback
	 * function is set to 'wpcf7_recaptcha_verify_response'. The priority is set to 9
	 * and the number of accepted arguments for the callback function is 2.
	 * This filter is added only if the function 'wpcf7_recaptcha_verifiy_response'
	 * exists.
	 *
	 * The second filter is added with the hook name 'wpcf7_spam' and the callback
	 * function is set to '\forge12\contactform7\CF7Captcha\TimerValidatorCF7::isSpam'.
	 * The priority is set to 100 and the number of accepted arguments for the callback
	 * function is 2. This filter is added only if the class '\forge12\contactform7\CF7Captcha\TimerValidatorCF7'
	 * exists.
	 *
	 * The third filter is added with the hook name 'wpcf7_spam' and the callback
	 * function is set to '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam'.
	 * The priority is set to 100 and the number of accepted arguments for the callback
	 * function is 2. This filter is added only if the class '\forge12\contactform7\CF7Captcha\CF7IPLog'
	 * exists.
	 *
	 * An action is added with the hook name 'wpcf7_mail_sent' and the callback function
	 * is set to '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP'. The priority is set
	 * to 100 and the number of accepted arguments for the callback function is 1.
	 * This action is added only if the class '\forge12\contactform7\CF7Captcha\CF7IPLog' exists.
	 *
	 * @return void
	 */
	public function afterSendDefaultMail() {
		// re add the filter to ensure for all other forms the recaptcha is used
		if ( function_exists( 'wpcf7_recaptcha_verifiy_response' ) ) {
			add_filter( 'wpcf7_spam', 'wpcf7_recaptcha_verify_response', 9, 2 );
		}

		// re add the filter for the forge12 spam captcha
		if ( class_exists( '\forge12\contactform7\CF7Captcha\TimerValidatorCF7' ) ) {
			add_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha\TimerValidatorCF7::isSpam', 100, 2 );
			add_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam', 100, 2 );
			add_action( 'wpcf7_mail_sent', '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP', 100, 1 );
		}
	}

	/**
	 * Updates the opt-in status by hash.
	 *
	 * @param string     $hash  The opt-in hash.
	 * @param int        $value The opt-in value to set.
	 * @param OptIn|null $OptIn The opt-in object. Optional.
	 *
	 * @return int Returns 0 if the opt-in is already confirmed, 1 if the opt-in is successfully updated.
	 */
	protected function updateOptInByHash( string $hash, int $value, ?OptIn $OptIn = null ): int {
		/**
		 * Init the optIn is not defined yet.
		 */
		$OptIn = $OptIn ?? OptIn::get_by_hash( $hash );

		/**
		 * If the OptIn is already confirmed - we skip the update.
		 */
		if ( $OptIn->is_confirmed() ) {
			do_action( 'f12_cf7_doubleoptin_already_confirmed', $hash, $OptIn );

			return 0;
		}

		/**
		 * Hook
		 */
		do_action( 'f12_cf7_doubleoptin_before_confirm', $hash, $OptIn );

		$OptIn->set_doubleoptin( $value );
		$OptIn->set_updatetime( time() );
		$OptIn->set_ipaddr_confirmation( IPHelper::getIPAdress() );

		$result = $OptIn->save();

		if ( $result ) {
			do_action( 'f12_cf7_doubleoptin_after_confirm', $hash, $OptIn );
		}

		return (int) $result;
	}

	/**
	 * Add additional placeholder like time, date, subject
	 *
	 * @formatter:off
	 *
	 * @param string $body The content containing the placeholder that will be replaced.
	 * @param OptIn $OptIn The OptIn Object.
	 *
	 * @param array $parameter {
	 *      @type string $formUrl The URL where the form is displayed.
	 *      @type string $subject The Subject of the Form
	 * }
	 *                       #
	 * @formatter:on
	 */
	protected function addPlaceholders( string $body, OptIn $OptIn, array $parameter ): string {
		# set the default timezone
		$timezone = get_option( 'timezone_string' );

		# set fallback timezone
		if ( empty( $timezone ) ) {
			$timezone = 'Europe/Berlin';
		}

		date_default_timezone_set( $timezone );
		$placeholder = array(
			'doubleoptin_form_url'     => $parameter['formUrl'],
			'doubleoptin_form_subject' => $parameter['subject'],
			'doubleoptin_form_date'    => date( get_option( 'date_format' ) ),
			'doubleoptin_form_time'    => date( get_option( 'time_format' ) ),
			'doubleoptin_form_email'   => get_option( 'admin_email' ),
			'doubleoptinlink'          => $OptIn->get_link_optin( $parameter ),
			'doubleoptoutlink'         => $OptIn->get_link_optout()
		);

		foreach ( $placeholder as $key => $value ) {
			$body = str_replace( '[' . $key . ']', $value, $body );
		}

		return $body;
	}

	/**
	 * Add Stylesheets
	 */
	public function validateOptIn(): bool {
		/**
		 * Skip if the hash has not been submitted.
		 */
		if ( ! isset( $_GET['optin'] ) ) {
			return false;
		}

		/**
		 * Get the Hash
		 */
		$hash = sanitize_text_field( $_GET['optin'] );

		/**
		 * Load the OptIn
		 */
		$OptIn = OptIn::get_by_hash( $hash );

		/**
		 * Skip if the OptIn does not exist
		 */
		if ( null == $OptIn ) {
			return false;
		}

		/**
		 * Skip if the OptIn is not from Type cf7.
		 */
		if ( ! $OptIn->isType( $this->type ) ) {
			return false;
		}

		/**
		 * Skip if the OptIn has been confirmed already or could not be updated.
		 */
		if ( $this->updateOptInByHash( $hash, 1 ) <= 0 ) {
			return false;
		}

		/**
		 * Enable / Disable default mail.
		 *
		 * Filter to allow developer to enable / disable the default mail.
		 *
		 * @param bool $status Enable (true) or disable (false) the default mail.
		 * @param int  $postId The ID of the Post / Form.
		 *
		 * @since 2.3.3
		 */
		if ( ! apply_filters( 'f12_cf7_doubleoptin_send_default_mail', true, $OptIn->get_cf_form_id() ) ) {
			return false;
		}

		/**
		 * Hook triggers before the default mail will be send.
		 *
		 * Action to allow developers to do custom actions before the default mail will be triggered.
		 *
		 * @param OptIn $OptIn
		 *
		 * @since 2.3.3
		 */
		do_action( 'f12_cf7_doubleoptin_before_send_default_mail', $OptIn );

		/**
		 * Hook triggers the default mail
		 *
		 * Action to allow developers to do custom actions before the default mail will be triggered.
		 *
		 * @param OptIn $OptIn
		 *
		 * @since 2.3.3
		 */
		do_action( 'f12_cf7_doubleoptin_trigger_default_mail', $OptIn );

		/**
		 * Hook triggers after the default mail has been sent
		 *
		 * Action to allow developers to do custom actions after the default mail has been triggered.
		 *
		 * @param OptIn $OptIn
		 *
		 * @since 2.3.3
		 */
		do_action( 'f12_cf7_doubleoptin_after_send_default_mail', $OptIn );

		return true;
	}


	/**
	 * Store the files
	 *
	 * @param array $inFiles
	 *
	 * @return array
	 */
	private function maybeStoreFiles( array $inFiles ): array {
		$outFiles = array();

		if ( empty( $files ) ) {
			return $outFiles;
		}

		foreach ( $files as $key => $subfiles ) {
			foreach ( $subfiles as $file ) {
				$newFile = $this->copyAndRenameFile( $file );
				if ( $newFile ) {
					$outFiles[] = $newFile;
				}
			}
		}

		return $outFiles;
	}

	/**
	 * Copy and rename a file
	 *
	 * @param string $file The path to the file to copy and rename
	 *
	 * @return string|null The path to the copied and renamed file, or null if the copy operation failed
	 */
	private function copyAndRenameFile( string $file ): ?string {
		$newFile                          = explode( '/', $file );
		$name                             = $newFile[ count( $newFile ) - 1 ];
		$name                             = time() . '_' . $name;
		$newFile[ count( $newFile ) - 1 ] = $name;
		$newFile                          = implode( "/", $newFile );
		if ( copy( $file, $newFile ) ) {
			return $newFile;
		}

		return null;
	}

	/**
	 * Create the OptIn
	 *
	 * @param int    $formId    The identifier of the form
	 * @param string $formHtml  The HTML code of the form
	 * @param array  $parameter The Post Parameter of the form.
	 * @param array  $files     The Files attached to the form.
	 *
	 * @return OptIn|null
	 */
	protected function maybeCreateOptIn( int $formId, string $formHtml, array $parameter, array $files = array() ): ?OptIn {
		/**
		 * Maybe copy the files to store them while waiting for the optin confirmation
		 */
		$files = $this->maybeStoreFiles( $files );

		/**
		 * Filter to manipulate the content parameter before storing them in the database
		 *
		 * @param array $parameter
		 *
		 * @since 2.3.3
		 */
		$parameter = \apply_filters( 'f12_cf7_doubleoptin_add_request_parameter', $parameter );

		/**
		 * Get the global settings for the formular.
		 */
		$formParameter = CF7DoubleOptIn::getInstance()->getParameter( $formId );

		/**
		 * Filter to fetch the recipient before creating the optin object.
		 *
		 * @param string $recipient     The Default recipient
		 * @param array  $formParameter @see CF7DoubleOptIn::getParameter() for details
		 * @param array  $parameter     The Post Parameter submitted by the visitor.
		 *
		 * @since 2.3.3
		 */
		$recipient = \apply_filters( 'f12_cf7_doubleoptin_get_recipient_' . $this->type, '', $formParameter, $parameter );

		/**
		 * Skip if no Mail has been found.
		 */
		if ( empty( $recipient ) ) {
			return null;
		}

		/**
		 * Set the Properties of the OptIn Object
		 */
		$properties = array(
			'cf_form_id'      => $formId,
			'doubleoptin'     => 0,
			'createtime'      => time(),
			'content'         => maybe_serialize( $parameter ),
			'files'           => maybe_serialize( $files ),
			'ipaddr_register' => IPHelper::getIPAdress(),
			'category'        => (int) $formParameter['category'],
			'form'            => $formHtml,
			'email'           => $recipient
		);

		$OptIn = new OptIn( $properties );

		if ( $OptIn->save() ) {
			return $OptIn;
		}

		return null;
	}

	/**
	 * Validate if the optin is enabled.
	 */
	protected function isOptinEnabled( int $formId ): bool {
		// Disable optin sending if the optin flag is set.
		if ( isset( $_GET['optin'] ) ) {
			return false;
		}

		$parameter = CF7DoubleOptIn::getInstance()->getParameter( $formId );

		if ( (int) $parameter['enable'] != 1 ) {
			return false;
		}

		// Check the custom condition
		if ( isset( $parameter['conditions'] ) ) {
			$condition = sanitize_text_field( $parameter['conditions'] );

			if ( ( $condition != 'disable' && $condition !== 'disabled' ) && ( ! isset( $_POST[ $condition ] ) || empty( $_POST[ $condition ] ) ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Removes files associated with the optin parameter.
	 *
	 * This method checks if the optin parameter is set and
	 * loads the OptIn object based on the hash value. If
	 * the OptIn does not exist or no files are found,
	 * the method will return. Otherwise, it will iterate
	 * through the files and delete each one.
	 *
	 * @return void
	 */
	public function removeFiles(): void {
		/**
		 * Skip if the optin parameter is not set.
		 */
		if ( ! isset( $_GET['optin'] ) ) {
			return;
		}

		$hash = esc_sql( $_GET['optin'] );

		/**
		 * Load the OptIn
		 */
		$OptIn = OptIn::get_by_hash( $hash );

		/**
		 * Skip if the OptIn does not exist
		 */
		if ( null == $OptIn ) {
			return;
		}

		/**
		 * Load all files
		 */
		$files = maybe_unserialize( $OptIn->get_files() );

		/**
		 * Skip if no files found
		 */
		if ( empty( $files ) ) {
			return;
		}

		foreach ( $files as $file ) {
			/**
			 * Skip if empty
			 */
			if ( ! empty( $file ) ) {
				continue;
			}

			/**
			 * Skip if no file found
			 */
			if ( ! is_file( $file ) ) {
				continue;
			}

			/**
			 * Delete the file
			 */
			if ( ! unlink( $file ) ) {
				error_log( "Could not delete file " . $file . "!" );
			}
		}
	}

}
```
