';
protected $svgLegacy = '';
const requiredApi1 = 'V1.1';
const requiredApi2 = 'V2';
private $v1RequiredFields = array(
'api_version' => 'API Version',
'consumer_key' => 'API Key',
'consumer_secret' => 'API Key Secret'
);
private $v2RequiredFields = array(
'api_version' => 'API Version',
'client_id' => 'Client ID',
'client_secret' => 'Client Secret'
);
protected $sync_fields = array(
'created_at' => array(
'label' => 'Register date',
'node' => array(
'me',
'mev2'
)
),
'description' => array(
'label' => 'Bio',
'node' => array(
'me',
'mev2'
)
),
'entities' => array(
'label' => 'Bio entities',
'node' => 'mev2',
'description' => self::requiredApi2
),
'lang' => array(
'label' => 'Language',
'node' => 'me',
'description' => self::requiredApi1
),
'location' => array(
'label' => 'Location',
'node' => array(
'me',
'mev2'
)
),
'pinned_tweet_id' => array(
'label' => 'Pinned Tweet ID',
'node' => 'mev2',
'description' => self::requiredApi2
),
'profile_url' => array(
'label' => 'Profile URL',
'node' => 'me',
'description' => self::requiredApi1
),
'protected' => array(
'label' => 'Tweet protection status',
'node' => 'mev2',
'description' => self::requiredApi2
),
'public_metrics' => array(
'label' => 'Public metrics',
'node' => 'mev2',
'description' => self::requiredApi2
),
'screen_name' => array(
'label' => 'Screen name',
'node' => 'me',
'description' => self::requiredApi1
),
'url' => array(
'label' => 'Owned website',
'node' => array(
'me',
'mev2'
)
),
'username' => array(
'label' => 'Screen name',
'node' => 'mev2',
'description' => self::requiredApi2
),
'verified' => array(
'label' => 'Is verified',
'node' => 'mev2',
'description' => self::requiredApi2
)
);
public function __construct() {
$this->id = 'twitter';
$this->docUrl = 'https://social-login.nextendweb.com/documentation/providers/x-formerly-twitter/';
$this->label = 'X (formerly Twitter)';
$this->path = dirname(__FILE__);
parent::__construct(array(
'consumer_key' => '',
'consumer_secret' => '',
'skin' => 'x',
'login_label' => 'Continue with X',
'register_label' => 'Sign up with X',
'link_label' => 'Link account with X',
'unlink_label' => 'Unlink account from X',
'profile_image_size' => 'normal',
'api_version' => '1.1',
'client_id' => '',
'client_secret' => ''
));
if ($this->isV2Api()) {
$this->requiredFields = $this->v2RequiredFields;
} else {
$this->authRedirectBehavior = 'default_redirect_but_app_has_restriction';
$this->requiredFields = $this->v1RequiredFields;
}
}
protected function forTranslation() {
__('Continue with X', 'nextend-facebook-connect');
__('Sign up with X', 'nextend-facebook-connect');
__('Link account with X', 'nextend-facebook-connect');
__('Unlink account from X', 'nextend-facebook-connect');
}
public function getRawDefaultButton() {
$skin = $this->settings->get('skin');
switch ($skin) {
case 'legacy':
$color = '#4ab3f4';
$svg = $this->svgLegacy;
break;
default:
$color = $this->color;
$svg = $this->svg;
}
return '';
}
public function getRawIconButton() {
$skin = $this->settings->get('skin');
switch ($skin) {
case 'legacy':
$color = '#4ab3f4';
$svg = $this->svgLegacy;
break;
default:
$color = $this->color;
$svg = $this->svg;
}
return '';
}
public function validateSettings($newData, $postedData) {
$newData = parent::validateSettings($newData, $postedData);
foreach ($postedData as $key => $value) {
switch ($key) {
case 'tested':
if ($postedData[$key] == '1' && (!isset($newData['tested']) || $newData['tested'] != '0')) {
$newData['tested'] = 1;
} else {
$newData['tested'] = 0;
}
break;
case 'api_version':
case 'consumer_key':
case 'consumer_secret':
case 'client_id':
case 'client_secret':
if ($this->settings->get('api_version') !== $postedData['api_version']) {
if ($this->isV2Api($postedData['api_version'])) {
$this->requiredFields = $this->v2RequiredFields;
} else {
$this->requiredFields = $this->v1RequiredFields;
}
}
$newData[$key] = trim(sanitize_text_field($value));
if ($this->settings->get($key) !== $newData[$key]) {
$newData['tested'] = 0;
}
if (isset($this->requiredFields[$key]) && empty($newData[$key])) {
Notices::addError(sprintf(__('The %1$s entered did not appear to be a valid. Please enter a valid %2$s.', 'nextend-facebook-connect'), $this->requiredFields[$key], $this->requiredFields[$key]));
}
break;
case 'skin':
case 'profile_image_size':
$newData[$key] = trim(sanitize_text_field($value));
break;
}
}
return $newData;
}
/**
* @return NextendSocialProviderTwitterClient
*/
public function getClient() {
if ($this->client === null) {
if ($this->isV2Api()) {
require_once dirname(__FILE__) . '/twitterv2-client.php';
$this->client = new NextendSocialProviderTwitterV2Client($this->id);
$this->client->setClientId($this->settings->get('client_id'));
$this->client->setClientSecret($this->settings->get('client_secret'));
} else {
require_once dirname(__FILE__) . '/twitter-client.php';
$this->client = new NextendSocialProviderTwitterClient($this->id, $this->settings->get('consumer_key'), $this->settings->get('consumer_secret'));
}
$this->client->setRedirectUri($this->getRedirectUriForAuthFlow());
}
return $this->client;
}
/**
* @return array|mixed|object
* @throws Exception
*/
protected function getCurrentUserInfo() {
if ($this->isV2Api()) {
return $this->getCurrentUserInfoV2();
} else {
return $this->getCurrentUserInfoV1();
}
}
/**
* getCurrentUserInfo() for API version 1.1
*
* @return array|mixed|object
* @throws Exception
*/
private function getCurrentUserInfoV1() {
$response = $this->getClient()
->get('account/verify_credentials', array(
'include_email' => 'true',
'include_entities' => 'false',
'skip_status' => 'true'
));
if (isset($response['id']) && isset($response['id_str'])) {
// On 32bit and Windows server, we must copy id_str to id as the id int representation won't be OK
$response['id'] = $response['id_str'];
}
return $response;
}
/**
* For API version 2
*
* @return array
* @throws Exception
*/
private function getCurrentUserInfoV2() {
$fields = array(
'id',
'name',
'confirmed_email',
'profile_image_url',
);
$extra_me_fields = apply_filters('nsl_twitter_sync_node_fields', array(), 'mev2');
$response = $this->getClient()
->get('users/me?user.fields=' . implode(',', array_merge($fields, $extra_me_fields)));
if (!empty($response['data'])) {
return $response['data'];
}
throw new NSLSanitizedRequestErrorMessageException(sprintf(__('Unexpected response: %s', 'nextend-facebook-connect'), json_encode($response)));
}
public function getMe() {
return $this->authUserData;
}
/**
* @param $key
*
* @return string
*/
public function getAuthUserData($key) {
if ($this->isV2Api()) {
return $this->getAuthUserDataV2($key);
} else {
return $this->getAuthUserDataV1($key);
}
}
/**
* getAuthUserData() for API version 1.1
*
* @param $key
*
* @return mixed|string
*/
private function getAuthUserDataV1($key) {
switch ($key) {
case 'id':
return $this->authUserData['id'];
case 'email':
return !empty($this->authUserData['email']) ? $this->authUserData['email'] : '';
case 'name':
return $this->authUserData['name'];
case 'username':
return $this->authUserData['screen_name'];
case 'first_name':
$name = explode(' ', $this->getAuthUserData('name'), 2);
return isset($name[0]) ? $name[0] : '';
case 'last_name':
$name = explode(' ', $this->getAuthUserData('name'), 2);
return isset($name[1]) ? $name[1] : '';
case 'picture':
$profile_image_size = $this->settings->get('profile_image_size');
$profile_image = $this->authUserData['profile_image_url_https'];
$avatar_url = '';
if (!empty($profile_image)) {
switch ($profile_image_size) {
case 'mini':
$avatar_url = str_replace('_normal.', '_' . $profile_image_size . '.', $profile_image);
break;
case 'normal':
$avatar_url = $profile_image;
break;
case 'bigger':
$avatar_url = str_replace('_normal.', '_' . $profile_image_size . '.', $profile_image);
break;
case 'original':
$avatar_url = str_replace('_normal.', '.', $profile_image);
break;
}
}
return $avatar_url;
}
return parent::getAuthUserData($key);
}
/**
* getAuthUserData() for API version 2
*
* @param $key
*
* @return mixed|string
*/
private function getAuthUserDataV2($key) {
switch ($key) {
case 'id':
return $this->authUserData['id'];
case 'email':
return !empty($this->authUserData['confirmed_email']) ? $this->authUserData['confirmed_email'] : '';
case 'name':
return $this->authUserData['name'];
case 'username':
return $this->authUserData['username'];
case 'first_name':
$name = explode(' ', $this->getAuthUserData('name'), 2);
return isset($name[0]) ? $name[0] : '';
case 'last_name':
$name = explode(' ', $this->getAuthUserData('name'), 2);
return isset($name[1]) ? $name[1] : '';
case 'picture':
$profile_image_size = $this->settings->get('profile_image_size');
$profile_image = $this->authUserData['profile_image_url'];
$avatar_url = '';
if (!empty($profile_image)) {
switch ($profile_image_size) {
case 'mini':
$avatar_url = str_replace('_normal.', '_' . $profile_image_size . '.', $profile_image);
break;
case 'normal':
$avatar_url = $profile_image;
break;
case 'bigger':
$avatar_url = str_replace('_normal.', '_' . $profile_image_size . '.', $profile_image);
break;
case 'original':
$avatar_url = str_replace('_normal.', '.', $profile_image);
break;
}
}
return $avatar_url;
}
return parent::getAuthUserData($key);
}
public function syncProfile($user_id, $provider, $data) {
if ($this->needUpdateAvatar($user_id)) {
if ($this->getAuthUserData('picture')) {
$this->updateAvatar($user_id, $this->getAuthUserData('picture'));
}
}
if (!empty($data['access_token_data'])) {
$this->storeAccessToken($user_id, $data['access_token_data']);
}
}
public function deleteLoginPersistentData() {
parent::deleteLoginPersistentData();
if ($this->client !== null) {
$this->client->deleteLoginPersistentData();
}
}
public function getAvatar($user_id) {
if (!$this->isUserConnected($user_id)) {
return false;
}
$picture = $this->getUserData($user_id, 'profile_picture');
if (!$picture || $picture == '') {
return false;
}
return $picture;
}
/**
* @param $api_version
*
* @return bool
*/
public function isV2Api($api_version = false) {
$api_version = $api_version ? $api_version : $this->settings->get('api_version');
return $api_version === '2';
}
public function deleteTokenPersistentData() {
parent::deleteTokenPersistentData();
Persistent::delete($this->id . '_code_verifier');
}
public function getSyncDataFieldDescription($fieldName) {
if (isset($this->sync_fields[$fieldName]['description'])) {
return sprintf(__('Required API: %1$s', 'nextend-facebook-connect'), $this->sync_fields[$fieldName]['description']);
}
return parent::getSyncDataFieldDescription($fieldName);
}
public function getProviderEmailVerificationStatus() {
/**
* V1: The email address (when) returned by the v1 API of Twitter / X is always verified
*
* V2: The email address (when) returned by the v2 API of Twitter / X is always verified
*
* So we can trust the account to be of the rightful owner in both cases
*/
return true;
}
}
NextendSocialLogin::addProvider(new NextendSocialProviderTwitter);