'#ffffff', 'textColor' => '#111827', 'accentColor' => '#2b6f5d', 'buttonTextColor' => '#ffffff', ); public static function register() { add_action( 'admin_init', array( __CLASS__, 'handle_post' ) ); } public static function url( array $args = array() ) { return add_query_arg( array_merge( array( 'page' => Admin_Page::MENU_SLUG ), $args ), admin_url( 'admin.php' ) ); } /** The WordPress admin's language as 404 Emails knows it. */ public static function api_locale() { $locale = function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale(); return 0 === strpos( (string) $locale, 'cs' ) ? 'cs' : 'en'; } /** * The forms (newest first) with sign-up counts, and the workspace's * groups - cached for a minute, dropped after every change made here. * * @return array{ok:bool,forbidden:bool,items:array,groups:array,signups7:int} */ public static function load_forms() { $cached = get_transient( self::LIST_TRANSIENT ); if ( is_array( $cached ) ) { return $cached; } $empty = array( 'ok' => false, 'forbidden' => false, 'items' => array(), 'groups' => array(), 'signups7' => 0, ); if ( ! Settings::is_connected() ) { return $empty; } $response = Api_Client::from_settings()->manage_list_forms(); if ( ! $response->success ) { $empty['forbidden'] = 'forbidden_scope' === $response->error_category; return $empty; } $result = array( 'ok' => true, 'forbidden' => false, 'items' => isset( $response->data['items'] ) && is_array( $response->data['items'] ) ? $response->data['items'] : array(), 'groups' => isset( $response->data['groups'] ) && is_array( $response->data['groups'] ) ? $response->data['groups'] : array(), 'signups7' => isset( $response->data['signupsLast7Days'] ) ? (int) $response->data['signupsLast7Days'] : 0, ); set_transient( self::LIST_TRANSIENT, $result, MINUTE_IN_SECONDS ); return $result; } /** After any change: the lists here, in the block and in "every page" are refreshed. */ public static function forget_cached_forms() { delete_transient( self::LIST_TRANSIENT ); delete_transient( Admin_Page::FORMS_TRANSIENT ); } // --- Changes ---------------------------------------------------------------- public static function handle_post() { if ( ! isset( $_POST['emails404_forms_action'] ) || ! current_user_can( Admin_Page::CAPABILITY ) ) { return; } $action = sanitize_key( wp_unslash( $_POST['emails404_forms_action'] ) ); $client = Api_Client::from_settings(); if ( self::ACTION_CREATE === $action ) { check_admin_referer( self::ACTION_CREATE, 'emails404_nonce' ); $starter = isset( $_POST['emails404_starter'] ) ? sanitize_key( wp_unslash( $_POST['emails404_starter'] ) ) : ''; $name = isset( $_POST['emails404_name'] ) ? sanitize_text_field( wp_unslash( $_POST['emails404_name'] ) ) : ''; $body = '' !== $starter ? array( 'starterId' => $starter ) : array( 'name' => $name ); $result = $client->manage_create_form( $body, self::api_locale() ); self::forget_cached_forms(); if ( $result->success && ! empty( $result->data['form']['id'] ) ) { self::redirect( array( 'view' => 'edit', 'form' => (string) $result->data['form']['id'], 'emails404_notice' => 'created' ) ); } self::flash( 'error', self::error_message( $result, __( 'The form couldn\'t be created. Please try again.', '404-emails' ) ) ); self::redirect( array( 'view' => 'new' ) ); } if ( self::ACTION_SAVE === $action ) { check_admin_referer( self::ACTION_SAVE, 'emails404_nonce' ); $form_id = isset( $_POST['emails404_form_id'] ) ? sanitize_text_field( wp_unslash( $_POST['emails404_form_id'] ) ) : ''; if ( ! Embed::is_valid_form_id( $form_id ) ) { self::redirect( array() ); } $raw = isset( $_POST['emails404_form'] ) && is_array( $_POST['emails404_form'] ) ? wp_unslash( $_POST['emails404_form'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- each field is cleaned in fields_from_post(). $fields = self::fields_from_post( $raw ); $result = $client->manage_update_form( $form_id, $fields ); self::forget_cached_forms(); if ( $result->success ) { $then = isset( $_POST['emails404_then'] ) ? sanitize_key( wp_unslash( $_POST['emails404_then'] ) ) : ''; if ( 'publish' === $then ) { $published = $client->manage_set_published( $form_id, true ); if ( ! $published->success ) { self::flash( 'error', self::error_message( $published, __( 'Saved, but the form couldn\'t be published.', '404-emails' ) ) ); self::redirect( array( 'view' => 'edit', 'form' => $form_id ) ); } self::redirect( array( 'view' => 'edit', 'form' => $form_id, 'emails404_notice' => 'published' ) ); } self::redirect( array( 'view' => 'edit', 'form' => $form_id, 'emails404_notice' => 'saved' ) ); } self::flash( 'error', self::error_message( $result, __( 'The form couldn\'t be saved. Please try again.', '404-emails' ) ), $fields ); self::redirect( array( 'view' => 'edit', 'form' => $form_id ) ); } if ( self::ACTION_PUBLISH === $action ) { check_admin_referer( self::ACTION_PUBLISH, 'emails404_nonce' ); $form_id = isset( $_POST['emails404_form_id'] ) ? sanitize_text_field( wp_unslash( $_POST['emails404_form_id'] ) ) : ''; $publish = ! empty( $_POST['emails404_publish'] ); if ( Embed::is_valid_form_id( $form_id ) ) { $result = $client->manage_set_published( $form_id, $publish ); self::forget_cached_forms(); if ( ! $result->success ) { self::flash( 'error', self::error_message( $result, __( 'That didn\'t work. Please try again.', '404-emails' ) ) ); } else { self::flash( 'success', $publish ? __( 'Published. The form can now collect sign-ups on your site.', '404-emails' ) : __( 'Unpublished. The form no longer collects sign-ups.', '404-emails' ) ); } } $back = isset( $_POST['emails404_back'] ) && 'edit' === $_POST['emails404_back'] ? array( 'view' => 'edit', 'form' => $form_id ) : array(); self::redirect( $back ); } } /** * The editor's fields as the PATCH body 404 Emails expects. Every field * is sent, so what's saved is exactly what's on screen. * * @param array $raw The unslashed emails404_form[...] fields. * @return array */ public static function fields_from_post( array $raw ) { $text = function ( $key, $multiline = false ) use ( $raw ) { if ( ! isset( $raw[ $key ] ) || is_array( $raw[ $key ] ) ) { return ''; } return $multiline ? sanitize_textarea_field( (string) $raw[ $key ] ) : sanitize_text_field( (string) $raw[ $key ] ); }; $color = function ( $key ) use ( $raw ) { $value = isset( $raw[ $key ] ) && is_string( $raw[ $key ] ) ? strtolower( trim( $raw[ $key ] ) ) : ''; return 1 === preg_match( '/^#[0-9a-f]{6}$/', $value ) ? $value : null; }; $choice = function ( $key, array $allowed ) use ( $raw ) { return isset( $raw[ $key ] ) && in_array( $raw[ $key ], $allowed, true ) ? $raw[ $key ] : null; }; $group_ids = array(); if ( isset( $raw['group_ids'] ) && is_array( $raw['group_ids'] ) ) { foreach ( $raw['group_ids'] as $group_id ) { if ( is_string( $group_id ) && 1 === preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $group_id ) ) { $group_ids[] = $group_id; } } } $button = $text( 'button_label' ); return array( 'name' => $text( 'name' ), 'heading' => $text( 'heading' ), 'description' => $text( 'description', true ), 'askFirstName' => ! empty( $raw['ask_first_name'] ), 'askLastName' => ! empty( $raw['ask_last_name'] ), 'consentText' => $text( 'consent_text' ), 'successMessage' => $text( 'success_message', true ), 'doubleOptInEnabled' => ! empty( $raw['double_opt_in'] ), 'groupIds' => array_values( array_unique( $group_ids ) ), 'style' => array( 'buttonLabel' => '' !== $button ? $button : null, 'backgroundColor' => $color( 'background_color' ), 'textColor' => $color( 'text_color' ), 'accentColor' => $color( 'accent_color' ), 'buttonTextColor' => $color( 'button_text_color' ), 'borderRadius' => $choice( 'border_radius', array( 'square', 'subtle', 'rounded' ) ), 'width' => $choice( 'width', array( 'narrow', 'standard' ) ), ), ); } /** 404 Emails' own explanation when it gave one, else a general message. */ private static function error_message( Api_Response $result, $fallback ) { if ( 'forbidden_scope' === $result->error_category ) { return __( 'This site needs to be connected again before forms can be changed here.', '404-emails' ); } if ( in_array( $result->error_category, array( 'timeout', 'unreachable', 'server_error' ), true ) ) { return __( '404 Emails couldn\'t be reached. Please try again in a moment.', '404-emails' ); } $message = is_array( $result->data ) && isset( $result->data['error']['message'] ) ? (string) $result->data['error']['message'] : ''; if ( is_array( $result->data ) && ! empty( $result->data['error']['fields'] ) && is_array( $result->data['error']['fields'] ) ) { $labels = self::field_labels(); $parts = array(); foreach ( $result->data['error']['fields'] as $field => $problem ) { $parts[] = ( isset( $labels[ $field ] ) ? $labels[ $field ] : $field ) . ': ' . $problem; } $message = __( 'Some fields need fixing:', '404-emails' ) . ' ' . implode( '; ', $parts ); } return '' !== $message ? $message : $fallback; } private static function field_labels() { return array( 'name' => __( 'Form name', '404-emails' ), 'heading' => __( 'Heading', '404-emails' ), 'description' => __( 'Text under the heading', '404-emails' ), 'consentText' => __( 'Consent line', '404-emails' ), 'successMessage' => __( 'Thank-you message', '404-emails' ), 'style' => __( 'Design', '404-emails' ), ); } private static function flash( $type, $message, $values = null ) { set_transient( self::FLASH_TRANSIENT . get_current_user_id(), array( 'type' => $type, 'message' => $message, 'values' => $values, ), MINUTE_IN_SECONDS ); } private static function take_flash() { $key = self::FLASH_TRANSIENT . get_current_user_id(); $flash = get_transient( $key ); delete_transient( $key ); return is_array( $flash ) ? $flash : null; } private static function redirect( array $args ) { wp_safe_redirect( self::url( $args ) ); exit; } // --- Screens ---------------------------------------------------------------- public static function render() { if ( ! current_user_can( Admin_Page::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', '404-emails' ) ); } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- choosing what to show only. $view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : ''; $form_id = isset( $_GET['form'] ) ? sanitize_text_field( wp_unslash( $_GET['form'] ) ) : ''; // phpcs:enable $flash = self::take_flash(); ?>
; ?>)
%2$s
' . esc_html__( '404 Emails couldn\'t be reached. Please try again in a moment.', '404-emails' ) . '
' . esc_html( $workspace ) . '' ); } else { esc_html_e( 'Connected to 404 Emails', '404-emails' ); } ?> ·
|
|
" aria-label="" /> |
' . esc_html__( 'That form wasn\'t found - it may have been archived in 404 Emails.', '404-emails' ) . '
'; self::render_list( $forms ); return; } $style = isset( $form['style'] ) && is_array( $form['style'] ) ? $form['style'] : array(); $v = array( 'name' => $form['name'], 'heading' => (string) $form['heading'], 'description' => (string) $form['description'], 'askFirstName' => (bool) $form['askFirstName'], 'askLastName' => (bool) $form['askLastName'], 'consentText' => $form['consentText'], 'successMessage' => (string) $form['successMessage'], 'doubleOptInEnabled' => (bool) $form['doubleOptInEnabled'], 'groupIds' => isset( $form['groupIds'] ) ? (array) $form['groupIds'] : array(), 'style' => $style, ); if ( is_array( $values ) ) { $v = array_merge( $v, $values ); $v['style'] = array_merge( $style, array_filter( isset( $values['style'] ) ? (array) $values['style'] : array(), 'is_string' ) ); } $s = $v['style']; $color = function ( $key ) use ( $s ) { return isset( $s[ $key ] ) && is_string( $s[ $key ] ) ? $s[ $key ] : self::DEFAULT_COLORS[ $key ]; }; $published = 'PUBLISHED' === $form['status']; $button = isset( $s['buttonLabel'] ) && is_string( $s['buttonLabel'] ) && '' !== $s['buttonLabel'] ? $s['buttonLabel'] : __( 'Subscribe', '404-emails' ); ?> 0, 'subtle' => 6, 'rounded' => 12, ); $width = array( 'narrow' => 384, 'standard' => 448, ); $r = isset( $style['borderRadius'], $radius[ $style['borderRadius'] ] ) ? $radius[ $style['borderRadius'] ] : 6; $w = isset( $style['width'], $width[ $style['width'] ] ) ? $width[ $style['width'] ] : 448; return sprintf( 'background:%s;color:%s;border-radius:%dpx;max-width:%dpx;--emails404-accent:%s;--emails404-button-text:%s;', $get( 'backgroundColor' ), $get( 'textColor' ), $r, $w, $get( 'accentColor' ), $get( 'buttonTextColor' ) ); } private static function button_css( array $style ) { $accent = isset( $style['accentColor'] ) && is_string( $style['accentColor'] ) ? $style['accentColor'] : self::DEFAULT_COLORS['accentColor']; $text = isset( $style['buttonTextColor'] ) && is_string( $style['buttonTextColor'] ) ? $style['buttonTextColor'] : self::DEFAULT_COLORS['buttonTextColor']; return sprintf( 'background:%s;color:%s;', $accent, $text ); } }