diff --git a/plugins/wporg-5ftf/includes/pledge-form.php b/plugins/wporg-5ftf/includes/pledge-form.php index 9fd1170..380b4a4 100755 --- a/plugins/wporg-5ftf/includes/pledge-form.php +++ b/plugins/wporg-5ftf/includes/pledge-form.php @@ -22,6 +22,7 @@ add_shortcode( '5ftf_pledge_form_manage', __NAMESPACE__ . '\render_form_manage' */ function render_form_new() { $action = isset( $_GET['action'] ) ? filter_input( INPUT_GET, 'action' ) : filter_input( INPUT_POST, 'action' ); + $pledge_id = 0; $data = get_form_submission(); $errors = []; $pledge = null; @@ -65,7 +66,7 @@ function render_form_new() { */ function process_form_new() { $submission = get_form_submission(); - $has_error = check_invalid_submission( $submission ); + $has_error = check_invalid_submission( $submission, 'create' ); if ( $has_error ) { return $has_error; } @@ -149,56 +150,99 @@ function process_pledge_confirmation_email( $pledge_id, $action, $unverified_tok * @return false|string */ function render_form_manage() { - $action = filter_input( INPUT_POST, 'action' ); - $messages = []; - $updated = false; + /* + * Prevent Gutenberg from executing this on the Edit Post screen. + * See https://github.com/WordPress/gutenberg/issues/18394 + */ + if ( is_admin() ) { + return ''; + } - // @todo Get pledge ID from somewhere. - $data = PledgeMeta\get_pledge_meta(); - $is_manage = true; + $messages = []; + $errors = []; + + $action = sanitize_text_field( $_REQUEST['action'] ?? '' ); + $pledge_id = absint( $_REQUEST['pledge_id'] ?? 0 ); + $auth_token = sanitize_text_field( $_REQUEST['auth_token'] ?? '' ); + $can_view_form = Auth\can_manage_pledge( $pledge_id, $auth_token ); + + if ( is_wp_error( $can_view_form ) ) { + // Can't manage pledge, only show errors. + $errors = array( $can_view_form->get_error_message() ); + + ob_start(); + require FiveForTheFuture\PATH . 'views/partial-result-messages.php'; + return ob_get_clean(); + } + + $contributors = Contributor\get_pledge_contributors( $pledge_id, $status = 'all' ); if ( 'Update Pledge' === $action ) { - $processed = process_form_manage(); + $results = process_form_manage( $pledge_id, $auth_token ); - if ( is_wp_error( $processed ) ) { - $messages = array_merge( $messages, $processed->get_error_messages() ); - } elseif ( 'success' === $processed ) { - $updated = true; + if ( is_wp_error( $results ) ) { + $errors = $results->get_error_messages(); + } else { + $messages = array( __( 'Your pledge has been updated.', 'wporg-5ftf' ) ); } } + $data = PledgeMeta\get_pledge_meta( $pledge_id ); + ob_start(); $readonly = false; + $is_manage = true; require FiveForTheFuture\PATH . 'views/form-pledge-manage.php'; return ob_get_clean(); } /** - * Process a submission from the Manage Existing Pledge form. + * Process a submission from the Manage Pledge form. * - * TODO This doesn't actually update any data yet when the form is submitted. - * - * @return string|WP_Error String "success" if the form processed correctly. Otherwise WP_Error. + * @return WP_Error|true An error if the pledge could not be saved. Otherwise true. */ -function process_form_manage() { +function process_form_manage( $pledge_id, $auth_token ) { + $errors = array(); + $nonce = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING ); + $nonce_action = 'manage_pledge_' . $pledge_id; + $has_valid_nonce = wp_verify_nonce( $nonce, $nonce_action ); + + /* + * This should be redundant, since it's also called by `render_form_manage()`, but it's good to also do it here + * just in case other code changes in the future, or this gets called by another flow, etc. + */ + $can_view_form = Auth\can_manage_pledge( $pledge_id, $auth_token ); + + if ( ! $has_valid_nonce || ! $can_view_form ) { + return new WP_Error( + 'invalid_token', + sprintf( + __( 'Your link has expired, please obtain a new one.', 'wporg-5ftf' ), + get_permalink( $pledge_id ) + ) + ); + } + $submission = get_form_submission(); - $has_error = check_invalid_submission( $submission ); + $has_error = check_invalid_submission( $submission, 'update' ); if ( $has_error ) { return $has_error; } - // todo email any new contributors for confirmation - // notify any removed contributors? - // ask them to update their profiles? - // automatically update contributor profiles? - // anything else? + PledgeMeta\save_pledge_meta( $pledge_id, $submission ); + + // @todo Upload & attach logo. + // @todo Save contributors. + + // If we made it to here, we've successfully saved the pledge. + return true; } /** * Get and sanitize $_POST values from a form submission. * - * @return array|bool + * @return array */ function get_form_submission() { $input_filters = array_merge( @@ -213,7 +257,8 @@ function get_form_submission() { $result = filter_input_array( INPUT_POST, $input_filters ); if ( ! $result ) { - return array_fill_keys( array_keys( $input_filters ), '' ); + $result = array_fill_keys( array_keys( $input_filters ), '' ); + $result['empty_post'] = true; } return $result; @@ -222,10 +267,13 @@ function get_form_submission() { /** * Check the submission for valid data. * + * @param array $submission The user input. + * @param string $context Whether this is a new pledge (`create`) or an edit to an existing one (`update`). + * * @return false|WP_Error Return any errors in the submission, or false if no errors. */ -function check_invalid_submission( $submission ) { - $has_required = PledgeMeta\has_required_pledge_meta( $submission ); +function check_invalid_submission( $submission, $context ) { + $has_required = PledgeMeta\has_required_pledge_meta( $submission, $context ); if ( is_wp_error( $has_required ) ) { return $has_required; } @@ -237,20 +285,22 @@ function check_invalid_submission( $submission ) { Pledge\CPT_ID ); - if ( Pledge\has_existing_pledge( $email, 'email' ) ) { - return new WP_Error( - 'existing_pledge_email', - __( 'This email address is already connected to an existing pledge.', 'wporg-5ftf' ) - ); - } + if ( 'create' === $context ) { + if ( Pledge\has_existing_pledge( $email, 'email' ) ) { + return new WP_Error( + 'existing_pledge_email', + __( 'This email address is already connected to an existing pledge.', 'wporg-5ftf' ) + ); + } - $domain = PledgeMeta\get_normalized_domain_from_url( $submission['org-url'] ); + $domain = PledgeMeta\get_normalized_domain_from_url( $submission['org-url'] ); - if ( Pledge\has_existing_pledge( $domain, 'domain' ) ) { - return new WP_Error( - 'existing_pledge_domain', - __( 'A pledge already exists for this domain.', 'wporg-5ftf' ) - ); + if ( Pledge\has_existing_pledge( $domain, 'domain' ) ) { + return new WP_Error( + 'existing_pledge_domain', + __( 'A pledge already exists for this domain.', 'wporg-5ftf' ) + ); + } } return false; diff --git a/plugins/wporg-5ftf/includes/pledge-meta.php b/plugins/wporg-5ftf/includes/pledge-meta.php index 5ac27a6..1a7c07f 100755 --- a/plugins/wporg-5ftf/includes/pledge-meta.php +++ b/plugins/wporg-5ftf/includes/pledge-meta.php @@ -28,34 +28,38 @@ add_action( 'added_post_meta', __NAMESPACE__ . '\update_generated_meta', 10, 4 /** * Define pledge meta fields and their properties. * - * @param string $context Optional. The part of the config to return. 'user_input', 'generated', or 'all'. + * @param string $subset Optional. The part of the config to return: 'user_input', 'generated', or 'all'. * * @return array */ -function get_pledge_meta_config( $context = 'all' ) { +function get_pledge_meta_config( $subset = 'all' ) { $user_input = array( 'org-description' => array( 'single' => true, 'sanitize_callback' => __NAMESPACE__ . '\sanitize_description', 'show_in_rest' => true, + 'context' => array( 'create', 'update' ), 'php_filter' => FILTER_UNSAFE_RAW, ), 'org-name' => array( 'single' => true, 'sanitize_callback' => 'sanitize_text_field', 'show_in_rest' => true, + 'context' => array( 'create', 'update' ), 'php_filter' => FILTER_SANITIZE_STRING, ), 'org-url' => array( 'single' => true, 'sanitize_callback' => 'esc_url_raw', 'show_in_rest' => true, + 'context' => array( 'create', 'update' ), 'php_filter' => FILTER_VALIDATE_URL, ), 'org-pledge-email' => array( 'single' => true, 'sanitize_callback' => 'sanitize_email', 'show_in_rest' => false, + 'context' => array( 'create' ), 'php_filter' => FILTER_VALIDATE_EMAIL, ), ); @@ -83,7 +87,7 @@ function get_pledge_meta_config( $context = 'all' ) { ), ); - switch ( $context ) { + switch ( $subset ) { case 'user_input': $return = $user_input; break; @@ -107,7 +111,6 @@ function get_pledge_meta_config( $context = 'all' ) { */ function sanitize_description( $insecure ) { $secure = wp_kses_data( $insecure ); - $secure = wpautop( $secure ); $secure = wp_unslash( wp_rel_nofollow( $secure ) ); return $secure; @@ -243,12 +246,13 @@ function save_pledge( $pledge_id, $pledge ) { $get_action = filter_input( INPUT_GET, 'action' ); $post_action = filter_input( INPUT_POST, 'action' ); $ignored_actions = array( 'trash', 'untrash', 'restore' ); + $context = ( 'editpost' === $post_action ) ? 'update' : 'create'; /* * This is only intended to run when the front end form and wp-admin forms are submitted, not when posts are * programmatically updated. */ - if ( 'Submit Pledge' !== $post_action && 'editpost' !== $post_action ) { + if ( ! in_array( $post_action, [ 'Submit Pledge', 'editpost' ], true ) ) { return; } @@ -268,7 +272,7 @@ function save_pledge( $pledge_id, $pledge ) { $submitted_meta = PledgeForm\get_form_submission(); - if ( is_wp_error( has_required_pledge_meta( $submitted_meta ) ) ) { + if ( is_wp_error( has_required_pledge_meta( $submitted_meta, $context ) ) ) { return; } @@ -294,6 +298,10 @@ function save_pledge_meta( $pledge_id, $new_values ) { $config = get_pledge_meta_config(); foreach ( $new_values as $key => $value ) { + // A null value can happen if the submission form did not have a given field. + if ( is_null( $value ) ) { + continue; + } if ( array_key_exists( $key, $config ) ) { $meta_key = META_PREFIX . $key; @@ -391,14 +399,22 @@ function update_single_cached_pledge_data( $pledge_id ) { /** * Check that an array contains values for all required keys. * - * @return bool|WP_Error True if all required values are present. Otherwise WP_Error. + * @param array $submission Form submission data. + * @param string $context Whether this is a new pledge (`create`) or an edit to an existing one (`update`). + * + * @return true|WP_Error True if all required values are present. Otherwise WP_Error. */ -function has_required_pledge_meta( array $submission ) { +function has_required_pledge_meta( array $submission, $context ) { $error = new WP_Error(); - $required = array_keys( get_pledge_meta_config( 'user_input' ) ); + $meta_config = get_pledge_meta_config( 'user_input' ); + $required = array_keys( $meta_config ); foreach ( $required as $key ) { + if ( ! in_array( $context, $meta_config[ $key ]['context'] ) ) { + continue; + } + if ( ! isset( $submission[ $key ] ) || is_null( $submission[ $key ] ) ) { $error->add( 'required_field_empty', @@ -428,19 +444,23 @@ function has_required_pledge_meta( array $submission ) { /** * Get the metadata for a given pledge, or a default set if no pledge is provided. * - * @param int $pledge_id - * @param string $context + * @param int $pledge_id Pledge to fetch data from. + * @param string $subset Optional. The part of the config to return: 'user_input', 'generated', or 'all'. + * * @return array Pledge data */ -function get_pledge_meta( $pledge_id = 0, $context = '' ) { +function get_pledge_meta( $pledge_id = 0, $subset = '' ) { // Get existing pledge, if it exists. $pledge = get_post( $pledge_id ); - $keys = get_pledge_meta_config( $context ); + $keys = get_pledge_meta_config( $subset ); $meta = array(); // Get POST'd submission, if it exists. $submission = PledgeForm\get_form_submission(); + if ( isset( $submission['empty_post'] ) && $submission['empty_post'] ) { + $submission = array(); + } foreach ( $keys as $key => $config ) { if ( isset( $submission[ $key ] ) ) { diff --git a/plugins/wporg-5ftf/views/form-pledge-manage.php b/plugins/wporg-5ftf/views/form-pledge-manage.php index ee94b2b..d620b3f 100644 --- a/plugins/wporg-5ftf/views/form-pledge-manage.php +++ b/plugins/wporg-5ftf/views/form-pledge-manage.php @@ -1,25 +1,38 @@ -
+ + + diff --git a/plugins/wporg-5ftf/views/inputs-pledge-org-info.php b/plugins/wporg-5ftf/views/inputs-pledge-org-info.php index c2b5ff8..a5fc662 100644 --- a/plugins/wporg-5ftf/views/inputs-pledge-org-info.php +++ b/plugins/wporg-5ftf/views/inputs-pledge-org-info.php @@ -1,8 +1,13 @@