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 . "!" ); } } } }