Class for handling form edit page behavior.
Description
Source
File: src/db-objects/forms/form-edit-page-handler.php
class Form_Edit_Page_Handler { /** * Form manager instance. * * @since 1.0.0 * @var Form_Manager */ private $form_manager; /** * Array of meta boxes as `$id => $args` pairs. * * @since 1.0.0 * @var array */ private $meta_boxes = array(); /** * Array of tabs as `$id => $args` pairs. * * @since 1.0.0 * @var array */ private $tabs = array(); /** * Current form storage. * * @since 1.0.0 * @var Form|null */ private $current_form = null; /** * Constructor. * * @since 1.0.0 * * @param Form_Manager $form_manager Form manager instance. */ public function __construct( $form_manager ) { $this->form_manager = $form_manager; } /** * Adds a meta box to the edit page. * * @since 1.0.0 * * @param string $id Meta box identifier. * @param array $args { * Optional. Meta box arguments. * * @type string $title Meta box title. * @type string $description Meta box description. * @type string $context Meta box content. Either 'normal', 'advanced' or 'side'. Default 'advanced'. * @type string $priority Meta box priority. Either 'high', 'core', 'default' or 'low'. Default 'default'. * } */ public function add_meta_box( $id, $args ) { $prefix = $this->form_manager->get_prefix(); if ( 0 !== strpos( $id, $prefix ) ) { $id = $prefix . $id; } $this->meta_boxes[ $id ] = wp_parse_args( $args, array( 'title' => '', 'description' => '', 'content' => 'advanced', 'priority' => 'default', ) ); $services = array( 'ajax' => $this->form_manager->ajax(), 'assets' => $this->form_manager->assets(), 'error_handler' => $this->form_manager->error_handler(), ); $this->meta_boxes[ $id ]['field_manager'] = new Field_Manager( $prefix, $services, array( 'get_value_callback' => array( $this, 'get_meta_values' ), 'get_value_callback_args' => array( $id ), 'update_value_callback' => array( $this, 'update_meta_values' ), 'update_value_callback_args' => array( $id, '{value}' ), 'name_prefix' => $id, 'render_mode' => 'form-table', ) ); } /** * Adds a tab to the edit page. * * @since 1.0.0 * * @param string $id Tab identifier. * @param array $args { * Optional. Tab arguments. * * @type string $title Tab title. * @type string $description Tab description. * @type string $meta_box Identifier of the meta box this tab should belong to. * } */ public function add_tab( $id, $args ) { if ( ! empty( $args['meta_box'] ) ) { $prefix = $this->form_manager->get_prefix(); if ( 0 !== strpos( $args['meta_box'], $prefix ) ) { $args['meta_box'] = $prefix . $args['meta_box']; } } $this->tabs[ $id ] = wp_parse_args( $args, array( 'title' => '', 'description' => '', 'meta_box' => '', ) ); } /** * Adds a field to the edit page. * * @since 1.0.0 * * @param string $id Field identifier. * @param string $type Identifier of the type. * @param array $args { * Optional. Field arguments. See the field class constructor for further arguments. * * @type string $tab Tab identifier this field belongs to. Default empty. * @type string $label Field label. Default empty. * @type string $description Field description. Default empty. * @type mixed $default Default value for the field. Default null. * @type array $input_classes Array of CSS classes for the field input. Default empty array. * @type array $label_classes Array of CSS classes for the field label. Default empty array. * @type array $input_attrs Array of additional input attributes as `$key => $value` pairs. * Default empty array. * } */ public function add_field( $id, $type, $args = array() ) { if ( isset( $args['tab'] ) ) { $args['section'] = $args['tab']; unset( $args['tab'] ); } if ( ! isset( $args['section'] ) ) { return; } if ( ! isset( $this->tabs[ $args['section'] ] ) ) { return; } if ( ! isset( $this->meta_boxes[ $this->tabs[ $args['section'] ]['meta_box'] ] ) ) { return; } $meta_box_args = $this->meta_boxes[ $this->tabs[ $args['section'] ]['meta_box'] ]; $meta_box_args['field_manager']->add( $id, $type, $args ); } /** * Renders form canvas if conditions are met. * * @since 1.0.0 * * @param WP_Post $post Current post. */ public function maybe_render_form_canvas( $post ) { $form = $this->form_manager->get( $post->ID ); if ( ! $form ) { return; } $this->render_form_canvas( $form ); } /** * Adds meta boxes if conditions are met. * * @since 1.0.0 * * @param WP_Post $post Current post. */ public function maybe_add_meta_boxes( $post ) { $form = $this->form_manager->get( $post->ID ); if ( ! $form ) { return; } $this->add_meta_boxes( $form ); } /** * Enqueues assets to load if conditions are met. * * @since 1.0.0 * * @param string $hook_suffix Current hook suffix. */ public function maybe_enqueue_assets( $hook_suffix ) { if ( 'post-new.php' !== $hook_suffix && 'post.php' !== $hook_suffix ) { return; } $target_post_type = $this->form_manager->get_prefix() . $this->form_manager->get_singular_slug(); if ( empty( $_GET['post_type'] ) || $target_post_type !== $_GET['post_type'] ) { if ( empty( $_GET['post'] ) || get_post_type( $_GET['post'] ) !== $target_post_type ) { return; } } $this->enqueue_assets(); } /** * Prints templates if conditions are met. * * @since 1.0.0 */ public function maybe_print_templates() { $target_post_type = $this->form_manager->get_prefix() . $this->form_manager->get_singular_slug(); if ( empty( $_GET['post_type'] ) || $target_post_type !== $_GET['post_type'] ) { if ( empty( $_GET['post'] ) || get_post_type( $_GET['post'] ) !== $target_post_type ) { return; } } $this->print_templates(); } /** * Handles a save request if conditions are met. * * @since 1.0.0 * * @param int $post_id Current post ID. */ public function maybe_handle_save_request( $post_id ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( wp_is_post_revision( $post_id ) ) { return; } if ( is_multisite() && ms_is_switched() ) { return; } $form = $this->form_manager->get( $post_id ); if ( ! $form ) { return; } $this->handle_save_request( $form ); } /** * Callback to get meta values for a specific meta box identifier. * * @since 1.0.0 * * @param string $meta_box_id Meta box identifier. * @return array Meta values stored for the meta box. */ public function get_meta_values( $meta_box_id ) { if ( ! $this->current_form ) { return array(); } return $this->form_manager->get_meta( $this->current_form->id, $meta_box_id, true ); } /** * Callback to update meta values for a specific meta box identifier. * * @since 1.0.0 * * @param string $meta_box_id Meta box identifier. * @param array $values Meta values to store for the meta box. */ public function update_meta_values( $meta_box_id, $values ) { if ( ! $this->current_form ) { return; } $this->form_manager->update_meta( $this->current_form->id, $meta_box_id, $values ); } /** * Handles the duplicate form action. * * Duplicates the form and redirects back to the referer URL. * * @since 1.0.0 */ public function action_duplicate_form() { if ( ! isset( $_REQUEST['form_id'] ) ) { wp_die( __( 'Missing form ID.', 'torro-forms' ), '', 400 ); } if ( ! isset( $_REQUEST['_wpnonce'] ) ) { wp_die( __( 'Missing nonce.', 'torro-forms' ), '', 400 ); } $form_id = (int) $_REQUEST['form_id']; if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], $this->form_manager->get_prefix() . 'duplicate_form_' . $form_id ) ) { wp_die( __( 'Invalid nonce.', 'torro-forms' ), '', 403 ); } $form = $this->form_manager->get( $form_id ); if ( ! $form ) { wp_die( __( 'Invalid form ID.', 'torro-forms' ), '', 404 ); } $new_form = $form->duplicate(); if ( is_wp_error( $new_form ) ) { $feedback = array( 'type' => 'error', /* translators: 1: form title, 2: error message */ 'message' => sprintf( __( 'The form “%1$s” could not be duplicated: %2$s', 'torro-forms' ), $form->title, $new_form->get_error_message() ), ); } else { $feedback = array( 'type' => 'success', /* translators: 1: form title, 2: new form edit URL */ 'message' => sprintf( __( 'The form “%1$s” was duplicated successfully. <a href="%2$s">View the duplicate</a>', 'torro-forms' ), $form->title, get_edit_post_link( $new_form->id ) ), ); } $meta_key = $this->form_manager->get_prefix() . 'duplicate_feedback'; $this->form_manager->update_meta( $form->id, $meta_key, $feedback ); $redirect_url = add_query_arg( $meta_key, $form->id, wp_get_referer() ); wp_redirect( $redirect_url ); exit; } /** * Displays feedback from the duplicate form action when applicable. * * @since 1.0.0 */ public function maybe_show_duplicate_form_feedback() { $meta_key = $this->form_manager->get_prefix() . 'duplicate_feedback'; if ( empty( $_GET[ $meta_key ] ) ) { return; } $form_id = (int) $_GET[ $meta_key ]; unset( $_GET[ $meta_key ] ); $feedback = $this->form_manager->get_meta( $form_id, $meta_key, true ); if ( ! is_array( $feedback ) ) { return; } $this->form_manager->delete_meta( $form_id, $meta_key ); ?> <div class="notice notice-<?php echo esc_attr( $feedback['type'] ); ?>"> <p><?php echo wp_kses( $feedback['message'], array( 'strong' => array(), 'a' => array( 'href' => array() ) ) ); ?></p> </div> <?php } /** * Displays a button to duplicate a form when applicable. * * @since 1.0.0 * * @param string $output Sample permalink HTML markup. * @param int $post_id Post ID. * @param string $new_title New sample permalink title. * @param string $new_slug New sample permalink slug. * @param WP_Post $post Post object. * @return string Sample permalink HTML, possibly including the additional button. */ public function maybe_add_duplicate_button( $output, $post_id, $new_title, $new_slug, $post ) { $prefix = $this->form_manager->get_prefix(); if ( $prefix . 'form' !== $post->post_type || 'auto-draft' === $post->post_status ) { return $output; } $nonce_action = $prefix . 'duplicate_form_' . $post->ID; $url = wp_nonce_url( admin_url( 'admin.php?action=' . $prefix . 'duplicate_form&form_id=' . $post->ID . '&_wp_http_referer=' . urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ), $nonce_action ); return $output . ' <a class="button button-small" href="' . esc_url( $url ) . '">' . esc_html( _x( 'Duplicate Form', 'action', 'torro-forms' ) ) . '</a>'; } /** * Displays a button to view form submissions when applicable. * * @since 1.0.0 * * @param string $output Sample permalink HTML markup. * @param int $post_id Post ID. * @param string $new_title New sample permalink title. * @param string $new_slug New sample permalink slug. * @param WP_Post $post Post object. * @return string Sample permalink HTML, possibly including the additional button. */ public function maybe_add_submissions_button( $output, $post_id, $new_title, $new_slug, $post ) { $prefix = $this->form_manager->get_prefix(); if ( $prefix . 'form' !== $post->post_type || 'auto-draft' === $post->post_status ) { return $output; } $url = add_query_arg( 'form_id', $post_id, torro()->admin_pages()->get( 'list_submissions' )->url ); return $output . ' <a class="button button-small" href="' . esc_url( $url ) . '">' . esc_html( _x( 'View Form Submissions', 'action', 'torro-forms' ) ) . '</a>'; } public function maybe_render_shortcode( $post ) { $prefix = $this->form_manager->get_prefix(); if ( $prefix . 'form' !== $post->post_type || 'auto-draft' === $post->post_status ) { return; } $this->form_manager->assets()->enqueue_script( 'clipboard' ); $this->form_manager->assets()->enqueue_style( 'clipboard' ); $id_attr = 'form-shortcode-' . $post->ID; ?> <div class="misc-pub-section form-shortcode"> <label for="<?php echo esc_attr( $id_attr ); ?>"><?php _e( 'Form Shortcode:', 'torro-forms' ); ?></label> <input id="<?php echo esc_attr( $id_attr ); ?>" class="clipboard-field" value="<?php echo esc_attr( sprintf( "[{$this->form_manager->get_prefix()}form id="%d"]", $post->ID ) ); ?>" readonly="readonly" /> <button type="button" class="clipboard-button button" data-clipboard-target="#<?php echo esc_attr( $id_attr ); ?>"> <?php $this->form_manager->assets()->render_icon( 'torro-icon-clippy', __( 'Copy to clipboard', 'torro-forms' ) ); ?> </button> </div> <?php } /** * Prints a 'novalidate' attribute for the post form if conditions are met. * * @since 1.0.0 * * @param WP_Post $post Post for which the form is currently being printed. */ public function maybe_print_post_form_novalidate( $post ) { $form = $this->form_manager->get( $post->ID ); if ( ! $form ) { return; } echo ' novalidate="novalidate"'; } /** * Renders form canvas. * * @since 1.0.0 * * @param Form $form Current form. */ private function render_form_canvas( $form ) { ?> <div id="torro-form-canvas" class="torro-form-canvas"> <div class="torro-form-canvas-header torro-form-canvas-tabs" role="tablist"> <button type="button" class="torro-form-canvas-tab add-button is-active" disabled="disabled"> <span aria-hidden="true">+</span><span class="screen-reader-text"><?php _e( 'Add New Container', 'torro-forms' ); ?></span> </button> </div> <div class="torro-form-canvas-content"> <div class="drag-drop-area is-empty"> <div class="content loader-content hide-if-no-js"> <?php _e( 'Loading form builder...', 'torro-forms' ); ?> <span class="spinner is-active"></span> </div> <div class="torro-notice notice-warning hide-if-js"> <p> <?php _e( 'It seems you have disabled JavaScript in your browser. Torro Forms requires JavaScript in order to edit your forms.', 'torro-forms' ); ?> </p> </div> </div> </div> <div class="torro-form-canvas-footer"></div> </div> <?php } /** * Adds meta boxes to the page. * * @since 1.0.0 * * @param Form $form Current form. */ private function add_meta_boxes( $form ) { $this->current_form = $form; if ( ! did_action( "{$this->form_manager->get_prefix()}add_form_meta_content" ) ) { /** * Fires when meta boxes for the form edit page should be added. * * @since 1.0.0 * * @param Form_Edit_Page_Handler $edit_page Form edit page. */ do_action( "{$this->form_manager->get_prefix()}add_form_meta_content", $this ); } foreach ( $this->meta_boxes as $id => $args ) { add_meta_box( $id, $args['title'], function( $post, $box ) { $prefix = $this->form_manager->get_prefix(); if ( ! empty( $box['args']['description'] ) ) { echo '<p class="description">' . $box['args']['description'] . '</p>'; } $tab_id_prefix = 'metabox-' . $box['id'] . '-tab-'; $tabpanel_id_prefix = 'metabox-' . $box['id'] . '-tabpanel-'; $tabs = wp_list_filter( $this->tabs, array( 'meta_box' => $box['id'] ) ); $first = true; /** * Fires before a form meta box is rendered. * * The dynamic portion of the hook name refers to the meta box identifier. * * @since 1.0.0 * * @param int $form_id Current form ID. */ do_action( "{$prefix}metabox_{$box['id']}_before", $this->current_form->id ); ?> <h3 class="torro-metabox-tab-wrapper" role="tablist"> <?php foreach ( $tabs as $id => $args ) : ?> <a id="<?php echo esc_attr( $tab_id_prefix . $id ); ?>" class="torro-metabox-tab" href="<?php echo esc_attr( '#' . $tabpanel_id_prefix . $id ); ?>" aria-controls="<?php echo esc_attr( $tabpanel_id_prefix . $id ); ?>" aria-selected="<?php echo $first ? 'true' : 'false'; ?>" role="tab"> <?php echo $args['title']; ?> </a> <?php $first = false; ?> <?php endforeach; ?> </h3> <?php $first = true; ?> <?php foreach ( $tabs as $id => $args ) : ?> <div id="<?php echo esc_attr( $tabpanel_id_prefix . $id ); ?>" class="torro-metabox-tab-panel" aria-labelledby="<?php echo esc_attr( $tab_id_prefix . $id ); ?>" aria-hidden="<?php echo $first ? 'false' : 'true'; ?>" role="tabpanel"> <?php /** * Fires before a form meta box tab is rendered. * * The dynamic portions of the hook name refer to the meta box identifier and * tab identifier respectively. * * @since 1.0.0 * * @param int $form_id Current form ID. */ do_action( "{$prefix}metabox_{$box['id']}_tab_{$id}_before", $this->current_form->id ); ?> <?php if ( ! empty( $args['description'] ) ) : ?> <p class="description"><?php echo $args['description']; ?></p> <?php endif; ?> <table class="form-table"> <?php $box['args']['field_manager']->render( $id ); ?> </table> <?php /** * Fires after a form meta box tab has been rendered. * * The dynamic portions of the hook name refer to the meta box identifier and * tab identifier respectively. * * @since 1.0.0 * * @param int $form_id Current form ID. */ do_action( "{$prefix}metabox_{$box['id']}_tab_{$id}_after", $this->current_form->id ); ?> </div> <?php $first = false; ?> <?php endforeach; ?> <?php /** * Fires after a form meta box has been rendered. * * The dynamic portion of the hook name refers to the meta box identifier. * * @since 1.0.0 * * @param int $form_id Current form ID. */ do_action( "{$prefix}metabox_{$box['id']}_after", $this->current_form->id ); ?> <?php }, null, $args['context'], $args['priority'], $args ); } /** * Fires when meta boxes for the form edit page should be added. * * @since 1.0.0 * * @param Form $form Form that is being edited. */ do_action( "{$this->form_manager->get_prefix()}add_form_meta_boxes", $form ); } /** * Enqueues assets to load on the page. * * @since 1.0.0 */ private function enqueue_assets() { $this->form_manager->assets()->enqueue_script( 'admin-fixed-sidebar' ); $this->form_manager->assets()->enqueue_script( 'admin-tooltip-descriptions' ); $this->form_manager->assets()->enqueue_style( 'admin-tooltip-descriptions' ); $this->form_manager->assets()->enqueue_script( 'admin-unload' ); $this->form_manager->assets()->enqueue_script( 'admin-form-builder' ); $this->form_manager->assets()->enqueue_style( 'admin-form-builder' ); if ( ! did_action( "{$this->form_manager->get_prefix()}add_form_meta_content" ) ) { /** This action is documented in src/db-objects/forms/form-edit-page-handler.php */ do_action( "{$this->form_manager->get_prefix()}add_form_meta_content", $this ); } foreach ( $this->meta_boxes as $id => $args ) { $args['field_manager']->enqueue(); } /** * Fires after scripts and stylesheets for the form builder have been enqueued. * * @since 1.0.0 * * @param Assets $assets The Assets API instance. */ do_action( "{$this->form_manager->get_prefix()}enqueue_form_builder_scripts", $this->form_manager->assets() ); } /** * Prints templates to use in JavaScript. * * @since 1.0.0 */ private function print_templates() { ?> <script type="text/html" id="tmpl-torro-failure"> <div class="torro-notice notice-error"> <p> <strong><?php _e( 'Error:', 'torro-forms' ); ?></strong> {{ data.message }} </p> </div> </script> <script type="text/html" id="tmpl-torro-form-canvas"> <div class="torro-form-canvas-header torro-form-canvas-tabs"> <button type="button" class="torro-form-canvas-tab add-button"> <span aria-hidden="true">+</span><span class="screen-reader-text"><?php _e( 'Add New Container', 'torro-forms' ); ?></span> </button> </div> <div class="torro-form-canvas-content"> <div class="torro-form-canvas-panel add-panel"> <div class="drag-drop-area is-empty"> <div class="content"><?php _e( 'Click the button above to add your first container', 'torro-forms' ); ?></div> </div> </div> </div> <div class="torro-form-canvas-footer"></div> </script> <script type="text/html" id="tmpl-torro-container-tab"> <span>{{ data.label }}</span> </script> <script type="text/html" id="tmpl-torro-container-panel"> <div class="drag-drop-area"></div> <div class="add-element-wrap"> <div class="{{ data.addingElement ? 'add-element-toggle-wrap is-expanded' : 'add-element-toggle-wrap' }}"> <button type="button" class="add-element-toggle" aria-controls="torro-{{ data.id }}-add-element-content-wrap" aria-expanded="{{ data.addingElement ? 'true' : 'false' }}"> <?php _e( 'Add element', 'torro-forms' ); ?> </button> </div> <div id="torro-{{ data.id }}-add-element-content-wrap" class="{{ data.addingElement ? 'add-element-content-wrap is-expanded' : 'add-element-content-wrap' }}" role="region"> <div class="torro-element-types"> <# _.each( data.elementTypes, function( elementType ) { #> <div class="torro-element-type torro-element-type-{{ elementType.slug }}{{ elementType.slug === data.selectedElementType ? ' is-selected' : '' }}" data-slug="{{ elementType.slug }}"> <div class="torro-element-type-header"> <# if ( ! _.isEmpty( elementType.icon_css_class ) ) { #> <span class="torro-element-type-header-icon {{ elementType.icon_css_class }}" aria-hidden="true"></span> <# } else if ( ! _.isEmpty( elementType.icon_svg_id ) ) { #> <svg class="torro-icon torro-element-type-header-icon" aria-hidden="true" role="img"> <use href="#{{ elementType.icon_svg_id }}" xlink:href="#{{ elementType.icon_svg_id }}"></use> </svg> <# } else { #> <img class="torro-element-type-header-icon" src="{{ elementType.icon_url }}" alt=""> <# } #> <span class="torro-element-type-header-title"> {{ elementType.title }} </span> </div> <div class="torro-element-type-content"> <p>{{ elementType.description }}</p> </div> </div> <# } ); #> </div> <button type="button" class="button add-element-button"{{{ data.selectedElementType ? '' : ' disabled' }}}> <?php _e( 'Add element', 'torro-forms' ); ?> </button> </div> </div> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>containers[{{ data.id }}][form_id]" value="{{ data.form_id }}" /> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>containers[{{ data.id }}][label]" value="{{ data.label }}" /> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>containers[{{ data.id }}][sort]" value="{{ data.sort }}" /> </script> <script type="text/html" id="tmpl-torro-container-footer-panel"> <button type="button" class="button-link button-link-delete delete-container-button"> <?php _e( 'Delete Page', 'torro-forms' ); ?> </button> </script> <script type="text/html" id="tmpl-torro-element"> <div class="torro-element-header"> <# if ( ! _.isEmpty( data.type.icon_css_class ) ) { #> <span class="torro-element-header-icon {{ data.type.icon_css_class }}" aria-hidden="true"></span> <# } else if ( ! _.isEmpty( data.type.icon_svg_id ) ) { #> <svg class="torro-icon torro-element-header-icon" aria-hidden="true" role="img"> <use href="#{{ data.type.icon_svg_id }}" xlink:href="#{{ data.type.icon_svg_id }}"></use> </svg> <# } else { #> <img class="torro-element-header-icon" src="{{ data.type.icon_url }}" alt=""> <# } #> <span class="torro-element-header-title"> {{ ! _.isEmpty( data.label ) ? data.label : data.type.title }} </span> <button type="button" class="torro-element-expand-button" aria-controls="torro-element-{{ data.id }}-content" aria-expanded="{{ data.active ? 'true' : 'false' }}"> <span class="torro-element-expand-button-icon" aria-hidden="true"></span><span class="screen-reader-text">{{ data.active ? '<?php _e( 'Hide Content', 'torro-forms' ); ?>' : '<?php _e( 'Show Content', 'torro-forms' ); ?>' }}</span> </button> </div> <div id="torro-element-{{ data.id }}-content" class="{{ data.active ? 'torro-element-content is-expanded' : 'torro-element-content' }}" role="region"> <div class="torro-element-content-main"> <div class="torro-element-content-tabs"></div> <div class="torro-element-content-panels"></div> </div> <div class="torro-element-content-footer"> <button type="button" class="button-link button-link-delete delete-element-button"> <?php _e( 'Delete Element', 'torro-forms' ); ?> </button> </div> </div> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>elements[{{ data.id }}][container_id]" value="{{ data.container_id }}" /> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>elements[{{ data.id }}][type]" value="{{ data.type.slug }}" /> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>elements[{{ data.id }}][sort]" value="{{ data.sort }}" /> </script> <script type="text/html" id="tmpl-torro-element-section-tab"> <button type="button" id="element-tab-{{ data.elementId }}-{{ data.slug }}" class="torro-element-content-tab torro-element-content-tab-{{ data.slug }}" data-slug="{{ data.slug }}" aria-controls="element-panel-{{ data.elementId }}-{{ data.slug }}" aria-selected="{{ data.active ? 'true' : 'false' }}" role="tab"> {{ data.title }} </button> </script> <script type="text/html" id="tmpl-torro-element-section-panel"> <div id="element-panel-{{ data.elementId }}-{{ data.slug }}" class="torro-element-content-panel torro-element-content-panel-{{ data.slug }}" aria-labelledby="element-tab-{{ data.elementId }}-{{ data.slug }}" aria-hidden="{{ data.active ? 'false' : 'true' }}" role="tabpanel"> <table class="torro-element-fields form-table"></table> </div> </script> <script type="text/html" id="tmpl-torro-element-field"> <tr{{{ _.attrs( data.wrapAttrs ) }}}> <th scope="row"> <div id="{{ data.id }}-label-wrap" class="label-wrap"></div> </th> <td> <div id="{{ data.id }}-content-wrap" class="content-wrap"></div> <# if ( data._element_setting ) { #> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>element_settings[{{ data._element_setting.id }}][element_id]" value="{{ data._element_setting.element_id }}" /> <input type="hidden" name="<?php echo $this->form_manager->get_prefix(); ?>element_settings[{{ data._element_setting.id }}][name]" value="{{ data._element_setting.name }}" /> <# } #> </td> </tr> </script> <?php /** * Fires after templates for the form builder have been printed. * * @since 1.0.0 */ do_action( "{$this->form_manager->get_prefix()}print_form_builder_templates" ); } /** * Handles a save request for the page. * * @since 1.0.0 * * @param Form $form Current form. */ private function handle_save_request( $form ) { $this->current_form = $form; $mappings = array( 'forms' => array( $form->id => $form->id, ), 'containers' => array(), 'elements' => array(), 'element_choices' => array(), 'element_settings' => array(), ); $errors = new WP_Error(); if ( isset( $_POST[ $this->form_manager->get_prefix() . 'containers' ] ) ) { $mappings = $this->save_containers( wp_unslash( $_POST[ $this->form_manager->get_prefix() . 'containers' ] ), $mappings, $errors ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'elements' ] ) ) { $mappings = $this->save_elements( wp_unslash( $_POST[ $this->form_manager->get_prefix() . 'elements' ] ), $mappings, $errors ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'element_choices' ] ) ) { $mappings = $this->save_element_choices( wp_unslash( $_POST[ $this->form_manager->get_prefix() . 'element_choices' ] ), $mappings, $errors ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'element_settings' ] ) ) { $mappings = $this->save_element_settings( wp_unslash( $_POST[ $this->form_manager->get_prefix() . 'element_settings' ] ), $mappings, $errors ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'deleted_containers' ] ) ) { $this->delete_containers( array_map( 'absint', $_POST[ $this->form_manager->get_prefix() . 'deleted_containers' ] ) ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'deleted_elements' ] ) ) { $this->delete_elements( array_map( 'absint', $_POST[ $this->form_manager->get_prefix() . 'deleted_elements' ] ) ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'deleted_element_choices' ] ) ) { $this->delete_element_choices( array_map( 'absint', $_POST[ $this->form_manager->get_prefix() . 'deleted_element_choices' ] ) ); } if ( isset( $_POST[ $this->form_manager->get_prefix() . 'deleted_element_settings' ] ) ) { $this->delete_element_settings( array_map( 'absint', $_POST[ $this->form_manager->get_prefix() . 'deleted_element_settings' ] ) ); } if ( ! did_action( "{$this->form_manager->get_prefix()}add_form_meta_content" ) ) { /** This action is documented in src/db-objects/forms/form-edit-page-handler.php */ do_action( "{$this->form_manager->get_prefix()}add_form_meta_content", $this ); } foreach ( $this->meta_boxes as $id => $args ) { if ( isset( $_POST[ $id ] ) ) { // TODO: Figure out how to deal with errors. $args['field_manager']->update_values( wp_unslash( $_POST[ $id ] ) ); } } /** * Fires after a form has been saved. * * @since 1.0.0 * * @param Form $form Form that has been saved. * @param array $mappings Array of ID mappings from the objects that have been saved. */ do_action( "{$this->form_manager->get_prefix()}save_form", $form, $mappings ); } /** * Saves containers. * * @since 1.0.0 * * @param array $containers Array of `$container_id => $container_data` pairs. * @param array $mappings Array of mappings to pass-through and modify. * @param WP_Error $errors Error object to append errors to. * @return array Modified mappings. */ private function save_containers( $containers, $mappings, $errors ) { $container_manager = $this->form_manager->get_child_manager( 'containers' ); foreach ( $containers as $id => $data ) { $data['form_id'] = key( $mappings['forms'] ); if ( $this->is_temp_id( $id ) ) { $container = $container_manager->create(); } else { $container = $container_manager->get( $id ); if ( ! $container ) { $container = $container_manager->create(); } } foreach ( $data as $key => $value ) { $container->$key = $value; } $status = $container->sync_upstream(); if ( is_wp_error( $status ) ) { $errors->add( $status->get_error_code(), $status->get_error_message(), array( 'id' => $id, 'data' => $data, ) ); } else { $mappings['containers'][ $id ] = $container->id; } } return $mappings; } /** * Saves elements. * * @since 1.0.0 * * @param array $elements Array of `$element_id => $element_data` pairs. * @param array $mappings Array of mappings to pass-through and modify. * @param WP_Error $errors Error object to append errors to. * @return array Modified mappings. */ private function save_elements( $elements, $mappings, $errors ) { $element_manager = $this->form_manager->get_child_manager( 'containers' )->get_child_manager( 'elements' ); foreach ( $elements as $id => $data ) { if ( empty( $data['container_id'] ) || ! isset( $mappings['containers'][ $data['container_id'] ] ) ) { continue; } $data['container_id'] = $mappings['containers'][ $data['container_id'] ]; if ( $this->is_temp_id( $id ) ) { $element = $element_manager->create(); } else { $element = $element_manager->get( $id ); if ( ! $element ) { $element = $element_manager->create(); } } foreach ( $data as $key => $value ) { $element->$key = $value; } $status = $element->sync_upstream(); if ( is_wp_error( $status ) ) { $errors->add( $status->get_error_code(), $status->get_error_message(), array( 'id' => $id, 'data' => $data, ) ); } else { $mappings['elements'][ $id ] = $element->id; } } return $mappings; } /** * Saves element choices. * * @since 1.0.0 * * @param array $element_choices Array of `$element_choice_id => $element_choice_data` pairs. * @param array $mappings Array of mappings to pass-through and modify. * @param WP_Error $errors Error object to append errors to. * @return array Modified mappings. */ private function save_element_choices( $element_choices, $mappings, $errors ) { $element_choice_manager = $this->form_manager->get_child_manager( 'containers' )->get_child_manager( 'elements' )->get_child_manager( 'element_choices' ); foreach ( $element_choices as $id => $data ) { if ( empty( $data['element_id'] ) || ! isset( $mappings['elements'][ $data['element_id'] ] ) ) { continue; } $data['element_id'] = $mappings['elements'][ $data['element_id'] ]; if ( $this->is_temp_id( $id ) ) { $element_choice = $element_choice_manager->create(); } else { $element_choice = $element_choice_manager->get( $id ); if ( ! $element_choice ) { $element_choice = $element_choice_manager->create(); } } foreach ( $data as $key => $value ) { $element_choice->$key = $value; } $status = $element_choice->sync_upstream(); if ( is_wp_error( $status ) ) { $errors->add( $status->get_error_code(), $status->get_error_message(), array( 'id' => $id, 'data' => $data, ) ); } else { $mappings['element_choices'][ $id ] = $element_choice->id; } } return $mappings; } /** * Saves element settings. * * @since 1.0.0 * * @param array $element_settings Array of `$element_setting_id => $element_setting_data` pairs. * @param array $mappings Array of mappings to pass-through and modify. * @param WP_Error $errors Error object to append errors to. * @return array Modified mappings. */ private function save_element_settings( $element_settings, $mappings, $errors ) { $element_setting_manager = $this->form_manager->get_child_manager( 'containers' )->get_child_manager( 'elements' )->get_child_manager( 'element_settings' ); foreach ( $element_settings as $id => $data ) { if ( empty( $data['element_id'] ) || ! isset( $mappings['elements'][ $data['element_id'] ] ) ) { continue; } $data['element_id'] = $mappings['elements'][ $data['element_id'] ]; if ( $this->is_temp_id( $id ) ) { $element_setting = $element_setting_manager->create(); } else { $element_setting = $element_setting_manager->get( $id ); if ( ! $element_setting ) { $element_setting = $element_setting_manager->create(); } } foreach ( $data as $key => $value ) { $element_setting->$key = $value; } $status = $element_setting->sync_upstream(); if ( is_wp_error( $status ) ) { $errors->add( $status->get_error_code(), $status->get_error_message(), array( 'id' => $id, 'data' => $data, ) ); } else { $mappings['element_settings'][ $id ] = $element_setting->id; } } return $mappings; } /** * Deletes containers with specific IDs. * * @since 1.0.0 * * @param array $container_ids Array of container IDs. */ private function delete_containers( $container_ids ) { $container_manager = $this->form_manager->get_child_manager( 'containers' ); foreach ( $container_ids as $container_id ) { $container = $container_manager->get( $container_id ); if ( ! $container ) { continue; } $container->delete(); } } /** * Deletes elements with specific IDs. * * @since 1.0.0 * * @param array $element_ids Array of element IDs. */ private function delete_elements( $element_ids ) { $element_manager = $this->form_manager->get_child_manager( 'containers' )->get_child_manager( 'elements' ); foreach ( $element_ids as $element_id ) { $element = $element_manager->get( $element_id ); if ( ! $element ) { continue; } $element->delete(); } } /** * Deletes element choices with specific IDs. * * @since 1.0.0 * * @param array $element_choice_ids Array of element choice IDs. */ private function delete_element_choices( $element_choice_ids ) { $element_choice_manager = $this->form_manager->get_child_manager( 'containers' )->get_child_manager( 'elements' )->get_child_manager( 'element_choices' ); foreach ( $element_choice_ids as $element_choice_id ) { $element_choice = $element_choice_manager->get( $element_choice_id ); if ( ! $element_choice ) { continue; } $element_choice->delete(); } } /** * Deletes element settings with specific IDs. * * @since 1.0.0 * * @param array $element_setting_ids Array of element setting IDs. */ private function delete_element_settings( $element_setting_ids ) { $element_setting_manager = $this->form_manager->get_child_manager( 'containers' )->get_child_manager( 'elements' )->get_child_manager( 'element_settings' ); foreach ( $element_setting_ids as $element_setting_id ) { $element_setting = $element_setting_manager->get( $element_setting_id ); if ( ! $element_setting ) { continue; } $element_setting->delete(); } } /** * Checks whether a specific ID is a temporary ID. * * @since 1.0.0 * * @param int $id Component ID. * @return bool True if temporary ID, false otherwise. */ private function is_temp_id( $id ) { return is_string( $id ) && 'temp_id_' === substr( $id, 0, 8 ); } }
Changelog
Version | Description |
---|---|
1.0.0 | Introduced. |
Methods
- __construct — Constructor.
- action_duplicate_form — Handles the duplicate form action.
- add_field — Adds a field to the edit page.
- add_meta_box — Adds a meta box to the edit page.
- add_meta_boxes — Adds meta boxes to the page.
- add_tab — Adds a tab to the edit page.
- delete_containers — Deletes containers with specific IDs.
- delete_element_choices — Deletes element choices with specific IDs.
- delete_element_settings — Deletes element settings with specific IDs.
- delete_elements — Deletes elements with specific IDs.
- enqueue_assets — Enqueues assets to load on the page.
- get_meta_values — Callback to get meta values for a specific meta box identifier.
- handle_save_request — Handles a save request for the page.
- is_temp_id — Checks whether a specific ID is a temporary ID.
- maybe_add_duplicate_button — Displays a button to duplicate a form when applicable.
- maybe_add_meta_boxes — Adds meta boxes if conditions are met.
- maybe_add_submissions_button — Displays a button to view form submissions when applicable.
- maybe_enqueue_assets — Enqueues assets to load if conditions are met.
- maybe_handle_save_request — Handles a save request if conditions are met.
- maybe_print_post_form_novalidate — Prints a 'novalidate' attribute for the post form if conditions are met.
- maybe_print_templates — Prints templates if conditions are met.
- maybe_render_form_canvas — Renders form canvas if conditions are met.
- maybe_render_shortcode — Renders a read-only field containing the form shortcode markup for a post if applicable.
- maybe_show_duplicate_form_feedback — Displays feedback from the duplicate form action when applicable.
- print_templates — Prints templates to use in JavaScript.
- render_form_canvas — Renders form canvas.
- save_containers — Saves containers.
- save_element_choices — Saves element choices.
- save_element_settings — Saves element settings.
- save_elements — Saves elements.
- update_meta_values — Callback to update meta values for a specific meta box identifier.