form = $form;
$this->rendered_honeypot = false;
$this->rendered_notices = false;
$this->rendered_hidden_fields = false;
$this->custom_field_templates = $this->init_custom_field_templates();
}
/**
* Return value of a protected class property.
*
* @since 1.5.2
*
* @param string $prop The class property.
* @return mixed
*/
public function __get( $prop ) {
if ( ! property_exists( $this, $prop ) ) {
return null;
}
return $this->$prop;
}
/**
* Register a custom field template.
*
* @since 1.5.0
*
* @param string $field_type The type of field we are registering this template for.
* @param string $class The template class name.
* @param string $path The path to the template.
* @return void
*/
public function register_custom_field_template( $field_type, $class, $path ) { // phpcs:ignore
$this->custom_field_templates[ $field_type ] = array(
'class' => $class,
'path' => $path,
);
}
/**
* Render a form.
*
* @since 1.5.0
*
* @return void
*/
public function render() {
$form_fields = $this->form->get_fields();
$this->enqueue_script();
if ( ( isset( $form_fields['donation_fields'] ) && 'bottom' !== charitable_get_option( 'donation_form_notices_display', 'top' ) ) || ! isset( $form_fields['donation_fields'] ) ) {
$this->render_notices();
}
$this->render_honeypot();
$this->render_hidden_fields();
$this->render_fields();
if ( isset( $form_fields['donation_fields'] ) && 'bottom' === charitable_get_option( 'donation_form_notices_display', 'top' ) ) {
$this->render_notices();
}
}
/**
* Enqueue the forms script.
*
* @since 1.6.44
*
* @return void
*/
public function enqueue_script() {
if ( ! wp_script_is( 'charitable-forms', 'enqueued' ) ) {
wp_enqueue_script( 'charitable-forms' );
}
}
/**
* Render notices before the form.
*
* @since 1.5.0
*
* @return void
*/
public function render_notices() {
charitable_template_from_session(
'form-fields/notices.php',
array(
'notices' => charitable_get_notices()->get_notices(),
),
'notices'
);
$this->rendered_notices = true;
/* Clear all notices. */
charitable_get_notices()->clear();
}
/**
* Render the honeypot fields.
*
* @since 1.5.0
*
* @return void
*/
public function render_honeypot() {
printf( '', esc_attr( $this->form->get_form_identifier() ) );
$this->rendered_honeypot = true;
}
/**
* Render a form's hidden fields.
*
* @since 1.5.0
*
* @return boolean True if any fields were rendered. False otherwise.
*/
public function render_hidden_fields() {
$fields = $this->form->get_hidden_fields();
if ( ! is_array( $fields ) || empty( $fields ) ) {
return false;
}
array_walk( $fields, array( $this, 'render_hidden_field' ) );
$this->rendered_hidden_fields = true;
}
/**
* Render all of a form's fields.
*
* @since 1.8.3.4
* @since 1.8.3.5
*
* @param array $fields Optional. A set of fields to display. If not set,
* we will look for fields in the form's `get_fields()`
* method (if it has one).
* @return boolean True if any fields were rendered. False otherwsie.
*/
public function render_fields( $fields = array() ) {
if ( empty( $fields ) ) {
$fields = $this->form->get_fields();
/* If still missing fields, return false and throw an error. */
if ( empty( $fields ) ) {
charitable_get_deprecated()->doing_it_wrong(
__METHOD__,
__( 'There are no fields to render.', 'charitable' ),
'1.5.0'
);
return false;
}
}
$i = 1;
$form_template = charitable_get_option( 'donation_form_template', false );
$opening_div = false;
foreach ( $fields as $key => $field ) {
// insert div group for certain fields.
if ( $i === 1 && ( 'minimal' === $form_template && ( 'title' === strtolower( $key ) || 'first_name' === strtolower( $key ) ) ) ) {
echo '
';
} elseif ( 'minimal' === $form_template && 'first_name' === strtolower( $key ) ) {
echo '
';
$opening_div = true;
}
$this->render_field(
$field,
$key,
array(
'index' => $i,
)
);
if ( 'minimal' === $form_template && 'last_name' === strtolower( $key ) && $opening_div ) {
echo '
';
}
$i += $this->increment_index( $field, $key, $i );
}
}
/**
* Render a hidden field.
*
* @since 1.5.0
*
* @param string|array $value Field value.
* @param string $key Field key.
* @return void
*/
public function render_hidden_field( $value, $key ) {
$attrs = '';
if ( is_array( $value ) ) {
if ( ! array_key_exists( 'value', $value ) ) {
return;
}
$args = $value;
$value = $args['value'];
unset( $args['value'] );
$attrs = charitable_get_arbitrary_attributes( array( 'attrs' => $args ) );
}
printf( '
', esc_attr( $key ), esc_attr( $value ), $attrs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Render a specific form field.
*
* @since 1.5.0
*
* @param array $field Field definition.
* @param string $key Field key.
* @param array $args Mixed array of arguments.
* @return boolean False if the field was not rendered. True otherwise.
*/
public function render_field( $field, $key, $args = array() ) {
if ( ! array_key_exists( 'type', $field ) ) {
return false;
}
/* Get our index and namespace, with defaults if they're not provided. */
$namespace = array_key_exists( 'namespace', $args ) ? $args['namespace'] : null;
$index = array_key_exists( 'index', $args ) ? $args['index'] : 0;
/* Set up some form attributes. */
$field['key'] = $this->get_field_name( $key, $namespace, $index );
$field['attrs'] = array_key_exists( 'attrs', $field ) ? $field['attrs'] : array();
/**
* Do something before the field is rendered.
*
* @since 1.0.0
* @since 1.5.4 Added $from_template parameter to avoid backwards compatibility issues.
*
* @param array $field Field definition.
* @param string $key The field's key.
* @param Charitable_Form $form The Charitable_Form object.
* @param int $index The current index.
* @param null $namespace Unused. Namespace for the form field's name attribute.
* @param false $from_template Whether the method was called from a template file.
*/
do_action( 'charitable_form_field', $field, $key, $this->form, $index, null, false );
/* Get the template and make sure it's valid. */
$template = $this->get_field_template( $field, $index );
if ( ! $template ) {
return false;
}
$template->set_view_args(
array(
'form' => $this->form,
'field' => $field,
'classes' => $this->get_field_classes( $field, $index ),
)
);
$template->render();
return true;
}
/**
* Return the key for a particular field.
*
* @since 1.5.0
*
* @param string $key Field key.
* @param string|null $namespace Namespace for the form field's name attribute.
* @param int $index The current index.
* @return string
*/
protected function get_field_name( $key, $namespace = null, $index = 0 ) { // phpcs:ignore
$name = $key;
if ( ! is_null( $namespace ) ) {
$name = $namespace . '[' . $name . ']';
}
/**
* Filter the name attribute to be used for the field.
*
* @since 1.0.0
*
* @param string $name The name attribute.
* @param string $key The field's key.
* @param string|null $namespace Namespace for the field's attribute.
* @param Charitable_Form $form The Charitable_Form object.
* @param int $index The current index.
*/
return apply_filters( 'charitable_form_field_key', $name, $key, $namespace, $this->form, $index );
}
/**
* Return a field template.
*
* @since 1.5.0
*
* @param array $field Field definition.
* @param int $index The current index.
* @return Charitable_Template|false Returns a template if the template file exists. If it doesn't returns false.
*/
public function get_field_template( $field, $index ) {
$template = $this->get_custom_template( $field['type'] );
/**
* Filter the template to be used for the form.
*
* Any callback hooking into this filter should return a `Charitable_Template`
* instance. Anything else will be ignored.
*
* @since 1.0.0
*
* @param false|Charitable_Template $template False by default.
* @param array $field Field definition.
* @param Charitable_Form $form The Charitable_Form object.
* @param int $index The current index.
*/
$template = apply_filters( 'charitable_form_field_template', $template, $field, $this->form, $index );
/* Fall back to default Charitable_Template if no template returned or if template was not object of 'Charitable_Template' class. */
if ( ! $this->is_valid_template( $template ) ) {
$template = new Charitable_Template( $this->get_template_name( $field ), false );
}
if ( ! $template->template_file_exists() ) {
return false;
}
return $template;
}
/**
* Return a custom template for a particular field.
*
* @since 1.5.0
*
* @param string $field_type The type of field.
* @return false|Charitable_Template False if there is no matching custom template.
*/
public function get_custom_template( $field_type ) {
if ( ! array_key_exists( $field_type, $this->custom_field_templates ) ) {
return false;
}
$class = $this->custom_field_templates[ $field_type ]['class'];
$path = $this->custom_field_templates[ $field_type ]['path'];
/* Final sanity check to make sure the template class exists. */
if ( ! class_exists( $class ) ) {
return false;
}
return new $class( $path, false );
}
/**
* Return the template name used for this field.
*
* @since 1.5.0
*
* @param array $field Field definition.
* @return string
*/
public function get_template_name( $field ) {
if ( $this->use_default_field_template( $field['type'] ) ) {
$template_name = 'form-fields/default.php';
} else {
$template_name = 'form-fields/' . $field['type'] . '.php';
}
/**
* Filter the template name.
*
* @since 1.0.0
*
* @param string $template_name Default template name.
*/
return apply_filters( 'charitable_form_field_template_name', $template_name );
}
/**
* Checks whether a template is valid.
*
* @since 1.5.0
*
* @param mixed $template Template we're checking.
* @return boolean
*/
public function is_valid_template( $template ) {
return is_object( $template ) && is_a( $template, 'Charitable_Template' );
}
/**
* Whether the given field type can use the default field template.
*
* @since 1.5.0
*
* @param string $field_type Type of field.
* @return boolean
*/
protected function use_default_field_template( $field_type ) {
/**
* Filter the list of field types that use the default template.
*
* @since 1.0.0
*
* @param string[] $types Field types.
*/
$default_field_types = apply_filters(
'charitable_default_template_field_types',
array(
'text',
'email',
'password',
'date',
)
);
return in_array( $field_type, $default_field_types );
}
/**
* Return classes that will be applied to the field.
*
* @since 1.5.0
*
* @param array $field Field definition.
* @param int $index Field index.
* @return string
*/
public function get_field_classes( $field, $index = 0 ) {
if ( 'hidden' == $field['type'] ) {
return;
}
$classes = $this->get_field_type_classes( $field['type'] );
if ( array_key_exists( 'class', $field ) ) {
$classes[] = $field['class'];
}
if ( array_key_exists( 'required', $field ) && $field['required'] ) {
$classes[] = 'required-field';
}
if ( array_key_exists( 'fullwidth', $field ) && $field['fullwidth'] ) {
$classes[] = 'fullwidth';
} elseif ( $index > 0 ) {
$classes[] = $index % 2 ? 'odd' : 'even';
}
/**
* Filter the array of classes before it is returned as a string.
*
* @since 1.0.0
*
* @param array $classes List of classes.
* @param array $field Field definition.
* @param int $index The field index.
*/
$classes = apply_filters( 'charitable_form_field_classes', $classes, $field, $index );
return implode( ' ', $classes );
}
/**
* Return array of classes based on the field type.
*
* @since 1.5.0
*
* @param string $type Type of field.
* @return string[]
*/
public function get_field_type_classes( $type ) {
$classes = array();
switch ( $type ) {
case 'paragraph':
$classes[] = 'charitable-form-content';
break;
case 'fieldset':
case 'donation-amount-wrapper':
$classes[] = 'charitable-fieldset';
break;
default:
$classes[] = 'charitable-form-field';
$classes[] = 'charitable-form-field-' . $type;
}
return $classes;
}
/**
* Set how much the index should be incremented by.
*
* @since 1.5.0
*
* @param array $field The field definition.
* @param string $key The key of the current field. May be empty.
* @param int|null $index The current index. May be null.
* @return int
*/
public function increment_index( $field, $key = '', $index = null ) {
if ( ! $this->should_increment( $field ) ) {
return 0;
}
/**
* Set the default increment.
*
* @since 1.0.0
*
* @param int $increment The default increment amount. Defaults to 1.
* @param array $field The field definition.
* @param string $key The key of the current field.
* @param Charitable_Form $form The form we are displaying.
* @param int $index The current index.
*/
return apply_filters( 'charitable_form_field_increment', 1, $field, $key, $this->form, $index );
}
/**
* Whether the index should be incremented.
*
* @since 1.5.0
*
* @param array $field Field definition.
* @return boolean
*/
protected function should_increment( $field ) {
if ( in_array( $field['type'], array( 'hidden', 'paragraph', 'fieldset' ) ) ) {
return false;
}
if ( array_key_exists( 'fullwidth', $field ) && $field['fullwidth'] ) {
return false;
}
return true;
}
/**
* Set up custom field templates.
*
* @since 1.5.0
*
* @return array
*/
protected function init_custom_field_templates() {
/**
* Filter the custom field templates.
*
* @since 1.5.0
*/
$templates = apply_filters(
'charitable_public_form_view_custom_field_templates',
array(
'donation-amount-wrapper' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/donation-amount-wrapper.php',
),
'donation-amount' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/donation-amount.php',
),
'donor-fields' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/donor-fields.php',
),
'details-fields' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/details-fields.php',
),
'meta-fields' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/meta-fields.php',
),
'gateway-fields' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/gateway-fields.php',
),
'cc-expiration' => array(
'class' => 'Charitable_Template',
'path' => 'donation-form/cc-expiration.php',
),
)
);
return array_filter( $templates, array( $this, 'sanitize_custom_field_template' ) );
}
/**
* Filter custom field templates to make sure they all have a class and path.
*
* @since 1.5.0
*
* @param array $template The registered template.
* @return boolean
*/
protected function sanitize_custom_field_template( $template ) {
return is_array( $template ) && array_key_exists( 'path', $template ) && array_key_exists( 'class', $template );
}
}
endif;