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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
<?php
/* this file is obsolete, marked to remove, redirecting to the right file */
require_once OPANDA_BIZPANDA_DIR . '/includes/themes.php';

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,251 @@
<?php
namespace bizpanda\includes\gates;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\exceptions\GateException;
use bizpanda\includes\gates\twitter\TwitterGate;
/**
* Action Gate.
* Provides common methods for Action flow.
*/
abstract class ActionGate extends Gate {
/**
* Process names of the identity data.
* @param $service \OPanda_Subscription
* @param $itemId
* @param $identityData
* @return
*/
public function prepareDataToSave( $service, $itemId, $identityData ) {
// move the values from the custom fields like FNAME, LNAME
if ( !empty( $service ) ) {
$formType = get_post_meta( $itemId, 'opanda_form_type', true );
$strFieldsJson = get_post_meta( $itemId, 'opanda_fields', true );
if ( 'custom-form' == $formType && !empty( $strFieldsJson ) ) {
$fieldsData = json_decode( $strFieldsJson, true );
$ids = $service->getNameFieldIds();
$newIdentityData = $identityData;
foreach( $identityData as $itemId => $itemValue ) {
foreach($fieldsData as $fieldData) {
if ( !isset( $fieldData['mapOptions']['id'] ) ) continue;
if ( $fieldData['fieldOptions']['id'] !== $itemId ) continue;
$mapId = $fieldData['mapOptions']['id'];
if ( in_array( $fieldData['mapOptions']['mapTo'], array( 'separator', 'html', 'label' ) ) ) {
unset($newIdentityData[$itemId]);
continue;
}
foreach( $ids as $nameFieldId => $nameFieldType ) {
if ( $mapId !== $nameFieldId ) continue;
$newIdentityData[$nameFieldType] = $itemValue;
unset($newIdentityData[$itemId]);
}
}
}
$identityData = $newIdentityData;
}
}
// splits the full name into 2 parts
if ( isset( $identityData['fullname'] ) ) {
$fullname = trim( $identityData['fullname'] );
unset( $identityData['fullname'] );
$parts = explode(' ', $fullname);
$nameParts = array();
foreach( $parts as $part ) {
if ( trim($part) == '' ) continue;
$nameParts[] = $part;
}
if ( count($nameParts) == 1 ) {
$identityData['name'] = $nameParts[0];
} else if ( count($nameParts) > 1) {
$identityData['name'] = $nameParts[0];
$identityData['displayName'] = implode(' ', $nameParts);
unset( $nameParts[0] );
$identityData['family'] = implode(' ', $nameParts);
}
}
return $identityData;
}
/**
* Replaces keys of identity data of the view 'cf3' with the ids of custom fields in the mailing services.
* @param $service \OPanda_Subscription
* @param $itemId
* @param $identityData
* @return array
*/
public function mapToServiceIds( $service, $itemId, $identityData ) {
$formType = get_post_meta( $itemId, 'opanda_form_type', true );
$strFieldsJson = get_post_meta( $itemId, 'opanda_fields', true );
if ( 'custom-form' !== $formType || empty( $strFieldsJson ) ) {
$data = array();
if ( isset( $identityData['email'] ) ) $data['email'] = $identityData['email'];
if ( isset( $identityData['name'] ) ) $data['name'] = $identityData['name'];
if ( isset( $identityData['family'] ) ) $data['family'] = $identityData['family'];
return $data;
}
$fieldsData = json_decode( $strFieldsJson, true );
$data = array();
foreach( $identityData as $itemId => $itemValue ) {
if ( in_array( $itemId, array('email', 'fullname', 'name', 'family', 'displayName') ) ) {
$data[$itemId] = $itemValue;
continue;
}
foreach($fieldsData as $fieldData) {
if ( $fieldData['fieldOptions']['id'] === $itemId ) {
$mapId = $fieldData['mapOptions']['id'];
$data[$mapId] = $service->prepareFieldValueToSave( $fieldData['mapOptions'], $itemValue );
}
}
}
return $data;
}
/**
* Replaces keys of identity data of the view 'cf3' with the labels the user enteres in the locker settings.
* @param $service
* @param $itemId
* @param $identityData
* @return array
*/
public function mapToCustomLabels( $service, $itemId, $identityData ) {
$formType = get_post_meta( $itemId, 'opanda_form_type', true );
$strFieldsJson = get_post_meta( $itemId, 'opanda_fields', true );
if ( 'custom-form' !== $formType || empty( $strFieldsJson ) ) return $identityData;
$fieldsData = json_decode( $strFieldsJson, true );
$data = array();
foreach( $identityData as $itemId => $itemValue ) {
if ( in_array( $itemId, array('email', 'fullname', 'name', 'family', 'displayName') ) ) {
$data[$itemId] = $itemValue;
continue;
}
foreach($fieldsData as $fieldData) {
if ( $fieldData['fieldOptions']['id'] !== $itemId ) continue;
$label = $fieldData['serviceOptions']['label'];
if ( empty( $label ) ) continue 2;
$data['{' . $label . '}'] = $itemValue;
continue 2;
}
$data[$itemId] = $itemValue;
}
return $data;
}
/**
* Verifies if the email is actually was received from the social proxy.
* @param $proxy string An URL that was used to make proxy requests.
* @param $source string A source name from where the data received (facebook, twitter etc.)
* @param $visitorId string A unique visitor ID used by proxy to identify requests.
* @param $email string An email to verify.
* @return bool True if the email is verified.
* @throws GateBridgeException
*/
protected function verifyEmail( $proxy, $source, $visitorId, $email ) {
if ( empty( $proxy ) || empty( $visitorId ) || empty( $email ) ) return false;
$remoteProxy = opanda_remote_social_proxy_url();
$localProxy = opanda_local_proxy_url();
// if the email was received from the remote proxy
if ( strpos( $proxy, $remoteProxy ) !== false ) {
$url = opanda_remote_social_proxy_url() . '/verify';
$response = wp_remote_post( $url, [
'body' => [
'email' => $email,
'visitorId' => $visitorId,
'source' => $source
]
]);
$content = isset( $response['body'] ) ? $response['body'] : '';
$json = json_decode( $content, true );
if ( !$json ) {
throw GateBridgeException::create( GateBridgeException::UNEXPECTED_RESPONSE, [
'response' => $response
]);
}
if ( isset( $json['error'] ) ) {
throw GateBridgeException::create( GateBridgeException::ERROR_RESPONSE, [
'clarification' => $json['error'],
'response' => $response
]);
}
if ( isset( $json['success'] ) && $json['success'] && $json['email'] === $email ) {
return true;
}
return false;
}
// if the email was received from the local proxy
if ( strpos( $proxy, $localProxy ) !== false ) {
// as visitor ID is passed in the array of the service data (not via the parameter visitorId),
// we need to re-init sessions to access the correct session data
$currentSessionId = $this->session->sessionId;
$this->session->sessionId = $visitorId;
$verifiedEmail = $this->getVisitorValue($source . '_email');
$this->session->sessionId = $currentSessionId;
if ( $email === $verifiedEmail ) return true;
return false;
}
return false;
}
}

View File

@@ -0,0 +1,187 @@
<?php
namespace bizpanda\includes\gates;
use bizpanda\includes\gates\context\ConfigService;
use bizpanda\includes\gates\context\IContextReader;
use bizpanda\includes\gates\context\IContextReaderWriter;
use bizpanda\includes\gates\context\RequestService;
use bizpanda\includes\gates\context\SessionService;
use bizpanda\includes\gates\exceptions\GateException;
use yii\base\BaseObject;
/**
* The base class for all handlers of requests to the proxy.
*/
class Gate {
/**
* A name of this gate (usually it's a service name: facebook, twitter etc).
* @var array
*/
protected $name = null;
/**
* Current visitor ID (used as a Session ID to store data across requests).
* @var string
*/
protected $visitorId = null;
/**
* Configuration Manager.
* @var IContextReader
*/
protected $config = null;
/**
* Request Manager.
* @var IContextReader
*/
protected $request = null;
/**
* Session Manager.
* @var IContextReaderWriter
*/
protected $session = null;
/**
* Gate constructor.
* @param $session IContextReaderWriter
* @param $config IContextReader
* @param $request IContextReader
*/
public function __construct( $session = null, $config = null, $request = null ) {
$this->config = $config;
$this->request = $request;
$this->session = $session;
if ( empty( $this->config ) ) $this->config = new ConfigService();
if ( empty( $this->request ) ) $this->request = new RequestService();
if ( empty( $this->session ) ) $this->session = new SessionService();
$this->visitorId = $this->getRequestParam('visitorId');
if ( empty( $this->visitorId ) ) $this->visitorId = $this->getGuid();
$this->session->init( $this->visitorId );
}
// ----------------------------------------------------------------------
// Helper Methods
// ----------------------------------------------------------------------
/**
* Returns a param from the $_REQUEST array.
* @param $name string A key of the param to return.
* @param $default mixed A value to return if the param doesn't exist.
* @return mixed A value to return.
*/
protected function getRequestParam($name, $default = null ) {
return $this->request->get( $name, $default );
}
/**
* Saves the value to the session storage.
* @param $name string A key to save value to the session.
* @param $value mixed A value to save.
*/
protected function setVisitorValue($name, $value ) {
$this->session->set( $name, $value );
}
/**
* Gets the value from the session storage.
* @param $name string A key to get value from the session.
* @param $default mixed A default value to return if the session doesn't contain any value.
* @return mixed A value from the session.
*/
protected function getVisitorValue( $name, $default = null ) {
return $this->session->get( $name, $default );
}
/**
* Converts a set of values to the respective native values.
* @param $values mixed[] A set of values to convert.
* @return mixed[] Normalized values.
*/
protected function normalizeValues($values = array() ) {
if ( empty( $values) ) return $values;
if ( !is_array( $values ) ) $values = array( $values );
foreach ( $values as $index => $value ) {
$values[$index] = is_array( $value )
? $this->normalizeValues( $value )
: $this->normalizeValue( $value );
}
return $values;
}
/**
* Converts string values to the respective native value.
* @param $value mixed Value to convert.
* @return mixed A normalized value.
*/
protected function normalizeValue($value = null ) {
if ( 'false' === $value ) $value = false;
elseif ( 'true' === $value ) $value = true;
elseif ( 'null' === $value ) $value = null;
return $value;
}
/**
* Generates a display name using the identity data.
* @param $identity mixed[] A user identity.
* @return string A display name.
*/
protected function buildDisplayName( $identity ) {
if ( !empty( $identity['displayName'] ) ) return $identity['displayName'];
$displayName = !empty( $identity['name'] ) ? $identity['name'] : '';
if ( !empty( $displayName ) && !empty( $identity['family'] )) $displayName .= ' ';
$displayName .= !empty( $identity['family'] ) ? $identity['family'] : '';
return $displayName;
}
/**
* Generates a GUID.
* @return string A guid.
*/
protected function getGuid() {
if (function_exists('com_create_guid') === true) {
$guid = trim(com_create_guid(), '{}');
} else {
$guid = sprintf(
'%04X%04X-%04X-%04X-%04X-%04X%04X%04X',
mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479),
mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)
);
}
return strtolower( str_replace('-', '', $guid) );
}
/**
* Converts a given string to a camel case string.
* See: https://stackoverflow.com/questions/2791998/convert-dashes-to-camelcase-in-php
* @param $string string A string to convert.
* @param bool $capitalizeFirstCharacter If true, the first letter will be capitalized.
* @param string $separator A string separator to find words.
* @return string|string[] Camel case string.
*/
protected function toCamelCase($string, $capitalizeFirstCharacter = false, $separator = '_') {
$str = str_replace($separator, '', ucwords($string, $separator));
if (!$capitalizeFirstCharacter) $str = lcfirst($str);
return $str;
}
}

View File

@@ -0,0 +1,242 @@
<?php
namespace bizpanda\includes\gates;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\Gate;
use bizpanda\includes\gates\GateException;
/**
* The class to proxy requests to gate API.
*/
class GateBridge
{
/**
* Contains the last HTTP status code returned.
* @var int
*/
public $httpCode;
/**
* Contains the last API call.
* @var string
*/
public $httpUrl;
/**
* Contains the last HTTP info returned.
* @var mixed[];
*/
public $httpInfo;
/**
* Contains the last HTTP headers returned.
* @var mixed[];
*/
public $httpHeaders;
/**
* Contains the last HTTP content returned.
* @var string;
*/
public $httpContent;
/**
* Information about last request (url, method, options)
* @var mixed[]
*/
public $lastRequestInfo = null;
/**
* Set timeout default.
* @var int
*/
public $timeout = 30;
/**
* Set connect timeout.
* @var int
*/
public $connectTimeout = 30;
/**
* Verify SSL Cert.
* @var bool
*/
public $sslVerifyPeer = false;
/**
* Set the useragent.
* @var string
*/
public $useragent = 'Social Login by OnePress v.1.0';
/**
* Make an HTTP request
*
* @param $url string An URL to make requests.
* @param $method string A request method (GET, POST, DELETE, PUT).
* @param mixed[] $options POST data, headers and other date to pass.
* @return bool|string|mixed[]
* @throws GateBridgeException
*/
function http($url, $method, $options = []) {
$this->httpInfo = array();
$ci = curl_init();
curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
if ( !empty( $options['headers'] ) ) {
$resultHeaders = [];
foreach ( $options['headers'] as $headerName => $headerValue ) {
$resultHeaders[] = $headerName . ': ' . $headerValue;
}
curl_setopt($ci, CURLOPT_HTTPHEADER, $resultHeaders);
}
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->sslVerifyPeer);
curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
curl_setopt($ci, CURLOPT_HEADER, FALSE);
switch ($method) {
case 'POST':
curl_setopt($ci, CURLOPT_POST, TRUE);
if (!empty( $options['data'] ) ) {
if ( is_array( $options['data'] ) ) {
curl_setopt($ci, CURLOPT_POSTFIELDS, http_build_query( $options['data'] ));
} else {
curl_setopt($ci, CURLOPT_POSTFIELDS, $options['data'] );
}
}
break;
case 'GET':
if ( !empty( $options['data'] ) ) {
$urlParts = explode('?', $url);
$url = $url . ( ( count( $urlParts ) == 1 ) ? '?' : '&' ) . http_build_query( $options['data'] );
}
break;
case 'DELETE':
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
if (!empty($options['data'])) {
$url = "{$url}?{$options['data']}";
}
break;
}
curl_setopt($ci, CURLOPT_URL, $url);
// saves information about this request
$this->lastRequestInfo = [
'url' => $url,
'method' => $method,
'options' => $options
];
$content = curl_exec($ci);
$this->httpContent = $content;
$this->httpCode = curl_getinfo($ci, CURLINFO_HTTP_CODE);
$this->httpInfo = array_merge($this->httpInfo, curl_getinfo($ci));
$this->httpUrl = $url;
curl_close($ci);
if ( empty( $content ) ) {
throw $this->createException( GateBridgeException::UNEXPECTED_RESPONSE, 'Empty response received.');
}
$json = json_decode( $content, true );
if ( !empty( $options['expectJson'] ) && $options['expectJson'] ) {
if ( null === $json ) {
throw $this->createException( GateBridgeException::UNEXPECTED_RESPONSE, 'JSON expected.');
}
return $json;
}
if ( !empty( $json ) ) return $json;
if ( !empty( $options['expectQuery'] ) && $options['expectQuery'] ){
$queryData = [];
parse_str( $content, $queryData );
return $queryData;
}
return $content;
}
/**
* Get the header info to store.
*/
function getHeader($ch, $header)
{
$i = strpos($header, ':');
if ( !empty( $i ) ) {
$key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
$value = trim(substr($header, $i + 2));
$this->httpHeaders[$key] = $value;
}
return strlen($header);
}
/**
* Creates an exception with the given code to throw.
* @param $code
* @param $clarification
* @return GateBridgeException
*/
function createException($code, $clarification = null ) {
return GateBridgeException::create( $code, [
'clarification' => $clarification,
'request' => $this->getLastRequestInfo(),
'response' => $this->getLastResponseInfo()
]);
}
/**
* Returns info about the last request.
* @return mixed[]
*/
function getLastRequestInfo() {
return $this->lastRequestInfo;
}
/**
* Returns info about the last response.
* @return mixed[]
*/
function getLastResponseInfo() {
return [
'url' => $this->httpUrl,
'code' => $this->httpCode,
'info' => $this->httpInfo,
'headers' => $this->httpHeaders,
'content' => $this->httpContent
];
}
}

View File

