'Withdraw from contract here',
'legal_confirm_text' => 'Confirm withdrawal',
);
/**
* Messages of the last save, field => message; read once per request.
*
* @var array|null
*/
private static $errors = null;
/**
* Hooks the section, the rows and the save.
*
* @return void
*/
public static function init() {
add_filter( Admin::SECTIONS_FILTER, array( __CLASS__, 'sections' ) );
add_filter( Admin::FIELDS_FILTER, array( __CLASS__, 'fields' ), 10, 2 );
add_filter( Admin::SAVE_FILTER, array( __CLASS__, 'save' ), 10, 4 );
}
/**
* Adds Legal wording right after Portal, while the statutory withdrawal is
* offered: the two texts belong to it. The values stay stored while it is not.
*
* @param array $sections Sections of the screen.
* @return array
*/
public static function sections( $sections ) {
$sections = (array) $sections;
if ( ! Routes::offered( 'withdrawal', new Settings() ) ) {
return $sections;
}
$sections[ self::SECTION ] = array(
'tab' => 'portal',
'label' => __( 'Legal wording', 'careback' ),
'position' => 15,
'render' => array( __CLASS__, 'render_legal' ),
);
return $sections;
}
/**
* Adds the returns policy page to Portal and the consent to Voluntary return.
*
* @param array $fields Rows of the section.
* @param string $section Section key.
* @return array
*/
public static function fields( $fields, $section = '' ) {
$fields = (array) $fields;
if ( 'portal' === $section ) {
$fields['returns_policy_page'] = array(
'position' => 30,
'render' => array( __CLASS__, 'render_policy_page' ),
);
} elseif ( 'route-return' === $section ) {
$fields['policy_consent'] = array(
'position' => 20,
'render' => array( __CLASS__, 'render_consent' ),
);
}
return $fields;
}
/**
* Legal wording: the two texts, each with its limit and our default as its
* placeholder.
*
* @param array $s Stored settings.
* @param string $screen Screen id.
* @param Settings $settings The screen's settings.
* @return void
*/
public static function render_legal( array $s, $screen = '', $settings = null ) {
unset( $screen, $settings );
$link = isset( $s['legal_link_text'] ) ? (string) $s['legal_link_text'] : '';
$confirm = isset( $s['legal_confirm_text'] ) ? (string) $s['legal_confirm_text'] : '';
?>
/>
'page',
'post_status' => array( 'publish', 'draft' ),
'posts_per_page' => 500, // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- a bounded list of the site's pages for one select, read on the settings screen only.
'orderby' => 'title',
'order' => 'ASC',
'no_found_rows' => true,
)
);
if ( $chosen && ! in_array( (int) $chosen->ID, wp_list_pluck( $pages, 'ID' ), true ) ) {
$pages[] = $chosen;
}
$selected = $chosen ? (int) $chosen->ID : 0;
?>
ID ) ) : ?>
ID );
switch ( $page->post_status ) {
case 'publish':
return $title;
case 'draft':
/* translators: %s: page title. */
return sprintf( __( '%s (draft)', 'careback' ), $title );
case 'private':
/* translators: %s: page title. */
return sprintf( __( '%s (private)', 'careback' ), $title );
default:
/* translators: %s: page title. */
return sprintf( __( '%s (not published)', 'careback' ), $title );
}
}
/**
* What the screen says about the chosen page when the form cannot link it,
* or when it asks for a password.
*
* @param \WP_Post|null $page The chosen page.
* @return void
*/
private static function page_state_note( $page ) {
if ( ! $page instanceof \WP_Post ) {
return;
}
$note = '';
if ( 'draft' === $page->post_status ) {
$note = __( 'This page is a draft, so the form does not show the link yet. Publish it, or choose another page.', 'careback' );
} elseif ( 'private' === $page->post_status ) {
$note = __( 'This page is private, so the form does not show the link. Publish it, or choose another page.', 'careback' );
} elseif ( 'publish' !== $page->post_status ) {
$note = __( 'This page is not published, so the form does not show the link yet. Publish it, or choose another page.', 'careback' );
} elseif ( post_password_required( $page ) || '' !== (string) $page->post_password ) {
$note = __( 'This page asks for a password.', 'careback' );
}
if ( '' !== $note ) {
echo '
' . esc_html( $note ) . '
';
}
}
/**
* Voluntary return → Returns policy: the consent switch, naming the page it
* uses and its state.
*
* @param array $s Stored settings.
* @param string $screen Screen id.
* @param Settings $settings The screen's settings.
* @return void
*/
public static function render_consent( array $s, $screen = '', $settings = null ) {
unset( $screen );
$settings = $settings instanceof Settings ? $settings : new Settings( $s );
$page = Returns_Policy::page( $settings );
$on = ! empty( $s[ Returns_Policy::CONSENT ] );
$error = self::error( Returns_Policy::CONSENT );
if ( $page ) {
$uses = sprintf(
/* translators: 1: title of the returns policy page, 2: its state, such as "published" or "draft". */
__( 'Uses the page set under Portal and form → Returns policy page: “%1$s” (%2$s). Never asked on a statutory withdrawal.', 'careback' ),
trim( wp_strip_all_tags( get_the_title( $page ) ) ),
self::state_word( (string) $page->post_status )
);
} else {
$uses = __( 'Uses the page set under Portal and form → Returns policy page; none is chosen yet. Never asked on a statutory withdrawal.', 'careback' );
}
?>
$max ) {
if ( ! isset( $posted[ $key ] ) || ! is_scalar( $posted[ $key ] ) ) {
continue;
}
$text = trim( sanitize_text_field( $posted[ $key ] ) );
if ( mb_strlen( $text ) > $max ) {
/* translators: %d: the most characters the field takes. */
$errors[ $key ] = sprintf( _n( 'Use up to %d character.', 'Use up to %d characters.', $max, 'careback' ), $max );
continue;
}
$own = Texts::defaults();
$word = (string) array_search( $key, Texts::LEGAL, true );
if ( self::LEGAL_ENGLISH[ $key ] === $text || ( isset( $own[ $word ] ) && $own[ $word ] === $text ) ) {
$text = '';
}
$values[ $key ] = $text;
}
// The returns policy page: a page of the site, or none.
$page = null;
if ( isset( $posted[ Returns_Policy::PAGE ] ) && is_scalar( $posted[ Returns_Policy::PAGE ] ) ) {
$id = absint( $posted[ Returns_Policy::PAGE ] );
$post = $id ? get_post( $id ) : null;
$id = $post instanceof \WP_Post && 'page' === $post->post_type && 'trash' !== $post->post_status ? $id : 0;
$page = $id ? get_post( $id ) : false;
$values[ Returns_Policy::PAGE ] = $id;
}
// The consent: only with a published page to agree to.
if ( isset( $posted['careback_policy_consent_shown'] ) ) {
$on = ! empty( $posted[ Returns_Policy::CONSENT ] );
if ( $on ) {
if ( null === $page ) {
$page = Returns_Policy::page( new Settings() );
}
if ( ! $page instanceof \WP_Post || 'publish' !== $page->post_status ) {
$on = false;
$errors[ Returns_Policy::CONSENT ] = __( 'Choose a published returns policy page first (Portal and form → Returns policy page).', 'careback' );
}
}
$values[ Returns_Policy::CONSENT ] = $on ? 1 : 0;
}
self::keep_errors( $errors );
return $values;
}
/**
* Keeps the messages of this save for the screen the save returns to.
*
* @param array $errors Field => message.
* @return void
*/
private static function keep_errors( array $errors ) {
$user = get_current_user_id();
if ( ! $user ) {
return;
}
if ( $errors ) {
set_transient( self::ERRORS . $user, $errors, 5 * MINUTE_IN_SECONDS );
} else {
delete_transient( self::ERRORS . $user );
}
self::$errors = null;
}
/**
* The message the last save left for a field, or ''. The messages are read
* once and then cleared, so they show on the screen right after the save.
*
* @param string $field Settings key.
* @return string
*/
public static function error( $field ) {
if ( null === self::$errors ) {
$user = get_current_user_id();
$stored = $user ? get_transient( self::ERRORS . $user ) : false;
self::$errors = is_array( $stored ) ? $stored : array();
if ( $user && false !== $stored ) {
delete_transient( self::ERRORS . $user );
}
}
return isset( self::$errors[ $field ] ) ? (string) self::$errors[ $field ] : '';
}
}