mirror of
https://github.com/WordPress/five-for-the-future.git
synced 2025-04-21 10:33:44 +03:00
Contributors: Reset profile hours when pledge deactivated.
See https://github.com/WordPress/five-for-the-future/issues/169
This commit is contained in:
parent
834c62c0d0
commit
c6d7bbb7c1
|
@ -166,6 +166,22 @@ function add_pledge_contributors( $pledge_id, $contributors ) {
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all of the contributors for the given pledge.
|
||||||
|
*
|
||||||
|
* Some contributors are sponsored by multiple companies. They'll have a `5ftf_contributor` post for each company,
|
||||||
|
* but only the post associated with the given pledge should be removed.
|
||||||
|
*/
|
||||||
|
function remove_pledge_contributors( int $pledge_id ) : void {
|
||||||
|
$contributors = get_pledge_contributors( $pledge_id, 'all' );
|
||||||
|
|
||||||
|
foreach ( $contributors as $status_group ) {
|
||||||
|
foreach ( $status_group as $contributor ) {
|
||||||
|
remove_contributor( $contributor->ID );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a contributor post from a pledge.
|
* Remove a contributor post from a pledge.
|
||||||
*
|
*
|
||||||
|
@ -186,6 +202,21 @@ function remove_contributor( $contributor_post_id ) {
|
||||||
Email\send_contributor_removed_email( $pledge_id, $contributor );
|
Email\send_contributor_removed_email( $pledge_id, $contributor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$has_additional_sponsors = get_posts( array(
|
||||||
|
'post_type' => CPT_ID,
|
||||||
|
'title' => $contributor->post_title,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
|
||||||
|
// `pending` contributors never confirmed they were associated with the company, so their profile data isn't
|
||||||
|
// tied to the pledge, and shouldn't be reset. If a user has multiple sponsors, we don't know which hours are
|
||||||
|
// sponsored by which company, so just leave them all.
|
||||||
|
if ( 'publish' === $old_status && ! $has_additional_sponsors ) {
|
||||||
|
$user = get_user_by( 'login', $contributor->post_title );
|
||||||
|
|
||||||
|
XProfile\reset_contribution_data( $user->ID );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Action: Fires when a contributor is removed from a pledge.
|
* Action: Fires when a contributor is removed from a pledge.
|
||||||
*
|
*
|
||||||
|
|
|
@ -128,13 +128,23 @@ function send_contributor_confirmation_emails( $pledge_id, $contributor_id = nul
|
||||||
function send_contributor_removed_email( $pledge_id, $contributor ) {
|
function send_contributor_removed_email( $pledge_id, $contributor ) {
|
||||||
$pledge = get_post( $pledge_id );
|
$pledge = get_post( $pledge_id );
|
||||||
$subject = "Removed from {$pledge->post_title} Five for the Future pledge";
|
$subject = "Removed from {$pledge->post_title} Five for the Future pledge";
|
||||||
$message = "Howdy {$contributor->post_title},\n\n";
|
$user = get_user_by( 'login', $contributor->post_title );
|
||||||
$message .= sprintf(
|
|
||||||
'This email is to notify you that your WordPress.org contributor profile is no longer linked to %1$s’s Five for the Future pledge. If this is unexpected news, it’s best to reach out directly to %1$s with questions. Have a great day!',
|
$message = sprintf( '
|
||||||
|
Howdy %1$s,
|
||||||
|
|
||||||
|
This email is to notify you that your WordPress.org contributor profile is no longer linked to %2$s’s Five for the Future pledge. If this is unexpected news, it’s best to reach out directly to %2$s with questions.
|
||||||
|
|
||||||
|
If they were the only sponsor linked to your account, then the "Hours Per Week" and "Contributor Teams" fields on your profile have been reset, so that teams have accurate data. If you still plan on contributing without sponsorship, please revisit your profile and enter your new hours and teams.
|
||||||
|
|
||||||
|
https://profiles.wordpress.org/me/profile/edit/group/5/
|
||||||
|
|
||||||
|
Have a great day!',
|
||||||
|
$contributor->post_title,
|
||||||
$pledge->post_title
|
$pledge->post_title
|
||||||
);
|
);
|
||||||
|
$message = str_replace( "\t", '', trim( $message ) );
|
||||||
|
|
||||||
$user = get_user_by( 'login', $contributor->post_title );
|
|
||||||
send_email( $user->user_email, $subject, $message, $pledge_id );
|
send_email( $user->user_email, $subject, $message, $pledge_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -387,6 +387,10 @@ function deactivate( $pledge_id, $notify = false, $reason = '' ) {
|
||||||
|
|
||||||
do_action( FiveForTheFuture\PREFIX . '_deactivated_pledge', $pledge_id, $notify, $reason, $result );
|
do_action( FiveForTheFuture\PREFIX . '_deactivated_pledge', $pledge_id, $notify, $reason, $result );
|
||||||
|
|
||||||
|
if ( ! is_wp_error( $result ) ) {
|
||||||
|
Contributor\remove_pledge_contributors( $pledge_id );
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,3 +170,21 @@ function get_contributor_user_data( $user_id ) {
|
||||||
|
|
||||||
return $formatted_data;
|
return $formatted_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the 5ftF data on a user's profile.
|
||||||
|
*/
|
||||||
|
function reset_contribution_data( $user_id ) : void {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$wpdb->query( $wpdb->prepare( '
|
||||||
|
DELETE FROM `bpmain_bp_xprofile_data`
|
||||||
|
WHERE
|
||||||
|
user_id = %d AND
|
||||||
|
field_id IN ( %d, %d, %d )',
|
||||||
|
$user_id,
|
||||||
|
FIELD_IDS['sponsored'],
|
||||||
|
FIELD_IDS['hours_per_week'],
|
||||||
|
FIELD_IDS['team_names'],
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ use const WordPressDotOrg\FiveForTheFuture\Pledge\CPT_ID as PLEDGE_POST_TYPE;
|
||||||
|
|
||||||
defined( 'WPINC' ) || die();
|
defined( 'WPINC' ) || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group auth
|
||||||
|
*/
|
||||||
class Test_Auth extends WP_UnitTestCase {
|
class Test_Auth extends WP_UnitTestCase {
|
||||||
// phpcs:ignore PSR2.Classes.PropertyDeclaration.Multiple
|
// phpcs:ignore PSR2.Classes.PropertyDeclaration.Multiple
|
||||||
protected static $pledge, $action, $page, $action_url, $token;
|
protected static $pledge, $action, $page, $action_url, $token;
|
||||||
|
|
268
plugins/wporg-5ftf/tests/test-contributor.php
Normal file
268
plugins/wporg-5ftf/tests/test-contributor.php
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use WordPressDotOrg\FiveForTheFuture\{ Contributor, Pledge, XProfile };
|
||||||
|
|
||||||
|
defined( 'WPINC' ) || die();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are integration tests rather than unit tests. They target the the highest functions in the call stack
|
||||||
|
* in order to test everything beneath them, to the extent that that's practical. `INPUT_POST` can't be mocked,
|
||||||
|
* so functions that reference it can't be used.
|
||||||
|
*
|
||||||
|
* @group contributor
|
||||||
|
*/
|
||||||
|
class Test_Contributor extends WP_UnitTestCase {
|
||||||
|
protected static $user_jane_id;
|
||||||
|
protected static $user_ashish_id;
|
||||||
|
protected static $page_for_organizations;
|
||||||
|
protected static $pledge_bluehost_id;
|
||||||
|
protected static $pledge_10up_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run once when class loads.
|
||||||
|
*/
|
||||||
|
public static function set_up_before_class() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
parent::set_up_before_class();
|
||||||
|
|
||||||
|
$wpdb->query( "
|
||||||
|
CREATE TABLE `bpmain_bp_xprofile_data` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`field_id` bigint unsigned NOT NULL DEFAULT '0',
|
||||||
|
`user_id` bigint unsigned NOT NULL DEFAULT '0',
|
||||||
|
`value` longtext NOT NULL,
|
||||||
|
`last_updated` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `field_id` (`field_id`),
|
||||||
|
KEY `user_id` (`user_id`)
|
||||||
|
)
|
||||||
|
ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3
|
||||||
|
" );
|
||||||
|
|
||||||
|
self::$user_jane_id = self::factory()->user->create( array(
|
||||||
|
'user_login' => 'jane',
|
||||||
|
'user_email' => 'jane@example.org',
|
||||||
|
) );
|
||||||
|
|
||||||
|
self::$user_ashish_id = self::factory()->user->create( array(
|
||||||
|
'user_login' => 'ashish',
|
||||||
|
'user_email' => 'ashish@example.org',
|
||||||
|
) );
|
||||||
|
|
||||||
|
self::$page_for_organizations = get_post( self::factory()->post->create( array(
|
||||||
|
'post_type' => 'page',
|
||||||
|
'post_title' => 'For Organizations',
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) ) );
|
||||||
|
|
||||||
|
$GLOBALS['post'] = self::$page_for_organizations; // `create_new_pledge()` assumes this exists.
|
||||||
|
|
||||||
|
self::$pledge_10up_id = Pledge\create_new_pledge( '10up' );
|
||||||
|
self::$pledge_bluehost_id = Pledge\create_new_pledge( 'BlueHost' );
|
||||||
|
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => self::$pledge_bluehost_id,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => self::$pledge_10up_id,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run before every test.
|
||||||
|
*/
|
||||||
|
public function set_up() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
parent::set_up();
|
||||||
|
|
||||||
|
$wpdb->query( 'TRUNCATE TABLE `bpmain_bp_xprofile_data` ' );
|
||||||
|
|
||||||
|
$wpdb->query( $wpdb->prepare( "
|
||||||
|
INSERT INTO `bpmain_bp_xprofile_data`
|
||||||
|
(`id`, `field_id`, `user_id`, `value`, `last_updated`)
|
||||||
|
VALUES
|
||||||
|
(NULL, 29, %d, '40', '2019-12-02 10:00:00' ),
|
||||||
|
(NULL, 30, %d, 'a:1:{i:0;s:9:\"Core Team\";}', '2019-12-03 11:00:00' ),
|
||||||
|
(NULL, 29, %d, '35', '2019-12-02 10:00:00' ),
|
||||||
|
(NULL, 30, %d, 'a:1:{i:0;s:18:\"Documentation Team\";}', '2019-12-03 11:00:00' )",
|
||||||
|
self::$user_jane_id,
|
||||||
|
self::$user_jane_id,
|
||||||
|
self::$user_ashish_id,
|
||||||
|
self::$user_ashish_id
|
||||||
|
) );
|
||||||
|
|
||||||
|
reset_phpmailer_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run once after all tests are finished.
|
||||||
|
*/
|
||||||
|
public static function tear_down_after_class() {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
parent::tear_down_after_class();
|
||||||
|
|
||||||
|
$wpdb->query( 'DROP TABLE `bpmain_bp_xprofile_data` ' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::remove_pledge_contributors
|
||||||
|
* @covers ::remove_contributors
|
||||||
|
*/
|
||||||
|
public function test_data_reset_once_no_active_sponsors() : void {
|
||||||
|
// Setup scenario where Jane is sponsored by two companies.
|
||||||
|
$mailer = tests_retrieve_phpmailer_instance();
|
||||||
|
$jane = get_user_by( 'id', self::$user_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
$tenup = get_post( self::$pledge_10up_id );
|
||||||
|
$bluehost = get_post( self::$pledge_bluehost_id );
|
||||||
|
$tenup_contributors = Contributor\add_pledge_contributors( $tenup->ID, array( $jane->user_login ) );
|
||||||
|
$bluehost_contributors = Contributor\add_pledge_contributors( $bluehost->ID, array( $jane->user_login ) );
|
||||||
|
$tenup_jane_id = $tenup_contributors[ $jane->user_login ];
|
||||||
|
$bluehost_jane_id = $bluehost_contributors[ $jane->user_login ];
|
||||||
|
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => $tenup_jane_id,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => $bluehost_jane_id,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$bluehost_jane = get_post( $bluehost_jane_id );
|
||||||
|
|
||||||
|
$this->assertSame( 'publish', $bluehost->post_status );
|
||||||
|
$this->assertSame( 'publish', $bluehost_jane->post_status );
|
||||||
|
$this->assertSame( 40, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Core Team', $jane_contribution['team_names'] );
|
||||||
|
|
||||||
|
// Deactivating a pledge shouldn't trigger a data resets if they have another active sponsor.
|
||||||
|
Pledge\deactivate( $bluehost->ID, false );
|
||||||
|
|
||||||
|
$bluehost = get_post( $bluehost->ID );
|
||||||
|
$bluehost_jane = get_post( $bluehost_jane->ID );
|
||||||
|
$tenup_jane = get_post( $tenup_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
|
||||||
|
$this->assertSame( Pledge\DEACTIVE_STATUS, $bluehost->post_status );
|
||||||
|
$this->assertSame( 'trash', $bluehost_jane->post_status );
|
||||||
|
$this->assertSame( 'publish', $tenup_jane->post_status );
|
||||||
|
$this->assertSame( 40, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Core Team', $jane_contribution['team_names'] );
|
||||||
|
|
||||||
|
$this->assertContains( $jane->user_email, $mailer->mock_sent[0]['to'][0] );
|
||||||
|
$this->assertSame( "Removed from $bluehost->post_title Five for the Future pledge", $mailer->mock_sent[0]['subject'] );
|
||||||
|
|
||||||
|
// Once the last sponsor has been deactivated, contribution data should be reset.
|
||||||
|
Pledge\deactivate( $tenup->ID, false );
|
||||||
|
|
||||||
|
$tenup = get_post( $tenup->ID );
|
||||||
|
$tenup_jane = get_post( $tenup_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
|
||||||
|
$this->assertSame( Pledge\DEACTIVE_STATUS, $tenup->post_status );
|
||||||
|
$this->assertSame( 'trash', $tenup_jane->post_status );
|
||||||
|
$this->assertSame( 0, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertEmpty( $jane_contribution['team_names'] );
|
||||||
|
$this->assertContains( $jane->user_email, $mailer->mock_sent[1]['to'][0] );
|
||||||
|
$this->assertSame( "Removed from $tenup->post_title Five for the Future pledge", $mailer->mock_sent[1]['subject'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::remove_pledge_contributors
|
||||||
|
* @covers ::remove_contributors
|
||||||
|
*/
|
||||||
|
public function test_data_not_reset_when_unconfirmed_sponsor() : void {
|
||||||
|
// Setup scenario where Jane was invited to join a company but didn't respond.
|
||||||
|
$mailer = tests_retrieve_phpmailer_instance();
|
||||||
|
$jane = get_user_by( 'id', self::$user_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
$tenup = get_post( self::$pledge_10up_id );
|
||||||
|
$tenup_contributors = Contributor\add_pledge_contributors( $tenup->ID, array( $jane->user_login ) );
|
||||||
|
$tenup_jane_id = $tenup_contributors[ $jane->user_login ];
|
||||||
|
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => $tenup_jane_id,
|
||||||
|
'post_status' => 'pending',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$tenup_jane = get_post( $tenup_jane_id );
|
||||||
|
|
||||||
|
$this->assertSame( 'publish', $tenup->post_status );
|
||||||
|
$this->assertSame( 'pending', $tenup_jane->post_status );
|
||||||
|
$this->assertSame( 40, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Core Team', $jane_contribution['team_names'] );
|
||||||
|
|
||||||
|
// Deactivating a pledge shouldn't trigger a data resets if they haven't confirmed their connection to the company.
|
||||||
|
Pledge\deactivate( $tenup->ID, false );
|
||||||
|
|
||||||
|
$tenup = get_post( $tenup->ID );
|
||||||
|
$tenup_jane = get_post( $tenup_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
|
||||||
|
$this->assertSame( Pledge\DEACTIVE_STATUS, $tenup->post_status );
|
||||||
|
$this->assertSame( 'trash', $tenup_jane->post_status );
|
||||||
|
$this->assertSame( 40, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Core Team', $jane_contribution['team_names'] );
|
||||||
|
|
||||||
|
$this->assertEmpty( $mailer->mock_sent );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::remove_contributors
|
||||||
|
*/
|
||||||
|
public function test_data_reset_when_single_contributor_removed_from_pledge() : void {
|
||||||
|
// Setup scenario where Jane and Ashish are sponsored by a company.
|
||||||
|
$mailer = tests_retrieve_phpmailer_instance();
|
||||||
|
$jane = get_user_by( 'id', self::$user_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
$ashish = get_user_by( 'id', self::$user_ashish_id );
|
||||||
|
$ashish_contribution = XProfile\get_contributor_user_data( $ashish->ID );
|
||||||
|
$tenup = get_post( self::$pledge_10up_id );
|
||||||
|
$tenup_contributors = Contributor\add_pledge_contributors( $tenup->ID, array( $jane->user_login, $ashish->user_login ) );
|
||||||
|
$tenup_jane_id = $tenup_contributors[ $jane->user_login ];
|
||||||
|
$tenup_ashish_id = $tenup_contributors[ $ashish->user_login ];
|
||||||
|
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => $tenup_jane_id,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
wp_update_post( array(
|
||||||
|
'ID' => $tenup_ashish_id,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
) );
|
||||||
|
|
||||||
|
$tenup_jane = get_post( $tenup_jane_id );
|
||||||
|
$tenup_ashish = get_post( $tenup_ashish_id );
|
||||||
|
|
||||||
|
$this->assertSame( 'publish', $tenup_ashish->post_status );
|
||||||
|
$this->assertSame( 'publish', $tenup_jane->post_status );
|
||||||
|
$this->assertSame( 40, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Core Team', $jane_contribution['team_names'] );
|
||||||
|
$this->assertSame( 35, $ashish_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Documentation Team', $ashish_contribution['team_names'] );
|
||||||
|
|
||||||
|
// Removing Jane should reset her data, but leave Ashish unaffected.
|
||||||
|
Contributor\remove_contributor( $tenup_jane_id );
|
||||||
|
|
||||||
|
$tenup_jane = get_post( $tenup_jane_id );
|
||||||
|
$jane_contribution = XProfile\get_contributor_user_data( $jane->ID );
|
||||||
|
$tenup_ashish = get_post( $tenup_ashish_id );
|
||||||
|
$ashish_contribution = XProfile\get_contributor_user_data( $ashish->ID );
|
||||||
|
|
||||||
|
$this->assertSame( 'trash', $tenup_jane->post_status );
|
||||||
|
$this->assertSame( 'publish', $tenup_ashish->post_status );
|
||||||
|
$this->assertSame( 0, $jane_contribution['hours_per_week'] );
|
||||||
|
$this->assertEmpty( $jane_contribution['team_names'] );
|
||||||
|
$this->assertSame( 35, $ashish_contribution['hours_per_week'] );
|
||||||
|
$this->assertContains( 'Documentation Team', $ashish_contribution['team_names'] );
|
||||||
|
$this->assertCount( 1, $mailer->mock_sent );
|
||||||
|
$this->assertContains( $jane->user_email, $mailer->mock_sent[0]['to'][0] );
|
||||||
|
$this->assertSame( "Removed from $tenup->post_title Five for the Future pledge", $mailer->mock_sent[0]['subject'] );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue