diff --git a/composer.json b/composer.json index b04a148f..da44fd4a 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "wp-coding-standards/wpcs": "^2.2", "wp-phpunit/wp-phpunit": "^5.4", "wpackagist-plugin/advanced-custom-fields": "5.8.12", + "wpackagist-plugin/buddypress": "^6.1.0", "wpackagist-plugin/easy-digital-downloads": "^2.9.23", "wpackagist-plugin/user-switching": "^1.5.5", "wpsh/local": "^0.2.3" diff --git a/composer.lock b/composer.lock index 03b357a7..f9baef74 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c025a44ff83e162076363f15803df9da", + "content-hash": "085543ec02907c7524270a6df052760b", "packages": [ { "name": "composer/installers", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/composer/installers.git", - "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca" + "reference": "1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/b93bcf0fa1fccb0b7d176b0967d969691cd74cca", - "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca", + "url": "https://api.github.com/repos/composer/installers/zipball/1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d", + "reference": "1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d", "shasum": "" }, "require": { @@ -28,17 +28,18 @@ "shama/baton": "*" }, "require-dev": { - "composer/composer": "1.6.* || 2.0.*@dev", - "composer/semver": "1.0.* || 2.0.*@dev", - "phpunit/phpunit": "^4.8.36", - "sebastian/comparator": "^1.2.4", + "composer/composer": "1.6.* || ^2.0", + "composer/semver": "^1 || ^3", + "phpstan/phpstan": "^0.12.55", + "phpstan/phpstan-phpunit": "^0.12.16", + "symfony/phpunit-bridge": "^4.2 || ^5", "symfony/process": "^2.3" }, "type": "composer-plugin", "extra": { "class": "Composer\\Installers\\Plugin", "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "1.x-dev" } }, "autoload": { @@ -76,6 +77,7 @@ "Porto", "RadPHP", "SMF", + "Starbug", "Thelia", "Whmcs", "WolfCMS", @@ -116,6 +118,7 @@ "phpbb", "piwik", "ppi", + "processwire", "puppet", "pxcms", "reindex", @@ -133,19 +136,23 @@ ], "support": { "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.9.0" + "source": "https://github.com/composer/installers/tree/v1.10.0" }, "funding": [ { "url": "https://packagist.com", "type": "custom" }, + { + "url": "https://github.com/composer", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2020-04-07T06:57:05+00:00" + "time": "2021-01-14T11:07:16+00:00" } ], "packages-dev": [ @@ -279,16 +286,16 @@ }, { "name": "composer/composer", - "version": "1.10.19", + "version": "1.10.20", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "196601d50c08c3fae389a417a7689367fcf37cef" + "reference": "e55d297525f0ecc805c813a0f63a40114fd670f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/196601d50c08c3fae389a417a7689367fcf37cef", - "reference": "196601d50c08c3fae389a417a7689367fcf37cef", + "url": "https://api.github.com/repos/composer/composer/zipball/e55d297525f0ecc805c813a0f63a40114fd670f6", + "reference": "e55d297525f0ecc805c813a0f63a40114fd670f6", "shasum": "" }, "require": { @@ -358,7 +365,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/composer/issues", - "source": "https://github.com/composer/composer/tree/1.10.19" + "source": "https://github.com/composer/composer/tree/1.10.20" }, "funding": [ { @@ -374,7 +381,7 @@ "type": "tidelift" } ], - "time": "2020-12-04T08:14:16+00:00" + "time": "2021-01-27T14:41:06+00:00" }, { "name": "composer/semver", @@ -1079,20 +1086,20 @@ }, { "name": "johnpbloch/wordpress", - "version": "5.6.0", + "version": "5.6.1", "source": { "type": "git", "url": "https://github.com/johnpbloch/wordpress.git", - "reference": "3055975734646c8d0b8caf7b5af168ced6ec4309" + "reference": "d7a597988102967cdfc28851b6b897d018613823" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/johnpbloch/wordpress/zipball/3055975734646c8d0b8caf7b5af168ced6ec4309", - "reference": "3055975734646c8d0b8caf7b5af168ced6ec4309", + "url": "https://api.github.com/repos/johnpbloch/wordpress/zipball/d7a597988102967cdfc28851b6b897d018613823", + "reference": "d7a597988102967cdfc28851b6b897d018613823", "shasum": "" }, "require": { - "johnpbloch/wordpress-core": "5.6.0", + "johnpbloch/wordpress-core": "5.6.1", "johnpbloch/wordpress-core-installer": "^1.0 || ^2.0", "php": ">=5.6.20" }, @@ -1121,20 +1128,20 @@ "source": "http://core.trac.wordpress.org/browser", "wiki": "http://codex.wordpress.org/" }, - "time": "2020-12-08T22:34:35+00:00" + "time": "2021-02-03T21:27:41+00:00" }, { "name": "johnpbloch/wordpress-core", - "version": "5.6.0", + "version": "5.6.1", "source": { "type": "git", "url": "https://github.com/johnpbloch/wordpress-core.git", - "reference": "f074617dd69f466302836d1ae5de75c0bd7b6dfd" + "reference": "82592ec73d42cf784da38adb0028a24dbacab1b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/johnpbloch/wordpress-core/zipball/f074617dd69f466302836d1ae5de75c0bd7b6dfd", - "reference": "f074617dd69f466302836d1ae5de75c0bd7b6dfd", + "url": "https://api.github.com/repos/johnpbloch/wordpress-core/zipball/82592ec73d42cf784da38adb0028a24dbacab1b4", + "reference": "82592ec73d42cf784da38adb0028a24dbacab1b4", "shasum": "" }, "require": { @@ -1142,7 +1149,7 @@ "php": ">=5.6.20" }, "provide": { - "wordpress/core-implementation": "5.6.0" + "wordpress/core-implementation": "5.6.1" }, "type": "wordpress-core", "notification-url": "https://packagist.org/downloads/", @@ -1169,7 +1176,7 @@ "source": "https://core.trac.wordpress.org/browser", "wiki": "https://codex.wordpress.org/" }, - "time": "2020-12-08T22:34:23+00:00" + "time": "2021-02-03T21:27:35+00:00" }, { "name": "johnpbloch/wordpress-core-installer", @@ -3145,16 +3152,16 @@ }, { "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.10.1", + "version": "v2.10.2", "source": { "type": "git", "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "c6716a98fe7bee25d31306e14fb62c3ffa16d70a" + "reference": "0775e0c683badad52c03b11c2cd86a9fdecb937a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/c6716a98fe7bee25d31306e14fb62c3ffa16d70a", - "reference": "c6716a98fe7bee25d31306e14fb62c3ffa16d70a", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/0775e0c683badad52c03b11c2cd86a9fdecb937a", + "reference": "0775e0c683badad52c03b11c2cd86a9fdecb937a", "shasum": "" }, "require": { @@ -3194,7 +3201,7 @@ "source": "https://github.com/sirbrillig/phpcs-variable-analysis", "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" }, - "time": "2020-12-12T18:28:57+00:00" + "time": "2021-01-08T16:31:05+00:00" }, { "name": "squizlabs/php_codesniffer", @@ -4288,12 +4295,12 @@ "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", + "url": "https://github.com/webmozarts/assert.git", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, @@ -4331,8 +4338,8 @@ "validate" ], "support": { - "issues": "https://github.com/webmozart/assert/issues", - "source": "https://github.com/webmozart/assert/tree/master" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.9.1" }, "time": "2020-07-08T17:02:28+00:00" }, @@ -6476,16 +6483,16 @@ }, { "name": "wp-phpunit/wp-phpunit", - "version": "5.6.0", + "version": "5.6.1", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "7130a214573cc8c12a0f8fe8a74b18b453bce1e9" + "reference": "f6b3fb65bccc0ff70b3bc7cc241935597dbd5562" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/7130a214573cc8c12a0f8fe8a74b18b453bce1e9", - "reference": "7130a214573cc8c12a0f8fe8a74b18b453bce1e9", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/f6b3fb65bccc0ff70b3bc7cc241935597dbd5562", + "reference": "f6b3fb65bccc0ff70b3bc7cc241935597dbd5562", "shasum": "" }, "type": "library", @@ -6520,7 +6527,7 @@ "issues": "https://github.com/wp-phpunit/issues", "source": "https://github.com/wp-phpunit/wp-phpunit" }, - "time": "2020-12-09T18:06:02+00:00" + "time": "2021-02-04T18:24:14+00:00" }, { "name": "wpackagist-plugin/advanced-custom-fields", @@ -6540,6 +6547,24 @@ "type": "wordpress-plugin", "homepage": "https://wordpress.org/plugins/advanced-custom-fields/" }, + { + "name": "wpackagist-plugin/buddypress", + "version": "6.4.0", + "source": { + "type": "svn", + "url": "https://plugins.svn.wordpress.org/buddypress/", + "reference": "tags/6.4.0" + }, + "dist": { + "type": "zip", + "url": "https://downloads.wordpress.org/plugin/buddypress.6.4.0.zip" + }, + "require": { + "composer/installers": "~1.0" + }, + "type": "wordpress-plugin", + "homepage": "https://wordpress.org/plugins/buddypress/" + }, { "name": "wpackagist-plugin/easy-digital-downloads", "version": "2.9.26", diff --git a/connectors/class-connector-buddypress.php b/connectors/class-connector-buddypress.php index 3bcecd9c..7c11cb9b 100644 --- a/connectors/class-connector-buddypress.php +++ b/connectors/class-connector-buddypress.php @@ -61,9 +61,11 @@ class Connector_BuddyPress extends Connector { 'groups_unban_member', 'groups_remove_member', + 'xprofile_field_before_save', 'xprofile_field_after_save', 'xprofile_fields_deleted_field', + 'xprofile_group_before_save', 'xprofile_group_after_save', 'xprofile_groups_deleted_group', ); @@ -427,6 +429,8 @@ public function check( $option, $old_value, $new_value ) { $replacement = str_replace( '-', '_', $option ); if ( method_exists( $this, 'check_' . $replacement ) ) { + $method = "check_{$replacement}"; + $this->{$method}( $old_value, $new_value ); call_user_func( array( $this, @@ -581,7 +585,7 @@ public function callback_bp_before_activity_delete( $args ) { * @param array $activities_ids Activity IDs of deleted activities. */ public function callback_bp_activity_deleted_activities( $activities_ids ) { - if ( 1 === count( $activities_ids ) && isset( $this->deleted_activity ) ) { // Single activity deletion. + if ( 1 === count( $activities_ids ) && ! empty( $this->deleted_activity ) ) { // Single activity deletion. $activity = $this->deleted_activity; $this->log( sprintf( @@ -747,11 +751,13 @@ public function group_action( $group, $action, $meta = array(), $message = null /* translators: %s: a group name (e.g. "Favourites") */ $message = esc_html__( '"%s" group deleted', 'stream' ); } elseif ( 'joined' === $action ) { - /* translators: %s: a group name (e.g. "Favourites") */ - $message = esc_html__( 'Joined group "%s"', 'stream' ); + /* translators: %2$s: a user display name, %1$s: a group name (e.g. "Jane Doe", "Favourites") */ + $message = esc_html__( '"%2$s" joined group "%1$s"', 'stream' ); + $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name; } elseif ( 'left' === $action ) { - /* translators: %s: a group name (e.g. "Favourites") */ - $message = esc_html__( 'Left group "%s"', 'stream' ); + /* translators: %2$s: a user display name, %1$s: a group name (e.g. "Jane Doe", "Favourites") */ + $message = esc_html__( '"%2$s" left group "%1$s"', 'stream' ); + $replacements[] = get_user_by( 'id', $meta['user_id'] )->display_name; } elseif ( 'banned' === $action ) { /* translators: %1$s: a user display name, %2$s: a group name (e.g. "Jane Doe", "Favourites") */ $message = esc_html__( 'Banned "%2$s" from "%1$s"', 'stream' ); @@ -1018,15 +1024,34 @@ public function field_action( $field, $action, $meta = array(), $message = null ); } + /** + * Capture field status before create/updating field so it can + * be logged in the "after_save" callback. + * + * @action xprofile_field_before_save + * + * @param BP_XProfile_Field $field Field object. + */ + public function callback_xprofile_field_before_save( $field ) { + $this->incoming_field_action = empty( $field->id ) ? 'created' : 'updated'; + } + /** * Logs field writes * * @action xprofile_field_after_save * - * @param object $field Field object. + * @param BP_XProfile_Field $field Field object. */ public function callback_xprofile_field_after_save( $field ) { - $action = isset( $field->id ) ? 'updated' : 'created'; + // Bail if the field's action wasn't captured. + if ( is_null( $this->incoming_field_action ) ) { + return; + } + + $action = $this->incoming_field_action; + unset( $this->incomming_field_action ); + $this->field_action( $field, $action ); } @@ -1087,6 +1112,19 @@ public function field_group_action( $group, $action, $meta = array(), $message = ); } + /** + * Capture field group status before create/updating field so it can + * be logged in the "after_save" callback. + * + * @action xprofile_group_before_save + * + * @param BP_XProfile_Group $group Field group object. + */ + public function callback_xprofile_group_before_save( $group ) { + $this->incoming_field_group_action = empty( $group->id ) ? 'created' : 'updated'; + } + + /** * Logs field group writes * @@ -1095,12 +1133,14 @@ public function field_group_action( $group, $action, $meta = array(), $message = * @param object $group Field group. */ public function callback_xprofile_group_after_save( $group ) { - global $wpdb; - /** - * A bit hacky, due to inconsistency with BP action scheme, - * see callback_xprofile_field_after_save for correct behavior. - */ - $action = ( $group->id === $wpdb->insert_id ) ? 'created' : 'updated'; + // Bail if the field group's action wasn't captured. + if ( is_null( $this->incoming_field_group_action ) ) { + return; + } + + $action = $this->incoming_field_group_action; + unset( $this->incomming_field_group_action ); + $this->field_group_action( $group, $action ); } diff --git a/phpunit.xml b/phpunit.xml index af2b1c44..f217da25 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -10,7 +10,7 @@ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3c911062..d6fe7c35 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -78,6 +78,29 @@ function( $status = false, $args = array(), $url = '') { } ); } +tests_add_filter( + 'bp_core_loaded', + function() { + $components = array( + 'members', + 'activity', + 'blogs', + 'friends', + 'groups', + 'messages', + 'notifications', + 'settings', + 'xprofile', + ); + $active_components =& buddypress()->active_components; + foreach( $components as $component ) { + $active_components[ $component ] = 1; + } + + \bp_delete_option( 'bp-active-components'); + \bp_delete_option( 'bp-deactivated-components' ); + } +); // @see https://core.trac.wordpress.org/browser/trunk/tests/phpunit/includes/bootstrap.php require $_tests_dir . '/includes/bootstrap.php'; diff --git a/tests/tests/connectors/test-class-connector-buddypress.php b/tests/tests/connectors/test-class-connector-buddypress.php new file mode 100644 index 00000000..2325e65d --- /dev/null +++ b/tests/tests/connectors/test-class-connector-buddypress.php @@ -0,0 +1,699 @@ +delete( (bool) $delete_data ) ) { + /* translators: %s: the field type */ + $message = sprintf( __( 'There was an error deleting the %s. Please try again.', 'buddypress' ), $field_type ); + $type = 'error'; + + return false; + } else { + /* translators: %s: the field type */ + $message = sprintf( __( 'The %s was deleted successfully!', 'buddypress' ), $field_type ); + $type = 'success'; + + /** + * Fires at the end of the field deletion process, if successful. + * + * @since 1.0.0 + * + * @param \BP_XProfile_Field $field Current BP_XProfile_Field object. + */ + do_action( 'xprofile_fields_deleted_field', $field ); + + return true; + } +} + +/** + * Handles the deletion of profile data groups. + * + * @param int $group_id ID of the group to delete. + */ +function xprofile_admin_delete_group( $group_id ) { + global $message, $type; + + // Handle the deletion of group. + $group = new \BP_XProfile_Group( $group_id ); + + if ( ! $group->delete() ) { + $message = _x( 'There was an error deleting the group. Please try again.', 'Error when deleting profile fields group', 'buddypress' ); + $type = 'error'; + + return false; + } else { + $message = _x( 'The group was deleted successfully.', 'Profile fields group was deleted successfully', 'buddypress' ); + $type = 'success'; + + /** + * Fires at the end of group deletion process, if successful. + * + * @since 1.0.0 + * + * @param \BP_XProfile_Group $group Current BP_XProfile_Group object. + */ + do_action( 'xprofile_groups_deleted_group', $group ); + + return true; + } +} + +class Test_WP_Stream_Connector_BuddyPress extends WP_StreamTestCase { + /** + * Run before each test + */ + public function setUp() { + parent::setUp(); + + // Make partial of Connector_BuddyPress class, with mocked "log" function. + $this->mock = $this->getMockBuilder( Connector_BuddyPress::class ) + ->setMethods( array( 'log' ) ) + ->getMock(); + + $this->mock->register(); + update_option( 'users_can_register', true ); + } + + /** + * Run after each test + */ + public function tearDown() { + parent::tearDown(); + } + + public function test_buddypress_installed_and_activated() { + $this->assertTrue( is_callable( 'buddypress' ) ); + } + + public function test_option_callbacks() { + // Expected log actions. + $this->mock->expects( $this->atLeastOnce() ) + ->method( 'log' ) + ->withConsecutive( + array( + /* translators: %s: setting name (e.g. "Group Creation") */ + $this->equalTo( _( '"%s" setting updated', 'stream' ) ), + $this->equalTo( + array( + 'option_title' => esc_html_x( 'Group Creation', 'buddypress', 'stream' ), + 'option' => 'bp_restrict_group_creation', + 'old_value' => null, + 'new_value' => false, + 'page' => 'bp-settings', + ) + ), + $this->equalTo( null ), + $this->equalTo( 'settings' ), + $this->equalTo( 'updated' ) + ), + array( + /* translators: %s: setting name (e.g. "Group Creation") */ + $this->equalTo( _( '"%s" setting updated', 'stream' ) ), + $this->equalTo( + array( + 'option_title' => esc_html_x( 'Group Creation', 'buddypress', 'stream' ), + 'option' => 'bp_restrict_group_creation', + 'old_value' => false, + 'new_value' => true, + 'page' => 'bp-settings', + ) + ), + $this->equalTo( null ), + $this->equalTo( 'settings' ), + $this->equalTo( 'updated' ) + ), + array( + /* translators: %s: setting name (e.g. "Group Creation") */ + $this->equalTo( _( '"%s" setting updated', 'stream' ) ), + $this->equalTo( + array( + 'option_title' => esc_html_x( 'Group Creation', 'buddypress', 'stream' ), + 'option' => 'bp_restrict_group_creation', + 'old_value' => null, + 'new_value' => null, + 'page' => 'bp-settings', + ) + ), + $this->equalTo( null ), + $this->equalTo( 'settings' ), + $this->equalTo( 'updated' ) + ), + array( + $this->equalTo( + sprintf( + // translators: Placeholder refers to component title (e.g. "Members") + __( '"%1$s" component %2$s', 'stream' ), + 'Extended Profiles', + 'activated' + ) + ), + $this->equalTo( + array( + 'option' => 'xprofile', + 'option_key' => 'bp-active-components', + 'old_value' => null, + 'value' => array( + 'title' => __( 'Extended Profiles', 'buddypress' ), + 'description' => __( 'Customize your community with fully editable profile fields that allow your users to describe themselves.', 'buddypress' ) + ), + ) + ), + + $this->equalTo( null ), + $this->equalTo( 'components' ), + $this->equalTo( 'activated' ), + ) + ); + + // Modify BuddyPress-related options to trigger callbacks. + add_option( 'bp_restrict_group_creation', false ); + update_option( 'bp_restrict_group_creation', true ); + delete_option( 'bp_restrict_group_creation' ); + + bp_update_option( 'bp-active-components', bp_core_get_components( 'all' ) ); + + // Check callback test actions. + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_add_option' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_update_option' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_delete_option' ) ); + } + + public function test_activity_callbacks() { + $activity_args = array( + 'component' => \buddypress()->activity->id, + 'content' => 'Testing testing 123', + 'primary_link' => 'http://example.com', + 'type' => 'activity_update', + 'recorded_time' => \bp_core_current_time(), + ); + + // Create activity for later use. + $activity = new \BP_Activity_Activity( \bp_activity_add( $activity_args ) ); + + // Expected log actions. + $this->mock->expects( $this->exactly( 3 ) ) + ->method( 'log' ) + ->withConsecutive( + array( + $this->equalTo( + sprintf( + /* translators: %s an activity title (e.g. "Update") */ + __( 'Marked activity "%s" as spam', 'stream' ), + wp_strip_all_tags( $activity->action ) + ) + ), + $this->equalTo( + array( + 'id' => $activity->id, + 'item_id' => $activity->item_id, + 'type' => $activity->type, + 'author' => $activity->user_id, + ) + ), + $this->equalTo( $activity->id ), + $this->equalTo( $activity->component ), + $this->equalTo( 'spammed' ), + ), + array( + $this->equalTo( + sprintf( + /* translators: %s: an activity title (e.g. "Update") */ + __( 'Unmarked activity "%s" as spam', 'stream' ), + wp_strip_all_tags( $activity->action ) + ) + ), + $this->equalTo( + array( + 'id' => $activity->id, + 'item_id' => $activity->item_id, + 'type' => $activity->type, + 'author' => $activity->user_id, + ) + ), + $this->equalTo( $activity->id ), + $this->equalTo( $activity->component ), + $this->equalTo( 'unspammed' ), + ), + array( + $this->equalTo( + sprintf( + /* translators: %s: an activity title (e.g. "Update") */ + __( '"%s" activity deleted', 'stream' ), + wp_strip_all_tags( $activity->action ) + ) + ), + $this->equalTo( + array( + 'id' => $activity->id, + 'item_id' => $activity->item_id, + 'type' => $activity->type, + 'author' => $activity->user_id, + ) + ), + $this->equalTo( $activity->id ), + $this->equalTo( $activity->component ), + $this->equalTo( 'deleted' ), + ) + ); + + // Update activity to trigger callbacks. + \bp_activity_mark_as_spam( $activity ); + \bp_activity_mark_as_ham( $activity ); + \bp_activity_delete_by_activity_id( $activity->id ); + + // Check callback test actions. + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_bp_activity_mark_as_spam' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_bp_activity_mark_as_ham' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_bp_before_activity_delete' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_bp_activity_deleted_activities' ) ); + } + + public function test_group_action_callbacks() { + // Authenticate as admin and re-evaluate user access. + wp_set_current_user( 1 ); + bp_update_is_item_admin( bp_user_has_access(), 'core' ); + + // Create users for later use. + $test_user_id = self::factory()->user->create( array( 'display_name' => 'testuser' ) ); + + // Expected log actions. + $this->mock->expects( $this->exactly( 13 ) ) + ->method( 'log' ) + ->withConsecutive( + array( + $this->equalTo( esc_html__( '"Test group" group created' ) ), + $this->callback( + function( $subject ) { + $expected = array( + 'name' => 'Test group', + 'slug' => 'test-group' + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'created' + ), + array( + $this->equalTo( esc_html__( '"Old test group" group updated' ) ), + $this->callback( + function( $subject ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'test-group' + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'updated' + ), + array( + $this->equalTo( esc_html__( '"Old test group" group updated' ) ), + $this->callback( + function( $subject ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group' + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'updated' + ), + array( + $this->equalTo( esc_html__( '"Old test group" group updated' ) ), + $this->callback( + function( $subject ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group' + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'updated' + ), + array( + $this->equalTo( esc_html__( '"testuser" joined group "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'joined' + ), + array( + $this->equalTo( __( 'Promoted "testuser" to "Moderator" in "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + 'status' => 'mod', + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'promoted' + ), + array( + $this->equalTo( __( 'Demoted "testuser" to "Member" in "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'demoted' + ), + array( + $this->equalTo( esc_html__( 'Banned "testuser" from "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'banned' + ), + array( + $this->equalTo( esc_html__( 'Unbanned "testuser" from "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'unbanned' + ), + array( + $this->equalTo( esc_html__( '"testuser" left group "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'left' + ), + array( + $this->equalTo( esc_html__( '"testuser" joined group "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'joined' + ), + array( + $this->equalTo( esc_html__( 'Removed "testuser" from "Old test group"' ) ), + $this->callback( + function( $subject ) use ( $test_user_id ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + 'user_id' => $test_user_id, + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'removed' + ), + array( + $this->equalTo( esc_html__( '"Old test group" group deleted' ) ), + $this->callback( + function( $subject ) { + $expected = array( + 'name' => 'Old test group', + 'slug' => 'old-test-group', + ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'groups', + 'deleted' + ) + ); + + // Create and manipulate Groups to trigger callbacks. + $group_args = array( + 'creator_id' => 1, + 'name' => 'Test group', + 'description' => 'Lorem ipsum dolor', + 'slug' => 'test-group', + 'status' => 'public', + ); + $group_id = \groups_create_group( $group_args ); + + \groups_create_group( + array( + 'group_id' => $group_id, + 'name' => 'Old test group', + ) + ); + + \groups_edit_base_group_details( + array( + 'group_id' => $group_id, + 'slug' => 'old-test-group', + 'description' => 'Lorem ipsum dolor two', + ) + ); + + // Manually reset duplicate log blocker flag + $this->mock->is_update = false; + + \groups_edit_group_settings( $group_id, false, 'public' ); + + \groups_join_group( $group_id, $test_user_id ); + \groups_promote_member( $test_user_id, $group_id, 'mod' ); + \groups_demote_member( $test_user_id, $group_id ); + + // In order to bypass bulk activity deletion logs. + $this->mock->ignore_activity_bulk_deletion = true; + \groups_ban_member( $test_user_id, $group_id ); + + \groups_unban_member( $test_user_id, $group_id ); + \groups_leave_group( $group_id, $test_user_id ); + \groups_join_group( $group_id, $test_user_id ); + + // In order to bypass bulk activity deletion logs. + $this->mock->ignore_activity_bulk_deletion = true; + \groups_remove_member( $test_user_id, $group_id ); + + \groups_delete_group( $group_id ); + + // Check callback test actions. + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_create_group' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_update_group' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_details_updated' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_settings_updated' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_leave_group' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_join_group' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_promote_member' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_demote_member' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_ban_member' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_unban_member' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_remove_member' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_groups_before_delete_group' ) ); + } + + public function test_profile_action_callbacks() { + // Expected log actions. + $this->mock->expects( $this->exactly( 6 ) ) + ->method( 'log' ) + ->withConsecutive( + array( + $this->equalTo( esc_html__( 'Created profile field group "Test field group"' ) ), + $this->callback( + function( $subject ) { + $expected = array( 'group_name' => 'Test field group' ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'profile_fields', + 'created', + ), + array( + $this->equalTo( esc_html__( 'Created profile field "Test field"' ) ), + $this->callback( + function( $subject ) { + $expected = array( 'field_name' => 'Test field' ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'profile_fields', + 'created', + ), + array( + $this->equalTo( esc_html__( 'Updated profile field "Test field"' ) ), + $this->callback( + function( $subject ) { + $expected = array( 'field_name' => 'Test field' ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'profile_fields', + 'updated', + ), + array( + $this->equalTo( esc_html__( 'Deleted profile field "Test field"' ) ), + $this->callback( + function( $subject ) { + $expected = array( 'field_name' => 'Test field' ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'profile_fields', + 'deleted', + ), + array( + $this->equalTo( esc_html__( 'Updated profile field group "Test field group"' ) ), + $this->callback( + function( $subject ) { + $expected = array( 'group_name' => 'Test field group' ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'profile_fields', + 'updated', + ), + array( + $this->equalTo( esc_html__( 'Deleted profile field group "Test field group"' ) ), + $this->callback( + function( $subject ) { + $expected = array( 'group_name' => 'Test field group' ); + return $expected === array_intersect_key( $expected, $subject ); + } + ), + $this->greaterThan( 0 ), + 'profile_fields', + 'deleted', + ) + ); + + // Create/trigger/update fields and field groups to trigger callbacks. + // Create new field group. + $field_group_args = array( + 'name' => 'Test field group', + 'description' => 'Lorem ipsum dolor', + 'slug' => 'test-field-group', + 'can_delete' => 'false', + ); + $group_id = \xprofile_insert_field_group( $field_group_args ); + $group = new \BP_XProfile_Group( $group_id ); + + // Create new field in field group. + $field_args = array( + 'name' => 'Test field', + 'description' => 'Lorem ipsum dolor', + 'type' => 'textbox', + 'field_group_id' => $group_id + ); + $field_id = \xprofile_insert_field( $field_args ); + $field = new \BP_XProfile_Field( $field_id ); + + // Update field. + $field->is_required = true; + $field->save(); + + // Delete field. + $this->assertTrue( xprofile_admin_delete_field( $field_id, 'textbox' ) ); + + // Update field group. + $group->can_delete = 1; + $group->save(); + + // Delete field group. + $this->assertTrue( xprofile_admin_delete_group( $group->id ) ); + + // Check callback test actions. + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_xprofile_group_before_save' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_xprofile_group_after_save' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_xprofile_field_before_save' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_xprofile_field_after_save' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_xprofile_fields_deleted_field' ) ); + $this->assertFalse( 0 === did_action( $this->action_prefix . 'callback_xprofile_groups_deleted_group' ) ); + } +}