What's the issue with my cURL request?

Hello! I’m trying to write a cURL request for my Wordpress site to integrate Discourse with woo subscriptions. Here’s my function, which doesn’t seem to work. Am I missing something?

function execute_discourse_curl_request($subscription) {

    $subscription_status = $subscription->get_status();

    $user_id = $subscription->get_user_id();
    $user = get_user_by('ID', $user_id);
    $username = $user->user_login;

    $request = "";

    if ($subscription_status == 'active') {
        $request = "PUT";
    }

    if ($subscription_status == 'cancelled' || $subscription_status == 'on-hold') {
        $request = "DELETE";
    }

    $api_key = get_option( 'discourse_custom_api_key' );

    $group_id = 41;
    $api_url = 'https://forum.example.com/groups/' . $group_id . '/members.json';
    
    $data = [
        'usernames' => [$username],
    ];
    
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => $api_url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => $request,
        CURLOPT_POSTFIELDS => json_encode($data),
        CURLOPT_HTTPHEADER => [
            "Api-Key: " . $api_key,
            "Api-Username: system",
            "Content-Type: application/json"
        ],
    ]);
    
    $response = curl_exec($curl);
    $err = curl_error($curl);
    
    curl_close($curl);

    if ($err) {
        $to = 'hello@example.com';
        $subject = 'Error in Discourse User Update cURL Request';
        $message = 'There was an error';
        wp_mail($to, $subject, $message);
    }
}
1 Like

Hello,

What error do you get? What does return the response?
This should help in understanding the issue.

The problem is that I don’t receive any error or confirmation. It appears that the cURL just doesn’t work. I can see that the Discourse API key was used when the cURL is executed, so there’s some success, but the user is not added or removed from the group as intended. I can confirm that the variables are all correct though.

It should return a payload on success or failure.

Did you check the content of $response?

Also, I believe the usernames param format should be a string “username1,username2,...” instead.

1 Like

Unless I’m doing something wrong, the request to "/groups/${group_id}/members.json" is kind of unintuitive. It also doesn’t seem to be documented and can’t be viewed in the browser’s network tab when submitting the Discourse “Add User” form on a group’s page. Again, I could be doing something wrong.

In any case, the path for the request is /groups/${group_id}/members.json, with the group’s numerical ID substituted for group_id. The body of the request requires a group_id parameter, but that parameter needs to be set to the group’s name. So the args become something like this:

$group_id = 45;
$group_name = 'publishers';
$args     = array(
    'method' => 'DELETE', // or 'PUT'
	'body'   => array(
		// 'group_id'  => $group_name, edit: this param isn't needed, not sure what was going on when I was testing it.
		'usernames' => 'sally,Ben',
	)
);

// path:
/groups/${group_id}/members.json

If you have the WP Discourse plugin configured on your WordPress site, you can use its static discourse_request helper function to avoid having to use curl. Note that I linked to the static function that’s in the plugin-utilities file, but the namespace you need to use for external requests is from wp-discourse/lib/utilities.php at main · discourse/wp-discourse · GitHub.

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

function zalg_add_users_to_group() {
	$group_id = 45;
    $group_name = 'publishers';
    $method = 'PUT'; // 'PUT' to add users; 'DELETE' to remove users
	$args     = array(
		'method' => $method,
		'body'   => array(
			// 'group_id'  => $group_name, edit: this param isn't needed
			'usernames' => 'sally,Ben',
		)
	);
	$response = DiscourseUtilities::discourse_request( "/groups/${group_id}/members.json", $args );
    // return, log, or handle the response
}

If you are using WordPress as the SSO provider for Discourse, there’s a helper function that allows you to add one user to one or more groups:

function zalg_sso_add_users_to_group() {
    $user_id = 1; // the user's WordPress user ID
    $group_names = 'foo,bar,baz'; // one or more comma separated group names, with no spaces after commas!
    DiscourseUtilities::add_user_to_discourse_group($user_id, $group_names);
}

To add multiple users to a single group, you’d still need to use the discourse_request method that’s in the first code example.

3 Likes

The response is just a 500 error :frowning:

This is heroic help, thanks Simon! This would be much easier if I can utilise existing helper functions in the WP Discourse plugin. I am using my Wordpress site for SSO.

I do however still get a critical error in my case when the function is fired. Not sure why. What you’re saying is a little above my head, but here’s what I’m using in my functions.php:

<?php

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

add_action('woocommerce_subscription_status_cancelled', 'zalg_sso_add_users_to_group', 10, 3);

function zalg_sso_add_users_to_group($subscription) {
    $user_id = $subscription->get_user_id();
    $group_names = 'premium';
    DiscourseUtilities::remove_user_to_discourse_group($user_id, $group_names);
}

Not sure what I’m missing?

The function to remove a user from one or more groups is called remove_user_from_discourse_group, you have called it remove_user_to_discourse_group.

The functions for adding and removing a user from one or more groups are here: wp-discourse/lib/utilities.php at 99325e15190f3a705284dbf582f1c4b2c0b21492 · discourse/wp-discourse · GitHub.

3 Likes

You’re right! Silly mistake. Thank you for your assistance Simon. I wish there were a tip jar. Let me know if there’s anything I can do for you. Cheers!

1 Like

To extend on this, are you aware of where I might be able to find helper functions to edit the user profile more? It would be great if I could set the user Profile Picture and Bio if possible.

There’s a helper function in the Utilities file for this:

DiscourseUtilities::sync_sso_record( $sso_params );

Yesterday I posted an example of how to use it: I cannot add user to the discouse forum from a wordpress website when user added in a membership - #10 by simon. The tricky part is to create the array for the sso_params argument. That array must contain the external_id field. It is set to the user’s WordPress ID:

	$sso_params = array(
		'external_id' => $user_id,
	);

You can include any fields from this list in the payload: discourse/lib/discourse_connect_base.rb at 8f52fd1051e20fdff41321c5cff99fda05af86c1 · discourse/discourse · GitHub. Note the BOOLS array that is shown just under the ACCESSORS list that I linked to. It indicates which of the possible options are booleans (true/false). When making requests from WordPress, any boolean fields must be set with the strings 'true' or 'false', not with the PHP true or false values.

For updating the avatar, set the avatar_url field in the $sso_params array and also set the avatar_force_update field to 'true'.

There’s a bio field that can be used for setting the bio.

The WP Discourse plugin is already setting the bio and avatar_url fields. It also has a “Force Avatar Update” setting in its SSO settings. The problem might be that these settings are only updated when a user logs into Discourse via the WordPress site. The sync_sso_record function updates the fields immediately, without requiring the user to login to Discourse.

Also, if you’re finding that users bios are not getting set as on Discourse, it could be that they aren’t being set on your WordPress site where the plugin is expecting them to be: wp-discourse/lib/plugin-utilities.php at 99325e15190f3a705284dbf582f1c4b2c0b21492 · discourse/wp-discourse · GitHub. If Woocommerce has a different field for bios, you could access that field and use it in a call to sync_sso_record.

3 Likes