*/ private static array $anchors = array(); /** * Render a form. * * @param array{list?: string, view?: string, name?: bool, title?: string, button?: string} $args List key (empty * for the first list), * view, name field, * heading and button * text. */ public static function render( array $args ): string { $key = sanitize_key( (string) ( $args['list'] ?? '' ) ); $mailing_list = Settings::get( $key ); if ( null === $mailing_list ) { return self::editor_hint( '' === $key ? __( 'GruVer Subscribe: no mailing list has been set up yet.', 'gruver-subscribe' ) /* translators: %s: key of the list */ : sprintf( __( 'GruVer Subscribe: there is no mailing list with the key ā€œ%sā€.', 'gruver-subscribe' ), $key ) ); } wp_enqueue_style( self::STYLE ); $key = $mailing_list->key; $view = self::view( (string) ( $args['view'] ?? '' ) ); $failed = Handler::current( $key ); $outcome = null !== $failed ? $failed->outcome : self::outcome( $key ); $typed = array( 'email' => null !== $failed ? $failed->email : '', 'name' => null !== $failed ? $failed->name : '', ); $id = wp_unique_id( 'gruver-subscribe-' ); $title = trim( (string) ( $args['title'] ?? '' ) ); $button = trim( (string) ( $args['button'] ?? '' ) ); $anchor = self::anchor( $key ); if ( isset( self::$anchors[ $anchor ] ) ) { $anchor = $id; } self::$anchors[ $anchor ] = true; $html = '
'; if ( '' !== $title ) { // A heading for the large form; bar and widget sit inside other content and get a plain line. $html .= self::VIEW_FORM === $view ? '

' . esc_html( $title ) . '

' : '

' . esc_html( $title ) . '

'; } if ( '' !== $outcome ) { self::no_cache(); $html .= '

' . esc_html( self::message( $outcome ) ) . '

'; } if ( Handler::SENT === $outcome ) { return $html . '
'; } // The bar shows the labels as placeholders; the labels stay for screen readers. $bar = self::VIEW_BAR === $view; $label_class = $bar ? ' class="gruver-subscribe__sr"' : ''; $fields = ''; if ( ! empty( $args['name'] ) ) { $fields .= '

' . '' . '' . '

'; } $fields .= '

' . '' . '' . '

' . Honeypot::html( $id . '-website' ); $consent = ''; $submit = '

'; // The form goes back to this page; after an error the browser lands on the form again. return $html . '
' . self::hidden( Handler::FIELD_FORM, '1' ) . self::hidden( Handler::FIELD_LIST, $key ) . self::hidden( TimeTrap::FIELD, TimeTrap::stamp( $key, time() ) ) . self::hidden( Handler::FIELD_CONSENT_HASH, Consent::hash( $mailing_list ) ) // Only for logged-in users: page caches skip their pages, visitors would get stale nonces. . ( is_user_logged_in() ? self::hidden( Handler::FIELD_NONCE, wp_create_nonce( Handler::NONCE_ACTION ) ) : '' ) . $fields // In the bar the consent sits below the row with field and button; the order of the markup follows the eye. . ( $bar ? $submit . $consent : $consent . $submit ) . '
'; } /** * A known view, the large form otherwise. * * @param string $view Requested view. */ public static function view( string $view ): string { return in_array( $view, self::VIEWS, true ) ? $view : self::VIEW_FORM; } /** * Id of the form of a list, also the fragment of the redirect. * * @param string $key List key. */ public static function anchor( string $key ): string { return '' === $key ? 'gruver-subscribe' : 'gruver-subscribe-' . $key; } /** * Text for an outcome of the handler. * * @param string $outcome One of Handler::OUTCOMES. */ public static function message( string $outcome ): string { return match ( $outcome ) { Handler::SENT => __( 'Almost done – please confirm the email we have just sent to your inbox.', 'gruver-subscribe' ), Handler::INVALID => __( 'Please check the email address.', 'gruver-subscribe' ), Handler::CONSENT => __( 'Please tick the box to agree.', 'gruver-subscribe' ), Handler::CONSENT_CHANGED => __( 'The consent text has changed. Please read it and tick the box again.', 'gruver-subscribe' ), Handler::TOO_FAST => __( 'That was very quick. Please wait a moment and send the form again.', 'gruver-subscribe' ), Handler::LIMITED => __( 'Too many attempts. Please try again in a few minutes.', 'gruver-subscribe' ), Handler::EXPIRED => __( 'The form has expired. Please send it again.', 'gruver-subscribe' ), default => __( 'Signing up is not possible right now. Please try again later.', 'gruver-subscribe' ), }; } /** * Outcome from the redirect after a success, if it belongs to this list. * * @param string $key List key. */ private static function outcome( string $key ): string { // Only the success arrives by redirect; errors are shown from the POST itself. return self::query( Handler::QUERY_LIST ) === $key && Handler::SENT === self::query( Handler::QUERY_STATUS ) ? Handler::SENT : ''; } /** * A page with a message depends on the visitor; page caches must not keep it. DONOTCACHEPAGE is the constant the * common cache plugins check before they store a page. */ private static function no_cache(): void { if ( ! defined( 'DONOTCACHEPAGE' ) ) { define( 'DONOTCACHEPAGE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- constant of the cache plugins, not of this plugin. } } /** * A value from the query string. * * @param string $name Parameter. */ private static function query( string $name ): string { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only selects which message to show after the redirect. return isset( $_GET[ $name ] ) && is_string( $_GET[ $name ] ) ? sanitize_key( wp_unslash( $_GET[ $name ] ) ) : ''; } /** * Path and query of the current page, without the parameters of an earlier redirect. */ private static function current_path(): string { $uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/'; return remove_query_arg( array( Handler::QUERY_STATUS, Handler::QUERY_LIST ), '' !== $uri ? $uri : '/' ); } /** * A hidden field. * * @param string $name Name. * @param string $value Value. */ private static function hidden( string $name, string $value ): string { return ''; } /** * A hint for people who edit the website; visitors see nothing. * * @param string $message Message. */ private static function editor_hint( string $message ): string { if ( ! current_user_can( 'edit_posts' ) ) { return ''; } $link = current_user_can( 'manage_options' ) ? ' ' . esc_html__( 'Open settings', 'gruver-subscribe' ) . '' : ''; return '

' . esc_html( $message ) . $link . '

'; } }