@@ -0,0 +1,216 @@
<?php
namespace bizpanda\includes\gates;
use bizpanda\includes\gates\exceptions\GateException;
/**
* OAuth Gate.
* Provides common methods for OAuth flow.
*/
abstract class OAuthGate extends Gate {
/**
* Current bridge ID (used to keep connection with a widget that initiates the auth flow).
* @var string
*/
protected $bridgeId = null;
/**
* Scopes of permissions to requests on authorization.
* @var string[]
*/
protected $permissionScopes = [];
/**
* A list of allowed scopes of permissions on authorization.
* @var string[]
*/
protected $allowedPermissionScopes = [];
/**
* Allowed types of requests.
* @var string[]
*/
protected $allowedRequestTypes = [];
/**
* OAuthGate constructor.
* @param $session
* @param $config
* @param $request
*/
public function __construct( $session = null, $config = null, $request = null ) {
parent::__construct( $session, $config, $request );
$this->bridgeId = $this->getRequestParam('bridgeId');
$this->allowedRequestTypes = array_merge(
$this->allowedRequestTypes,
['init', 'auth', 'callback', 'get_user_info']
);
}
/**
* Handles the proxy request.
* @throws GateException
*/
public function handleRequest() {
// the request type is to determine which action we should to run
$requestType = $this->getRequestParam('requestType');
if ( empty( $this->name ) ) {
throw GateException::create(GateException::APP_NOT_CONFIGURED, 'The gate name is missed.' );
}
if ( empty( $this->allowedPermissionScopes ) ) {
throw GateException::create(GateException::APP_NOT_CONFIGURED, 'Allowed permissions are not set.' );
}
if ( empty( $this->allowedRequestTypes ) ) {
throw GateException::create(GateException::APP_NOT_CONFIGURED, 'Allowed request types are not set.' );
}
if ( !in_array( $requestType, $this->allowedRequestTypes ) ) {
throw GateException::create(GateException::INVALID_INBOUND_REQUEST, 'The request type [' . $requestType . '] is not allowed.' );
}
$this->permissionScopes = $this->getFlowScopes();
$method = 'do' . $this->toCamelCase( $requestType, true );
$result = $this->{$method}();
// required for the email verification in future
// used in Wordpress to verify if the email is actually received from the app
if ( $requestType == 'get_user_info' && isset( $result['identity']['email'] )) {
$this->setVisitorValue($this->name . '_email', $result['identity']['email'] );
}
return $result;
}
/**
* Returns scopes of permissions using within the current auth flow.
* @throws GateException
*/
protected function getFlowScopes() {
$scopes = $this->getRequestParam('scope');
if ( empty( $scopes ) ) $scopes = $this->getVisitorValue($this->name . '_permission_scopes', null );
if ( empty( $scopes ) ) throw GateException::create(GateException::SCOPE_MISSED );
$parts = explode(',', $scopes);
$result = [];
foreach( $parts as $part ) {
if ( !in_array( $part, $this->allowedPermissionScopes) ) continue;
$result[] = $part;
}
return $result;
}
/**
* Saves scopes of permissions for the current flow.
* @param $scope
*/
protected function setFlowScopes( $scope ) {
$scope = implode(',', $scope);
$this->setVisitorValue($this->name . '_permission_scopes', $scope );
}
/**
* Adds extra mandatory variables into returned data.
* @param array $data
* @return array
*/
public function prepareResult( $data = [] ) {
// returns when sessions expires (1440 is the default value)
$sessionLifetime = (int)ini_get('session.gc_maxlifetime');
if ( empty( $sessionLifetime ) ) $sessionLifetime = 1440;
$expires = time() + $sessionLifetime;
$mandatory = [
'visitorId' => $this->visitorId,
'bridgeId' => $this->bridgeId,
'expires' => $expires
];
return array_merge( $mandatory, $data );
}
/**
* Returns an object that is responsible for calls to API.
* @param bool $authorized If true, creates a bridge for authorized requests.
* @return OAuthGateBridge
*/
public abstract function createBridge( $authorized = false );
/**
* Returns options needed to create a bridge.
* @param bool $authorized If true, creates a bridge for authorized requests.
* @return mixed[]
*/
public abstract function getOptionsForBridge( $authorized = false );
/**
* Inits an OAuth request.
*/
public function doInit() {
$bridge = $this->createBridge();
$authUrl = $bridge->getAuthorizeURL([
'bridgeId' => $this->bridgeId,
'visitorId' => $this->visitorId
]);
$this->setFlowScopes( $this->permissionScopes );
return $this->prepareResult([
'authUrl' => $authUrl
]);
}
/**
* The same as doInit.
*/
public function doAuth() {
return $this->doInit();
}
/**
* Handles the callback request.
* @throws GateException
*/
public function doCallback() {
$bridge = $this->createBridge();
$code = $this->getRequestParam('code');
if ( empty( $code ) ) {
throw GateException::create(GateException::INVALID_INBOUND_REQUEST, 'The code parameter is not set.' );
}
$tokenData = $bridge->getAccessToken($code);
$this->setVisitorValue($this->name . '_access_token', $tokenData['accessToken'] );
return $this->prepareResult([
'auth' => $tokenData,
]);
}
/**
* Returns user profile info.
* @return mixed
*/
public abstract function doGetUserInfo();
}

View File

@@ -0,0 +1,127 @@
<?php
namespace bizpanda\includes\gates;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\Gate;
use bizpanda\includes\gates\GateException;
/**
* The bridge implementing methods for OAuth flow.
*/
abstract class OAuthGateBridge extends GateBridge
{
/**
* Client ID.
* @var string
*/
protected $clientId = null;
/**
* Client Secret.
* @var string
*/
protected $clientSecret = null;
/**
* Callback URL.
* @var string
*/
protected $callbackUrl = null;
/**
* Permissions to authorize.
* @var string[]
*/
protected $permissions = null;
/**
* Access token.
* @var string
*/
protected $accessToken = null;
/**
* Access token secret.
* @var null
*/
protected $accessTokenSecret = null;
/**
* OAuthGateBridge constructor.
* @param array $options
*/
public function __construct( $options = [] )
{
$this->clientId = $options['clientId'];
$this->clientSecret = $options['clientSecret'];
$this->callbackUrl = $options['callbackUrl'];
$this->permissions = $options['permissions'];
if ( isset( $options['accessToken']) ) $this->accessToken = $options['accessToken'];
if ( isset( $options['accessTokenSecret']) ) $this->accessTokenSecret = $options['accessTokenSecret'];
}
/**
* Gets a redirect URL to authorize a user.
* @param array $stateParams Extra data for the state param if applied.
* @return string
*/
abstract function getAuthorizeURL( $stateParams = [] );
/**
* Get an access token using the code received on callback.
* @param $code
* @return mixed
*/
abstract function getAccessToken( $code );
/**
* Gets a user info after authorization.
* @return mixed[] A response with the user data from API.
*/
abstract function getUserInfo();
// ---------------------------------------------------------------------------
// Helper methods
// ---------------------------------------------------------------------------
/**
* Builds the state argument for the authorization URL.
* @param $stateArgs mixed[] Arguments that have to be included into the state argument.
* @return string|null
*/
protected function buildStateParam( $stateArgs ) {
if ( empty( $stateArgs ) ) return null;
$state = [];
foreach($stateArgs as $extraName => $extraValue ) {
$state[] = $extraName . '=' . $extraValue;
}
return implode('&', $state);
}
/**
* Returns UNIX-timestamp.
* @return int
*/
protected function generateTimestamp () {
return time();
}
/**
* Returns a nonce.
* @return int
*/
protected function generateNonce() {
$mt = microtime();
$rand = mt_rand();
return md5($mt . $rand);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace bizpanda\includes\gates\context;
/**
* Returns values from the configuration storage.
*/
class ConfigService implements IContextReader
{
/**
* @inheritDoc
*/
public function init($id = null)
{
// no init needed
}
/**
* Returns a value from configuration.
* @param $name string A key of the config to return.
* @param $default mixed A value to return if the param doesn't exist.
* @return mixed A value from the config.
*/
public function get( $name, $default = null ) {
return opanda_get_handler_options( $name );
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace bizpanda\includes\gates\context;
/**
* Interface IContextReader
*/
interface IContextReader
{
/**
* Inits the context.
* @param $id string An custom ID to init.
*/
public function init( $id = null );
/**
* Gets a value with a given key/name.
* @param $name string A key to get a value.
* @param null $default A default value if there is not any values exist.
* @return mixed A value.
*/
public function get( $name, $default = null );
}

View File

@@ -0,0 +1,16 @@
<?php
namespace bizpanda\includes\gates\context;
/**
* Interface IContextReader
*/
interface IContextReaderWriter extends IContextReader
{
/**
* Saves a value into the context.
* @param $name string A key of the value to save.
* @param $value mixed A value to save.
*/
public function set( $name, $value );
}

View File

@@ -0,0 +1,31 @@
<?php
namespace bizpanda\includes\gates\context;
/**
* Returns values from the current request.
*/
class RequestService implements IContextReader
{
/**
* @inheritDoc
*/
public function init($id = null)
{
// no init needed
}
/**
* Returns a param from the $_REQUEST array.
* @param $name string A key of the param to return.
* @param $default mixed A value to return if the param doesn't exist.
* @return mixed A value to return.
*/
public function get( $name, $default = null ) {
$prefixedName = 'opanda' . ucfirst( $name );
if ( isset( $_REQUEST[$prefixedName] ) ) return $_REQUEST[$prefixedName];
return ( isset( $_REQUEST[$name] ) ) ? $_REQUEST[$name] : $default;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace bizpanda\includes\gates\context;
/**
* Manages the session.
*/
class SessionService implements IContextReaderWriter
{
public $sessionId = null;
/**
* Inits the session.
* @param $id string An ID of the session.
*/
public function init( $id = null ) {
$this->sessionId = $id;
}
/**
* Saves a value into the session.
* @param $name string A key of the value to save.
* @param $value mixed A value to save.
*/
public function set( $name, $value ) {
if ( defined('W3TC') ) {
setcookie( 'opanda_' . md5($name), $value, time() + 60 * 60 * 1 , COOKIEPATH, COOKIE_DOMAIN );
} else {
set_transient( 'opanda_' . md5($this->sessionId . '_' . $name), $value, 60 * 60 * 1 );
}
}
/**
* Returns a value from the session.
* @param $name string A key of the session value to return.
* @param $default mixed A value to return if the param doesn't exist.
* @return mixed A value from the session.
*/
public function get( $name, $default = null ) {
if ( defined('W3TC') ) {
$cookieName = 'opanda_' . md5($name);
$value = isset( $_COOKIE[$cookieName] ) ? $_COOKIE[$cookieName] : null;
if ( empty( $value ) ) return $default;
return $value;
} else {
$value = get_transient( 'opanda_' . md5($this->sessionId . '_' . $name) );
if ( empty( $value ) ) return $default;
return $value;
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace bizpanda\includes\gates\exceptions;
use Throwable;
/**
* An exception which shows the error for public.
*/
class GateBridgeException extends GateException {
const UNEXPECTED_RESPONSE = 'gate.bridge.unexpected-response';
const ERROR_RESPONSE = 'gate.bridge.error-response';
const NOT_AUTHENTICATED = 'gate.bridge.not-supported';
const NOT_SUPPORTED = 'gate.bridge.not-supported';
public static function create( $code, $details = [] ) {
$baseData = [
'code' => $code,
'details' => $details
];
$exceptionData = [];
switch ( $code ) {
case self::UNEXPECTED_RESPONSE:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'Unexpected response.'
];
break;
case self::ERROR_RESPONSE:
$exceptionData = [
'priority' => GateExceptionPriority::NORMAL,
'message' => 'Error occurred during the request.'
];
break;
case self::NOT_AUTHENTICATED:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'Authentication failed.'
];
break;
case self::NOT_SUPPORTED:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'The method is not supported.',
];
break;
}
$exceptionData = array_merge($baseData, $exceptionData);
return new GateBridgeException($exceptionData);
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace bizpanda\includes\gates\exceptions;
/**
* An exception that shows the error for public.
*/
class GateException extends \Exception {
const INVALID_INBOUND_REQUEST = 'gate.invalid-request';
const SCOPE_MISSED = 'gate.scope-missed';
const APP_NOT_CONFIGURED = 'gate.app-not-configured';
const AUTH_FLOW_BROKEN = 'gate.auth-flow-broken';
const INVALID_VISITOR_ID = 'gate.invalid-visitor-id';
public static function create( $code, $details = [] ) {
if ( is_string( $details ) ) {
$details = [
'clarification' => $details
];
}
$exceptionData = [];
$baseData = [
'code' => $code,
'details' => $details
];
switch ( $code ) {
case self::INVALID_INBOUND_REQUEST:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'Invalid request type.'
];
break;
case self::SCOPE_MISSED:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'The scope is not set.'
];
break;
case self::APP_NOT_CONFIGURED:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'The app is not configured properly.',
];
break;
case self::AUTH_FLOW_BROKEN:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'Authorization flow broken. Please try again.',
];
break;
case self::INVALID_VISITOR_ID:
$exceptionData = [
'priority' => GateExceptionPriority::HIGH,
'message' => 'Invalid Visitor ID.',
];
break;
}
$exceptionData = array_merge($baseData, $exceptionData);
throw new GateException($exceptionData);
}
protected $exceptionCode = null;
protected $exceptionDetails = null;
protected $exceptionPriority = null;
public function __construct( $data )
{
parent::__construct($data['message'], 0, null);
$this->exceptionPriority = isset( $data['priority'] ) ? $data['priority'] : GateExceptionPriority::NORMAL;
$this->exceptionCode = $data['code'];
$this->exceptionDetails = $data['details'];
}
public function getExceptionCode() {
return $this->exceptionCode;
}
public function getExceptionDetails() {
return $this->exceptionDetails;
}
public function getExceptionPriority() {
return !empty( $this->exceptionPriority ) ? $this->exceptionPriority : GateExceptionPriority::NORMAL;
}
public function getExceptionVisibleMessage() {
$error = $this->getMessage();
if ( isset( $this->exceptionDetails['clarification'] ) ) {
$error = trim($error, '.') . '. ' . trim( $this->exceptionDetails['clarification'], '.' ) . '.';
}
return $error;
}
/**
* Returns exception data to log.
* @return array
*/
public function getDataToLog() {
return [
'code' => $this->exceptionCode,
'message' => $this->getExceptionVisibleMessage(),
'details' => $this->getExceptionDetails(),
'priority' => $this->getExceptionPriority(),
'trace' => $this->getTraceAsString()
];
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace bizpanda\includes\gates\exceptions;
/**
* Priorities of Gate Exceptions
*/
class GateExceptionPriority extends \Exception {
const LOW = 'low';
const NORMAL = 'normal';
const HIGH = 'high';
}

View File

@@ -0,0 +1,107 @@
<?php
namespace bizpanda\includes\gates\facebook;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\GateBridge;
use bizpanda\includes\gates\OAuthGateBridge;
/**
* Proxy for requests to the Facebook API.
*/
class FacebookBridge 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.facebook.com/v7.0/dialog/oauth';
$args = [
'scope' => $this->permissions,
'response_type' => 'code',
'redirect_uri' => $this->callbackUrl,
'client_id' => $this->clientId,
'display' => 'popup',
'auth_type' => 'rerequest',
'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://graph.facebook.com/v7.0/oauth/access_token';
$requestData = [
'code' => $code,
'redirect_uri' => $this->callbackUrl,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
];
$json = $this->requestJson($endpointUrl, 'GET', [
'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() {
$endpointUrl = 'https://graph.facebook.com/me';
$url = $endpointUrl . '?fields=email,first_name,last_name,gender,link&access_token=' . $this->accessToken;
return $this->requestJson($url, 'GET');
}
/**
* Makes a request to Facebook 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'] ) ) {
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,116 @@
<?php
namespace bizpanda\includes\gates\facebook;
use bizpanda\includes\gates\Gate;
use bizpanda\includes\gates\exceptions\GateException;
use bizpanda\includes\gates\OAuthGate;
use bizpanda\includes\gates\OAuthGateBridge;
use Yii;
/**
* The class to proxy the request to the Facebook API.
*/
class FacebookGate extends OAuthGate {
/**
* FacebookGate 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 = 'facebook';
$this->allowedPermissionScopes = ['userinfo'];
}
/**
* @inheritDoc
*/
public function createBridge( $authorized = false ) {
$config = $this->getOptionsForBridge( $authorized );
return new FacebookBridge( $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, 'App Id or App Secret are not set.' );
}
$options['permissions'] = $this->getFacebookPermissions( $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 Facebook permissions.
* @param array $permissionScopes A set of scopes to convert to Facebook permissions.
* @return string
*/
public function getFacebookPermissions( $permissionScopes ) {
$permissions = [];
foreach( $permissionScopes as $scope ) {
if ( 'userinfo' === $scope ) {
$permissions[] = 'public_profile';
$permissions[] = 'email';
}
}
return implode(' ', $permissions);
}
/**
* Returns user profile info.
* @return mixed
*/
public function doGetUserInfo() {
$bridge = $this->createBridge( true );
$result = $bridge->getUserInfo();
$identity = [
'source' => 'facebook',
'email' => isset( $result['email'] ) ? $result['email'] : false,
'displayName' => false,
'name' => isset( $result['first_name'] ) ? $result['first_name'] : false,
'family' => isset( $result['last_name'] ) ? $result['last_name'] : false,
'gender' => isset( $result['gender'] ) ? $result['gender'] : false,
'social' => true,
'facebookId' => isset( $result['id'] ) ? $result['id'] : false,
'facebookUrl' => isset( $result['link'] ) ? $result['link'] : false,
'image' => false
];
$identity['displayName'] = $this->buildDisplayName( $identity );
if ( !empty( $identity['facebookId'] ) ) {
$identity['image'] = 'https://graph.facebook.com/' . $identity['facebookId'] . '/picture?type=large';
}
return $this->prepareResult([
'identity' => $identity,
]);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,143 @@
<?php
namespace bizpanda\includes\gates\google;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\OAuthGateBridge;
/**
* The class to proxy the request to the Google API.
*/
class GoogleBridge 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://accounts.google.com/o/oauth2/v2/auth';
$args = [
'scope' => $this->permissions,
'access_type' => 'online',
'include_granted_scopes' => 'true',
'response_type' => 'code',
'redirect_uri' => $this->callbackUrl,
'client_id' => $this->clientId,
'display' => 'popup',
'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://oauth2.googleapis.com/token';
$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() {
$endpointUrl = 'https://www.googleapis.com/oauth2/v2/userinfo';
$url = $endpointUrl . '?access_token=' . $this->accessToken;
return $this->requestJson($url, 'GET');
}
/**
* Subscribes to YouTube channel.
* @param $channelId string A channel to subscribe to.
* @return string
* @throws GateBridgeException
*/
function subscribeToYoutube( $channelId ) {
$endpointUrl = 'https://www.googleapis.com/youtube/v3/subscriptions';
$url = $endpointUrl . '?access_token=' . $this->accessToken . '&part=snippet';
$data = json_encode([
'snippet' => [
'resourceId' => [
'kind' => 'youtube#channel',
'channelId' => $channelId
]
]
]);
$headers = [
'Content-Type' => 'application/json'
];
return $this->requestJson($url, 'POST', [
'data' => $data,
'headers' => $headers
]);
}
/**
* 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 Google 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,135 @@
<?php
namespace bizpanda\includes\gates\google;
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 Google API.
*/
class GoogleGate extends OAuthGate {
/**
* GoogleGate 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 = 'google';
$this->allowedRequestTypes = array_merge(
$this->allowedRequestTypes,
['subscribe_to_youtube']
);
$this->allowedPermissionScopes = ['userinfo', 'youtube'];
}
/**
* @inheritDoc
*/
public function createBridge( $authorized = false ) {
$config = $this->getOptionsForBridge( $authorized );
return new GoogleBridge( $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->getGooglePermissions( $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 getGooglePermissions( $scopes = [] ) {
$googleScopes = [];
foreach( $scopes as $scope ) {
if ( 'userinfo' === $scope ) {
$googleScopes[] = 'https://www.googleapis.com/auth/userinfo.profile';
$googleScopes[] = 'https://www.googleapis.com/auth/userinfo.email';
} else if ( 'youtube' === $scope ) {
$googleScopes[] = 'https://www.googleapis.com/auth/youtube';
}
}
return implode(' ', $googleScopes);
}
/**
* Returns user profile info.
* @return mixed
*/
public function doGetUserInfo() {
$bridge = $this->createBridge( true );
$result = $bridge->getUserInfo();
$identity = [
'source' => 'google',
'email' => isset( $result['email'] ) ? $result['email'] : false,
'displayName' => isset( $result['name'] ) ? $result['name'] : false,
'name' => isset( $result['given_name'] ) ? $result['given_name'] : false,
'family' => isset( $result['family_name'] ) ? $result['family_name'] : false,
'social' => true,
'googleId' => isset( $result['id'] ) ? $result['id'] : false,
'googleUrl' => false,
'image' => isset( $result['picture'] ) ? $result['picture'] : false
];
return $this->prepareResult([
'identity' => $identity,
]);
}
/**
* Performs subscription to YouTube channel.
* @throws GateException
*/
public function doSubscribeToYouTube() {
$channelId = $this->getRequestParam('channelId');
if ( empty( $channelId ) ) throw new GateException('Invalid request [channelId].');
$bridge = $this->createBridge( true );
$result = $bridge->subscribeToYoutube( $channelId );
return $this->prepareResult([
'response' => $result,
]);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,37 @@
<?php
namespace bizpanda\includes\gates\lead;
use bizpanda\includes\gates\ActionGate;
use \bizpanda\includes\gates\exceptions\GateException;
/**
* The class to proxy the request to the Twitter API.
*/
class LeadGate extends ActionGate {
/**
* Handles the proxy request.
*/
public function handleRequest() {
// - context data
$contextData = $this->getRequestParam('contextData', []);
$contextData = $this->normalizeValues( $contextData );
// - idetity data
$identityData = $this->getRequestParam('identityData', []);
$identityData = $this->normalizeValues( $identityData );
// prepares data received from custom fields to be transferred to the mailing service
$identityData = $this->prepareDataToSave( null, null, $identityData );
require_once OPANDA_BIZPANDA_DIR . '/admin/includes/leads.php';
\OPanda_Leads::add( $identityData, $contextData );
}
}

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

View File

@@ -0,0 +1,144 @@
<?php
namespace bizpanda\includes\gates\signup;
use bizpanda\includes\gates\ActionGate;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\exceptions\GateException;
/**
* Sign-in Up Handler
*/
class SignupGate extends ActionGate {
/**
* Handles the proxy request.
*/
public function handleRequest() {
// - context data
$contextData = $this->getRequestParam('contextData');
$contextData = $this->normalizeValues( $contextData );
// - identity data
$identityData = $this->getRequestParam('identityData');
$identityData = $this->normalizeValues( $identityData );
// - service data
$serviceData = $this->getRequestParam('serviceData');
$serviceData = $this->normalizeValues( $serviceData );
// prepares data received from custom fields to be transferred to the mailing service
$identityData = $this->prepareDataToSave( null, null, $identityData );
require_once OPANDA_BIZPANDA_DIR . '/admin/includes/leads.php';
\OPanda_Leads::add( $identityData, $contextData );
$email = $identityData['email'];
if ( empty( $email ) ) {
throw GateException::create(GateException::INVALID_INBOUND_REQUEST, 'The email is not specified.');
}
// validate email in order to sign in user
$visitorId = isset( $serviceData['visitorId'] ) ? trim ( $serviceData['visitorId'] ) : false;
$proxy = isset( $serviceData['proxy'] ) ? trim ( $serviceData['proxy'] ) : false;
$source = isset( $identityData['source'] ) ? trim ( $identityData['source'] ) : false;
$canSignin = false;
$isNewUser = !email_exists( $email );
// if it's a new user
if ( $isNewUser ) {
$username = $this->generateUsername( $email );
$random_password = wp_generate_password( $length = 12, false );
$userId = wp_create_user( $username, $random_password, $email );
if ( $userId ) {
if ( isset( $identityData['name'] ) ) update_user_meta( $userId, 'first_name', $identityData['name'] );
if ( isset( $identityData['family'] ) ) update_user_meta( $userId, 'last_name', $identityData['family'] );
// saves an user ID received from the social network
// facebookId, twitterId, googleId, linkedinIt etc.
if ( !empty( $source ) ) {
$fieldName = $source . 'Id';
if ( isset( $identityData[$fieldName] ) ) {
update_user_meta( $userId, "opanda_" . $source . "_id", $identityData[$fieldName] );
}
}
}
wp_new_user_notification( $userId, null );
do_action('opanda_registered', $identityData, $contextData );
$canSignin = true;
// if a user with the given email exists, we need to verify it before sign-in
} else {
$user = get_user_by( 'email', $email );
$userId = $user->ID;
if ( !empty( $source ) ) {
// step 1: verify that the email is received from the proxy
if ( !$this->verifyEmail( $proxy, $source, $visitorId, $email ) ) {
throw GateException::create(GateException::INVALID_INBOUND_REQUEST, __('Unable to verify the email.', 'bizpanda'));
}
// step 2: checks if the user was registered via the sign-in locker early
$fieldName = $source . 'Id';
if ( isset( $identityData[$fieldName] ) ) {
$savedId = get_user_meta($userId, $source . "_id", true);
if ( !empty( $savedId ) && $savedId == $identityData[$fieldName] ) {
$canSignin = true;
}
}
}
}
// safe, the email is verified above
if ( $canSignin && !is_user_logged_in() ) {
wp_set_auth_cookie( $userId, true );
}
}
/**
* Generates a username.
* @param $email
* @return bool|mixed|string
*/
protected function generateUsername( $email ) {
$parts = explode ('@', $email);
if ( count( $parts ) < 2 ) return false;
$username = $parts[0];
if ( !username_exists( $username ) ) return $username;
$index = 0;
while(true) {
$index++;
$username = $parts[0] . $index;
if ( !username_exists( $username ) ) return $username;
}
}
}

View File

@@ -0,0 +1,224 @@
<?php
namespace bizpanda\includes\gates\subscription;
use bizpanda\includes\gates\ActionGate;
use bizpanda\includes\gates\exceptions\GateException;
/**
* Subscription Gate.
*/
class SubscriptionGate extends ActionGate {
/**
* Handles the proxy request.
* @throws GateException
*/
public function handleRequest() {
$requestType = $this->getRequestParam('requestType');
$service = $this->getRequestParam('service');
if( empty( $requestType ) || empty( $service ) ) {
throw GateException::create( GateException::INVALID_INBOUND_REQUEST, 'The "requestType" or "service" are not defined.');
}
require_once OPANDA_BIZPANDA_DIR . '/admin/includes/subscriptions.php';
$service = \OPanda_SubscriptionServices::getCurrentService();
if ( empty( $service) ) {
throw GateException::create( GateException::APP_NOT_CONFIGURED, 'The subscription service is not set.');
}
// - service name
$options = $this->config->get( 'subscription' );
$serviceName = $options['service'];
if ( $serviceName !== $service->name ) {
throw GateException::create( GateException::APP_NOT_CONFIGURED, sprintf( 'Invalid subscription service "%s".', $serviceName ));
}
// - request type
$requestType = strtolower( $requestType );
$allowed = array('check', 'subscribe');
if ( !in_array( $requestType, $allowed ) ) {
throw GateException::create( GateException::INVALID_INBOUND_REQUEST, sprintf( 'Invalid request. The action "%s" not found.', $requestType ));
}
// - identity data
$identityData = $this->getRequestParam('identityData', []);
$identityData = $this->normalizeValues( $identityData );
if ( empty( $identityData['email'] )) {
throw GateException::create( GateException::INVALID_INBOUND_REQUEST, sprintf( 'Unable to subscribe. The email is not specified.' , $requestType ));
}
if ( get_option('opanda_forbid_temp_emails', false) ) {
$tempDomains = get_option('opanda_temp_domains', false);
if ( !empty( $tempDomains ) ) {
$tempDomains = explode(',', $tempDomains);
foreach( $tempDomains as $tempDomain ) {
$tempDomain = trim($tempDomain);
if ( false !== strpos($identityData['email'], $tempDomain) ) {
throw new GateException([
'message' => get_option('opanda_res_errors_temporary_email', 'Sorry, temporary email addresses cannot be used to unlock content.')
]);
}
}
}
}
// - service data
$serviceData = $this->getRequestParam('serviceData', []);
$serviceData = $this->normalizeValues( $serviceData );
// - context data
$contextData = $this->getRequestParam('contextData', []);
$contextData = $this->normalizeValues( $contextData );
// - list id
$listId = $this->getRequestParam('listId', null);
if ( empty( $listId ) ) {
throw GateException::create( GateException::INVALID_INBOUND_REQUEST, 'Unable to subscribe. The list ID is not specified.');
}
// - double opt-in
$doubleOptin = $this->getRequestParam('doubleOptin', true);
$doubleOptin = $this->normalizeValue( $doubleOptin );
// - confirmation
$confirm = $this->getRequestParam('confirm', true);
$confirm = $this->normalizeValue( $confirm );
// verifying user data if needed while subscribing
// works for social subscription
$verified = false;
$mailServiceInfo = \OPanda_SubscriptionServices::getServiceInfo();
if ( 'subscribe' === $requestType ) {
if ( $doubleOptin && in_array( 'quick', $mailServiceInfo['modes'] ) ) {
$visitorId = isset( $serviceData['visitorId'] ) ? trim ( $serviceData['visitorId'] ) : false;
$proxy = isset( $serviceData['proxy'] ) ? trim ( $serviceData['proxy'] ) : false;
$source = isset( $identityData['source'] ) ? trim ( $identityData['source'] ) : false;
$email = isset( $identityData['email'] ) ? trim ( $identityData['email'] ) : false;
$verified = $this->verifyEmail( $proxy, $source, $visitorId, $email );
}
}
// prepares data received from custom fields to be transferred to the mailing service
$itemId = intval( $contextData['itemId'] );
$identityData = $this->prepareDataToSave( $service, $itemId, $identityData );
$serviceReadyData = $this->mapToServiceIds( $service, $itemId, $identityData );
$identityData = $this->mapToCustomLabels( $service, $itemId, $identityData );
// checks if the subscription has to be procces via WP
$subscribeMode = get_post_meta($itemId, 'opanda_subscribe_mode', true);
$subscribeDelivery = get_post_meta($itemId, 'opanda_subscribe_delivery', true);
$isWpSubscription = false;
if ( $service->hasSingleOptIn()
&& in_array( $subscribeMode, array('double-optin', 'quick-double-optin') )
&& ( $service->isTransactional() || $subscribeDelivery == 'wordpress' ) ) {
$isWpSubscription = true;
}
// creating subscription service
$result = array();
try {
if ( 'subscribe' === $requestType ) {
if ( $isWpSubscription ) {
// if the use signs in via a social network and we managed to confirm that the email is real,
// then we can skip the confirmation process
if ( $verified ) {
\OPanda_Leads::add( $identityData, $contextData, true, true );
return $service->subscribe( $serviceReadyData, $listId, false, $contextData, $verified );
} else {
$result = $service->wpSubscribe( $identityData, $serviceReadyData, $contextData, $listId, $verified );
}
} else {
$result = $service->subscribe( $serviceReadyData, $listId, $doubleOptin, $contextData, $verified );
}
do_action('opanda_subscribe',
( $result && isset( $result['status'] ) ) ? $result['status'] : 'error',
$identityData, $contextData, $isWpSubscription
);
} elseif ( 'check' === $requestType ) {
if ( $isWpSubscription ) {
$result = $service->wpCheck( $identityData, $serviceReadyData, $contextData, $listId, $verified );
} else {
$result = $service->check( $serviceReadyData, $listId, $contextData );
}
do_action('opanda_check',
( $result && isset( $result['status'] ) ) ? $result['status'] : 'error',
$identityData, $contextData, $isWpSubscription
);
}
} catch( \OPanda_SubscriptionException $exception ) {
throw new GateException([
'code' => 'gate.subscription-error',
'message' => $exception->getMessage(),
'details' => []
]);
} catch ( \Exception $exception ) {
throw $exception;
}
$result = apply_filters('opanda_subscription_result', $result, $identityData);
// calls the hook to save the lead in the database
if ( $result && isset( $result['status'] ) ) {
$actionData = array(
'identity' => $identityData,
'requestType' => $requestType,
'service' => $options['service'],
'list' => $listId,
'doubleOptin' => $doubleOptin,
'confirm' => $confirm,
'context' => $contextData
);
if ( 'subscribed' === $result['status'] ) {
do_action('opanda_subscribed', $actionData);
} else {
do_action('opanda_pending', $actionData);
}
}
return $result;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,338 @@
<?php
namespace bizpanda\includes\gates\twitter;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\OAuthGateBridge;
/**
* The class to proxy the request to the Twitter API.
*/
class TwitterBridge extends OAuthGateBridge {
const API_ENDPOINT = 'https://api.twitter.com/';
const API_VERSION = '1.1';
const API_OAUTH_VERSION = '1.0';
/**
* Get a request_token from Twitter
* @param array $stateParams
* @return mixed[]
* @throws GateBridgeException
*/
function getRequestToken( $stateParams = [] ) {
$callbackUrl = $this->callbackUrl;
if ( !empty( $stateParams ) ) {
if ( strpos( $callbackUrl, '?' ) === false ) {
$callbackUrl .= '?' . http_build_query( $stateParams );
} else {
$callbackUrl .= '&' . http_build_query( $stateParams );
}
}
$response = $this->requestQuery('oauth/request_token', 'POST', [
'oauthParams' => [
'oauth_callback' => $callbackUrl
],
// the parameter 'x_auth_access_type' doesn't work with include_email option
// 'data' => [
// 'x_auth_access_type' => $this->permissions
// ],
'expectQuery' => true
]);
if ( !isset( $response['oauth_token'] ) ) {
throw $this->createException( GateBridgeException::UNEXPECTED_RESPONSE, 'The parameter [oauth_token] is not set.' );
}
return $response;
}
/**
* Gets a redirect URL to authorize a user.
* @param array $stateParams Extra data for the state param if applied.
* @return string
* @throws GateBridgeException
*/
function getAuthorizeURL( $stateParams = [] ) {
$token = $this->getRequestToken( $stateParams );
$token = $token['oauth_token'];
return self::API_ENDPOINT . 'oauth/authorize' . "?oauth_token={$token}";
}
/**
* Exchange request token and secret for an access token and
* secret, to sign API calls.
*
* @returns array("oauth_token" => "the-access-token",
* "oauth_token_secret" => "the-access-secret",
* "user_id" => "9436992",
* "screen_name" => "abraham")
*/
function getAccessTokenByVerifier( $token, $verifier ) {
$host = 'oauth/access_token?oauth_token=' . $token . '&oauth_verifier=' . $verifier;
return $this->requestQuery( $host, 'POST');
}
/**
* @inheritDoc
*/
function getAccessToken($code) {
throw GateBridgeException::create( GateBridgeException::NOT_SUPPORTED );
}
/**
* @inheritDoc
*/
function getUserInfo(){
return $this->get('account/verify_credentials', [
'skip_status' => 1,
'include_email' => 'true'
]);
return $result;
}
// -----------------------------------------------------------------------------
// Helper Methods
// -----------------------------------------------------------------------------
/**
* Makes GET request to the API.
* @param $host
* @param $data
* @return string
* @throws GateBridgeException
*/
public function get($host, $data) {
return $this->requestJson( $host, 'GET', [
'data' => $data
]);
}
/**
* Makes POST request to the API.
* @param $host
* @param $data
* @return string
* @throws GateBridgeException
*/
public function post($host, $data) {
return $this->requestJson( $host, 'POST', [
'data' => $data
]);
}
/**
* Makes a request to API and expects the query string as a response.
* @param $host
* @param $method
* @param array $options
* @return mixed[]
* @throws GateBridgeException
*/
function requestQuery($host, $method, $options = [] ) {
$options['expectQuery'] = true;
$data = $this->request( $host, $method, $options );
return $this->throwExceptionOnErrors( $data );
}
/**
* Makes a request to API and expects the json as a response.
* @param $host
* @param $method
* @param array $options
* @return string
* @throws GateBridgeException
*/
function requestJson( $host, $method, $options = [] ) {
$options['expectJson'] = true;
$json = $this->request( $host, $method, $options );
return $this->throwExceptionOnErrors( $json );
}
/**
* A common point for any request to Twitter API.
* @param $host
* @param $method
* @param array $options
* @return mixed[]
* @throws GateBridgeException
*/
function request($host, $method, $options = [] ) {
$isOAuthRequest = ( strpos( $host, 'oauth' ) === 0 );
if ( $isOAuthRequest ) {
$url = self::API_ENDPOINT . $host;
} else {
$url = self::API_ENDPOINT . self::API_VERSION . '/' . $host . '.json';
}
$signUrl = $url;
$authorizationParams = [
'oauth_consumer_key' => $this->clientId,
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => $this->generateTimestamp(),
'oauth_nonce' => $this->generateNonce(),
'oauth_version' => self::API_OAUTH_VERSION
];
// if this request is authorized, add the access token
if ( $this->accessToken ) {
$authorizationParams['oauth_token'] = $this->accessToken;
}
// if the options extra parameters to pass, add it too
if ( isset( $options['oauthParams'] )) {
$authorizationParams = array_merge($authorizationParams, $options['oauthParams'] );
}
// adds all GET/POST params to the signature
$urlParams = [];
$query = parse_url($url, PHP_URL_QUERY );
parse_str( $query, $urlParams );
// filters empty params, otherwise we will get an incorrect signature
$postParams = !empty( $options['data'] ) ? $options['data'] : [];
$postParamsFiltered = [];
foreach( $postParams as $postParamName => $postParam ) {
if ( $postParam === null || $postParam === false ) continue;
$postParamsFiltered[$postParamName] = $postParam;
}
$options['data'] = $postParamsFiltered;
$signatureParams = array_merge($authorizationParams, $urlParams, $postParamsFiltered);
$authorizationParams['oauth_signature'] = $this->signRequest($method, $url, $signatureParams);
$headers =[
'Authorization' => $this->generateAuthorizationHeader( $authorizationParams )
];
if ( !isset( $options['headers'] ) ) $options['headers'] = [];
$options['headers'] = array_merge( $options['headers'], $headers);
$json = $this->http( $url, $method, $options );
/*
if ( $host == 'account/verify_credentials' ) {
return [
'url' => $url,
'signatureParams' => $signatureParams,
'authorizationParams' => $authorizationParams,
'urlParams' => $urlParams,
'postParams' => $postParams,
'options' => $options,
'content' => $json,
'request' => $this->getLastRequestInfo(),
'response' => $this->getLastResponseInfo(),
'sign-key' => $this->generateSigningKey(),
'access-token' => $this->accessToken,
'access-token-secret' => $this->accessTokenSecret,
];
}
*/
return $json;
}
/**
* Throws an exception if the response contains errors.
* @param $data
* @return mixed[] Returns response data back.
* @throws GateBridgeException
*/
function throwExceptionOnErrors( $data ) {
if ( isset( $data['errors'] ) && isset( $data['errors'][0]['message'] ) ) {
// [CODE: 32] Could not authenticate you.
if ( 32 === $data['errors'][0]['code'] ) {
throw $this->createException( GateBridgeException::NOT_AUTHENTICATED, 'Please make sure that Twitter API Key and Secret Key are set correctly.' );
}
$clarification = sprintf( '[CODE: %d] %s.' , $data['errors'][0]['code'], $data['errors'][0]['message'] );
throw $this->createException( GateBridgeException::ERROR_RESPONSE, $clarification );
}
return $data;
}
/**
* Signs the request.
* @param $method
* @param $url
* @param $params
* @return string
*/
function signRequest( $method, $url, $params ) {
ksort( $params );
$strParams = [];
foreach( $params as $key => $value ) {
$strParams[] = rawurlencode( $key ) . '=' . rawurlencode( $value );
}
$strParams = implode('&', $strParams);
$message = $method . '&'. rawurlencode($url) . '&' . rawurlencode($strParams);
$key = $this->generateSigningKey();
$encrypted = hash_hmac('sha1', $message, $key, true);
return base64_encode($encrypted);
}
/**
* Generates the signing key.
* @return string
*/
function generateSigningKey() {
$key = rawurlencode( $this->clientSecret ) . '&';
if ( $this->accessTokenSecret ) $key = $key . rawurlencode( $this->accessTokenSecret );
return $key;
}
/**
* Generates the authorization header.
* @param array $params
* @return string
*/
function generateAuthorizationHeader( $params = [] ) {
$header = 'OAuth ';
ksort( $params );
$strParams = [];
foreach( $params as $key => $value ) {
$strParams[] = rawurlencode( $key ) . '="' . rawurlencode( $value ) . '"';
}
$strParams = implode(', ', $strParams);
return $header . $strParams;
}
}

View File

@@ -0,0 +1,251 @@
<?php
namespace bizpanda\includes\gates\twitter;
use bizpanda\includes\gates\exceptions\GateBridgeException;
use bizpanda\includes\gates\Gate;
use bizpanda\includes\gates\exceptions\GateException;
use bizpanda\includes\gates\google\FacebookBridge;
use bizpanda\includes\gates\OAuthGate;
use Yii;
/**
* The class to proxy the request to the Twitter API.
*/
class TwitterGate extends OAuthGate {
public function __construct($session = null, $config = null, $request = null)
{
parent::__construct($session, $config, $request);
$this->name = 'twitter';
$this->allowedRequestTypes = array_merge(
$this->allowedRequestTypes,
['follow', 'tweet', 'get_tweets', 'get_followers']
);
$this->allowedPermissionScopes = ['read', 'write'];
}
/**
* @inheritDoc
*/
public function createBridge( $authorized = false ) {
$config = $this->getOptionsForBridge( $authorized );
return new TwitterBridge( $config );
}
/**
* @inheritDoc
*/
public function getOptionsForBridge( $authorized = false ) {
$scopes = $this->getFlowScopes();
$scope = $scopes[0]; // read or write
$twitterOptions = $this->config->get( $this->name );
$options = $twitterOptions[$scope];
$options['callbackUrl'] = $twitterOptions['callbackUrl'];
if ( !isset( $options['clientId'] ) || !isset( $options['clientSecret'] ) ) {
throw GateException::create(GateException::APP_NOT_CONFIGURED, 'API Key or API Secret are not set.' );
}
$options['permissions'] = $scope;
if ( $authorized ) {
$options['accessToken'] = $this->getVisitorValue($this->name . '_oauth_token', null );
$options['accessTokenSecret'] = $this->getVisitorValue($this->name . '_oauth_secret', null );
if ( empty( $options['accessToken'] ) ) {
throw GateException::create(GateException::AUTH_FLOW_BROKEN, 'The access token is not set.' );
}
if ( empty( $options['accessTokenSecret'] ) ) {
throw GateException::create(GateException::AUTH_FLOW_BROKEN, 'The access token secret is not set.' );
}
}
return $options;
}
/**
* @inheritDoc
*/
public function doCallback() {
$token = $this->getRequestParam('oauthToken');
$verifier = $this->getRequestParam('oauthVerifier');
if ( empty( $token ) || empty( $verifier ) ) {
throw GateException::create(GateException::INVALID_INBOUND_REQUEST, 'Parameters [oauthToken] or [oauthVerifier] are not passed.' );
}
$bridge = $this->createBridge( false );
$accessToken = $bridge->getAccessTokenByVerifier( $token, $verifier );
$this->setVisitorValue($this->name . '_oauth_token', $accessToken['oauth_token'] );
$this->setVisitorValue($this->name . '_oauth_secret', $accessToken['oauth_token_secret'] );
return $this->prepareResult([
'userId' => $accessToken['user_id'],
'oauthToken' => $accessToken['oauth_token'],
'oauthTokenSecret' => $accessToken['oauth_token_secret']
]);
}
/**
* @inheritDoc
*/
public function doGetUserInfo() {
$bridge = $this->createBridge(true);
$response = $bridge->getUserInfo();
$identity = [
'source' => 'twitter',
'email' => isset( $response['email'] ) ? $response['email'] : false,
'name' => isset( $response['name'] ) ? $response['name'] : false,
'displayName' => isset( $response['screen_name'] ) ? $response['screen_name'] : false,
'twitterId' => isset( $response['screen_name'] ) ? $response['screen_name'] : false,
'twitterUrl' => isset( $response['screen_name'] ) ? ( 'https://twitter.com/' . $response['screen_name'] ) : ''
];
if ( $identity['name'] ) {
$nameParts = explode(' ', $identity['name']);
if ( count( $nameParts ) == 2 ) {
$identity['name'] = $nameParts[0];
$identity['family'] = $nameParts[1];
}
}
if ( isset( $response['profile_image_url'] ) ) {
$identity['image'] = str_replace('_normal', '', $response['profile_image_url']);
} else {
$identity['image'] = false;
}
return $this->prepareResult([
'identity' => $identity
]);
}
/**
* Gets the 3 last tweets.
* @return array
* @throws GateBridgeException
*/
public function doGetTweets() {
$bridge = $this->createBridge(true);
$response = $bridge->get('statuses/user_timeline', [
'count' => 3
]);
return $this->prepareResult([
'response' => $response
]);
}
/**
* Finds a followers with the specified screen name.
* @return array
* @throws GateException
*/
public function doGetFollowers() {
$bridge = $this->createBridge(true);
$screenName = $this->getRequestParam('screenName');
if ( empty( $screenName) ) throw new GateException( "The screen name is not set." );
$response = $bridge->get('friendships/lookup', [
'screen_name' => $screenName
]);
return $this->prepareResult([
'response' => $response
]);
}
/**
* Tweets the specified message.
* @return array
* @throws GateException
*/
protected function doTweet() {
$bridge = $this->createBridge(true);
$message = $this->getRequestParam('tweetMessage');
if ( empty( $message) ) throw new GateException( "The tweet text is not specified." );
$response = null;
try {
$response = $bridge->post('statuses/update', [
'status' => $message
]);
return $this->prepareResult([
'response' => $response
]);
} catch ( GateBridgeException $exception ) {
$details = $exception->getExceptionDetails();
if ( strpos( $details['clarification'], '187' ) > 0 ) {
// error 187: status is a duplicate.
// already tweeted
return $this->prepareResult([
'success' => true
]);
} else {
throw $exception;
}
}
}
/**
* Follows the specified profile.
* @return array
* @throws GateException
*/
protected function doFollow() {
$bridge = $this->createBridge(true);
$followTo = $this->getRequestParam('followTo');
if ( empty( $followTo) ) throw new GateException( "The user name to follow is not specified" );
$notifications = $this->getRequestParam('notifications', true);
$notifications = $this->normalizeValue( $notifications );
$response = $bridge->get('friendships/lookup', [
'screen_name' => $followTo
]);
if ( isset( $response[0]->connections ) && in_array( 'following', $response[0]->connections ) ) {
return $this->prepareResult([
'success' => true
]);
}
$requestData = ['screen_name' => $followTo];
if ( !empty( $notifications ) ) $requestData['follow'] = $notifications;
$response = $bridge->post('friendships/create', $requestData);
return $this->prepareResult([
'response' => $response
]);
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* The file contains a class to configure the metabox Advanced Options.
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 1.0.0
*/
/**
* The class to configure the metabox Advanced Options.
*
* @since 1.0.0
*/
class OPanda_AdvancedOptionsMetaBox extends FactoryMetaboxes321_FormMetabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 1.0.0
* @var string
*/
public $title;
/**
* A prefix that will be used for names of input fields in the form.
*
* Inherited from the class FactoryFormMetabox.
*
* @since 1.0.0
* @var string
*/
public $scope = 'opanda';
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $priority = 'core';
/**
* The part of the page where the edit screen section should be shown ('normal', 'advanced', or 'side').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $context = 'side';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Advanced Options', 'bizpanda');
}
public $cssClass = 'factory-bootstrap-331';
/**
* Configures a form that will be inside the metabox.
*
* @see FactoryMetaboxes321_FormMetabox
* @since 1.0.0
*
* @param FactoryForms328_Form $form A form object to configure.
* @return void
*/
public function form( $form ) {
$options = array(
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'close',
'title' => __('Close Icon', 'bizpanda'),
'hint' => __('Shows the Close Icon at the corner.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/close-icon.png',
'default' => false
),
array(
'type' => 'textbox',
'name' => 'timer',
'title' => __('Timer Interval', 'bizpanda'),
'hint' => __('Sets a countdown interval for the locker.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/timer-icon.png',
'default' => false
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'ajax',
'title' => __('AJAX', 'bizpanda'),
'hint' => __('If On, locked content will be cut from a page source code.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/ajax-icon.png',
'default' => false
),
array(
'type' => 'html',
'html' => '<div id="opanda-ajax-disabled" class="alert alert-warning">The option AJAX is not applied when the "transparence" or "blurring" overlap modes selected.</div>'
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'highlight',
'title' => __('Highlight', 'bizpanda'),
'hint' => __('Defines whether the locker must use the Highlight effect.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/highlight-icon.png',
'default' => true
)
);
if ( OPanda_Items::isCurrentFree() ) {
$options[] = array(
'type' => 'html',
'html' => '<div style="display: none;" class="factory-fontawesome-320 opanda-overlay-note opanda-premium-note">' . __( '<i class="fa fa-star-o"></i> Go Premium <i class="fa fa-star-o"></i><br />To Unlock These Features <a href="#" class="opnada-button">Learn More</a>', 'bizpanda' ) . '</div>'
);
}
$options = apply_filters('opanda_advanced_options', $options);
$form->add($options);
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_AdvancedOptionsMetaBox', $bizpanda);

View File

@@ -0,0 +1,260 @@
<?php
/**
* The file contains a class to configure the metabox Basic Options.
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 1.0.0
*/
/**
* The class to configure the metabox Basic Options.
*
* @since 1.0.0
*/
class OPanda_BasicOptionsMetaBox extends FactoryMetaboxes321_FormMetabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 1.0.0
* @var string
*/
public $title;
/**
* A prefix that will be used for names of input fields in the form.
* Inherited from the class FactoryFormMetabox.
*
* @since 1.0.0
* @var string
*/
public $scope = 'opanda';
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $priority = 'core';
public $cssClass = 'factory-bootstrap-331 factory-fontawesome-320';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Basic Options', 'bizpanda');
}
/**
* Configures a form that will be inside the metabox.
*
* @see FactoryMetaboxes321_FormMetabox
* @since 1.0.0
*
* @param FactoryForms328_Form $form A form object to configure.
* @return void
*/
public function form( $form ) {
$itemType = OPanda_Items::getCurrentItem();
$form->add(
array(
'type' => 'hidden',
'name' => 'item',
'default' => isset( $_GET['opanda_item'] ) ? $_GET['opanda_item'] : null
)
);
$defaultHeader = __('This content is locked!', 'bizpanda');
$defaultMessage = __('Please support us, use one of the buttons below to unlock the content.', 'bizpanda');
switch ($itemType['name']) {
case 'email-locker':
$defaultHeader = __('This Content Is Only For Subscribers', 'bizpanda');
$defaultMessage = __('Please subscribe to unlock this content. Just enter your email below.', 'bizpanda');
break;
case 'connect-locker':
$defaultHeader = __('Sing In To Unlock This Content', 'bizpanda');
$defaultMessage = __('Please sign in. It\'s free. Just click one of the buttons below to get instant access.', 'bizpanda');
break;
}
$textOptions = array(
array(
'type' => 'textbox',
'name' => 'header',
'title' => __('Locker Header', 'bizpanda'),
'hint' => __('Type a header which attracts attention or calls to action. You can leave this field empty.', 'bizpanda'),
'default' => $defaultHeader
),
array(
'type' => 'wp-editor',
'name' => 'message',
'title' => __('Locker Message', 'bizpanda'),
'hint' => __('Type a message which will appear under the header.', 'bizpanda').'<br /><br />'.
__('Shortcodes: [post_title], [post_url].', 'bizpanda'),
'default' => $defaultMessage,
'tinymce' => array(
'setup' => 'function(ed){ window.bizpanda.lockerEditor.bindWpEditorChange( ed ); }',
'height' => 100
),
'layout' => array(
'hint-position' => 'left'
)
),
);
$textOptions = apply_filters('opanda_text_options', $textOptions );
$form->add($textOptions);
if ( 'email-locker' === $itemType['name'] ) {
$form->add( array(
'type' => 'columns',
'items' => array(
array(
'type' => 'textbox',
'name' => 'button_text',
'title' => __('Buttton Text', 'bizpanda'),
'hint' => __('The text on the button. Call to action!'),
'default' => __('subscribe to unlock', 'bizpanda'),
'column' => 1
),
array(
'type' => 'textbox',
'name' => 'after_button',
'title' => __('After Buttton', 'bizpanda'),
'hint' => __('The text below the button. Guarantee something.'),
'default' => __('Your email address is 100% safe from spam!', 'bizpanda'),
'column' => 2
)
)
));
}
$form->add( array(
'type' => 'html',
'html' => array( $this, 'showOtherFrontendTextNote' )
));
$form->add( array(
'type' => 'separator'
));
require_once OPANDA_BIZPANDA_DIR . '/includes/themes.php';
$form->add(array(
array(
'type' => 'dropdown',
'hasHints' => true,
'name' => 'style',
'data' => OPanda_ThemeManager::getThemes( OPanda_Items::getCurrentItemName(), 'dropdown'),
'title' => __('Theme', 'bizpanda'),
'hint' => __('Select the most suitable theme.', 'bizpanda'),
'default' => OPanda_Items::isCurrentPremium() ? 'flat' : 'secrets'
)
));
if ( OPanda_Items::isCurrentPremium() ) {
$form->add(array(
array(
'type' => 'dropdown',
'way' => 'buttons',
'name' => 'overlap',
'data' => array(
array('full', '<i class="fa fa-lock"></i>'.__('Full (classic)', 'bizpanda')),
array('transparence', '<i class="fa fa-adjust"></i>'.__('Transparency', 'bizpanda') ),
array('blurring', '<i class="fa fa-bullseye"></i>'.__('Blurring', 'bizpanda') )
),
'title' => __('Overlap Mode', 'bizpanda'),
'hint' => __('Choose the way how your locker should lock the content.', 'bizpanda'),
'default' => 'full'
)
));
} else {
$form->add(array(
array(
'type' => 'dropdown',
'way' => 'buttons',
'name' => 'overlap',
'data' => array(
array('full', '<i class="fa fa-lock"></i>Full (classic)'),
array('transparence', '<i class="fa fa-adjust"></i>Transparency' ),
array('blurring', '<i class="fa fa-bullseye"></i>Blurring', sprintf( __( 'This option is available only in the <a href="%s" target="_blank">premium version</a> of the plugin (the transparency mode will be used in the free version)', 'bizpanda' ), opanda_get_premium_url( null, 'blurring' ) ) )
),
'title' => __('Overlap Mode', 'bizpanda'),
'hint' => __('Choose the way how your locker should lock the content.', 'bizpanda'),
'default' => 'full'
)
));
}
$form->add(array(
array(
'type' => 'dropdown',
'name' => 'overlap_position',
'data' => array(
array('top', __( 'Top Position', 'bizpanda' ) ),
array('middle', __( 'Middle Position', 'bizpanda' ) ),
array('scroll', __( 'Scrolling (N/A in Preview)', 'bizpanda' ) )
),
'title' => '',
'hint' => '',
'default' => 'middle'
)
));
}
/**
* Replaces the 'blurring' overlap with 'transparence' in the free version.
*
* @since 1.0.0
* @param type $postId
*/
public function onSavingForm( $postId ) {
if ( !OPanda_Items::isCurrentFree() ) return;
$overlap = isset ( $_POST['opanda_overlap'] ) ? $_POST['opanda_overlap'] : null;
if ( $overlap == 'blurring' ) $_POST['opanda_overlap'] = 'transparence';
}
public function showOtherFrontendTextNote() {
?>
<div class="form-group opanda-edit-common-text">
<label class="col-sm-2 control-label"></label>
<div class="control-group controls col-sm-10">
<?php printf( __('<a href="%s" target="_blank">Click here</a> to edit the front-end text shared for all lockers.', 'bizpanda'), opanda_get_settings_url('text') ) ?>
</div>
</div>
<?php
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_BasicOptionsMetaBox', $bizpanda);

View File

@@ -0,0 +1,516 @@
<?php
/**
* The file contains a class to configure the metabox Bulk Locking.
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 1.0.0
*/
/**
* The class to show info on how the plugin support is provided.
*
* @since 1.0.0
*/
class OPanda_BulkLockingMetaBox extends FactoryMetaboxes321_Metabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 1.0.0
* @var string
*/
public $title;
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $priority = 'core';
/**
* The part of the page where the edit screen section should be shown ('normal', 'advanced', or 'side').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $context = 'side';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Batch Locking', 'bizpanda');
}
public function configure( $scripts, $styles ){
$scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/metaboxes/bulk-lock.010000.js');
}
/**
* Renders content of the metabox.
*
* @see FactoryMetaboxes321_Metabox
* @since 1.0.0
*
* @return void
*/
public function html()
{
global $post;
$options = get_post_meta($post->ID, 'opanda_bulk_locking', true);
// current bulk locker for the "skip & lock" and "more tag" modes
$bulkLockerId = intval( get_option('onp_sl_bulk_locker', 0) );
$bulkLocker = null;
if ( $bulkLockerId !== 0 && $bulkLockerId != $post->ID ) {
$bulkLocker = get_post($bulkLockerId);
if ( $bulkLocker && empty( $bulkLocker->post_title ) ) $bulkLocker->post_title = __('No title', 'bizpanda');
}
// gets values for the form
$setupStateClass = empty( $options ) ? 'onp-sl-empty-state' : 'onp-sl-has-options-state';
$wayStateClass = '';
if ( !empty($options) && isset( $options['way'] ) ) {
if ( $options['way'] == 'skip-lock' ) $wayStateClass = 'onp-sl-skip-lock-state';
elseif ( $options['way'] == 'more-tag' ) $wayStateClass = 'onp-sl-more-tag-state';
elseif ( $options['way'] == 'css-selector' ) $wayStateClass = 'onp-sl-css-selector-state';
}
$skipAndLockStateClass = '';
if ( !empty($options) && $options['way'] == 'skip-lock' ) {
if ( $options['skip_number'] == 0 ) $skipAndLockStateClass = 'onp-sl-skip-lock-0-state';
elseif ( $options['skip_number'] == 1 ) $skipAndLockStateClass = 'onp-sl-skip-lock-1-state';
elseif ( $options['skip_number'] > 1 ) $skipAndLockStateClass = 'onp-sl-skip-lock-2-state';
}
$ruleStateClass = '';
$defaultWay = 'skip-lock';
if ( !empty($options) ) $defaultWay = $options['way'];
$skipNumber = 1;
if ( !empty($options) && $options['way'] == 'skip-lock' ) {
$skipNumber = intval( $options['skip_number'] );
}
$cssSelector = '';
if ( !empty($options) && $options['way'] == 'css-selector' ) {
$cssSelector = urldecode( $options['css_selector'] );
}
$excludePosts = '';
if ( !empty($options) && !empty( $options['exclude_posts'] ) ) {
$excludePosts = implode(', ', $options['exclude_posts']);
$ruleStateClass .= ' onp-sl-exclude-post-ids-rule-state';
}
$excludeCategories = '';
if ( !empty($options) && !empty( $options['exclude_categories'] ) ) {
$excludeCategories = implode(', ', $options['exclude_categories']);
$ruleStateClass .= ' onp-sl-exclude-categories-ids-rule-state';
}
$postTypes = '';
if ( !empty($options) && !empty( $options['post_types'] ) ) {
$postTypes = implode(', ', $options['post_types'] );
$ruleStateClass .= ' onp-sl-post-types-rule-state';
}
$checkedPostTypes = array('post', 'page');
if ( !empty($options) && !empty( $options['post_types'] ) ) {
$checkedPostTypes = $options['post_types'];
}
$types = get_post_types( array('public' => true), 'objects' );
// get interrelated option
$interrelated = get_option('opanda_interrelation', false);
$interrelatedClass = ( !$interrelated ) ? 'onp-sl-not-interrelation' : '';
?>
<script>
if ( !window.bizpanda ) window.bizpanda = {};
if ( !window.bizpanda.lang ) window.bizpanda.lang = {};
window.bizpanda.lang.everyPostWillBeLockedEntirelyExceptFirstsParagraphs =
"<?php _e('Every post will be locked entirely except %s paragraphs placed at the beginning.', 'bizpanda') ?>";
window.bizpanda.lang.appliesToTypes =
"<?php _e('Applies to types: %s', 'bizpanda') ?>";
window.bizpanda.lang.excludesPosts =
"<?php _e('Excludes posts: %s', 'bizpanda') ?>";
window.bizpanda.lang.excludesCategories =
"<?php _e('Excludes categories: %s', 'bizpanda') ?>";
</script>
<div class="onp-sl-visibility-options-disabled" style="display: none;">
<div class="alert alert-warning">
<?php _e( 'You set the the batch locking based on CSS Selector. The visibility options don\'t support CSS selectors.', 'bizpanda') ?>
</div>
</div>
<div id="onp-sl-bulk-lock-options">
<?php if ( !empty($options) ) { ?>
<?php foreach( $options as $optionKey => $optionValue ) { ?>
<?php if (in_array($optionKey, array('exclude_posts','exclude_categories','post_types'))) {
$optionValue = implode(',', $optionValue);
} ?>
<input type="hidden" name="onp_sl_<?php echo $optionKey ?>" value="<?php echo $optionValue ?>" />
<?php } ?>
<?php } ?>
</div>
<div class="factory-bootstrap-331 factory-fontawesome-320">
<div class="onp-sl-description-section">
<?php _e('Batch Locking allows to apply the locker shortcode to your posts automatically.', 'bizpanda') ?>
</div>
<div class="onp-sl-setup-section <?php echo $setupStateClass ?>">
<div class="onp-sl-empty-content">
<span class="onp-sl-nolock"><?php _e('No batch lock', 'bizpanda') ?></span>
<a class="btn btn-default" href="#onp-sl-bulk-lock-modal" role="button" data-toggle="factory-modal">
<i class="fa fa-cog"></i> <?php _e('Setup Batch Lock', 'bizpanda') ?>
</a>
</div>
<div class="onp-sl-has-options-content <?php echo $wayStateClass ?> <?php echo $ruleStateClass ?>">
<div class="onp-sl-way-description onp-sl-skip-lock-content <?php echo $skipAndLockStateClass ?>">
<span class="onp-sl-skip-lock-0-content">
<?php echo _e('Every post will be locked entirely.', 'bizpanda') ?>
</span>
<span class="onp-sl-skip-lock-1-content">
<?php echo _e('Every post will be locked entirely except the first paragraph.', 'bizpanda') ?>
</span>
<span class="onp-sl-skip-lock-2-content">
<?php echo sprintf( __('Every post will be locked entirely except %s paragraphs placed at the beginning.', 'bizpanda'), $skipNumber ) ?>
</span>
</div>
<div class="onp-sl-way-description onp-sl-more-tag-content">
<?php echo _e('Content placed after the More Tag will be locked in every post.', 'bizpanda') ?>
</div>
<div class="onp-sl-way-description onp-sl-css-selector-content">
<p><?php echo _e('Every content matching the given CSS selector will be locked on every page:', 'bizpanda') ?></p>
<p><strong class="onp-sl-css-selector-view"><?php echo $cssSelector ?></strong></p>
</div>
<div class='onp-sl-rules'>
<span class='onp-sl-post-types-rule'>
<?php printf( __('Applies to types: %s', 'bizpanda'), $postTypes ) ?>
</span>
<span class='onp-sl-exclude-post-ids-rule'>
<?php printf( __('Excludes posts: %s', 'bizpanda'), $excludePosts ) ?>
</span>
<span class='onp-sl-exclude-categories-ids-rule'>
<?php printf( __('Excludes categories: %s', 'bizpanda'), $excludeCategories ) ?>
</span>
</div>
<div class="onp-sl-controls">
<a class="btn btn-primary onp-sl-cancel" href="#onp-sl-bulk-lock-modal" role="button" data-toggle="modal" id="onp-sl-setup-bult-locking-btn">
<i class="fa fa-times-circle"></i> <?php _e('Cancel', 'bizpanda') ?>
</a>
<a class="btn btn-primary onp-sl-setup-bulk-locking" href="#onp-sl-bulk-lock-modal" role="button" data-toggle="factory-modal">
<i class="fa fa-cog"></i> <?php _e('Setup Batch Lock', 'bizpanda') ?>
</a>
</div>
</div>
</div>
<div class="<?php echo $setupStateClass ?> <?php echo $interrelatedClass ?> onp-sl-interrelation-hint">
<?php printf( __('Recommended to turn on the Interrelation option on the <a target="_blank" href="%s">Common Settings</a> page. It allows to unlock all lockers when one is unlocked.', 'bizpanda'), opanda_get_admin_url("settings", array( 'opanda_screen' => 'lock' ) ) ) ?>
</div>
<div class="onp-sl-after-change-hint">
<i class="fa fa-exclamation-triangle"></i>
<?php _e('Don\'t forget to apply made changes via the Update button above.', 'bizpanda') ?>
</div>
<div class="modal fade" id="onp-sl-bulk-lock-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel"><?php _e('Select Way Of Batch Locking', 'bizpanda') ?></h4>
</div>
<div class="modal-body">
<div class="btn-group" id="onp-sl-bulk-locking-way-selector" data-toggle="buttons-radio">
<button data-target="#onp-sl-skip-lock-options" type="button" class="btn btn-default value skip-lock <?php if ( $defaultWay == 'skip-lock') echo 'active'; ?>" data-name="skip-lock"><i class="fa fa-sort-amount-desc"></i> <?php _e('Skip & Lock', 'bizpanda' ) ?></button>
<button data-target="#onp-sl-more-tags-options" type="button" class="btn btn-default value more-tag <?php if ( $defaultWay == 'more-tag') echo 'active'; ?>" data-name="more-tag"><i class="fa fa-scissors"></i> <?php _e('More Tag', 'bizpanda' ) ?></button>
<button data-target="#onp-sl-css-selector-options" type="button" class="btn btn-default value css-selector <?php if ( $defaultWay == 'css-selector') echo 'active'; ?>" data-name="css-selector"><i class="fa fa-hand-o-up"></i> <?php _e('CSS Selector', 'bizpanda' ) ?></button>
</div>
<div id="onp-sl-skip-lock-options" class="onp-sl-bulk-locking-options <?php if ( $defaultWay !== 'skip-lock') echo 'hide'; ?>">
<div class="onp-sl-description">
<?php _e('Enter the number of paragraphs which will be visible for users at the beginning of your posts and which will be free from locking. The remaining paragraphs will be locked.', 'bizpanda') ?>
</div>
<div class="onp-sl-content">
<div class="onp-sl-form">
<div class='onp-sl-skip-number-row'>
<label><?php _e('The number of paragraphs to skip', 'bizpanda') ?></label>
<input type="text" class="form-control onp-sl-skip-number" maxlength="3" min="0" value="<?php echo $skipNumber; ?>" />
<div class="help-block help-block-error">
<?php _e('Please enter a positive integer value.') ?>
</div>
</div>
<div class="onp-sl-limits">
<div class='onp-sl-exclude'>
<div class='onp-sl-row'>
<label><?php _e('Exclude Posts', 'bizpanda') ?></label>
<input type="text" class="form-control onp-sl-exclude-posts" value="<?php echo $excludePosts; ?>" />
<div class="help-block">
<?php _e('(Optional) Enter posts IDs comma separated, for example, "19,25,33".', 'bizpanda') ?>
</div>
</div>
<div class='onp-sl-row'>
<label><?php _e('Exclude Categories', 'bizpanda') ?></label>
<input type="text" class="form-control onp-sl-exclude-categories" value="<?php echo $excludeCategories; ?>" />
<div class="help-block">
<?php _e('(Optional) Enter categories IDs comma separated, for example, "4,7".', 'bizpanda') ?>
</div>
</div>
</div>
<div class='onp-sl-post-types'>
<strong><?php _e('Posts types to lock', 'bizpanda') ?></strong>
<div class="help-block">
<?php _e('Choose post types for batch locking.', 'bizpanda') ?>
</div>
<ul>
<?php foreach($types as $type) {?>
<li>
<label for='onp-sl-post-type-<?php echo $type->name ?>-lock-skip'>
<input type='checkbox' class='onp-sl-post-type onp-sl-<?php echo $type->name ?>' id='onp-sl-post-type-<?php echo $type->name ?>-lock-skip' value='<?php echo $type->name ?>' <?php if ( in_array($type->name, $checkedPostTypes ) ) { echo 'checked="checked"'; } ?> />
<?php echo $type->label ?>
</label>
</li>
<?php } ?>
</ul>
<div class="help-block help-block-error">
<?php _e('Please choose at least one post type to lock. Otherwise, nothing to lock.', 'bizpanda') ?>
</div>
</div>
</div>
</div>
<div class="onp-sl-example">
<div class="onp-sl-description">
<strong><?php _e('For example,', 'bizpanda') ?></strong>
<?php _e('If you enter 2, two first paragraphs will be visible, others will be locked.', 'bizpanda') ?>
</div>
<div class="onp-sl-page">
<div class="onp-sl-skipped">
<div class="onp-sl-p"></div>
<div class="onp-sl-p"></div>
</div>
<div class="onp-sl-locked">
<div class="onp-sl-hint">
<i class="fa fa-lock"></i>
<?php _e('this will be locked', 'bizpanda') ?>
<i class="fa fa-lock"></i>
</div>
<div class="onp-sl-p"></div>
<div class="onp-sl-p"></div>
<div class="onp-sl-p"></div>
</div>
</div>
</div>
</div>
</div>
<div id="onp-sl-more-tags-options" class="onp-sl-bulk-locking-options <?php if ( $defaultWay !== 'more-tag') echo 'hide'; ?>">
<div class="onp-sl-description">
<?php _e('All content after the More Tag will be locked in all your posts. If a post doesn\'t have the More Tag, the post will be shown without locking.', 'bizpanda') ?>
</div>
<div class="onp-sl-content">
<div class="onp-sl-form onp-sl-limits">
<div class='onp-sl-exclude'>
<div class='onp-sl-row'>
<label><?php _e('Exclude Posts', 'bizpanda') ?></label>
<input type="text" class="form-control onp-sl-exclude-posts" value="<?php echo $excludePosts; ?>" />
<div class="help-block">
<?php _e('(Optional) Enter posts IDs comma separated, for example, "19,25,33".', 'bizpanda') ?>
</div>
</div>
<div class='onp-sl-row'>
<label><?php _e('Exclude Categories', 'bizpanda') ?></label>
<input type="text" class="form-control onp-sl-exclude-categories" value="<?php echo $excludeCategories; ?>" />
<div class="help-block">
<?php _e('(Optional) Enter categories IDs comma separated, for example, "4,7".', 'bizpanda') ?>
</div>
</div>
</div>
<div class='onp-sl-post-types'>
<strong><?php _e('Posts types to lock', 'bizpanda') ?></strong>
<div class="help-block">
<?php _e('Choose post types for batch locking.', 'bizpanda') ?>
</div>
<ul>
<?php foreach($types as $type) {?>
<li>
<label for='onp-sl-post-type-<?php echo $type->name ?>-more-tag'>
<input type='checkbox' class='onp-sl-post-type onp-sl-<?php echo $type->name ?>' id='onp-sl-post-type-<?php echo $type->name ?>-more-tag' value='<?php echo $type->name ?>' <?php if ( in_array($type->name, $checkedPostTypes ) ) { echo 'checked="checked"'; } ?> />
<?php echo $type->label ?>
</label>
</li>
<?php } ?>
</ul>
<div class="help-block help-block-error">
<?php _e('Please choose at least one post type to lock. Otherwise, nothing to lock.', 'bizpanda') ?>
</div>
</div>
</div>
<strong class="onp-sl-title"><?php _e('What is the More Tag?', 'bizpanda') ?></strong>
<div class="onp-sl-image"></div>
<p>
<?php _e('Check out <a href="http://en.support.wordpress.com/splitting-content/more-tag/" target="_blank">Splitting Content > More Tag</a> to lean more.', 'bizpanda') ?>
</p>
</div>
</div>
<div id="onp-sl-css-selector-options" class="onp-sl-bulk-locking-options <?php if ( $defaultWay !== 'css-selector') echo 'hide'; ?>">
<div class="onp-sl-description">
<p>
<?php _e('CSS selectors allow accurately choose which content will be locked by usign CSS classes or IDs of elements placed on pages. If you don\'t know what is it, please don\'t use it.', 'bizpanda') ?>
</p>
<p>
<?php _e('Check out <a href="http://www.w3schools.com/cssref/css_selectors.asp" target="_blank">CSS Selector Reference</a> to lean more.', 'bizpanda') ?>
</p>
</div>
<div class="onp-sl-content">
<div class="onp-sl-form">
<label><?php _e('CSS Selector', 'bizpanda') ?></label>
<input type="text" class="form-control onp-sl-css-selector" value="<?php echo htmlentities($cssSelector); ?>" />
<div class="help-block">
<?php _e('For example, "#somecontent .my-class, .my-another-class"', 'bizpanda') ?>
</div>
<div class="help-block help-block-error">
<?php _e('Please enter a css selector to lock.', 'bizpanda') ?>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><?php _e('Cancel', 'bizpanda') ?></button>
<button type="button" class="btn btn-primary" id="onp-sl-save-bulk-locking-btn"><?php _e('Save', 'bizpanda') ?></button>
</div>
</div>
</div>
</div>
</div>
<?php
}
public function addBulkLockingWayCssClass( $classes ) {
$classes[] = $this->way;
return $classes;
}
public function save( $postId ) {
// clear previos settings
$bulkLockers = get_option('onp_sl_bulk_lockers', array());
if ( !is_array($bulkLockers) ) $bulkLockers = array();
if ( isset( $bulkLockers[$postId] ) ) unset( $bulkLockers[$postId] );
if ( !isset( $_POST['onp_sl_way'] ) ) {
delete_post_meta($postId, 'opanda_bulk_locking');
delete_option('onp_sl_bulk_lockers');
add_option('onp_sl_bulk_lockers', $bulkLockers);
return;
} else {
$way = $_POST['onp_sl_way'];
if ( !in_array( $way, array( 'skip-lock', 'more-tag', 'css-selector' )) ) {
delete_post_meta($postId, 'opanda_bulk_locking');
delete_option('onp_sl_bulk_lockers');
add_option('onp_sl_bulk_lockers', $bulkLockers);
return;
}
$data = array('way' => $way);
if ( $way == 'skip-lock' ) {
$data['skip_number'] = intval( $_POST['onp_sl_skip_number'] );
} elseif( $way == 'css-selector' ) {
$data['css_selector'] = $_POST['onp_sl_css_selector'];
}
if ( $way == 'skip-lock' || $way == 'more-tag' ) {
$postTypes = isset( $_POST['onp_sl_post_types'] ) ? $_POST['onp_sl_post_types'] : '';
$excludePosts = isset( $_POST['onp_sl_exclude_posts'] ) ? $_POST['onp_sl_exclude_posts'] : '';
$excludeCategories = isset( $_POST['onp_sl_exclude_categories'] ) ? $_POST['onp_sl_exclude_categories'] : '';
$data['post_types'] = explode ( ',', $postTypes );
$data['post_types'] = !empty( $data['post_types'] ) ? array_map( 'trim', $data['post_types'] ) : array();
$data['exclude_posts'] = !empty( $excludePosts )
? $this->_normalizeIntValArray( explode ( ',', $excludePosts ) )
: array();
$data['exclude_categories'] = !empty( $excludeCategories )
? $this->_normalizeIntValArray( explode ( ',', $excludeCategories ) )
: array();
}
$bulkLockers[$postId] = $data;
delete_option('onp_sl_bulk_lockers');
add_option('onp_sl_bulk_lockers', $bulkLockers);
update_post_meta($postId, 'opanda_bulk_locking', $data);
}
}
function _normalizeIntValArray( $arr ) {
$arr = !empty( $arr ) ? array_map( 'intval', $arr ) : array();
$return = array();
foreach( $arr as $value ) {
if ( $value == 0 ) continue;
$return[] = $value;
}
return $return;
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_BulkLockingMetaBox', $bizpanda);

View File

@@ -0,0 +1,96 @@
<?php
/**
* The file contains a class to configure the metabox Bulk Locking.
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 1.0.0
*/
/**
* The class to show info on how the plugin support is provided.
*
* @since 1.0.0
*/
class OPanda_ManualLockingMetaBox extends FactoryMetaboxes321_Metabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 1.0.0
* @var string
*/
public $title;
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $priority = 'core';
/**
* The part of the page where the edit screen section should be shown ('normal', 'advanced', or 'side').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $context = 'side';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Manual Locking <i>(recommended)</i>', 'bizpanda');
}
public function configure( $scripts, $styles ){
$scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/metaboxes/manual-lock.010000.js');
}
/**
* Renders content of the metabox.
*
* @see FactoryMetaboxes321_Metabox
* @since 1.0.0
*
* @return void
*/
public function html()
{
global $post;
$isSystem = get_post_meta( $post->ID, 'opanda_is_system', true);
$item = OPanda_Items::getCurrentItem();
$shortcodeName = $item['shortcode'];
$shortcode = '[' . $shortcodeName . '] [/' . $shortcodeName . ']';
if (!$isSystem) $shortcode = '[' . $shortcodeName . ' id="' . $post->ID . '"] [/' . $shortcodeName . ']';
?>
<div class="factory-bootstrap-331 factory-fontawesome-320">
<p class="onp-sl-description-section">
<?php _e('Wrap content you want to lock via the following shortcode in your post editor:', 'bizpanda') ?>
<input class="onp-sl-shortcode" type="text" value='<?php echo $shortcode ?>' />
</p>
</div>
<?php
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_ManualLockingMetaBox', $bizpanda);

View File

@@ -0,0 +1,122 @@
<?php
/**
* The file contains a metabox to show the Locker Preview.
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 1.0.0
*/
/**
* The class to render the metabox Locker Preview'.
*
* @since 1.0.0
*/
class OPanda_PreviewMetaBox extends FactoryMetaboxes321_Metabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 1.0.0
* @var string
*/
public $title;
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $priority = 'core';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Locker Preview', 'bizpanda');
}
/**
* Renders content of the metabox.
*
* @see FactoryMetaboxes321_Metabox
* @since 1.0.0
*
* @return void
*/
public function html()
{
global $bizpanda;
$query_string = '?action=onp_sl_preview';
$query_string = apply_filters('opanda_preview_url', $query_string);
$extra_data = array();
$extra_data['data-url'] = admin_url('admin-ajax.php') . $query_string;
$extra_data = apply_filters('onp_sl_preview_data_wrap', $extra_data);
// ToDo: remove, it's obsoleted
$dataPrint = sizeof($extra_data) ? ' ' : '';
foreach( $extra_data as $key => $val) $dataPrint .= $key.'="'.$val.'" ';
$dataPrint = rtrim($dataPrint, ' ');
$showStyleRollerOffer = ( BizPanda::isSinglePlugin() && BizPanda::hasPlugin('sociallocker') );
?>
<script>
function onp_sl_update_preview_height(height) {
jQuery("#lock-preview-wrap iframe").height(height);
}
var pluginName = '<?php echo $bizpanda->pluginName; ?>';
window.opanda_proxy_url = '<?php echo opanda_local_proxy_url() ?>';
window.opanda_terms = '<?php echo opanda_terms_url() ?>';
window.opanda_privacy_policy = '<?php echo opanda_privacy_policy_url() ?>';
window.opanda_privacy_policy_title = '<?php echo get_option('opanda_res_misc_privacy_policy', __('Privacy Policy', 'bizpanda') ) ?>';
window.opanda_subscription_service_name = '<?php echo get_option('opanda_subscription_service', 'none') ?>';
window.opanda_site_name = '<?php echo get_bloginfo("name") ?>';
window.opanda_home_url = '<?php echo get_home_url() ?>';
<?php if ( defined('ONP_OP_STYLER_PLUGIN_ACTIVE') ) { ?>
window.onp_sl_styleroller = true;
<?php } else { ?>
window.onp_sl_styleroller = false;
window.onp_sl_styleroller_offer_text = '<?php _e('Want more themes?', 'bizpanda') ?>';
window.onp_sl_styleroller_offer_url = '<?php echo $bizpanda->options['styleroller'] ?>';
<?php } ?>
<?php if ( $showStyleRollerOffer ) { ?>
window.onp_sl_show_styleroller_offer = true;
<?php } else { ?>
window.onp_sl_show_styleroller_offer = false;
<?php } ?>
</script>
<?php do_action('bizpanda_print_preview_variables') ?>
<p class="note"><strong><?php _e('Note', 'bizpanda'); ?>:</strong> <?php _e('In the preview mode, some features of the locker may not work properly.', 'bizpanda'); ?></p>
<div id="lock-preview-wrap"<?php echo $dataPrint; ?>>
<iframe
allowtransparency="1"
frameborder="0"
hspace="0"
marginheight="0"
marginwidth="0"
name="preview"
vspace="0"
width="100%">
<?php _e('Your browser doen\'t support the iframe tag.', 'bizpanda'); ?>
</iframe>
</div>
<?php
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_PreviewMetaBox', $bizpanda);

View File

@@ -0,0 +1,178 @@
<?php
/**
* The file contains a class to configure the metabox "More Features?".
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 1.0.0
*/
/**
* The class to configure the metabox "More Features?".
*
* @since 1.0.0
*/
class OPanda_TermsOptionsMetaBox extends FactoryMetaboxes321_FormMetabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 1.0.0
* @var string
*/
public $title;
/**
* A prefix that will be used for names of input fields in the form.
*
* Inherited from the class FactoryFormMetabox.
*
* @since 2.3.0
* @var string
*/
public $scope = 'opanda';
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $priority = 'core';
/**
* The part of the page where the edit screen section should be shown ('normal', 'advanced', or 'side').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 1.0.0
* @var string
*/
public $context = 'side';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Terms & Policies', 'bizpanda');
}
public $cssClass = 'factory-bootstrap-331 factory-fontawesome-320';
public function configure( $scripts, $styles ){
$scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/metaboxes/terms.010000.js');
}
/**
* Configures a form that will be inside the metabox.
*
* @see FactoryMetaboxes321_FormMetabox
* @since 1.0.0
*
* @param FactoryForms328_Form $form A form object to configure.
* @return void
*/
public function form( $form ) {
$itemType = OPanda_Items::getCurrentItem();
$hint = __('Consent Checkbox for GDPR compatibility.', 'bizpanda');
if ( 'social-locker' === $itemType['name'] ) {
$hint = sprintf( __('Consent Checkbox for <a href="%s" target="_blank">GDPR compatibility.</a>', 'bizpanda'), opanda_get_help_url('gdpr-social-locker') );
} elseif ( 'signin-locker' === $itemType['name'] ) {
$hint = sprintf( __('Consent Checkbox for <a href="%s" target="_blank">GDPR compatibility.</a>', 'bizpanda'), opanda_get_help_url('gdpr-signin-locker') );
} elseif ( 'email-locker' === $itemType['name'] ) {
$hint = sprintf( __('Consent Checkbox for <a href="%s" target="_blank">GDPR compatibility.</a>', 'bizpanda'), opanda_get_help_url('gdpr-email-locker') );
}
$options = array(
array(
'type' => 'html',
'html' => array(&$this, 'showTermsContentNote')
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'agreement_checkbox',
'title' => '<i class="fa fa-check-square-o" style="font-size: 17px; margin-right: 8px; top: 2px; position: relative;"></i>' . __('Consent Checkbox', 'bizpanda'),
'hint' => $hint,
'default' => false
),
array(
'type' => 'html',
'html' => array($this, 'htmlConsentCheckboxOption')
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'agreement_note',
'title' => '<i class="fa fa-flag" style="font-size: 17px; margin-right: 8px;"></i>' . __('Footer Reference', 'bizpanda'),
'hint' => __('Shows a reference to Terms & Policies at bottom.', 'bizpanda'),
'default' => false
),
);
$options = apply_filters('opanda_terms_and_policies_options', $options);
$form->add($options);
}
public function htmlConsentCheckboxOption() {
$consentCheckbox = $this->provider->getValue('agreement_checkbox', false);
$checkboxPosiion = $this->provider->getValue('agreement_checkbox_position', 'bottom');
if ( empty($checkboxPosiion) ) $checkboxPosiion = 'bottom';
?>
<div class='onp-sl-sub <?php if ( !$consentCheckbox ) { echo 'hide'; } ?>' id='onp-sl-agreement_checkbox-options'>
<label class='control-label' style="margin-bottom: 5px;"><?php _e('The checkbox will appear at:', 'bizpanda') ?></label>
<select class='form-control' name='<?php echo $this->scope ?>_agreement_checkbox_position' id="<?php echo $this->scope ?>_agreement_checkbox_position">
<option value='top' <?php selected('top', $checkboxPosiion) ?>><?php _e('Top', 'bizpanda') ?></option>
<option value='bottom' <?php selected('bottom', $checkboxPosiion) ?>><?php _e('Bottom', 'bizpanda') ?></option>
</select>
</div>
<?php
}
public function showTermsContentNote() {
?>
<?php printf( __('You can change content of your Terms & Policies pages <a href="%s" target="_blank">here</a>.'), admin_url('admin.php?page=settings-' . $this->plugin->pluginName . '&opanda_screen=terms&action=index') ) ?>
<?php
}
/**
* Saves some extra options.
*/
public function onSavingForm( $post_id) {
parent::onSavingForm( $post_id );
$checkbox = isset( $_POST[$this->scope . '_agreement_checkbox'] )
? $_POST[$this->scope . '_agreement_checkbox']
: false;
$position = isset( $_POST[$this->scope . '_agreement_checkbox_position'] )
? $_POST[$this->scope . '_agreement_checkbox_position']
: 'bottom';
if ( !$checkbox ) $position = false;
$this->provider->setValue('agreement_checkbox_position', $position );
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_TermsOptionsMetaBox', $bizpanda);

View File

@@ -0,0 +1,670 @@
<?php
/**
* The file contains a class to configure the metabox Visibility Options.
*
* Created via the Factory Metaboxes.
*
* @author Paul Kashtanoff <paul@byonepress.com>
* @copyright (c) 2013, OnePress Ltd
*
* @package core
* @since 2.3.0
*/
/**
* The class to configure the metabox Visibility Options.
*
* @since 2.3.0
*/
class OPanda_VisabilityOptionsMetaBox extends FactoryMetaboxes321_FormMetabox
{
/**
* A visible title of the metabox.
*
* Inherited from the class FactoryMetabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*
* @since 2.3.0
* @var string
*/
public $title;
/**
* A prefix that will be used for names of input fields in the form.
*
* Inherited from the class FactoryFormMetabox.
*
* @since 2.3.0
* @var string
*/
public $scope = 'opanda';
/**
* The priority within the context where the boxes should show ('high', 'core', 'default' or 'low').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 2.3.0
* @var string
*/
public $priority = 'core';
/**
* The part of the page where the edit screen section should be shown ('normal', 'advanced', or 'side').
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
* Inherited from the class FactoryMetabox.
*
* @since 2.3.0
* @var string
*/
public $context = 'side';
public function __construct( $plugin ) {
parent::__construct( $plugin );
$this->title = __('Visibility Options', 'bizpanda');
}
public $cssClass = 'factory-bootstrap-331 factory-fontawesome-320';
public function configure( $scripts, $styles ){
$scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/widgets/condition-editor.010000.js');
$scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/metaboxes/visability.010001.js');
}
/**
* Configures a form that will be inside the metabox.
*
* @see FactoryMetaboxes321_FormMetabox
* @since 1.0.0
*
* @param FactoryForms328_Form $form A form object to configure.
* @return void
*/
public function form( $form ) {
$options = array(
array(
'type' => 'html',
'html' => array($this, 'htmlSwitcher')
),
array(
'type' => 'hidden',
'name' => 'visibility_mode',
'default' => 'simple'
),
array(
'type' => 'div',
'id' => 'bp-simple-visibility-options',
'items' => array(
array(
'type' => 'textbox',
'name' => 'delay',
'title' => __('Lock Delay', 'bizpanda'),
'hint' => __('Displays the locker after the specified interval (in seconds).', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/timer-icon.png',
'default' => false
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'hide_for_member',
'title' => __('Hide For Members', 'bizpanda'),
'hint' => __('If on, hides the locker for registered members.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/member-icon.png',
'default' => false
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'relock',
'title' => __('ReLock', 'bizpanda'),
'hint' => __('If on, being unlocked the locker will appear again after a specified interval.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/icon-relock-3.png',
'default' => false
),
array(
'type' => 'html',
'html' => array($this, 'htmlReLockOptions')
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'always',
'title' => '<i class="fa fa-umbrella" style="font-size: 17px; margin-right: 8px;"></i>' . __('Show Always', 'bizpanda'),
'hint' => __('If on, the locker appears always even it was unlocked.', 'bizpanda'),
'default' => false
),
array(
'type' => 'checkbox',
'way' => 'buttons',
'name' => 'mobile',
'title' => __('Mobile', 'bizpanda'),
'hint' => __('If on, the locker will appear on mobile devices.', 'bizpanda'),
'icon' => OPANDA_BIZPANDA_URL . '/assets/admin/img/mobile-icon.png',
'default' => true
)
)
),
array(
'type' => 'div',
'id' => 'bp-advanced-visibility-options',
'items' => array(
array(
'type' => 'html',
'html' => array( $this, 'visibilityFilters' )
),
array(
'type' => 'hidden',
'name' => 'visibility_filters'
)
)
)
);
if ( OPanda_Items::isCurrentFree() ) {
$options[] = array(
'type' => 'html',
'html' => '<div style="display: none;" class="factory-fontawesome-320 opanda-overlay-note opanda-premium-note">' . __( '<i class="fa fa-star-o"></i> Go Premium <i class="fa fa-star-o"></i><br />To Unlock These Features <a href="#" class="opnada-button">Learn More</a>', 'bizpanda' ) . '</div>'
);
}
$options = apply_filters( 'opanda_visability_options', $options, $this );
$form->add( $options );
}
public function htmlReLockOptions() {
$relock = $this->provider->getValue('relock', false);
$interval = $this->provider->getValue('relock_interval', 0);
if ( $interval == 0 ) $interval = '';
$units = $this->provider->getValue('relock_interval_units', 'days');
?>
<div class='onp-sl-sub <?php if ( !$relock ) { echo 'hide'; } ?>' id='onp-sl-relock-options'>
<label class='control-label'><?php _e('The locker will reappear after:', 'bizpanda') ?></label>
<input type='text' class='form-control input' name='<?php echo $this->scope ?>_relock_interval' value='<?php echo $interval ?>' />
<select class='form-control' name='<?php echo $this->scope ?>_relock_interval_units'>
<option value='days' <?php selected('days', $units) ?>><?php _e('day(s)', 'bizpanda') ?></option>
<option value='hours' <?php selected('hours', $units) ?>><?php _e('hour(s)', 'bizpanda') ?></option>
<option value='minutes' <?php selected('minutes', $units) ?>><?php _e('minute(s)', 'bizpanda') ?></option>
</select>
<p style="margin: 6px 0 0 0; font-size: 12px;"><?php _e('Any changes will apply only for new users.', 'bizpanda') ?></p>
</div>
<?php
}
/**
* Shows html for the options' switcher.
*/
public function htmlSwitcher() {
?>
<div class="bp-options-switcher">
<span class="bp-label"><?php _e('Options Mode:', 'bizpanda') ?></span>
<div class="btn-group bp-swither-ctrl">
<a href="#" class="btn btn-sm btn-default btn-btn-simple" data-value="simple"><?php _e('Simple', 'bizpanda') ?></a>
<a href="#" class="btn btn-sm btn-default btn-btn-advanced" data-value="advanced"><?php _e('Advanced', 'bizpanda') ?></a>
</div>
</div>
<?php
}
/**
* Shows a popup to display advanded options.
*/
public function visibilityFilters() {
// filter parameters
$groupedFilterParams = array(
array(
'id' => 'user',
'title' => __('User', 'bizpanda'),
'items' => array(
array(
'id' => 'user-role',
'title' =>__('Role', 'bizpanda'),
'type' => 'select',
'values' => array(
'type' => 'ajax',
'action' => 'bp_ajax_get_user_roles'
),
'description' => __('A role of the user who views your website. The role "guest" is applied to unregistered users.', 'bizpanda')
),
array(
'id' => 'user-registered',
'title' =>__('Registration Date', 'bizpanda'),
'type' => 'date',
'description' => __('The date when the user who views your website was registered. For unregistered users this date always equals to 1 Jan 1970.', 'bizpanda')
),
array(
'id' => 'user-mobile',
'title' =>__('Mobile Device', 'bizpanda'),
'type' => 'select',
'values' => array(
array('value' => 'yes', 'title' => __('Yes', 'bizpanda') ),
array('value' => 'no', 'title' => __('No', 'bizpanda') )
),
'description' => __('Determines whether the user views your website from mobile device or not.', 'bizpanda')
),
array(
'id' => 'user-cookie-name',
'title' =>__('Cookie Name', 'bizpanda'),
'type' => 'text',
'onlyEquals' => true,
'description' => __('Determines whether the user\'s browser has a cookie with a given name.', 'bizpanda')
)
)
),
array(
'id' => 'session',
'title' => __('Session', 'bizpanda'),
'items' => array(
array(
'id' => 'session-pageviews',
'title' =>__('Total Pageviews', 'bizpanda'),
'type' => 'integer',
'description' => sprintf( __('The total count of pageviews made by the user within one\'s current session on your website. You can specify a duration of the sessions <a href="%s" target="_blank">here</a>.', 'bizpanda'), opanda_get_admin_url('settings', array('opanda_screen' => 'lock') ) )
),
array(
'id' => 'session-locker-pageviews',
'title' =>__('Locker Pageviews', 'bizpanda'),
'type' => 'integer',
'description' => sprintf( __('The count of views of pages where lockers located, made by the user within one\'s current session on your website. You can specify a duration of the sessions <a href="%s" target="_blank">here</a>.', 'bizpanda'), opanda_get_admin_url('settings', array('opanda_screen' => 'lock') ) )
),
array(
'id' => 'session-landing-page',
'title' =>__('Landing Page', 'bizpanda'),
'type' => 'text',
'description' => sprintf( __('A page of your website from which the user starts one\'s current session. You can specify a duration of the sessions <a href="%s" target="_blank">here</a>.', 'bizpanda'), opanda_get_admin_url('settings', array('opanda_screen' => 'lock') ) )
),
array(
'id' => 'session-referrer',
'title' =>__('Referrer', 'bizpanda'),
'type' => 'text',
'description' => sprintf( __('A referrer URL which has brought the user to your website within the user\'s current session. You can specify a duration of the sessions <a href="%s" target="_blank">here</a>.', 'bizpanda'), opanda_get_admin_url('settings', array('opanda_screen' => 'lock') ) )
)
)
),
array(
'id' => 'location',
'title' => __('Location', 'bizpanda'),
'items' => array(
array(
'id' => 'location-page',
'title' =>__('Current Page', 'bizpanda'),
'type' => 'text',
'description' => __('An URL of the current page where a user who views your website is located.', 'bizpanda')
),
array(
'id' => 'location-referrer',
'title' =>__('Current Referrer', 'bizpanda'),
'type' => 'text',
'description' => __('A referrer URL which has brought a user to the current page.', 'bizpanda')
)
)
),
array(
'id' => 'post',
'title' => __('Post', 'bizpanda'),
'items' => array(
array(
'id' => 'post-published',
'title' =>__('Publication Date', 'bizpanda'),
'type' => 'date',
'description' => __('The publication date of a post where a user who views your website is located currently.', 'bizpanda')
)
)
),
);
$groupedFilterParams = apply_filters('bp_visibility_filter_params', $groupedFilterParams);
$filterParams = array();
foreach( $groupedFilterParams as $filterGroup ) {
$filterParams = array_merge( $filterParams, $filterGroup['items'] );
}
// templates
$templates = array(
array(
'id' => 'hide_for_members',
'title' => __('[Hide For Members]: Show the locker only for guests', 'bizpanda'),
'filter' => array(
'type' => 'showif',
'conditions' => array(
array(
'type' => 'condition',
'param' => 'user-role',
'operator' => 'equals',
'value' => 'guest'
)
)
)
),
array(
'id' => 'mobile',
'title' => __('[Hide On Mobile]: Hide the locker on mobile devices', 'bizpanda'),
'filter' => array(
'type' => 'hideif',
'conditions' => array(
array(
'type' => 'condition',
'param' => 'user-mobile',
'operator' => 'equals',
'value' => 'yes'
)
)
)
),
array(
'id' => 'delayed_lock',
'title' => __('[Delayed Lock]: Show the locker only in posts older than 5 days', 'bizpanda'),
'filter' => array(
'type' => 'showif',
'conditions' => array(
array(
'type' => 'condition',
'param' => 'post-published',
'operator' => 'older',
'value' => array(
'type' => 'relative',
'unitsCount' => 5,
'units' => 'days'
)
)
)
)
)
);
$templates = apply_filters('bp_visibility_templates', $templates);
?>
<div class="bp-advanded-options">
<div class="bp-button-wrap">
<a class="btn btn-default" href="#bp-advanced-visability-options" role="button" data-toggle="factory-modal">
<i class="fa fa-cog"></i> <?php _e('Setup Visibility Conditions', 'bizpanda') ?>
</a>
</div>
<div class="modal fade bp-model" id="bp-advanced-visability-options" tabindex="-1" role="dialog" aria-labelledby="advancedVisabilityOptions" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel"><?php _e('Visibility Conditions', 'bizpanda') ?></h4>
<p style="margin-bottom: 0px;"><?php _e('Filters are applied consistently. Use templates to set up quickly the most popular conditions.') ?></p>
</div>
<div class="modal-body">
<script>
window.bp = window.bp || {};
window.bp.filtersParams = <?php echo json_encode( $filterParams ) ?>;
window.bp.templates = <?php echo json_encode( $templates ) ?>;
</script>
<div class="bp-editor-wrap">
<div class="bp-when-empty">
<?php _e('No filters specified. <a href="#" class="bp-add-filter">Click here</a> to add one.', 'bizpanda') ?>
</div>
<div class="bp-filters"></div>
</div>
<div class="bp-filter bp-template">
<div class="bp-point"></div>
<div class="bp-head">
<div class="bp-left">
<span style="margin-left: 0px;"><strong>Type:</strong></span>
<select class="bp-filter-type">
<option value="showif"><?php _e('Show Locker IF', 'bizpanda') ?></option>
<option value="hideif"><?php _e('Hide Locker IF', 'bizpanda') ?></option>
</select>
<span>Or</span>
<a href="#" class="button btn-remove-filter"><?php _e('Remove', 'bizpanda') ?></a>
</div>
<div class="bp-templates bp-right">
<span><strong><?php _e('Template', 'bizpanda') ?></strong></span>
<select class="bp-select-template">
<option><?php _e('- select a template -', 'bizpanda') ?></option>
<?php foreach ( $templates as $template ) { ?>
<option value="<?php echo $template['id'] ?>"><?php echo $template['title'] ?></option>
<?php } ?>
</select>
<a href="#" class="button bp-btn-apply-template"><?php _e('Apply', 'bizpanda') ?></a>
</div>
</div>
<div class="bp-box">
<div class="bp-when-empty">
<?php _e('No conditions specified. <a href="#" class="bp-link-add">Click here</a> to add one.', 'bizpanda') ?>
</div>
<div class="bp-conditions"></div>
</div>
</div>
<div class="bp-scope bp-template">
<div class="bp-and"><span><?php _e('and', 'bizpanda') ?></span></div>
</div>
<div class="bp-condition bp-template">
<div class="bp-or"><?php _e('or', 'bizpanda') ?></div>
<span class="bp-params">
<select class="bp-param-select">
<?php foreach( $groupedFilterParams as $filterParam ) { ?>
<optgroup label="<?php echo $filterParam['title'] ?>">
<?php foreach( $filterParam['items'] as $param ) { ?>
<option value="<?php echo $param['id'] ?>">
<?php echo $param['title'] ?>
</option>
<?php } ?>
</optgroup>
<?php } ?>
</select>
<i class="bp-hint">
<span class="bp-hint-icon"></span>
<span class="bp-hint-content"></span>
</i>
</span>
<span class="bp-operators">
<select class="bp-operator-select">
<option value="equals"><?php _e('Equals', 'bizpanda') ?></option>
<option value="notequal"><?php _e('Doesn\'t Equal', 'bizpanda') ?></option>
<option value="greater"><?php _e('Greater Than', 'bizpanda') ?></option>
<option value="less"><?php _e('Less Than', 'bizpanda') ?></option>
<option value="older"><?php _e('Older Than', 'bizpanda') ?></option>
<option value="younger"><?php _e('Younger Than', 'bizpanda') ?></option>
<option value="contains"><?php _e('Contains', 'bizpanda') ?></option>
<option value="notcontain"><?php _e('Doesn\'t Сontain', 'bizpanda') ?></option>
<option value="between"><?php _e('Between', 'bizpanda') ?></option>
</select>
</span>
<span class="bp-value"></span>
<span class="bp-controls">
<div class="btn-group">
<a href="#" class="btn btn-sm btn-default bp-btn-remove">-</a>
<a href="#" class="btn btn-sm btn-default bp-btn-or"><?php _e('OR', 'bizpanda') ?></a>
<a href="#" class="btn btn-sm btn-default bp-btn-and"><?php _e('AND', 'bizpanda') ?></a>
</div>
</span>
</div>
<div class="bp-date-control bp-relative bp-template">
<div class="bp-inputs">
<div class="bp-between-date">
<div class="bp-absolute-date">
<span class="bp-label"> <?php _e('from', 'bizpanda') ?> </span>
<div class="bp-date-control bp-date-start" data-date="today">
<input size="16" type="text" readonly="readonly" class="bp-date-value-start" data-date="today" />
<i class="fa fa-calendar"></i>
</div>
<span class="bp-label"> <?php _e('to', 'bizpanda') ?> </span>
<div class="bp-date-control bp-date-end" data-date="today">
<input size="16" type="text" readonly="readonly" class="bp-date-value-end" data-date="today" />
<i class="fa fa-calendar"></i>
</div>
</div>
<div class="bp-relative-date">
<span class="bp-label"> <?php _e('older than', 'bizpanda') ?> </span>
<input type="text" class="bp-date-value bp-date-value-start" value="1" />
<select class="bp-date-start-units">
<option value="seconds"><?php _e('Second(s)', 'bizpanda') ?></option>
<option value="minutes"><?php _e('Minutes(s)', 'bizpanda') ?></option>
<option value="hours"><?php _e('Hours(s)', 'bizpanda') ?></option>
<option value="days"><?php _e('Day(s)', 'bizpanda') ?></option>
<option value="weeks"><?php _e('Week(s)', 'bizpanda') ?></option>
<option value="months"><?php _e('Month(s)', 'bizpanda') ?></option>
<option value="years"><?php _e('Year(s)', 'bizpanda') ?></option>
</select>
<span class="bp-label"> <?php _e(', younger than', 'bizpanda') ?> </span>
<input type="text" class="bp-date-value bp-date-value-end" value="2" />
<select class="bp-date-end-units">
<option value="seconds"><?php _e('Second(s)', 'bizpanda') ?></option>
<option value="minutes"><?php _e('Minutes(s)', 'bizpanda') ?></option>
<option value="hours"><?php _e('Hours(s)', 'bizpanda') ?></option>
<option value="days"><?php _e('Day(s)', 'bizpanda') ?></option>
<option value="weeks"><?php _e('Week(s)', 'bizpanda') ?></option>
<option value="months"><?php _e('Month(s)', 'bizpanda') ?></option>
<option value="years"><?php _e('Year(s)', 'bizpanda') ?></option>
</select>
</div>
</div>
<div class="bp-solo-date">
<div class="bp-absolute-date">
<div class="bp-date-control" data-date="today">
<input size="16" type="text" class="bp-date-value" readonly="readonly" data-date="today" />
<i class="fa fa-calendar"></i>
</div>
</div>
<div class="bp-relative-date">
<input type="text" class="bp-date-value" value="1" />
<select class="bp-date-value-units">
<option value="seconds"><?php _e('Second(s)', 'bizpanda') ?></option>
<option value="minutes"><?php _e('Minutes(s)', 'bizpanda') ?></option>
<option value="hours"><?php _e('Hours(s)', 'bizpanda') ?></option>
<option value="days"><?php _e('Day(s)', 'bizpanda') ?></option>
<option value="weeks"><?php _e('Week(s)', 'bizpanda') ?></option>
<option value="months"><?php _e('Month(s)', 'bizpanda') ?></option>
<option value="years"><?php _e('Year(s)', 'bizpanda') ?></option>
</select>
</div>
</div>
</div>
<div class="bp-switcher">
<label><input type="radio" checked="checked" value="relative" /> <span><?php _e('relative', 'bizpanda') ?></span></label>
<label><input type="radio" value="absolute" /> </span><?php _e('absolute', 'bizpanda') ?></span></label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default bp-add-filter bp-btn-left"><?php _e('+ Add Filter', 'bizpanda') ?></button>
<button type="button" class="btn btn-default bp-cancel" data-dismiss="modal"><?php _e('Cancel', 'bizpanda') ?></button>
<button type="button" class="btn btn-primary bp-save"><?php _e('Save', 'bizpanda') ?></button>
</div>
</div>
</div>
</div>
</div>
<?php
}
/**
* Saves some extra options.
*/
public function onSavingForm( $post_id) {
parent::onSavingForm( $post_id );
// saves delay lock options
$delay = isset( $_POST[$this->scope . '_lock_delay'] );
$interval = isset( $_POST[$this->scope . '_lock_delay_interval'] )
? intval( $_POST[$this->scope . '_lock_delay_interval'] )
: 0;
if ( $interval < 0 ) $interval = 0;
$units = isset( $_POST[$this->scope . '_lock_delay_interval_units'] )
? $_POST[$this->scope . '_lock_delay_interval_units']
: null;
if ( !$units || !in_array($units, array('days', 'hours', 'minutes') )) {
$units = 'days';
}
if ( !$interval ) $_POST[$this->scope . '_lock_delay'] = null;
if ( !$delay ) {
$interval = 0;
$units = 'days';
}
$intervalInMinutes = $interval;
if ( $units == 'days' ) $intervalInMinutes = 24 * 60 * $interval;
if ( $units == 'hours' ) $intervalInMinutes = 60 * $interval;
$this->provider->setValue('lock_delay_interval_in_seconds', $intervalInMinutes * 60 );
$this->provider->setValue('lock_delay_interval', $interval);
$this->provider->setValue('lock_delay_interval_units', $units);
// saves relock options
$delay = isset( $_POST[$this->scope . '_relock'] );
$interval = isset( $_POST[$this->scope . '_relock_interval'] )
? intval( $_POST[$this->scope . '_relock_interval'] )
: 0;
if ( $interval < 0 ) $interval = 0;
$units = isset( $_POST[$this->scope . '_relock_interval_units'] )
? $_POST[$this->scope . '_relock_interval_units']
: null;
if ( !$units || !in_array($units, array('days', 'hours', 'minutes') )) {
$units = 'days';
}
if ( !$interval ) $_POST[$this->scope . '_relock'] = null;
if ( !$delay ) {
$interval = 0;
$units = 'days';
}
$intervalInMinutes = $interval;
if ( $units == 'days' ) $intervalInMinutes = 24 * 60 * $interval;
if ( $units == 'hours' ) $intervalInMinutes = 60 * $interval;
$this->provider->setValue('relock_interval_in_seconds', $intervalInMinutes * 60 );
$this->provider->setValue('relock_interval', $interval);
$this->provider->setValue('relock_interval_units', $units);
do_action('onp_sl_visability_options_on_save', $this);
}
}
global $bizpanda;
FactoryMetaboxes321::register('OPanda_VisabilityOptionsMetaBox', $bizpanda);

View File

@@ -0,0 +1,108 @@
<?php
class OPanda_Items {
private static $_available = null;
public static function isPremium( $name ) {
$item = self::getItem( $name );
return isset( $item['type'] ) && $item['type'] === 'premium';
}
public static function isFree( $name ) {
$item = self::getItem( $name );
return isset( $item['type'] ) && $item['type'] === 'free';
}
public static function isCurrentPremium() {
$name = self::getCurrentItemName();
return self::isPremium( $name );
}
public static function isCurrentFree() {
$name = self::getCurrentItemName();
return self::isFree( $name );
}
public static function getItem( $name ) {
$available = self::getAvailable();
return isset( $available[$name] ) ? $available[$name] : null;
}
public static function getCurrentItem() {
$name = self::getCurrentItemName();
if ( empty( $name ) ) return null;
return self::getItem( $name );
}
public static function getItemById( $id ) {
$name = self::getItemNameById();
if ( empty( $name ) ) return null;
return self::getItem( $name );
}
public static function getItemNameById( $id ) {
return get_post_meta( $id, 'opanda_item', true );
}
public static function getCurrentItemName() {
// - from the query
$item = isset( $_GET['opanda_item'] ) ? $_GET['opanda_item'] : null;
// - from the form hidden field
if ( empty( $item ) ) {
$item = isset( $_POST['opanda_item'] ) ? $_POST['opanda_item'] : null;
}
// - from the port meta data
if ( !empty( $_GET['post'] ) ) {
$postId = intval( $_GET['post'] );
$value = get_post_meta( $postId, 'opanda_item', true );
if ( !empty( $value ) ) $item = $value;
}
return $item;
}
public static function getAvailable() {
if ( self::$_available !== null ) return self::$_available;
$items = array();
self::$_available = apply_filters( 'opanda_items', $items );
return self::$_available;
}
public static function getAvailableNames( $returnFalseForEmpty = false ) {
$available = self::getAvailable();
$result = array_keys( $available );
if ( empty( $result ) ) return $returnFalseForEmpty ? false : 'empty';
return $result;
}
public static function isAvailable( $name ) {
$available = self::getAvailable();
return isset( $available[$name] );
}
public static function isCurrentAvailable() {
$name = self::getCurrentItemName();
if ( empty( $name ) ) return false;
return self::isAvailable( $name );
}
public static function getPremiumUrl( $name ) {
$item = self::getItem( $name );
if ( empty( $item ) ) return false;
if ( isset( $item['plugin']->options['premium'] ) ) return $item['plugin']->options['premium'];
return false;
}
public static function getCurrentPremiumUrl() {
$name = self::getCurrentItemName();
if ( empty( $name ) ) return false;
return self::getPremiumUrl( $name );
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* Opt-In Panda Type
* Declaration for custom post type of Social Locler.
* @link http://codex.wordpress.org/Post_Types
*/
class OPanda_PandaItemType extends FactoryTypes322_Type {
/**
* Custom post name.
* @var string
*/
public $name = 'opanda-item';
/**
* Singular title for labels of the type in the admin panel.
* @var string
*/
public $singularTitle = 'Opt-In Panda';
/**
* Plural title for labels of the type in the admin panel.
* @var string
*/
public $pluralTitle = 'Opt-In Pandas';
/**
* Template that defines a set of type options.
* Allowed values: public, private, internal.
* @var string
*/
public $template = 'private';
/**
* Capabilities for roles that have access to manage the type.
* @link http://codex.wordpress.org/Roles_and_Capabilities
* @var array
*/
public $capabilities = array('administrator');
public function useit() { global $sociallocker;
if ( in_array( $sociallocker->license->type, array( 'paid','trial' ) ) ) {
return true;
}
return false;
}
function __construct($plugin) {
parent::__construct($plugin);
$this->pluralTitle = __('Lockers', 'bizpanda');
$this->singularTitle = __('Locker', 'bizpanda');
}
/**
* Type configurator.
*/
public function configure() {
global $bizpanda;
/**
* Labels
*/
$pluralName = $this->pluralTitle;
$singularName = $this->singularTitle;
$labels = array(
'singular_name' => $this->singularTitle,
'name' => $this->pluralTitle,
'all_items' => sprintf( __('All Lockers', 'bizpanda'), $pluralName ),
'add_new' => sprintf( __('+ New Locker', 'bizpanda'), $singularName ),
'add_new_item' => sprintf( __('Add new', 'bizpanda'), $singularName ),
'edit' => sprintf( __('Edit', 'bizpanda') ),
'edit_item' => sprintf( __('Edit Item', 'bizpanda'), $singularName ),
'new_item' => sprintf( __('New Item', 'bizpanda'), $singularName ),
'view' => sprintf( __('View', 'factory') ),
'view_item' => sprintf( __('View Item', 'bizpanda'), $singularName ),
'search_items' => sprintf( __('Search Items', 'bizpanda'), $pluralName ),
'not_found' => sprintf( __('No Items found', 'bizpanda'), $pluralName ),
'not_found_in_trash' => sprintf( __('No Items found in trash', 'bizpanda'), $pluralName ),
'parent' => sprintf( __('Parent Item', 'bizpanda'), $pluralName )
);
$this->options['labels'] = apply_filters('opanda_items_lables', $labels);
/**
* Menu
*/
$this->menu->title = BizPanda::getMenuTitle();
$this->menu->icon = BizPanda::getMenuIcon();
/**
* View table
*/
$this->viewTable = 'OPanda_ItemsViewTable';
/**
* Scripts & styles
*/
$this->scripts->request( array( 'jquery', 'jquery-effects-highlight', 'jquery-effects-slide' ) );
$this->scripts->request( array(
'bootstrap.transition',
'bootstrap.datepicker',
'bootstrap.tab',
'holder.more-link',
'control.checkbox',
'control.dropdown',
'control.list',
'bootstrap.modal',
), 'bootstrap' );
$this->styles->request( array(
'bootstrap.core',
'bootstrap.datepicker',
'bootstrap.form-group',
'bootstrap.form-metabox',
'bootstrap.tab',
'bootstrap.wp-editor',
'bootstrap.separator',
'control.checkbox',
'control.dropdown',
'control.list',
'holder.more-link'
), 'bootstrap' );
$this->scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/filters.010000.js');
$this->scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/libs/json2.js');
$this->scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/preview.010000.js');
$this->scripts->add( OPANDA_BIZPANDA_URL . '/assets/admin/js/item-edit.050600.js')->request('jquery-ui-sortable');
$this->styles->add( OPANDA_BIZPANDA_URL . '/assets/admin/css/item-edit.css');
$this->styles->add( OPANDA_BIZPANDA_URL . '/assets/admin/css/item-edit.010000-en_US.css');
do_action( 'opanda_panda-item_edit_assets', $this->scripts, $this->styles );
}
}
global $bizpanda;
FactoryTypes322::register('OPanda_PandaItemType', $bizpanda);

View File

@@ -0,0 +1,258 @@
<?php
/**
* A base shortcode for all lockers
*
* @since 1.0.0
*/
class OPanda_LockerShortcode extends FactoryShortcodes320_Shortcode {
// -------------------------------------------------------------------------------------
// Includes assets
// -------------------------------------------------------------------------------------
public $assetsInHeader = true;
/**
* Defines what assets need to include.
* The method is called separate from the Render method during shortcode registration.
*/
public function assets( $attrs = array(), $fromBody = false, $fromHook = false ) {
if ( is_admin() ) return false;
if ( is_array( $attrs) ) {
foreach( $attrs as $attr ) {
$id = isset( $attr['id'] ) ? (int)$attr['id'] : $this->getDefaultId();
OPanda_AssetsManager::requestAssets( $id, $fromBody, $fromHook );
}
return true;
} else {
return false;
}
}
// -------------------------------------------------------------------------------------
// Content render
// -------------------------------------------------------------------------------------
public function html($attr, $content) {
global $post;
global $wp_embed; global $sociallocker;
if ( in_array( $sociallocker->license->type, array( 'free' ) ) ) {
echo $content;
return;
}
$id = isset( $attr['id'] ) ? (int)$attr['id'] : $this->getDefaultId();
if ( !empty( $id ) ) $lockerMeta = get_post_meta($id, '');
if ( empty( $id ) || empty($lockerMeta) || empty($lockerMeta['opanda_item']) ) {
printf( __('<div><strong>[Locker] The locker [id=%d] doesn\'t exist or the default lockers were deleted.</strong></div>', 'bizpanda'), $id );
return;
}
// runs nested shortcodes
$content = $wp_embed->autoembed($content);
$content = do_shortcode( $content );
// passcode
if ( OPanda_AssetsManager::autoUnlock( $id ) ) {
echo $content;
return;
}
// - RSS and Members
// if it's a premium build, check premium features such
// as RSS feeds and logged in users.
if (is_feed()) {
if ( get_option('opanda_rss', false) ) {
echo $content;
return;
} else {
return;
}
}
if ( is_user_logged_in() && OPanda_AssetsManager::getLockerOption($id, 'hide_for_member', false) ) {
echo '<p>' . $content . '</p>';
return;
}
if ( !empty($post) && OPanda_AssetsManager::getLockerOption($id, 'lock_delay', false) ) {
$lockDelayInterval = OPanda_AssetsManager::getLockerOption($id, 'lock_delay_interval_in_seconds');
$createdTime = get_post_time('U', true, $post);
$currentTime = time();
if ( $currentTime - $createdTime <= $lockDelayInterval ) {
echo '<p>' . $content . '</p>';
return;
}
}
// if returns:
// 'content' - shows the locker content
// 'nothing' - shows nothing (cut content)
// 'locker' or other values - shows the locker
$whatToShow = apply_filters('onp_sl_what_to_show', 'locker', $id );
if ( 'content' === $whatToShow ) { echo $content; return; }
if ( 'nothing' === $whatToShow ) return;
$content = preg_replace( '/^<br \/>/', '', $content );
$content = preg_replace( '/<br \/>$/', '', $content );
$lockData = OPanda_AssetsManager::getLockerDataToPrint( $id );
// -
// use the shortcode attrs if specified instead of configured option
if ( isset( $attr['url'] ) ) {
$lockData['options']['facebook']['like']['url'] = $attr['url'];
$lockData['options']['facebook']['share']['url'] = $attr['url'];
$lockData['options']['twitter']['tweet']['url'] = $attr['url'];
$lockData['options']['google']['plus']['url'] = $attr['url'];
$lockData['options']['google']['share']['url'] = $attr['url'];
$lockData['options']['linkedin']['share']['url'] = $attr['url'];
}
if ( isset( $attr['title'] ) ) {
$lockData['options']['text']['title'] = $attr['title'];
}
if ( isset( $attr['message'] ) ) {
$lockData['options']['text']['message'] = $attr['message'];
}
if ( isset( $attr['theme'] ) ) {
$lockData['options']['theme'] = $attr['theme'];
}
$lockData['options']['lazy'] = opanda_get_option('lazy', false) ? true : false;
$isAjax = false;
$lockData['ajax'] = false;
// - AJAX
// if it's a premium build, check is ajax required?
$contentHash = null;
if (isset( $lockerMeta['opanda_ajax'] ) && $lockerMeta['opanda_ajax'][0] ) {
if ( 'full' == OPanda_AssetsManager::getLockerOption($id, 'overlap', false, 'full') ) {
$isAjax = true;
$ajaxContent = '<p>' . $content . '</p>';
$lockData['contentHash'] = $contentHash = md5( $ajaxContent );
$lockData['ajax'] = true;
$metaKey = 'opanda_locker_content_hash_' . $contentHash;
if ( !isset( $lockerMeta[$metaKey] ) ) {
add_post_meta($id, $metaKey, $ajaxContent, true);
}
}
}
$dynamicTheme = get_option('opanda_dynamic_theme', 0);
$lockData['stats'] = get_option('opanda_tracking', false) ? true : false;
$this->lockId = "onpLock" . rand(100000, 999999);
$this->lockData = $lockData;
$overlap = $lockData['options']['overlap']['mode'];
$contentVisibility = get_option('opanda_content_visibility', 'auto');
$hideContent = $overlap === 'full';
if ( $contentVisibility == 'always_hidden') {
$hideContent = true;
} elseif ( $contentVisibility == 'always_visible') {
$hideContent = false;
}
if ($isAjax) { ?>
<div class="onp-locker-call" style="display: none;" data-lock-id="<?php echo $this->lockId ?>"></div>
<?php } else { ?>
<div class="onp-locker-call" <?php if ( $hideContent ) { ?>style="display: none;"<?php } ?> data-lock-id="<?php echo $this->lockId ?>">
<p><?php echo $content ?></p>
</div>
<?php } ?>
<?php
if ( $dynamicTheme ) { ?>
<script type="text/bp-data" class="onp-optinpanda-params">
<?php echo json_encode( $lockData ) ?>
</script>
<?php do_action('opanda_print_locker_assets', $this->lockData['lockerId'], $this->lockData, $this->lockId ); ?>
<?php } else {
add_action('wp_footer', array($this, 'wp_footer'), 1);
add_action('login_footer', array($this, 'wp_footer'), 1);
}
}
public function wp_footer() {
$dynamicTheme = get_option('opanda_dynamic_theme', false);
if ( !$dynamicTheme ) $this->printOptions();
}
public function printOptions() {
?>
<script>
if ( !window.bizpanda ) window.bizpanda = {};
if ( !window.bizpanda.lockerOptions ) window.bizpanda.lockerOptions = {};
window.bizpanda.lockerOptions['<?php echo $this->lockId; ?>'] = <?php echo json_encode( $this->lockData ) ?>;
</script>
<?php do_action('opanda_print_locker_assets', $this->lockData['lockerId'], $this->lockData, $this->lockId ); ?>
<?php
}
// -------------------------------------------------------------------------------------
// Shortcode Tracking
// -------------------------------------------------------------------------------------
/**
* Defines whether the changes of post what includes shortcodes are tracked.
* @var boolean
*/
public $track = true;
/**
* The function that will be called when a post containing a current shortcode is changed.
* @param string $shortcode
* @param mixed[] $attr
* @param string $content
* @param integer $postId
*/
public function onTrack($shortcode, $attr, $content, $postId) {
$id = isset( $attr['id'] ) ? (int)$attr['id'] : $this->getDefaultId();
$lockerMeta = get_post_meta($id, '');
if (empty($lockerMeta)) return;
foreach($lockerMeta as $metaKey => $metaValue) {
if (strpos($metaKey, 'opanda_locker_content_hash_') === 0) {
delete_post_meta($id, $metaKey);
}
}
}
}

View File

@@ -0,0 +1,254 @@
<?php
/**
* Theme Manager Class
*
* Manages themes available to use.
*
* @since 3.3.3
*/
class OPanda_ThemeManager {
/**
* The flat to used to call the hook 'onp_sl_register_themes' once.
*
* @since 3.3.3
* @var bool
*/
private static $themesRegistered = false;
/**
* Contains an array of registred themes.
*
* @since 3.3.3
* @var mixed[]
*/
private static $themes;
/**
* Returns all registered themes.
*
* @since 3.3.3
* @param string $format the format of the output array, available values: 'dropdown'.
* @return mixed[]
*/
public static function getThemes( $item = null, $format = null ) {
$themes = array();
if ( !self::$themesRegistered ) {
do_action('onp_sl_register_themes', $item);
self::$themesRegistered = true;
}
$themes = self::$themes;
if ( $item ) {
$allThemes = $themes;
$themes = array();
foreach( $allThemes as $themeName => $themeData ) {
if ( isset( $themeData['items'] ) && !in_array( $item, $themeData['items'] ) ) continue;
$themes[$themeName] = $themeData;
}
}
if ( 'dropdown' === $format ) {
$output = array();
foreach( $themes as $theme ) {
$socialButtons = isset( $theme['socialButtons'] ) ? $theme['socialButtons'] : [
'allowedDisplay' => [],
'defaultDisplay' => false
];
$output[] = array(
'title' => $theme['title'],
'value' => $theme['name'],
'hint' => isset( $theme['hint'] ) ? $theme['hint'] : null,
'data' => array(
'socialButtonsAllowedDisplay' => implode(',', $socialButtons['allowedDisplay'] ),
'socialButtonsDefaultDisplay' => $socialButtons['defaultDisplay'],
'preview' => isset( $theme['preview'] ) ? $theme['preview'] : null,
'previewHeight' => isset( $theme['previewHeight'] ) ? $theme['previewHeight'] : null
)
);
}
return $output;
}
return $themes;
}
/**
* Returns display modes available for social buttons.
*
* @since 5.5.5
* @param string $format the format of the output array, available values: 'dropdown'.
* @return mixed[]
*/
public static function getSocialButtonsDisplayModes( $format = null ) {
$themes = self::getThemes('social-locker');
$modes = [
['value' => 'native', 'title' => __('Native Buttons', 'opanda')],
['value' => 'covers', 'title' => __('Styled Buttons', 'opanda')],
['value' => 'covers-native', 'title' => __('Flip Buttons', 'opanda')],
];
return $modes;
}
/**
* Returns sizes available for social buttons.
*
* @since 5.5.5
* @param string $format the format of the output array, available values: 'dropdown'.
* @return mixed[]
*/
public static function getSocialButtonsSizes( $format = null ) {
$sizes = [
['value' => 'standard', 'title' => __('Standard', 'opanda')],
['value' => 'large', 'title' => __('Large', 'opanda')]
];
return $sizes;
}
/**
* Registers a new theme.
*
* @since 3.3.3
* @param mixed $themeOptions
* @return void
*/
public static function registerTheme( $themeOptions ) {
self::$themes[$themeOptions['name']] = $themeOptions;
}
/**
* Returns editable options for a given theme.
*
* @since 3.3.3
* @param string $themeName A theme name for which we need to return the options.
* @return mixed[]
*/
public static function getEditableOptions( $themeName ) {
$themes = self::getThemes();
if ( isset( $themes[$themeName] )) {
$path = $themes[$themeName]['path'] . '/editable-options.php';
if ( !file_exists($path)) return false;
require_once $path;
}
$options = array();
$functionToCall = 'onp_sl_get_' . str_replace('-', '_', $themeName ) . '_theme_editable_options';
if (function_exists($functionToCall)) $options = $functionToCall();
$options = apply_filters( 'onp_sl_editable_' . $themeName . '_theme_options', $options, $themeName) ;
$options = apply_filters( 'onp_sl_editable_theme_options', $options, $themeName) ;
return $options;
}
/**
* Returns CSS converting rules.
*
* @since 3.3.3
* @param string $themeName A theme name for which we need to return the rules.
* @return mixed[]
*/
public static function getRulesToGenerateCSS( $themeName ) {
$themes = self::getThemes();
if ( isset( $themes[$themeName] )) {
$path = $themes[$themeName]['path'] . '/css-rules.php';
if ( !file_exists($path)) return false;
require_once $path;
}
$rules = array();
$functionToCall = 'onp_sl_get_' . str_replace('-', '_', $themeName ) . '_theme_css_rules';
if (function_exists($functionToCall)) $rules = $functionToCall();
$rules = apply_filters( 'onp_sl_' . $themeName . '_theme_css_rules', $rules, $themeName) ;
$rules = apply_filters( 'onp_sl_theme_css_rules', $rules, $themeName);
return $rules;
}
}
/**
* Helper which returns a set of editable options for changing background.
*
* @since 1.0.2
* @param type $name A name base for the options.
*/
function opanda_background_editor_options( $name, $sets = array() ) {
$defaultType = isset( $sets['default'] ) ? $sets['default']['type'] : 'color';
$options = array(
'type' => 'control-group',
'name' => $name . '_type',
'default' => $name . '_' . $defaultType . '_item',
'title' => isset( $sets['title'] ) ? $sets['title'] : null,
'items' => array(
array(
'type' => 'control-group-item',
'title' => __('Color', 'bizpanda'),
'name' => $name . '_color_item',
'items' => array(
array(
'type' => 'color-and-opacity',
'name' => $name . '_color',
'title' => __('Set up color and opacity:', 'bizpanda'),
'default' => ( isset( $sets['default'] ) && $defaultType == 'color' ) ? $sets['default']['value'] : null
)
)
),
array(
'type' => 'control-group-item',
'title' => __('Gradient', 'bizpanda'),
'name' => $name . '_gradient_item',
'items' => array(
array(
'type' => 'gradient',
'name' => $name . '_gradient',
'title' => __('Set up gradient', 'bizpanda'),
'default' => ( isset( $sets['default'] ) && $defaultType == 'gradient' ) ? $sets['default']['value'] : null
)
)
),
array(
'type' => 'control-group-item',
'title' => __('Pattern', 'bizpanda'),
'name' => $name . '_image_item',
'items' => array(
array(
'type' => 'pattern',
'name' => $name . '_image',
'title' => __('Set up pattern', 'bizpanda'),
'default' => ( isset( $sets['default'] ) && $defaultType == 'image' ) ? $sets['default']['value'] : null,
'patterns' => ( isset( $sets['patterns']) ) ? $sets['patterns'] : array()
)
)
)
)
);
return $options;
}