, the ID, a data attribute or the * classes of the element around its fields. */ const PATTERN = '/^(?:form(?:#[A-Za-z][\w-]{0,60}|\[name="[\w\-\[\]\.\/:]{1,80}"\]|\[data-[a-z][a-z0-9-]{0,44}(?:="[\w\-\[\]\.\/:]{1,80}")?\]|\[action\$="[\w\-\[\]\.\/:]{1,80}"\]|(?:\.[A-Za-z][\w-]{2,40}){1,2}|(?::has\(\[name="[\w\-\[\]\.\/:]{1,80}"\]\)){1,3})|(?:div|section|aside|article|fieldset|li|dialog)(?:#[A-Za-z][\w-]{0,60}|\[data-[a-z][a-z0-9-]{0,44}(?:="[\w\-\[\]\.\/:]{1,80}")?\]|(?:\.[A-Za-z][\w-]{2,40}){1,2}))$/'; /** * Stored forms. * * @return array */ public static function all() { $saved = get_option( self::OPTION, array() ); return is_array( $saved ) ? array_filter( $saved, 'is_array' ) : array(); } /** * Whether a selector is one the tracker builds. * * @param string $selector Selector. * @return bool */ public static function is_valid( $selector ) { return (bool) preg_match( self::PATTERN, (string) $selector ); } /** * Records a form a visitor sent. * * @param string $selector Selector of the form. * @param string $name Name of the form. * @param string $url Page it was sent on. * @param string $title Title of that page. * @param bool $newsletter Whether it is a sign-up with only an email address. * @return bool Whether submissions of the form are saved as leads. */ public static function record( $selector, $name, $url, $title, $newsletter ) { if ( ! self::is_valid( $selector ) ) { return false; } $selection = Form_Selection::get(); $picked = in_array( $selector, wp_list_pluck( $selection['custom'], 'selector' ), true ); $declined = in_array( $selector, $selection['declined'], true ); $forms = self::all(); $name = mb_substr( sanitize_text_field( (string) $name ), 0, 80 ); $known = $forms[ $selector ] ?? null; $forms[ $selector ] = array( 'name' => '' !== $name ? $name : (string) ( $known['name'] ?? '' ), 'url' => Admin\Form_Scan::is_own_url( (string) $url ) ? esc_url_raw( strtok( (string) $url, '?#' ) ) : (string) ( $known['url'] ?? '' ), 'title' => mb_substr( sanitize_text_field( (string) $title ), 0, 120 ), 'newsletter' => (bool) $newsletter, 'auto' => (bool) ( $known['auto'] ?? false ), 'count' => (int) ( $known['count'] ?? 0 ) + 1, 'last' => time(), ); $auto = ! $picked && ! $declined && ! $newsletter && Settings::get( 'found_forms' ) && count( array_filter( wp_list_pluck( $forms, 'auto' ) ) ) < self::MAX_AUTO; if ( $auto ) { $forms[ $selector ]['auto'] = true; } self::save( $forms ); if ( $auto ) { Form_Selection::add( $selector, $forms[ $selector ]['name'] ); } return $picked || $auto; } /** * Saves the forms, the most recently sent first. * * @param array $forms Forms by selector. */ private static function save( array $forms ) { uasort( $forms, static function ( $a, $b ) { return (int) $b['last'] <=> (int) $a['last']; } ); update_option( self::OPTION, array_slice( $forms, 0, self::MAX_FORMS, true ), false ); } }