is_ready() ) {
return $this->note();
}
++$this->count;
return $this->form( self::ID_PREFIX . $this->count, $config ) . $this->assets();
}
/**
* Where the form would be, for someone who can set it up. Page caches skip logged-in users,
* so this is never served to a visitor from a cache.
*
* @return string
*/
private function note() {
if ( ! current_user_can( ContactFormScreen::CAPABILITY ) ) {
return '';
}
return sprintf(
'
',
esc_url( ContactFormScreen::url() ),
esc_html__( 'Contact form: set up spam protection to show this form', 'adminkeep' )
);
}
/**
* The form. Send is disabled until the front-end script has set up the spam check, so
* without that script nothing can be sent; the noscript line says why.
*
* @param string $id The form's id.
* @param ContactFormConfig $config Configuration in force.
* @return string
*/
private function form( $id, ContactFormConfig $config ) {
$html = '';
return $html;
}
/**
* One labelled field with the place its own error message goes.
*
* @param string $id The form's id.
* @param string $name Field name.
* @param string $label Label text.
* @param string $control The control, with %1$s for its id and %2$s for its message's id.
* @return string
*/
private function field( $id, $name, $label, $control ) {
$field_id = $id . '-' . $name;
$error_id = $field_id . '-error';
return '
';
}
/**
* What the front-end script needs, as data attributes of the form. Nothing per user or per
* request: see the class comment. The nonce is added only when it is the chosen check. Read back with dataset, so text is escaped for the
* attribute only and an owner's "Use <info@…>" reaches the page as typed.
*
* @param ContactFormConfig $config Configuration in force.
* @return string Attributes, with a leading space.
*/
private function data_attributes( ContactFormConfig $config ) {
$data = array(
'route' => esc_url_raw( rest_url( RestController::NAMESPACE_V1 . '/contact-form' ) ),
'provider' => (string) $config->get( 'provider' ),
'site-key' => $config->site_key(),
'action' => CaptchaVerifier::ACTION,
'i18n-sending' => __( 'Sending…', 'adminkeep' ),
'i18n-error' => $config->error_text(),
'i18n-check' => __( 'Please complete the check above.', 'adminkeep' ),
'i18n-blocked' => __( 'The spam check could not load. Please allow it, or contact us another way.', 'adminkeep' ),
);
if ( 'nonce' === $data['provider'] ) {
$data['nonce'] = ContactFormNonce::create();
}
$html = '';
foreach ( $data as $name => $value ) {
// Not esc_attr(): it leaves an existing entity alone, so an owner's typed "<" would
// read back as "<". Encoding everything makes dataset return exactly what was typed.
$html .= ' data-' . $name . '="' . htmlspecialchars( (string) $value, ENT_QUOTES, 'UTF-8' ) . '"';
}
return $html;
}
/**
* The form's style and script, for the end of every render.
*
* @return string
*/
private function assets() {
if ( null === self::$assets ) {
self::$assets = array(
'css' => $this->read( 'assets/css/contact-form.css' ),
'js' => $this->read( 'assets/js/contact-form.js' ),
);
}
// A stylesheet of ours cannot hold a closing tag; stripped in case a file ever does.
$css = str_ireplace( '' . $css . '' . wp_get_inline_script_tag( self::$assets['js'] );
}
/**
* A file of the plugin's own, or '' if it is missing.
*
* @param string $path Path inside the plugin.
* @return string
*/
private function read( $path ) {
if ( ! is_readable( ADMINKEEP_DIR . $path ) ) {
return '';
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- A local file shipped with the plugin.
$source = (string) file_get_contents( ADMINKEEP_DIR . $path );
// No empty line may survive: wpautop() (a legacy Text widget, older themes and builders run
// the shortcode's output through it) turns a blank line inside a script or style into
// a closing and an opening paragraph tag, which breaks both.
return trim( (string) preg_replace( "/\n[ \t\r]*(?:\n[ \t\r]*)+/", "\n", str_replace( "\r\n", "\n", $source ) ) );
}
}