Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
<?php
namespace bizpanda\includes\gates\linkedin;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\OAuthGateBridge;
/**
* The class to proxy the request to the LinkedIn API.
*/
class LinkedinBridge extends OAuthGateBridge {
/**
* Returns an URL to authorize and grant permissions.
* @param array $stateArgs An extra data to add into the state argument. They will be passed back on callback.
* @return string An URL to authorize.
*/
public function getAuthorizeURL($stateArgs = [] ) {
$endpointUrl = ' https://www.linkedin.com/oauth/v2/authorization';
$args = [
'scope' => $this->permissions,
'response_type' => 'code',
'redirect_uri' => $this->callbackUrl,
'client_id' => $this->clientId,
'state' => $this->buildStateParam( $stateArgs )
];
return $endpointUrl . '?' . http_build_query( $args );
}
/**
* Gets the access token using the code received on the authorization step.
* @param $code Code received on the authorization step.
* @return mixed[]
* @throws GateBridgeException
*/
function getAccessToken( $code ) {
$endpointUrl = 'https://www.linkedin.com/oauth/v2/accessToken';
$requestData = [
'code' => $code,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->callbackUrl,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
];
$json = $this->requestJson($endpointUrl, 'POST', [
'data' => $requestData
]);
if ( !isset( $json['access_token'] ) ) {
throw $this->createException( GateBridgeException::UNEXPECTED_RESPONSE, 'The parameter [access_token] is not set.' );
}
return [
'accessToken' => $json['access_token']
];
}
/**
* Gets user info.
*/
function getUserInfo() {
return [
'profile' => $this->getUserProfile(),
'email' => $this->getUserEmail()
];
}
/**
* Gets user profile.
*/
function getUserProfile() {
$endpointUrl = 'https://api.linkedin.com/v2/';
$url = $endpointUrl . 'me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))';
$headers = [
'Authorization' => 'Bearer ' . $this->accessToken
];
$result = $this->requestJson($url, 'GET', [
'headers' => $headers
]);
$data = [
'id' => isset( $result['id'] ) ? $result['id'] : false,
'firstName' => false,
'lastName' => false,
'image' => false
];
$local = false;
if ( isset( $result['firstName']['preferredLocale'] ) ) {
$local = $result['firstName']['preferredLocale']['language'] . '_' . $result['firstName']['preferredLocale']['country'];
}
if ( !empty( $local ) && isset( $result['firstName']['localized'][$local] ) ) {
$data['firstName'] = $result['firstName']['localized'][$local];
}
if ( !empty( $local ) && isset( $result['firstName']['localized'][$local] ) ) {
$data['lastName'] = $result['lastName']['localized'][$local];
}
if ( isset( $result['profilePicture']['displayImage~']['elements'][0]['identifiers'][0]['identifier'] ) ) {
$data['image'] = $result['profilePicture']['displayImage~']['elements'][0]['identifiers'][0]['identifier'];
}
return $data;
}
/**
* Gets user info.
*/
function getUserEmail() {
$endpointUrl = 'https://api.linkedin.com/v2/';
$url = $endpointUrl . 'emailAddress?q=members&projection=(elements*(handle~))';
$headers = [
'Authorization' => 'Bearer ' . $this->accessToken
];
$result = $this->requestJson($url, 'GET', [
'headers' => $headers
]);
if ( isset( $result['elements'][0]['handle~']['emailAddress'] ) ) {
return $result['elements'][0]['handle~']['emailAddress'];
}
return null;
}
/**
* Makes a request to Google API.
* @param $url
* @param $method
* @param array $options
* @return string
* @throws GateBridgeException
*/
function requestJson($url, $method, $options = [] ) {
$options['expectJson'] = true;
$json = $this->http( $url, $method, $options );
return $this->throwExceptionOnErrors( $json );
}
/**
* Throws an exception if the response contains errors.
* @param $json
* @return mixed[] Returns response data back.
* @throws GateBridgeException
*/
function throwExceptionOnErrors( $json ) {
if ( isset( $json['error'] ) && isset( $json['error_description'] ) ) {
if ( 'invalid_client' === $json['error'] ) {
throw $this->createException( GateBridgeException::NOT_AUTHENTICATED, 'Please make sure that LinkedIn Client ID and Client Secret are set correctly.' );
}
throw $this->createException( GateBridgeException::ERROR_RESPONSE, $json['error_description']);
} else if ( isset( $json['error'] ) && isset( $json['error']['message'] ) ) {
throw $this->createException( GateBridgeException::ERROR_RESPONSE, $json['error']['message']);
}
return $json;
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace bizpanda\includes\gates\linkedin;
use bizpanda\includes\gates\Gate;
use bizpanda\includes\gates\exceptions\GateException;
use bizpanda\includes\gates\OAuthGate;
use Yii;
/**
* The class to proxy the request to the LinkedIn API.
*/
class LinkedinGate extends OAuthGate {
/**
* LinkedinGate constructor.
* @param null $session
* @param null $config
* @param null $request
*/
public function __construct($session = null, $config = null, $request = null)
{
parent::__construct($session, $config, $request);
$this->name = 'linkedin';
$this->allowedPermissionScopes = ['userinfo'];
}
/**
* @inheritDoc
*/
public function createBridge( $authorized = false ) {
$config = $this->getOptionsForBridge( $authorized );
return new LinkedinBridge( $config );
}
/**
* @inheritDoc
*/
public function getOptionsForBridge( $authorized = false ) {
$options = $this->config->get( $this->name );
if ( !isset( $options['clientId'] ) || !isset( $options['clientSecret'] ) ) {
throw GateException::create(GateException::APP_NOT_CONFIGURED, 'Client Id or Client Secret are not set.' );
}
$options['permissions'] = $this->getLinkedInPermissions( $this->permissionScopes );
if ( $authorized ) {
$options['accessToken'] = $this->getVisitorValue($this->name . '_access_token', null );
if ( empty( $options['accessToken'] ) ) {
throw GateException::create(GateException::AUTH_FLOW_BROKEN, 'The access token is not set.' );
}
}
return $options;
}
/**
* Converts app scopes to the google scopes.
* @param array $scopes
* @return string
*/
public function getLinkedInPermissions( $scopes = [] ) {
$permissions = [];
foreach( $scopes as $scope ) {
if ( 'userinfo' === $scope ) {
$permissions[] = LinkedinScope::READ_LITE_PROFILE;
$permissions[] = LinkedinScope::READ_EMAIL_ADDRESS;
}
}
return implode(' ', $permissions);
}
/**
* Returns user profile info.
* @return mixed
*/
public function doGetUserInfo() {
$bridge = $this->createBridge( true );
$result = $bridge->getUserInfo();
$identity = [
'linkedinId' => isset( $result['id'] ) ? $result['id'] : false,
'source' => 'linkedin',
'email' => isset( $result['email'] ) ? $result['email'] : false,
'displayName' => false,
'name' => isset( $result['profile']['firstName'] ) ? $result['profile']['firstName'] : false,
'family' => isset( $result['profile']['lastName'] ) ? $result['profile']['lastName'] : false,
'social' => true,
'image' => isset( $result['profile']['image'] ) ? $result['profile']['image'] : false
];
$identity['displayName'] = $this->buildDisplayName( $identity );
return $this->prepareResult([
'identity' => $identity,
]);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace bizpanda\includes\gates\linkedin;
class LinkedinScope
{
/**
* Allows to read basic information about profile, such as name
*/
const READ_BASIC_PROFILE = 'r_basicprofile';
/**
* Request a minimum information about the user
* Use this scope when implementing "Sign In with LI"
*/
const READ_LITE_PROFILE = 'r_liteprofile';
const READ_FULL_PROFILE = 'r_fullprofile';
/**
* Enables access to email address field
*/
const READ_EMAIL_ADDRESS = 'r_emailaddress';
/**
* Manage and delete your data including your profile, posts, invitations, and messages
*/
const COMPLIANCE = 'w_compliance';
/**
* Enables managing business company
*/
const MANAGE_COMPANY = 'rw_organization_admin';
/**
* Post, comment and like posts on behalf of an organization.
*/
const SHARE_AS_ORGANIZATION = 'w_organization_social';
/**
* Retrieve organizations' posts, comments, and likes.
*/
const READ_ORGANIZATION_SHARES = 'r_organization_social';
/**
* Post, comment and like posts on behalf of an authenticated member.
*/
const SHARE_AS_USER = 'w_member_social';
/**
* Restricted API!
*/
const READ_USER_CONTENT = 'r_member_social';
/**
* Read and write access to ads.
*/
const ADS_MANAGEMENT = 'rw_ads';
const READ_ADS = 'r_ads';
const READ_LEADS = 'r_ads_leadgen_automation';
const READ_ADS_REPORTING = 'r_ads_reporting';
const READ_WRITE_DMP_SEGMENTS = 'rw_dmp_segments';
}

File diff suppressed because one or more lines are too long