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-10-24 17:55:45 +03:00
use WP_Error , WP_User ;
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-11-09 01:32:28 +02:00
add_shortcode ( '5ftf_pledge_form_manage_link' , __NAMESPACE__ . '\render_manage_link_request' );
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-10-24 17:55:45 +03:00
$data = get_form_submission ();
2019-11-07 23:43:09 +02:00
$errors = [];
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-10-25 22:07:09 +03:00
$view = 'form-pledge-new.php' ;
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-30 01:37:14 +02:00
} elseif ( 'confirm_pledge_email' === $action ) {
2019-10-25 22:07:09 +03:00
$view = 'form-pledge-confirm-email.php' ;
$pledge_id = filter_input ( INPUT_GET , 'pledge_id' , FILTER_VALIDATE_INT );
$unverified_token = filter_input ( INPUT_GET , 'auth_token' , FILTER_SANITIZE_STRING );
2019-10-26 18:23:41 +03:00
$email_confirmed = process_pledge_confirmation_email ( $pledge_id , $action , $unverified_token );
2019-10-30 01:37:14 +02:00
$pledge = get_post ( $pledge_id );
2019-11-07 23:43:09 +02:00
2019-10-30 01:37:14 +02:00
} elseif ( filter_input ( INPUT_GET , 'resend_pledge_confirmation' ) ) {
2019-10-27 07:18:28 +02:00
$pledge_id = filter_input ( INPUT_GET , 'pledge_id' , FILTER_VALIDATE_INT );
$complete = true ;
2019-11-20 17:40:45 +02:00
Email\send_pledge_confirmation_email ( $pledge_id , get_post () -> ID );
2019-09-28 03:41:19 +03:00
}
2019-10-04 22:35:02 +03:00
ob_start ();
2019-10-09 01:29:35 +03:00
$readonly = false ;
2019-10-25 22:07:09 +03:00
require FiveForTheFuture\get_views_path () . $view ;
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-10-30 01:37:14 +02:00
$has_error = check_invalid_submission ( $submission );
2019-10-28 17:47:56 +02:00
if ( $has_error ) {
return $has_error ;
2019-09-28 03:41:19 +03:00
}
2019-10-22 01:43:20 +03:00
$contributors = 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-25 22:07:09 +03:00
/**
* Process a request to confirm a company ' s email address .
*
* @ param int $pledge_id
* @ param string $action
* @ param array $unverified_token
*
* @ return bool
*/
2019-10-26 18:23:41 +03:00
function process_pledge_confirmation_email ( $pledge_id , $action , $unverified_token ) {
2019-10-25 22:07:09 +03:00
$meta_key = PledgeMeta\META_PREFIX . 'pledge-email-confirmed' ;
$already_confirmed = get_post ( $pledge_id ) -> $meta_key ;
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 .
*/
return true ;
}
2019-11-20 17:40:45 +02:00
$email_confirmed = Auth\is_valid_authentication_token ( $pledge_id , $action , $unverified_token );
2019-10-25 22:07:09 +03:00
if ( $email_confirmed ) {
update_post_meta ( $pledge_id , $meta_key , true );
2019-11-14 20:48:17 +02:00
wp_update_post ( array (
'ID' => $pledge_id ,
'post_status' => 'publish' ,
) );
2019-11-20 17:40:45 +02:00
Email\send_contributor_confirmation_emails ( $pledge_id );
2019-10-25 22:07:09 +03:00
}
return $email_confirmed ;
}
2019-10-04 22:35:02 +03:00
/**
* Render the form ( s ) for managing existing pledges .
*
* @ return false | string
*/
function render_form_manage () {
$action = filter_input ( INPUT_POST , 'action' );
$messages = [];
$updated = false ;
2019-10-09 01:29:35 +03:00
// @todo Get pledge ID from somewhere.
$data = PledgeMeta\get_pledge_meta ();
2019-10-04 22:35:02 +03:00
if ( 'Update Pledge' === $action ) {
$processed = process_form_manage ();
if ( is_wp_error ( $processed ) ) {
$messages = array_merge ( $messages , $processed -> get_error_messages () );
} elseif ( 'success' === $processed ) {
$updated = true ;
}
}
ob_start ();
2019-10-09 01:29:35 +03:00
$readonly = false ;
2019-10-04 22:35:02 +03:00
require FiveForTheFuture\PATH . 'views/form-pledge-manage.php' ;
return ob_get_clean ();
}
2019-11-09 01:32:28 +02:00
/**
* Render the `render_manage_link_request` shortcode .
*/
function render_manage_link_request () {
// @todo enable when https://github.com/WordPress/five-for-the-future/issues/6 is done
if ( ! defined ( 'WPORG_SANDBOXED' ) || ! WPORG_SANDBOXED ) {
return ;
}
$result = process_manage_link_request ();
if ( is_wp_error ( $result ) ) {
$errors = array ( $result -> get_error_message () );
} elseif ( ! is_null ( $result ) ) {
$messages = array ( $result );
}
require_once FiveForTheFuture\get_views_path () . 'form-pledge-request-manage-link.php' ;
}
/**
* Process a request for a pledge management link .
*
* @ return null | string | WP_Error `null` if the form wasn ' t submitted ; `string` with a success message ;
* `WP_Error` with an error message .
*/
function process_manage_link_request () {
if ( ! filter_input ( INPUT_POST , 'get_manage_pledge_link' ) ) {
return null ;
}
$unverified_pledge_id = filter_input ( INPUT_POST , 'pledge_id' , FILTER_VALIDATE_INT );
$unverified_admin_email = filter_input ( INPUT_POST , 'pledge_admin_address' , FILTER_VALIDATE_EMAIL );
$valid_admin_email = get_post ( $unverified_pledge_id ) -> { PledgeMeta\META_PREFIX . 'org-pledge-email' };
if ( $valid_admin_email && $valid_admin_email === $unverified_admin_email ) {
$verified_pledge_id = $unverified_pledge_id ; // The addresses will only match is the pledge ID is valid.
2019-11-20 17:40:45 +02:00
$message_sent = Email\send_manage_pledge_link ( $verified_pledge_id );
2019-11-09 01:32:28 +02:00
if ( $message_sent ) {
$result = __ ( " Thanks! We've emailed you a link you can open in order to update your pledge. " , 'wporg-5ftf' );
} else {
$result = new WP_Error ( 'email_failed' , __ ( 'There was an error while trying to send the email.' , 'wporg-5ftf' ) );
}
} else {
$error_message = sprintf (
__ ( 'That\'s not the address that we have for this pledge, please try a different one. If none of the addresses you try are working, please <a href="%s">email us</a> for help.' , 'wporg-5ftf' ),
get_permalink ( get_page_by_path ( 'report' ) )
);
$result = new WP_Error ( 'invalid_pledge_email' , $error_message );
}
return $result ;
}
2019-10-04 22:35:02 +03:00
/**
2019-10-19 02:56:21 +03:00
* Process a submission from the Manage Existing Pledge form .
2019-10-04 22:35:02 +03:00
*
2019-10-21 21:10:55 +03:00
* TODO This doesn ' t actually update any data yet when the form is submitted .
*
2019-10-04 22:35:02 +03:00
* @ return string | WP_Error String " success " if the form processed correctly . Otherwise WP_Error .
*/
function process_form_manage () {
2019-10-19 02:56:21 +03:00
$submission = get_form_submission ();
2019-11-14 20:48:17 +02:00
$has_error = check_invalid_submission ( $submission );
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
// todo email any new contributors for confirmation
// notify any removed contributors?
// ask them to update their profiles?
// automatically update contributor profiles?
// anything else?
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-10-19 02:56:21 +03:00
* @ return array | bool
*/
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 ) {
return array_fill_keys ( array_keys ( $input_filters ), '' );
}
return $result ;
2019-10-19 02:56:21 +03:00
}
/**
* Check a key value against existing pledges to see if one already exists .
2019-10-04 22:35:02 +03:00
*
2019-10-19 02:56:21 +03:00
* @ param string $key The value to match against other pledges .
* @ param string $key_type The type of value being matched . `email` or `domain` .
* @ param int $current_pledge_id Optional . The post ID of the pledge to compare against others .
2019-10-04 22:35:02 +03:00
*
* @ return bool
*/
2019-10-19 02:56:21 +03:00
function has_existing_pledge ( $key , $key_type , int $current_pledge_id = 0 ) {
2019-10-04 22:35:02 +03:00
$args = array (
'post_type' => Pledge\CPT_ID ,
2019-10-19 02:56:21 +03:00
'post_status' => array ( 'draft' , 'pending' , 'publish' ),
2019-10-04 22:35:02 +03:00
);
2019-10-19 02:56:21 +03:00
switch ( $key_type ) {
case 'email' :
$args [ 'meta_query' ] = array (
array (
'key' => PledgeMeta\META_PREFIX . 'org-pledge-email' ,
'value' => $key ,
),
);
break ;
case 'domain' :
$args [ 'meta_query' ] = array (
array (
'key' => PledgeMeta\META_PREFIX . 'org-domain' ,
'value' => $key ,
),
);
break ;
}
2019-10-04 22:35:02 +03:00
if ( $current_pledge_id ) {
$args [ 'exclude' ] = array ( $current_pledge_id );
}
$matching_pledge = get_posts ( $args );
return ! empty ( $matching_pledge );
}
2019-10-19 02:56:21 +03:00
/**
* Ensure each item in a list of usernames is valid and corresponds to a user .
*
* @ param string $contributors A comma - separated list of username strings .
*
* @ return array | WP_Error An array of sanitized wporg usernames on success . Otherwise WP_Error .
*/
function parse_contributors ( $contributors ) {
$invalid_contributors = array ();
$sanitized_contributors = array ();
2019-10-30 21:30:00 +02:00
$contributors = str_replace ( '@' , '' , $contributors );
2019-10-19 02:56:21 +03:00
$contributors = explode ( ',' , $contributors );
foreach ( $contributors as $wporg_username ) {
$sanitized_username = sanitize_user ( $wporg_username );
$user = get_user_by ( 'login' , $sanitized_username );
2019-10-30 21:28:51 +02:00
if ( ! $user instanceof WP_User ) {
$user = get_user_by ( 'slug' , $sanitized_username );
}
2019-10-19 02:56:21 +03:00
if ( $user instanceof WP_User ) {
2019-10-31 06:30:34 +02:00
$sanitized_contributors [] = $user -> user_login ;
2019-10-19 02:56:21 +03:00
} else {
$invalid_contributors [] = $wporg_username ;
}
}
if ( ! empty ( $invalid_contributors ) ) {
/* translators: Used between sponsor names in a list, there is a space after the comma. */
$item_separator = _x ( ', ' , 'list item separator' , 'wporg' );
return new WP_Error (
'invalid_contributor' ,
sprintf (
/* translators: %s is a list of usernames. */
__ ( 'The following contributor usernames are not valid: %s' , 'wporg' ),
implode ( $item_separator , $invalid_contributors )
)
);
}
if ( empty ( $sanitized_contributors ) ) {
return new WP_Error (
'contributor_required' ,
__ ( 'The pledge must have at least one contributor username.' , 'wporg' )
);
}
$sanitized_contributors = array_unique ( $sanitized_contributors );
return $sanitized_contributors ;
}
2019-10-28 17:47:56 +02:00
/**
* Check the submission for valid data .
*
* @ 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 );
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
);
if ( has_existing_pledge ( $email , 'email' ) ) {
return new WP_Error (
'existing_pledge_email' ,
__ ( 'This email address is already connected to an existing pledge.' , 'wporg' )
);
}
$domain = PledgeMeta\get_normalized_domain_from_url ( $submission [ 'org-url' ] );
if ( has_existing_pledge ( $domain , 'domain' ) ) {
return new WP_Error (
'existing_pledge_domain' ,
__ ( 'A pledge already exists for this domain.' , 'wporg' )
);
}
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 ;
}