2019-09-28 03:41:19 +03:00
< ? php
2019-10-01 03:04:49 +03:00
/**
2019-10-09 01:29:35 +03:00
* Render and process the pledge forms .
2019-10-01 03:04:49 +03:00
*/
2019-09-28 03:41:19 +03:00
namespace WordPressDotOrg\FiveForTheFuture\PledgeForm ;
2019-10-01 03:04:49 +03:00
2019-09-28 03:41:19 +03:00
use WordPressDotOrg\FiveForTheFuture ;
2019-11-20 17:40:45 +02:00
use WordPressDotOrg\FiveForTheFuture\ { Auth , Contributor , Email , Pledge , PledgeMeta };
2019-12-03 22:09:35 +02:00
use WP_Error ;
2019-09-28 03:41:19 +03:00
defined ( 'WPINC' ) || die ();
2019-10-04 22:35:02 +03:00
// Todo make this into simple optionless blocks instead?
add_shortcode ( '5ftf_pledge_form_new' , __NAMESPACE__ . '\render_form_new' );
add_shortcode ( '5ftf_pledge_form_manage' , __NAMESPACE__ . '\render_form_manage' );
2019-10-01 03:04:49 +03:00
2019-11-26 22:37:19 +02:00
// Short-circuit out of both shortcodes for shared functionality (confirming admin email, resending the pledge
// confirmation email).
add_filter ( 'pre_do_shortcode_tag' , __NAMESPACE__ . '\process_confirmed_email' , 10 , 2 );
add_filter ( 'pre_do_shortcode_tag' , __NAMESPACE__ . '\process_resend_confirm_email' , 10 , 2 );
2019-10-01 03:04:49 +03:00
/**
2019-10-04 22:35:02 +03:00
* Render the form ( s ) for creating new pledges .
2019-10-01 03:04:49 +03:00
*
* @ return false | string
*/
2019-10-04 22:35:02 +03:00
function render_form_new () {
2019-10-25 22:07:09 +03:00
$action = isset ( $_GET [ 'action' ] ) ? filter_input ( INPUT_GET , 'action' ) : filter_input ( INPUT_POST , 'action' );
2019-12-03 22:09:35 +02:00
$is_manage = false ;
2019-11-21 23:42:59 +02:00
$pledge_id = 0 ;
2019-10-24 17:55:45 +03:00
$data = get_form_submission ();
2020-11-13 22:16:06 +02:00
$errors = array ();
2019-10-30 01:37:14 +02:00
$pledge = null ;
2019-10-24 17:55:45 +03:00
$complete = false ;
2019-11-08 01:31:02 +02:00
$directory_url = home_url ( 'pledges' );
2019-09-28 03:41:19 +03:00
2019-10-04 22:35:02 +03:00
if ( 'Submit Pledge' === $action ) {
2019-10-30 01:37:14 +02:00
$pledge_id = process_form_new ();
2019-09-28 03:41:19 +03:00
2019-10-30 01:37:14 +02:00
if ( is_wp_error ( $pledge_id ) ) {
2019-11-07 23:43:09 +02:00
$errors = array_merge ( $errors , $pledge_id -> get_error_messages () );
2019-10-30 01:37:14 +02:00
} elseif ( is_int ( $pledge_id ) ) {
2019-09-28 03:41:19 +03:00
$complete = true ;
}
}
2019-10-04 22:35:02 +03:00
ob_start ();
2019-10-09 01:29:35 +03:00
$readonly = false ;
2019-12-11 23:12:23 +02:00
require FiveForTheFuture\get_views_path () . 'form-pledge-new.php' ;
2019-09-28 03:41:19 +03:00
2019-10-04 22:35:02 +03:00
return ob_get_clean ();
2019-09-28 03:41:19 +03:00
}
/**
2019-10-19 02:56:21 +03:00
* Process a submission from the New Pledge form .
2019-09-28 03:41:19 +03:00
*
2019-10-30 01:37:14 +02:00
* @ return int | WP_Error The post ID of the new pledge if the form processed correctly . Otherwise WP_Error .
2019-09-28 03:41:19 +03:00
*/
2019-10-04 22:35:02 +03:00
function process_form_new () {
2019-10-19 02:56:21 +03:00
$submission = get_form_submission ();
2019-11-21 23:42:59 +02:00
$has_error = check_invalid_submission ( $submission , 'create' );
2019-10-28 17:47:56 +02:00
if ( $has_error ) {
return $has_error ;
2019-09-28 03:41:19 +03:00
}
2019-11-21 23:30:50 +02:00
$contributors = Contributor\parse_contributors ( $submission [ 'pledge-contributors' ] );
2019-10-19 02:56:21 +03:00
if ( is_wp_error ( $contributors ) ) {
return $contributors ;
}
2019-10-28 17:47:56 +02:00
$logo_attachment_id = upload_image ( $_FILES [ 'org-logo' ] );
if ( is_wp_error ( $logo_attachment_id ) ) {
2019-10-30 02:22:11 +02:00
return $logo_attachment_id ;
2019-10-28 17:47:56 +02:00
}
2019-09-28 03:41:19 +03:00
$name = sanitize_meta (
2019-10-04 22:35:02 +03:00
PledgeMeta\META_PREFIX . 'org-name' ,
$submission [ 'org-name' ],
2019-09-28 03:41:19 +03:00
'post' ,
2019-10-01 03:04:49 +03:00
Pledge\CPT_ID
2019-09-28 03:41:19 +03:00
);
2019-10-22 01:43:20 +03:00
$new_pledge_id = Pledge\create_new_pledge ( $name );
2019-09-28 03:41:19 +03:00
2019-10-22 01:43:20 +03:00
if ( is_wp_error ( $new_pledge_id ) ) {
return $new_pledge_id ;
}
2019-10-29 21:46:13 +02:00
Contributor\add_pledge_contributors ( $new_pledge_id , $contributors );
2019-09-28 03:41:19 +03:00
2019-10-28 17:47:56 +02:00
// Attach logo to the pledge.
2019-10-30 02:22:11 +02:00
wp_update_post ( array (
2019-10-28 17:47:56 +02:00
'ID' => $logo_attachment_id ,
'post_parent' => $new_pledge_id ,
) );
2019-10-30 02:22:11 +02:00
set_post_thumbnail ( $new_pledge_id , $logo_attachment_id );
2019-10-28 17:47:56 +02:00
2019-10-30 01:37:14 +02:00
return $new_pledge_id ;
2019-09-28 03:41:19 +03:00
}
2019-10-04 22:35:02 +03:00
/**
* Render the form ( s ) for managing existing pledges .
*
* @ return false | string
*/
function render_form_manage () {
2019-11-21 23:42:59 +02:00
/*
* Prevent Gutenberg from executing this on the Edit Post screen .
* See https :// github . com / WordPress / gutenberg / issues / 18394
*/
if ( is_admin () ) {
return '' ;
}
2020-11-13 22:16:06 +02:00
$messages = array ();
$errors = array ();
2019-10-04 22:35:02 +03:00
2019-11-21 23:42:59 +02:00
$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 ) ) {
2019-12-11 23:12:23 +02:00
$errors = array ( strip_tags ( $can_view_form -> get_error_message () ) );
2020-11-13 22:16:06 +02:00
} elseif ( ! Pledge\is_active_pledge ( $pledge_id ) ) {
2019-12-10 21:07:48 +02:00
$errors = array (
sprintf (
__ ( 'This pledge has been removed from Five for the Future. If this was a mistake, please <a href="%s">contact us</a> to reactivate your pledge.' , 'wporg-5ftf' ),
get_permalink ( get_page_by_path ( 'report' ) )
),
);
}
2019-11-21 23:42:59 +02:00
2019-12-11 23:12:23 +02:00
if ( Pledge\is_active_pledge ( $pledge_id ) && is_wp_error ( $can_view_form ) ) {
ob_start ();
require FiveForTheFuture\get_views_path () . 'partial-request-manage-link.php' ;
return ob_get_clean ();
}
2019-12-10 21:07:48 +02:00
if ( count ( $errors ) > 0 ) {
2019-11-21 23:42:59 +02:00
ob_start ();
2019-12-11 23:12:23 +02:00
require FiveForTheFuture\get_views_path () . 'partial-result-messages.php' ;
2019-11-21 23:42:59 +02:00
return ob_get_clean ();
}
2019-12-10 21:07:48 +02:00
if ( 'remove-pledge' === $action ) {
$results = process_form_remove ( $pledge_id , $auth_token );
if ( is_wp_error ( $results ) ) {
$errors = $results -> get_error_messages ();
} else {
$messages = array (
sprintf (
__ ( 'Your pledge has been removed. If this was a mistake, please <a href="%s">contact us</a> to reactivate your pledge.' , 'wporg-5ftf' ),
get_permalink ( get_page_by_path ( 'report' ) )
),
);
}
ob_start ();
2019-12-11 23:12:23 +02:00
require FiveForTheFuture\get_views_path () . 'partial-result-messages.php' ;
2019-12-10 21:07:48 +02:00
return ob_get_clean ();
2020-11-13 22:16:06 +02:00
} elseif ( 'Update Pledge' === $action ) {
2019-11-21 23:42:59 +02:00
$results = process_form_manage ( $pledge_id , $auth_token );
2019-10-04 22:35:02 +03:00
2019-11-21 23:42:59 +02:00
if ( is_wp_error ( $results ) ) {
$errors = $results -> get_error_messages ();
} else {
$messages = array ( __ ( 'Your pledge has been updated.' , 'wporg-5ftf' ) );
2019-11-26 22:37:19 +02:00
$meta_key = PledgeMeta\META_PREFIX . 'pledge-email-confirmed' ;
if ( ! get_post ( $pledge_id ) -> $meta_key ) {
$messages [] = __ ( 'You must confirm your new email address before it will be visible.' , 'wporg-5ftf' );
}
2019-10-04 22:35:02 +03:00
}
}
2019-11-26 19:57:14 +02:00
$data = PledgeMeta\get_pledge_meta ( $pledge_id );
$contributors = Contributor\get_pledge_contributors_data ( $pledge_id );
2019-11-21 23:42:59 +02:00
2019-10-04 22:35:02 +03:00
ob_start ();
2019-11-26 22:37:19 +02:00
$readonly = false ;
2019-11-21 23:42:59 +02:00
$is_manage = true ;
2019-12-11 23:12:23 +02:00
require FiveForTheFuture\get_views_path () . 'form-pledge-manage.php' ;
2019-10-04 22:35:02 +03:00
return ob_get_clean ();
}
/**
2019-11-21 23:42:59 +02:00
* Process a submission from the Manage Pledge form .
2019-10-04 22:35:02 +03:00
*
2019-11-21 23:42:59 +02:00
* @ return WP_Error | true An error if the pledge could not be saved . Otherwise true .
2019-10-04 22:35:02 +03:00
*/
2019-11-21 23:42:59 +02:00
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 );
2019-11-26 19:57:14 +02:00
if ( ! $has_valid_nonce || is_wp_error ( $can_view_form ) ) {
2019-11-21 23:42:59 +02:00
return new WP_Error (
'invalid_token' ,
sprintf (
__ ( 'Your link has expired, please <a href="%s">obtain a new one</a>.' , 'wporg-5ftf' ),
get_permalink ( $pledge_id )
)
);
}
2019-10-19 02:56:21 +03:00
$submission = get_form_submission ();
2019-11-26 22:37:19 +02:00
$has_error = check_invalid_submission ( $submission , 'update' );
2019-10-28 17:47:56 +02:00
if ( $has_error ) {
return $has_error ;
2019-10-04 22:35:02 +03:00
}
2019-10-25 22:07:09 +03:00
2019-11-21 23:42:59 +02:00
PledgeMeta\save_pledge_meta ( $pledge_id , $submission );
2019-11-22 02:00:12 +02:00
if ( isset ( $_FILES [ 'org-logo' ], $_FILES [ 'org-logo' ][ 'tmp_name' ] ) && ! empty ( $_FILES [ 'org-logo' ][ 'tmp_name' ] ) ) {
$original_logo_id = get_post_thumbnail_id ( $pledge_id );
$logo_attachment_id = upload_image ( $_FILES [ 'org-logo' ] );
if ( is_wp_error ( $logo_attachment_id ) ) {
return $logo_attachment_id ;
}
// Attach new logo to the pledge.
wp_update_post ( array (
'ID' => $logo_attachment_id ,
'post_parent' => $pledge_id ,
) );
$updated = set_post_thumbnail ( $pledge_id , $logo_attachment_id );
// Trash the old logo.
if ( $updated ) {
$orig_logo_attachment = get_post ( $original_logo_id );
if ( $orig_logo_attachment && $pledge_id === $orig_logo_attachment -> post_parent ) {
wp_delete_attachment ( $original_logo_id );
}
}
}
2019-11-21 23:42:59 +02:00
// If we made it to here, we've successfully saved the pledge.
return true ;
2019-10-04 22:35:02 +03:00
}
2019-12-10 21:07:48 +02:00
/**
* Process a submission from the Manage Pledge form .
*
* @ return WP_Error | true An error if the pledge could not be saved . Otherwise true .
*/
function process_form_remove ( $pledge_id , $auth_token ) {
$errors = array ();
$nonce = filter_input ( INPUT_POST , '_wpnonce' , FILTER_SANITIZE_STRING );
$nonce_action = 'remove_pledge_' . $pledge_id ;
$has_valid_nonce = wp_verify_nonce ( $nonce , $nonce_action );
$can_view_form = Auth\can_manage_pledge ( $pledge_id , $auth_token );
if ( ! $has_valid_nonce || is_wp_error ( $can_view_form ) ) {
return new WP_Error (
'invalid_token' ,
sprintf (
__ ( 'Your link has expired, please <a href="%s">obtain a new one</a>.' , 'wporg-5ftf' ),
get_permalink ( $pledge_id )
)
);
}
$result = Pledge\deactivate ( $pledge_id , true );
if ( is_wp_error ( $result ) ) {
return $result ;
}
// If we made it to here, we've successfully removed the pledge.
return true ;
}
2019-11-26 22:37:19 +02:00
/**
* Process a request to confirm a company ' s email address .
*
* @ param string | false $value Short - circuit return value .
* @ param string $tag Shortcode name .
*
* @ return bool | string
*/
function process_confirmed_email ( $value , $tag ) {
2019-12-03 22:09:35 +02:00
if ( ! in_array ( $tag , array ( '5ftf_pledge_form_new' , '5ftf_pledge_form_manage' ), true ) ) {
2019-11-26 22:37:19 +02:00
return $value ;
}
$action = sanitize_text_field ( $_REQUEST [ 'action' ] ? ? '' );
if ( 'confirm_pledge_email' !== $action ) {
return $value ;
}
$pledge_id = filter_input ( INPUT_GET , 'pledge_id' , FILTER_VALIDATE_INT );
$auth_token = filter_input ( INPUT_GET , 'auth_token' , FILTER_SANITIZE_STRING );
$meta_key = PledgeMeta\META_PREFIX . 'pledge-email-confirmed' ;
$already_confirmed = get_post ( $pledge_id ) -> $meta_key ;
$email_confirmed = false ;
$is_new_pledge = '5ftf_pledge_form_new' === $tag ;
if ( $already_confirmed ) {
/*
* If they refresh the page after confirming , they ' d otherwise get an error because the token had been
* used , and might be confused and think that the address wasn ' t confirmed .
*
* This leaks the fact that the address is confirmed , because it will return true even if the token is
* invalid , but there aren ' t any security / privacy implications of that .
*/
$email_confirmed = true ;
}
$email_confirmed = Auth\is_valid_authentication_token ( $pledge_id , $action , $auth_token );
if ( $email_confirmed ) {
update_post_meta ( $pledge_id , $meta_key , true );
wp_update_post ( array (
'ID' => $pledge_id ,
'post_status' => 'publish' ,
) );
if ( $is_new_pledge ) {
Email\send_contributor_confirmation_emails ( $pledge_id );
}
}
ob_start ();
$directory_url = home_url ( 'pledges' );
$pledge = get_post ( $pledge_id );
require FiveForTheFuture\get_views_path () . 'form-pledge-confirm-email.php' ;
return ob_get_clean ();
}
/**
* Process a request to resed a company ' s confirmation email .
*
* @ param string | false $value Short - circuit return value .
* @ param string $tag Shortcode name .
*
* @ return bool | string
*/
function process_resend_confirm_email ( $value , $tag ) {
2019-12-03 22:09:35 +02:00
if ( ! in_array ( $tag , array ( '5ftf_pledge_form_new' , '5ftf_pledge_form_manage' ), true ) ) {
2019-11-26 22:37:19 +02:00
return $value ;
}
$action = sanitize_text_field ( $_REQUEST [ 'action' ] ? ? '' );
if ( 'resend_pledge_confirmation' !== $action ) {
return $value ;
}
$pledge_id = filter_input ( INPUT_GET , 'pledge_id' , FILTER_VALIDATE_INT );
Email\send_pledge_confirmation_email ( $pledge_id , get_post () -> ID );
$messages = array (
sprintf (
__ ( 'We’ ve emailed you a new link to confirm your address for %s.' , 'wporg-5ftf' ),
get_the_title ( $pledge_id )
),
);
ob_start ();
require FiveForTheFuture\get_views_path () . 'partial-result-messages.php' ;
return ob_get_clean ();
}
2019-10-04 22:35:02 +03:00
/**
2019-10-19 02:56:21 +03:00
* Get and sanitize $_POST values from a form submission .
2019-10-04 22:35:02 +03:00
*
2019-11-21 23:42:59 +02:00
* @ return array
2019-10-19 02:56:21 +03:00
*/
function get_form_submission () {
$input_filters = array_merge (
// Inputs that correspond to meta values.
wp_list_pluck ( PledgeMeta\get_pledge_meta_config ( 'user_input' ), 'php_filter' ),
// Inputs with no corresponding meta value.
array (
2019-10-22 01:43:20 +03:00
'pledge-contributors' => FILTER_SANITIZE_STRING ,
2019-10-19 02:56:21 +03:00
'pledge-agreement' => FILTER_VALIDATE_BOOLEAN ,
)
);
2019-11-16 00:40:53 +02:00
$result = filter_input_array ( INPUT_POST , $input_filters );
if ( ! $result ) {
2019-11-26 22:37:19 +02:00
$result = array_fill_keys ( array_keys ( $input_filters ), '' );
2019-11-21 23:42:59 +02:00
$result [ 'empty_post' ] = true ;
2019-11-16 00:40:53 +02:00
}
return $result ;
2019-10-19 02:56:21 +03:00
}
2019-10-28 17:47:56 +02:00
/**
* Check the submission for valid data .
*
2019-11-21 23:42:59 +02:00
* @ param array $submission The user input .
* @ param string $context Whether this is a new pledge ( `create` ) or an edit to an existing one ( `update` ) .
*
2019-10-28 17:47:56 +02:00
* @ return false | WP_Error Return any errors in the submission , or false if no errors .
*/
2019-11-21 23:42:59 +02:00
function check_invalid_submission ( $submission , $context ) {
$has_required = PledgeMeta\has_required_pledge_meta ( $submission , $context );
2019-10-28 17:47:56 +02:00
if ( is_wp_error ( $has_required ) ) {
return $has_required ;
}
$email = sanitize_meta (
PledgeMeta\META_PREFIX . 'org-pledge-email' ,
$submission [ 'org-pledge-email' ],
'post' ,
Pledge\CPT_ID
);
2019-11-21 23:42:59 +02:00
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' )
);
}
2019-10-28 17:47:56 +02:00
2019-11-21 23:42:59 +02:00
$domain = PledgeMeta\get_normalized_domain_from_url ( $submission [ 'org-url' ] );
2019-10-28 17:47:56 +02:00
2019-11-21 23:42:59 +02:00
if ( Pledge\has_existing_pledge ( $domain , 'domain' ) ) {
return new WP_Error (
'existing_pledge_domain' ,
__ ( 'A pledge already exists for this domain.' , 'wporg-5ftf' )
);
}
2019-10-28 17:47:56 +02:00
}
return false ;
}
/**
* Upload the logo image into the media library .
*
* @ param array $logo $_FILES array for the uploaded logo .
* @ return int | WP_Error Upload attachment ID , or WP_Error if there was an error .
*/
function upload_image ( $logo ) {
if ( ! $logo ) {
return false ;
}
// Process image.
if ( ! function_exists ( 'media_handle_upload' ) ) {
require_once ABSPATH . 'wp-admin/includes/image.php' ;
require_once ABSPATH . 'wp-admin/includes/file.php' ;
require_once ABSPATH . 'wp-admin/includes/media.php' ;
}
if ( ! function_exists ( 'check_upload_size' ) ) {
require_once ABSPATH . 'wp-includes/ms-functions.php' ;
require_once ABSPATH . 'wp-admin/includes/ms.php' ;
}
add_filter ( 'upload_mimes' , __NAMESPACE__ . '\safelist_image_mimes' );
add_filter ( 'pre_site_option_fileupload_maxk' , __NAMESPACE__ . '\restrict_file_size' );
add_filter ( 'wp_handle_sideload_prefilter' , 'check_upload_size' );
$logo_id = \media_handle_sideload ( $logo , 0 );
remove_filter ( 'upload_mimes' , __NAMESPACE__ . '\safelist_image_mimes' );
remove_filter ( 'pre_site_option_fileupload_maxk' , __NAMESPACE__ . '\restrict_file_size' );
remove_filter ( 'wp_handle_sideload_prefilter' , 'check_upload_size' );
return $logo_id ;
}
/**
* Only allow image mime types .
*
* @ param array $mimes Mime types keyed by the file extension regex corresponding to those types .
*/
function safelist_image_mimes ( $mimes ) {
return array (
'jpg|jpeg|jpe' => 'image/jpeg' ,
'gif' => 'image/gif' ,
'png' => 'image/png' ,
);
}
/**
* Restrict images uploaded by this form to be less than 5 MB .
*
* @ param bool $value Null – returning a value will short - circuit the option lookup .
*/
function restrict_file_size ( $value ) {
return 5 * MB_IN_BYTES ;
}