diff --git a/classes/WP_Form.php b/classes/WP_Form.php index dc9fc07..53131a0 100644 --- a/classes/WP_Form.php +++ b/classes/WP_Form.php @@ -74,6 +74,30 @@ public function remove_element( $key ) { return $this; } + public function get_elements_as_array() { + $result = (array)$this->get_elements_as_array_prepare( $this->elements ); + return $result; + } + + public function get_elements_as_array_prepare( array $elements ) { + $elements = array_values( $elements ); + $result = array(); + + $number_of = count( $elements ); + for( $i = 0; $i < $number_of; $i++ ) { + if( $elements[$i] instanceof WP_Form_Aggregate ) { + $result[$i]['elements'] = $this->get_elements_as_array_prepare( $elements[$i]->get_children() ); + } + + $result[$i]['priority'] = $elements[$i]->get_priority(); + $result[$i]['label'] = $elements[$i]->get_label(); + $result[$i]['description'] = $elements[$i]->get_description(); + $result[$i]['attributes'] = $elements[$i]->get_all_attributes(); + $result[$i]['errors'] = $elements[$i]->get_errors(); + } + return $result; + } + /** * @param $key * @return null|WP_Form_Component diff --git a/classes/WP_Form_Listener.php b/classes/WP_Form_Listener.php index eeb5468..d87999e 100644 --- a/classes/WP_Form_Listener.php +++ b/classes/WP_Form_Listener.php @@ -6,6 +6,8 @@ class WP_Form_Listener { private function add_hooks() { add_action( 'init', array( $this, 'check_form_submission' ), 111, 0 ); + add_action( 'wp_ajax_wp_forms', array( $this, 'check_form_submission_ajax' ) ); + add_action( 'wp_ajax_nopriv_wp_forms', array( $this, 'check_form_submission_ajax' ) ); } public function check_form_submission() { @@ -22,9 +24,41 @@ public function check_form_submission() { return; } $submission->submit(); + $submission->prepare_form(); $submission->redirect(); } + /** + * Similar to check_form_submission() but for AJAX requests. + * + * @see check_form_submission() + * + * @throws Exception + */ + public function check_form_submission_ajax() { + $kk = ''; + if ( empty($_REQUEST['data']['wp_form_id']) || empty($_REQUEST['data']['wp_form_nonce']) ) { + return; + } + if ( !wp_verify_nonce($_REQUEST['data']['wp_form_nonce'], $_REQUEST['data']['wp_form_id']) ) { + return; + } + + $form = wp_get_form($_REQUEST['data']['wp_form_id']); + $submission = new WP_Form_Submission($form, $_REQUEST['data']); + if ( !$submission->is_valid() ) { + /** + * похоже здесь это не надо вызывать, т к возможно эта штука втыкает ошибки в хтмл, + * а нам нужно просто подготовить ошибки и отправить их + */ + $submission->prepare_form(); + $submission->send_ajax_answer(); + return; + } + $submission->submit_ajax(); + $submission->send_ajax_answer(); + } + /********** Singleton *************/ /** diff --git a/classes/WP_Form_Submission.php b/classes/WP_Form_Submission.php index 959629e..17035c1 100644 --- a/classes/WP_Form_Submission.php +++ b/classes/WP_Form_Submission.php @@ -34,6 +34,10 @@ public function submit() { } } + public function submit_ajax() { + $this->submit(); + } + /** * @param string $key The form element name, as it would be displayed * in the HTML. @@ -92,6 +96,16 @@ public function set_redirect( $url = '' ) { $this->redirect = $url; } + public function send_ajax_answer() { + $elements = $this->form->get_elements_as_array(); + if ( !$this->is_valid() ) { + wp_send_json_error( $elements ); + } + else { + wp_send_json_success( $elements ); + } + } + /** * Set values and errors on the form * to prepare it for rendering diff --git a/classes/elements/WP_Form_Element.php b/classes/elements/WP_Form_Element.php index cc2ef3a..b434efb 100644 --- a/classes/elements/WP_Form_Element.php +++ b/classes/elements/WP_Form_Element.php @@ -21,6 +21,7 @@ class WP_Form_Element implements WP_Form_Component, WP_Form_Attributes_Interface protected $value = ''; protected $description = ''; protected $errors = array(); + protected $content = ''; /** @var WP_Form_Attributes_Interface */ protected $attributes = NULL; @@ -196,6 +197,15 @@ public function clear_errors() { return $this; } + public function set_content( $content ) { + $this->content = $content; + return $this; + } + + public function get_content() { + return $this->content; + } + /** * @return string */ diff --git a/classes/elements/WP_Form_Element_Checkboxes.php b/classes/elements/WP_Form_Element_Checkboxes.php index c2ef0e9..15ceffb 100644 --- a/classes/elements/WP_Form_Element_Checkboxes.php +++ b/classes/elements/WP_Form_Element_Checkboxes.php @@ -3,4 +3,11 @@ class WP_Form_Element_Checkboxes extends WP_Form_Element_Multiple { protected $type = 'checkboxes'; protected $default_view = 'WP_Form_View_Checkboxes'; + + public function get_selected() { + if ( !empty($this->value) ) { + return $this->value; + } + return $this->default_value; + } } diff --git a/classes/elements/WP_Form_Element_Markup.php b/classes/elements/WP_Form_Element_Markup.php new file mode 100644 index 0000000..89b1846 --- /dev/null +++ b/classes/elements/WP_Form_Element_Markup.php @@ -0,0 +1,7 @@ +get_name(); + } + if ( empty($key) ) { + throw new InvalidArgumentException(__('Cannot add nameless element to a wrapper', 'wp-forms')); + } + $this->elements[$key] = $element; + return $this; + } + + /** + * Remove a child component + * + * @param string $key + * @return $this + */ + public function remove_element( $key ) { + if ( isset($this->elements[$key]) ) { + unset($this->elements[$key]); + } + return $this; + } + + /** + * Get the element with the given key + * + * @param string $key + * + * @return WP_Form_Component|NULL + */ + public function get_element( $key ) { + if ( !empty($this->elements[$key]) ) { + return $this->elements[$key]; + } + foreach ( $this->elements as $e ) { + if ( $e instanceof WP_Form_Aggregate ) { + $child = $e->get_element($key); + if ( !empty($child) ) { + return $child; + } + } + } + return NULL; + } + + /** + * Get an array of all child components, sorted by priority. + * + * @return WP_Form_Component[] + */ + public function get_children() { + $elements = WP_Form_Element::sort_elements($this->elements); + return $elements; + } +} diff --git a/classes/views/WP_Form_View_Checkboxes.php b/classes/views/WP_Form_View_Checkboxes.php index 806e4de..21d218a 100644 --- a/classes/views/WP_Form_View_Checkboxes.php +++ b/classes/views/WP_Form_View_Checkboxes.php @@ -7,14 +7,19 @@ protected function checkboxes( WP_Form_Element_Checkboxes $element ) { throw new LogicException(__('Cannot render checkbox group without a name', 'wp-forms')); } $options = $element->get_options(); + $selected = (array) $element->get_selected(); $output = ''; foreach ( $options as $key => $label ) { - $output .= $this->checkbox( $key, $label, $attributes ); + $checked = false; + if ( in_array( $key, $selected ) ) { + $checked = true; + } + $output .= $this->checkbox( $key, $label, $attributes, $checked ); } return $output; } - protected function checkbox( $key, $label, $attributes ) { + protected function checkbox( $key, $label, $attributes, $checked ) { $checkbox = WP_Form_Element::create('checkbox') ->set_name($attributes['name'].'[]') ->set_label($label) @@ -32,6 +37,9 @@ protected function checkbox( $key, $label, $attributes ) { foreach ( $attributes as $att => $value ) { $checkbox->set_attribute($att, $value); } + if( $checked ) { + $checkbox->set_attribute( 'checked', 'checked' ); + } do_action('wp_form_checkbox_group_member', $checkbox); return $checkbox->render(); } diff --git a/classes/views/WP_Form_View_Markup.php b/classes/views/WP_Form_View_Markup.php new file mode 100644 index 0000000..e9ce71d --- /dev/null +++ b/classes/views/WP_Form_View_Markup.php @@ -0,0 +1,26 @@ +get_type(); + if ( method_exists( $this, $type ) ) { + return call_user_func( array( $this, $type ), $element ); + } elseif ( $element instanceof WP_Form_Element ) { + return $this->markup($element); // fallback to generic + } + return ''; + } + + protected function markup( WP_Form_Element $element ) { + $content = $element->get_content(); + + $attributes = $element->get_all_attributes(); + + unset($attributes['value'], $attributes['type']); + $attributes = WP_Form_View::prepare_attributes($attributes); + + $template = '
%s
'; + return sprintf( $template, $attributes, $content ); + } + +} diff --git a/classes/views/WP_Form_View_Textarea.php b/classes/views/WP_Form_View_Textarea.php index 76ad0e3..d801337 100644 --- a/classes/views/WP_Form_View_Textarea.php +++ b/classes/views/WP_Form_View_Textarea.php @@ -15,7 +15,7 @@ protected function textarea( WP_Form_Element $element ) { $attributes = $element->get_all_attributes(); $value = ''; if ( isset($attributes['value']) ) { - $value = $attributes['value']; + $value = esc_textarea( $attributes['value'] ); unset($attributes['value']); } $attributes = WP_Form_View::prepare_attributes($attributes); diff --git a/classes/views/WP_Form_View_Wrapper.php b/classes/views/WP_Form_View_Wrapper.php new file mode 100644 index 0000000..bc99683 --- /dev/null +++ b/classes/views/WP_Form_View_Wrapper.php @@ -0,0 +1,60 @@ +get_type(); + if ( method_exists( $this, $type ) ) { + return call_user_func( array( $this, $type ), $element ); + } elseif ( $element instanceof WP_Form_Element_Wrapper ) { + return $this->wrapper($element); // fallback to generic
+ } + return ''; + } + + protected function wrapper( WP_Form_Element_Wrapper $element ) { + $children = $this->render_children($element); + + $attributes = $element->get_all_attributes(); + // Because this is just a tag (div, span) we don't need this attrs in tag. + unset($attributes['name'], $attributes['tag_name'], $attributes['type'], $attributes['value']); + $attributes = WP_Form_View::prepare_attributes($attributes); + + $tag_name = $element->get_attribute('tag_name'); + if( empty( $tag_name ) ) { + $tag_name = 'div'; + } + $output = sprintf( + '<%1$s %2$s>%3$s', + $tag_name, + $attributes, + $children + ); + return $output; + } + + /** + * Walk through each child and render it + * + * @param WP_Form_Aggregate $fieldset + * @return string + */ + protected function render_children( WP_Form_Aggregate $wrapper ) { + $children = ''; + foreach ( $wrapper->get_children() as $child ) { + $children .= $this->render_child($child); + } + return $children; + } + + /** + * @param WP_Form_Component $element + * @return string + */ + protected function render_child( WP_Form_Component $element ) { + return $element->render(); + } + +} diff --git a/classes/views/decorators/WP_Form_Decorator_Errors.php b/classes/views/decorators/WP_Form_Decorator_Errors.php index b6b9ba1..4b5d281 100644 --- a/classes/views/decorators/WP_Form_Decorator_Errors.php +++ b/classes/views/decorators/WP_Form_Decorator_Errors.php @@ -1,16 +1,46 @@ get_errors(); - $output = ''; if ( $errors ) { - $output = ''; } - return $output . $this->component_view->render($element); + + return $this->component_view->render($element); } } diff --git a/wp-forms.php b/wp-forms.php index 66502be..3cb7afa 100644 --- a/wp-forms.php +++ b/wp-forms.php @@ -5,7 +5,7 @@ Description: An API for creating, altering, validating, and submitting forms via code. Author: Flightless Author URI: http://flightless.us/ -Version: 0.4 +Version: 0.5.1 */ /* Copyright (c) 2013 Flightless, Inc. http://flightless.us/