self::MODE_AUTO, 'attach_pdf' => true, 'share_link' => false, 'link_days' => self::DEFAULT_LINK_DAYS, 'incomplete' => self::INCOMPLETE_HOLD, 'insights' => true, 'link_key' => '', ); } /** * Cleans stored or posted options over a base. * * @param array $raw Stored or posted values. * @param array|null $base Defaults for missing keys; defaults() when null. * @return array */ public static function normalize( array $raw, $base = null ) { $base = is_array( $base ) ? $base : self::defaults(); $get = static function ( $key ) use ( $raw, $base ) { return array_key_exists( $key, $raw ) ? $raw[ $key ] : $base[ $key ]; }; $days = is_scalar( $get( 'link_days' ) ) ? (int) $get( 'link_days' ) : 0; $key = is_string( $get( 'link_key' ) ) ? $get( 'link_key' ) : ''; return array( 'mode' => self::MODE_REVIEW === $get( 'mode' ) ? self::MODE_REVIEW : self::MODE_AUTO, 'attach_pdf' => is_scalar( $get( 'attach_pdf' ) ) && (bool) $get( 'attach_pdf' ), 'share_link' => is_scalar( $get( 'share_link' ) ) && (bool) $get( 'share_link' ), 'link_days' => $days >= self::MIN_LINK_DAYS ? min( self::MAX_LINK_DAYS, $days ) : self::DEFAULT_LINK_DAYS, 'incomplete' => self::INCOMPLETE_HOLD === $get( 'incomplete' ) ? self::INCOMPLETE_HOLD : self::INCOMPLETE_SEND, 'insights' => is_scalar( $get( 'insights' ) ) && (bool) $get( 'insights' ), 'link_key' => preg_match( '/^[A-Za-z0-9]{16,64}$/', $key ) ? $key : '', ); } /** * Reads posted checkbox-style values: an unticked box is absent from the form. * * @param array $posted Posted delivery fields. * @param array $current Current options (keeps the link key). * @return array */ public static function from_form( array $posted, array $current ) { return self::normalize( array( 'mode' => isset( $posted['mode'] ) ? $posted['mode'] : self::MODE_AUTO, 'attach_pdf' => ! empty( $posted['attach_pdf'] ) && is_scalar( $posted['attach_pdf'] ), 'share_link' => ! empty( $posted['share_link'] ) && is_scalar( $posted['share_link'] ), 'link_days' => isset( $posted['link_days'] ) ? $posted['link_days'] : self::DEFAULT_LINK_DAYS, 'incomplete' => isset( $posted['incomplete'] ) ? $posted['incomplete'] : self::INCOMPLETE_HOLD, 'insights' => ! empty( $posted['insights'] ) && is_scalar( $posted['insights'] ), 'link_key' => isset( $current['link_key'] ) ? $current['link_key'] : '', ), $current ); } /** * A fresh secret used to sign this site's share links. Changing it revokes every * link issued before. * * @return string */ public static function new_link_key() { return wp_generate_password( 32, false, false ); } /** * The signing key after a settings save. Turning links on (again), a missing key * or "Revoke links" starts a new key, so links sent before can never come back. * * @param array $delivery Normalised options from from_form(). * @param bool $links_were_on Whether links were on before this save. * @param bool $revoke Whether "Revoke links" was ticked. * @return array */ public static function with_link_key( array $delivery, $links_were_on, $revoke ) { if ( $delivery['share_link'] && ( ! $links_were_on || '' === $delivery['link_key'] || $revoke ) ) { $delivery['link_key'] = self::new_link_key(); } elseif ( $revoke ) { $delivery['link_key'] = ''; } return $delivery; } }