File: /var/www/vhosts/uyarreklam.com.tr/httpdocs/OMAPI.tar
Actions.php 0000644 00000015726 15153673736 0006710 0 ustar 00 <?php
/**
* Actions class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Actions class.
*
* @since 1.0.0
*/
class OMAPI_Actions {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
// Add validation messages.
add_action( 'admin_init', array( $this, 'maybe_fetch_missing_data' ), 99 );
// We can run upgrade routines on cron runs and admin requests.
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
add_action( 'optin_monster_api_global_loaded', array( $this, 'check_upgrade_routines' ), 99 );
} else {
add_action( 'admin_init', array( $this, 'check_upgrade_routines_admin' ), 100 );
}
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* When the plugin is first installed
* Or Migrated from a pre-1.8.0 version
* We need to fetch some additional data
*
* @since 1.8.0
*
* @return void
*/
public function maybe_fetch_missing_data() {
$creds = $this->base->get_api_credentials();
$option = $this->base->get_option();
$changed = false;
// Set some onboarding connection related variables.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$onboarding_connect = empty( $_GET['onboardingConnect'] ) ? 'false' : sanitize_key( wp_unslash( $_GET['onboardingConnect'] ) );
$connection_token = $this->base->get_option( 'connectionToken' );
$is_onboarding_connect = ! empty( $connection_token ) && wp_validate_boolean( $onboarding_connect );
// Determine if we're missing API key credentials.
$missing_api_key = ! OMAPI_ApiKey::has_credentials();
// If we don't have an API Key yet, we can't fetch anything else.
if ( $missing_api_key && ! $is_onboarding_connect ) {
return;
}
// Set the onboarding credentials if we don't already have credentials.
if ( empty( $creds ) ) {
$creds = array( 'onboardingApiKey' => 'omwpoct_' . $connection_token );
}
// Fetch the userId and accountId, if we don't have them.
if (
empty( $option['userId'] )
|| empty( $option['accountId'] )
|| empty( $option['accountUserId'] )
|| empty( $option['currentLevel'] )
|| empty( $option['plan'] )
|| empty( $creds['apikey'] )
) {
$result = OMAPI_Api::fetch_me( $option, $creds );
if ( ! is_wp_error( $result ) ) {
$changed = true;
$option = $result;
}
}
if ( $changed && ! empty( $option['connectionToken'] ) ) {
unset( $option['connectionToken'] );
}
// Fetch the SiteIds for this site, if we don't have them.
if (
empty( $option['siteIds'] )
|| empty( $option['siteId'] )
|| $this->site_ids_are_numeric( $option['siteIds'] )
|| ! isset( $option['apiCname'] )
) {
$result = $this->base->sites->fetch();
if ( ! is_wp_error( $result ) ) {
$option = array_merge( $option, $result );
$changed = true;
}
}
// Only update the option if we've changed something.
if ( $changed ) {
update_option( 'optin_monster_api', $option );
}
}
/**
* In one version of the Plugin, we fetched the numeric SiteIds,
* But we actually needed the alphanumeric SiteIds.
*
* So we use this check to determine if we need to re-fetch Site Ids.
*
* @param array $site_ids Site ids to convert.
* @return bool True if the ids are numeric.
*/
protected function site_ids_are_numeric( $site_ids ) {
foreach ( $site_ids as $id ) {
if ( ! ctype_digit( (string) $id ) ) {
return false;
}
}
return true;
}
/**
* Runs upgrade routines in the admin, and refreshes the page if needed
* (if options changed, etc).
*
* @since 2.6.5
*
* @return void
*/
public function check_upgrade_routines_admin() {
$refresh = $this->check_upgrade_routines();
if ( $refresh ) {
wp_safe_redirect( esc_url_raw( add_query_arg( 'om', 1 ) ) );
exit;
}
}
/**
* Handles running the upgrade routines for each version.
*
* @since 2.6.5
*
* @return bool Whether page should be refreshed.
*/
public function check_upgrade_routines() {
$in_progress = get_option( 'optinmonster_current_upgrade' );
if ( ! empty( $in_progress ) ) {
return false;
}
$refresh = false;
$plugin_version = $this->base->version;
$upgrade_completed = get_option( 'optinmonster_upgrade_completed', 0 );
$upgrade_map = array(
'2.6.5' => 'v265_upgrades',
'2.9.0' => 'v290_upgrades',
);
foreach ( $upgrade_map as $upgrade_version => $method ) {
if (
version_compare( $plugin_version, $upgrade_version, '>=' )
&& version_compare( $upgrade_completed, $upgrade_version, '<' )
) {
update_option( 'optinmonster_current_upgrade', $upgrade_version );
$refresh = $this->{$method}();
delete_option( 'optinmonster_current_upgrade' );
}
}
if ( (string) $plugin_version !== (string) $upgrade_completed ) {
if ( empty( $this->base->notifications ) ) {
$this->base->notifications = new OMAPI_Notifications();
}
$this->base->notifications->update();
update_option( 'optinmonster_upgrade_completed', $plugin_version );
}
return $refresh;
}
/**
* Upgrades for version 2.6.5.
*
* @since 2.6.5
*
* @return bool Whether upgrade routine was completed successfully.
*/
public function v265_upgrades() {
$creds = $this->base->get_api_credentials();
// Missing previous api key to verify.
if ( empty( $creds['apikey'] ) ) {
return false;
}
$api = OMAPI_Api::build( 'v1', 'verify/', 'POST', $creds );
$results = $api->request();
// Current key is fine.
if ( ! is_wp_error( $results ) ) {
return false;
}
$error_code = ! empty( $api->response_body->code )
? $api->response_body->code
: 0;
if (
in_array( (string) $api->response_code, array( '410', '401', '424', '403' ), true )
&& '10051' === (string) $error_code
) {
OMAPI_ApiKey::regenerate( $creds['apikey'] );
// Regenerated, so we want to refresh the page.
return true;
}
// No luck.
return false;
}
/**
* Upgrades for version 2.9.0.
*
* This adds an admin_url to the site.
*
* @since 2.9.0
*
* @return bool Whether upgrade routine was completed successfully.
*/
public function v290_upgrades() {
$creds = $this->base->get_api_credentials();
$site_id = $this->base->get_site_id();
if ( empty( $creds['apikey'] ) || empty( $site_id ) ) {
return false;
}
$args = array(
'admin_url' => esc_url_raw( get_admin_url() ),
);
$api = OMAPI_Api::build( 'v2', 'sites/' . $site_id, 'PUT', $creds );
$results = $api->request( $args );
return ! is_wp_error( $results );
}
}
Ajax.php 0000644 00000002726 15153673736 0006167 0 ustar 00 <?php
/**
* Ajax class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Ajax class.
*
* @since 1.0.0
*/
class OMAPI_Ajax {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
// Load non-WordPress style ajax requests.
// phpcs:ignore Generic.Commenting.Todo.TaskFound
// TODO move all of this to RestApi, and use rest api for these requests!
add_action( 'init', array( $this, 'ajax' ), 999 );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Callback to process external ajax requests.
*
* @since 1.0.0
*/
public function ajax() {
if (
! isset( $_REQUEST['action'] )
|| empty( $_REQUEST['optin-monster-ajax-route'] )
) {
return;
}
check_ajax_referer( 'omapi', 'nonce' );
switch ( $_REQUEST['action'] ) {
case 'mailpoet':
$this->base->mailpoet->handle_ajax_call();
break;
default:
break;
}
}
}
Api.php 0000644 00000034254 15153673736 0006016 0 ustar 00 <?php
/**
* Api class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Api class.
*
* @since 1.0.0
*/
class OMAPI_Api {
/**
* Holds the last instantiated instance of this class.
*
* @var OMAPI_Api
*/
protected static $instance = null;
/**
* Base API route.
*
* @since 1.0.0
*
* @var string
*/
public $base = OPTINMONSTER_API_URL;
/**
* Current API route.
*
* @since 1.0.0
*
* @var bool|string
*/
public $route = false;
/**
* Full API URL endpoint.
*
* @since 1.0.0
*
* @var bool|string
*/
public $url = false;
/**
* Current API method.
*
* @since 1.0.0
*
* @var bool|string
*/
public $method = false;
/**
* API Username.
*
* @since 1.0.0
*
* @var bool|string
*/
public $user = false;
/**
* API Key.
*
* @since 1.0.0
*
* @var bool|string
*/
public $key = false;
/**
* New API Key.
*
* @since 1.3.4
*
* @var bool|string
*/
public $apikey = false;
/**
* Plugin slug.
*
* @since 1.0.0
*
* @var bool|string
*/
public $plugin = false;
/**
* The Api Version (v1 or v2) for this request.
*
* @since 1.8.0
*
* @var string
*/
public $version = 'v1';
/**
* Additional data to add to request body
*
* @since 1.0.0
*
* @var array
*/
protected $additional_data = array();
/**
* The HTTP response array.
*
* @since 1.6.5
*
* @var null|array
*/
public $response = null;
/**
* The HTTP response code.
*
* @since 1.6.5
*
* @var int
*/
public $response_code = 0;
/**
* The parsed HTTP response body.
*
* @since 1.6.5
*
* @var mixed
*/
public $response_body = null;
/**
* JSON decode error from decoding the response, if found.
*
* @since 2.6.6
*
* @var mixed
*/
public $decode_error = null;
/**
* Builds the API Object
*
* @since 1.8.0
*
* @param string $version The Api Version (v1 or v2).
* @param string $route The Api Endpoint/route.
* @param string $method The Request method.
* @param array $creds Array of API credentials.
*
* @return self
*/
public static function build( $version, $route, $method = 'POST', $creds = array() ) {
if ( empty( $creds ) ) {
$creds = OMAPI::get_instance()->get_api_credentials();
if ( ! empty( $creds ) ) {
// Check if we have the new API and if so only use it.
$creds = ! empty( $creds['apikey'] )
? array( 'apikey' => $creds['apikey'] )
: array(
'user' => ! empty( $creds['user'] ) ? $creds['user'] : '',
'key' => ! empty( $creds['key'] ) ? $creds['key'] : '',
);
}
}
return new self( $route, $creds, $method, $version );
}
/**
* Primary class constructor.
*
* @since 1.0.0
*
* @param string $route The API route to target.
* @param array $creds Array of API credentials.
* @param string $method The API method.
* @param string $version The version number of our API.
*/
public function __construct( $route, $creds, $method = 'POST', $version = 'v1' ) {
// Set class properties.
$this->route = $route;
$this->version = $version;
$this->method = $method;
$this->user = ! empty( $creds['user'] ) ? $creds['user'] : '';
$this->key = ! empty( $creds['key'] ) ? $creds['key'] : '';
$this->apikey = ! empty( $creds['apikey'] ) ? $creds['apikey'] : '';
$this->plugin = OMAPI::get_instance()->plugin_slug;
self::$instance = $this;
}
/**
* Processes the API request.
*
* @since 1.0.0
*
* @param array $args Request args.
*
* @return mixed $value The response to the API call.
*/
public function request( $args = array() ) {
// Build the body of the request.
$body = array(
'omapi-user' => $this->user,
'omapi-key' => $this->key,
);
$body = array_filter( $body );
// If a plugin API request, add the data.
if ( 'info' === $this->route || 'update' === $this->route ) {
$body['omapi-plugin'] = $this->plugin;
}
// Add in additional data if needed.
if ( ! empty( $this->additional_data ) ) {
$body['omapi-data'] = maybe_serialize( $this->additional_data );
}
$body = wp_parse_args( $args, $body );
$url = in_array( $this->method, array( 'GET', 'DELETE' ), true )
? add_query_arg( array_map( 'urlencode', $body ), $this->get_url() )
: $this->get_url();
$url = esc_url_raw( $url );
$plugins = new OMAPI_Plugins();
// Build the headers of the request.
$headers = array(
'Content-Type' => 'application/x-www-form-urlencoded',
'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0',
'Pragma' => 'no-cache',
'Expires' => 0,
'Origin' => site_url(),
'OMAPI-Referer' => site_url(),
'OMAPI-Sender' => 'WordPress',
'OMAPI-Site' => esc_attr( get_option( 'blogname' ) ),
'OMAPI-Version' => esc_attr( OMAPI::get_instance()->version ),
'OMAPI-Plugins' => $plugins->get_active_plugins_header_value(),
);
if ( $this->apikey ) {
$headers['X-OptinMonster-ApiKey'] = $this->apikey;
}
// If the onboarding key is set, let's use that as the API key.
if ( ! empty( $args['onboardingApiKey'] ) ) {
$headers['X-OptinMonster-ApiKey'] = $args['onboardingApiKey'];
}
// Setup data to be sent to the API.
$data = array(
'headers' => $headers,
'body' => $body,
'timeout' => 3000,
'sslverify' => false,
'method' => $this->method,
);
// Perform the query and retrieve the response.
$this->handle_response( wp_remote_request( $url, $data ) );
// Bail out early if there are any errors.
if ( is_wp_error( $this->response ) ) {
return $this->response;
}
// If we used the legacy api-creds, we'll get back a new api key.
if (
empty( $this->apikey )
&& ! empty( $this->response['headers']['x-optinmonster-apikey'] )
) {
$this->apikey = sanitize_text_field( $this->response['headers']['x-optinmonster-apikey'] );
}
$error = $this->check_response_error();
// Bail out early if there are any errors.
if ( is_wp_error( $error ) ) {
return $error;
}
// Return the json decoded content.
return $this->response_body;
}
/**
* Handle setting up the object properties from the response.
*
* @since 2.6.6
*
* @param object $response The response object from wp_remote_request.
*
* @return void
*/
public function handle_response( $response ) {
$this->response = $response;
// Get the response code and response body.
$this->response_code = wp_remote_retrieve_response_code( $response );
$this->response_body = json_decode( wp_remote_retrieve_body( $response ) );
$this->decode_error = json_last_error();
}
/**
* Check for an error response, and return an applicable WP_Error instance.
*
* @since 2.6.6
*
* @return boolean|WP_Error False if no errors, and WP_Error object if found.
*/
public function check_response_error() {
$code = (int) $this->response_code;
if ( $code < 400 ) {
return false;
}
// If not successful status header, send back error.
$type = ! empty( $this->response_body->type ) ? $this->response_body->type : 'api-error';
$message = ! empty( $this->response_body->message ) ? stripslashes( $this->response_body->message ) : '';
if ( empty( $message ) ) {
$message = ! empty( $this->response_body->status_message ) ? stripslashes( $this->response_body->status_message ) : '';
}
if ( empty( $message ) ) {
$message = ! empty( $this->response_body->error ) ? stripslashes( $this->response_body->error ) : 'unknown';
}
$message = sprintf(
/* translators: %1$s - API response code, %2$s - returned error from API. */
__( 'The API returned a <strong>%1$s</strong> response with this message: <strong>%2$s</strong>', 'optin-monster-api' ),
$this->response_code,
$message
);
return new WP_Error( $type, $message, $this->response_code );
}
/**
* The gets the URL based on our base, endpoint and version
*
* @since 1.8.0
*
* @return string The API url.
*/
public function get_url() {
return $this->base . '/' . $this->version . '/' . $this->route;
}
/**
* Sets a class property.
*
* @since 1.0.0
*
* @param string $key The property to set.
* @param string $val The value to set for the property.
* @return mixed $value The response to the API call.
*/
public function set( $key, $val ) {
$this->{$key} = $val;
}
/**
* Allow additional data to be passed in the request
*
* @since 1.0.0
*
* @param array $data The data to set.
*
* @return void
*/
public function set_additional_data( array $data ) {
$this->additional_data = array_merge( $this->additional_data, $data );
}
/**
* Clear additional data
*
* @since 1.9.0
*
* return void
*/
public function clear_additional_data() {
$this->additional_data = null;
return $this;
}
/**
* Get the request credentials for this API object.
*
* @since 2.3.0
*
* @return array Array containing API credentials.
*/
public function get_creds() {
return ! empty( $this->apikey )
? array( 'apikey' => $this->apikey )
: array(
'user' => $this->user,
'key' => $this->key,
);
}
/**
* Returns the last instantiated instance of this class.
*
* @since 1.9.10
*
* @return OMAPI_Api A single instance of this class.
*/
public static function instance() {
return self::$instance;
}
/**
* Fetch from the OM /me route, and cache results if no error..
*
* @since 2.6.6
*
* @param bool $refresh Whether to refresh the cache.
* @param array $creds Existing credentials array.
*
* @return array Requested /me data.
*/
public static function fetch_me_cached( $refresh = false, $creds = array() ) {
$api = self::build( 'v2', 'me?includeOnboarding=true', 'GET', $creds );
$creds = array( $api->user, $api->key, $api->apikey );
$creds = array_filter( $creds );
$creds = array_values( $creds );
$cache_key = 'omapp_me_cached' . md5( implode( ':', $creds ) );
$result = get_transient( $cache_key );
if ( empty( $result ) || $refresh ) {
$result = $api->request();
if ( ! is_wp_error( $result ) ) {
set_transient( $cache_key, $result, DAY_IN_SECONDS );
// Force the option to be updated when we gather new data from the API.
self::return_option_from_fetch( $result, array(), $creds, true );
}
}
return $result;
}
/**
* Fetch from the OM /me route to complete the plugin connection after onboarding.
*
* This differs from `OMAPI_Api::fetch_me_cached` in that there is no results
* caching. Fresh results are pulled every time this method is called.
*
* @since 2.16.6
*
* @param array $creds Existing credentials array.
*
* @return array Requested /me data.
*/
public static function fetch_me_onboarding( $creds = array() ) {
$api = self::build( 'v2', 'me?includeOnboarding=true', 'GET', $creds );
$result = $api->request( array( 'onboardingApiKey' => $creds['onboardingApiKey'] ) );
if ( ! is_wp_error( $result ) ) {
// Force the option to be updated when we gather new data from the API.
self::return_option_from_fetch( $result, array(), $creds, true );
OMAPI_ApiKey::init_connection( $api->apikey );
}
return $result;
}
/**
* Fetch from the OM /me route, and store data to our options.
*
* @since 2.0.0
*
* @param array $option Existing options array.
* @param array $creds Existing credentials array.
*
* @return array Updated options array.
*/
public static function fetch_me( $option = array(), $creds = array() ) {
if ( ! empty( $creds['onboardingApiKey'] ) ) {
$option = array();
$result = self::fetch_me_onboarding( $creds );
} else {
$result = self::fetch_me_cached( true, $creds );
}
if ( is_wp_error( $result ) ) {
return $result;
}
return self::return_option_from_fetch( $result, $option, $creds, empty( $option ) );
}
/**
* Return the option after fetching data from the /me route, potentially
* updating it in the database as well.
*
* @since 2.6.13
*
* @param stdClass $result The /me route result.
* @param array $option Possible option to be passed.
* @param array $creds Possible creds to be passed.
* @param bool $should_update Flag to update the option in the database or not.
*
* @return array Updated options array.
*/
public static function return_option_from_fetch( $result, $option = array(), $creds = array(), $should_update = false ) {
$api = self::instance();
if ( $should_update ) {
$option = OMAPI::get_instance()->get_option();
}
// Make sure to set the new api key, if we have it.
if ( empty( $option['api']['apikey'] ) && ! empty( $api->apikey ) ) {
$option['api'] = array( 'apikey' => $api->apikey );
if ( $api->user && $api->key ) {
// Notify user of credentials replacement.
OMAPI::get_instance()->notifications->add_event(
array(
'type' => 'success',
'title' => 'Your API Access Credentials have been updated',
'content' => 'We have automatically replaced your deprecated user/key OptinMonster connection credentials with a new API key.',
'btns' => array(
'main' => array(
'text' => 'Manage API Keys',
'url' => esc_url_raw( OPTINMONSTER_APP_URL . '/account/api/' ),
),
),
)
);
}
}
if ( isset( $result->id ) ) {
/*
* The user id connecting the plugin. It could be the owner or any sub-account.
* This key should not be used to embed codes or other API usage.
* In those cases, the owner's id (accountUserId) would be the one to use.
*/
$option['userId'] = $result->id;
}
$to_store = array( 'accountId', 'accountUserId', 'currentLevel', 'plan', 'revenueAttribution' );
foreach ( $to_store as $key ) {
if ( isset( $result->{$key} ) ) {
$option[ $key ] = is_object( $result->{$key} ) ? (array) $result->{$key} : $result->{$key};
}
}
if ( $should_update ) {
OMAPI::get_instance()->save->update_option( $option, $creds );
}
return $option;
}
/**
* Get the home/rest/admin url args.
*
* @since 2.13.0
*
* @return array
*/
public static function get_url_args() {
return array(
'homeUrl' => esc_url_raw( home_url() ),
'restUrl' => esc_url_raw( get_rest_url() ),
'adminUrl' => esc_url_raw( get_admin_url() ),
);
}
}
ApiAuth.php 0000644 00000004637 15153673736 0006642 0 ustar 00 <?php
/**
* Api Auth class.
*
* @since 2.6.5
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Api Auth class.
*
* @since 2.6.5
*/
class OMAPI_ApiAuth {
/**
* Get the auth token from the DB..
*
* @since 2.6.5
*
* @return array Auth token array.
*/
public static function get_token() {
return get_option(
'optinmonster_site_token',
array(
'expires' => 0,
'tt' => '',
)
);
}
/**
* Check if token exists in DB.
*
* @since 2.6.5
*
* @return boolean Whether it exists.
*/
public static function has_token() {
$token = self::get_token();
return ! empty( $token['expires'] ) && ! empty( $token['tt'] );
}
/**
* Get the tt value from the auth token (or generate the auth token).
*
* @since 2.6.5
*
* @return string The tt value from the auth token.
*/
public static function get_tt() {
$token = self::get_token();
if ( empty( $token['tt'] ) ) {
// if TT is empty, generate a new one, save it and then return it.
$token = array(
'expires' => time() + ( 2 * MINUTE_IN_SECONDS ),
'tt' => self::generate_tt(),
);
update_option( 'optinmonster_site_token', $token );
}
return $token['tt'];
}
/**
* Generate the tt value (long random string).
*
* @since 2.6.5
*
* @return string Tt value.
*/
public static function generate_tt() {
return hash( 'sha512', wp_generate_password( 128, true, true ) . AUTH_SALT . uniqid( '', true ) );
}
/**
* Validate whether given tt value matches auth token tt value,
* and whether the auth token has expired.
*
* @since 2.6.5
*
* @param string $passed_tt The tt value to validate.
*
* @return bool Whether tt value is validated with the token.
*/
public static function validate_token( $passed_tt = '' ) {
if ( empty( $passed_tt ) ) {
return false;
}
$token = self::get_token();
if ( empty( $token ) ) {
return false;
}
$expired = ! empty( $token['expires'] ) ? $token['expires'] < time() : true;
$tt = ! empty( $token['tt'] ) ? $token['tt'] : '';
$matches = hash_equals( $tt, $passed_tt );
return $matches && ! $expired;
}
/**
* Delete the auth token.
*
* @since 2.6.5
*
* @return bool True if the option was deleted, false otherwise.
*/
public static function delete_token() {
return delete_option( 'optinmonster_site_token' );
}
}
ApiKey.php 0000644 00000012125 15153673736 0006460 0 ustar 00 <?php
/**
* Mailpoet integration class.
*
* @since 2.0.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* OM API Key management class.
*
* @since 2.0.0
*/
class OMAPI_ApiKey {
/**
* Handles storing the API key and initiating the API connection.
*
* @since 2.0.0
*
* @param string $apikey The OM api key.
*
* @return bool True if the Key can be validated
*/
public static function init_connection( $apikey ) {
$base = OMAPI::get_instance();
$creds = compact( 'apikey' );
$option = $base->get_option();
$option['api']['apikey'] = $apikey;
// Let's store the api-key first.
$base->save->update_option( $option, $creds );
// Go ahead and remove the old user and key.
$option['api']['user'] = '';
$option['api']['key'] = '';
// Remove any error messages.
$option['is_invalid'] = false;
$option['is_expired'] = false;
$option['is_disabled'] = false;
$option['connected'] = time();
$option['auto_updates'] = 'all';
$option['usage_tracking'] = true;
// Remove any pre-saved site/user/account data, so we re-fetch it elsewhere.
unset( $option['siteId'] );
unset( $option['siteIds'] );
unset( $option['customApiUrl'] );
unset( $option['apiCname'] );
unset( $option['userId'] );
unset( $option['accountUserId'] );
unset( $option['accountId'] );
unset( $option['currentLevel'] );
unset( $option['plan'] );
unset( $option['revenueAttribution'] );
// Fetch the userId and accountId now.
$option = OMAPI_Api::fetch_me( $option, $creds );
if ( is_wp_error( $option ) ) {
return $option;
}
// Fetch the SiteIds for this site now.
$result = $base->sites->fetch( $apikey );
if ( is_wp_error( $result ) ) {
return $result;
}
$option = array_merge( $option, $result );
// Fetch the campaigns for this site now.
$base->refresh->refresh( $apikey );
// Save the option one more time, with all the new good stuff..
$base->save->update_option( $option, $creds );
return $option;
}
/**
* Remove the API key and disconnect from the OptinMonster app.
*
* @since 2.0.0
*
* @return mixed The results of update_option.
*/
public static function disconnect() {
$option = OMAPI::get_instance()->get_option();
$option['connected'] = 0;
$option['api']['apikey'] = '';
// Remove any pre-saved site/user/account data, so we re-fetch it elsewhere.
unset( $option['userId'] );
unset( $option['accountUserId'] );
unset( $option['accountId'] );
unset( $option['currentLevel'] );
unset( $option['plan'] );
unset( $option['siteId'] );
unset( $option['siteIds'] );
unset( $option['customApiUrl'] );
unset( $option['apiCname'] );
unset( $option['api']['user'] );
unset( $option['api']['key'] );
// Save the updated option.
return OMAPI::get_instance()->save->update_option( $option );
}
/**
* Determine if we can store the given api key.
*
* @since 2.0.0
*
* @param string $apikey The OM api key.
*
* @return bool True if the Key can be validated
*/
public static function verify( $apikey ) {
$creds = array( 'apikey' => $apikey );
// Verify this new API Key works by posting to the Legacy route.
return OMAPI_Api::build( 'v1', 'verify/', 'POST', $creds )->request();
}
/**
* Validate this API Key
* We validate an API Key by fetching the Sites this key can fetch
* And then confirming that this key has access to at least one of these sites
*
* @since 2.0.0
*
* @param string $apikey The OM api key.
*
* @return bool True if the Key can be validated
*/
public static function validate( $apikey ) {
if ( empty( $apikey ) ) {
return false;
}
$site_ids = OMAPI::get_instance()->get_site_ids();
if ( empty( $site_ids ) ) {
return false;
}
$api_key_sites = OMAPI::get_instance()->sites->fetch( $apikey, true );
if ( is_wp_error( $api_key_sites ) || empty( $api_key_sites['siteIds'] ) ) {
return false;
}
foreach ( $site_ids as $site_id ) {
// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
if ( in_array( $site_id, $api_key_sites['siteIds'] ) ) {
return true;
}
}
return false;
}
/**
* Determine if we have a valid api key stored.
*
* @since 2.0.0
*
* @return bool
*/
public static function has_credentials() {
$creds = OMAPI::get_instance()->get_api_credentials();
return ! empty( $creds['apikey'] ) || self::has_legacy();
}
/**
* Determine if we have legacy api credentials.
*
* @since 2.0.0
*
* @return bool
*/
public static function has_legacy() {
$creds = OMAPI::get_instance()->get_api_credentials();
return ! empty( $creds['user'] ) && ! empty( $creds['key'] );
}
/**
* Handles regnerating api key.
*
* @since 2.6.5
*
* @param string $apikey Api Key to replace after regeneration.
*
* @return mixed $value The response to the API call.
*/
public static function regenerate( $apikey ) {
return OMAPI_Api::build( 'v2', 'key/regenerate', 'POST', compact( 'apikey' ) )
->request(
array(
'tt' => OMAPI_ApiAuth::get_tt(),
)
);
}
}
AssetLoader.php 0000644 00000013131 15153673736 0007502 0 ustar 00 <?php
/**
* Asset Loader class.
*
* @since 2.0.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* A special asset loader built specifically to enqueue
* JS and CSS built by create-react-app
*
* @author Justin Sternberg <jsternberg@awesomemotive.com>
*/
class OMAPI_AssetLoader {
/**
* The directory in which the assets can be found
*
* @since 2.0.0
*
* @var string
*/
protected $directory;
/**
* The asset file to load.
*
* @since 2.0.0
*
* @var string
*/
protected $manifestFile = 'manifest.json';
/**
* The array of script handles.
*
* @since 2.0.0
*
* @var array
*/
public $handles = array(
'js' => array(),
'css' => array(),
);
/**
* The class constructor.
*
* @since 2.0.0
*
* @param string $directory The base directory to use.
* @param string $manifestFile The manifest file to use.
*/
public function __construct( $directory, $manifestFile = 'manifest.json' ) {
$this->directory = $directory;
$this->manifestFile = $manifestFile;
}
/**
* Uses the built-in WP asset enqueuing to add the manifest assets.
*
* @since 2.0.0
*
* @param array $options Enqueue options.
* @return void
* @throws Exception If webpack assets not found.
*/
public function enqueue( array $options = array() ) {
$defaults = array(
'base_path' => $this->directory,
'base_url' => $this->directory,
'version' => time(),
);
$options = wp_parse_args( $options, $defaults );
$assets = $this->getAssetsList( $options['base_path'] );
if ( empty( $assets ) ) {
throw new \Exception( 'No webpack assets found.' );
}
foreach ( $assets as $assetPath ) {
$isJs = preg_match( '/\.js$/', $assetPath );
$isCss = ! $isJs && preg_match( '/\.css$/', $assetPath );
// Ignore source maps and images.
if ( ! $isCss && ! $isJs ) {
continue;
}
$handle = basename( $assetPath );
$handle = 0 !== strpos( $handle, 'wp-om-' ) ? 'wp-om-' . $handle : $handle;
$uri = $this->getAssetUri( trim( $assetPath, '/' ), $options['base_url'] );
if ( $isJs || $isCss ) {
$should_enqueue = apply_filters( 'optin_monster_should_enqueue_asset', true, $handle, $uri, $isJs, $this );
if ( ! $should_enqueue ) {
continue;
}
if ( $isJs ) {
$this->handles['js'][] = $handle;
wp_enqueue_script( $handle, $uri, array(), $options['version'], true );
} else {
$this->handles['css'][] = $handle;
wp_enqueue_style( $handle, $uri, array(), $options['version'] );
}
}
}
}
/**
* Localize data for the enqueued script(s).
*
* @since 2.0.0
*
* @param array $args Array of data to send to JS.
*
* @return OMAPI_AssetLoader
*/
public function localize( $args ) {
foreach ( $this->handles['js'] as $handle ) {
OMAPI_Utils::add_inline_script( $handle, 'omWpApi', $args );
if ( isset( $args['omStaticDataKey'] ) ) {
OMAPI_Utils::add_inline_script( $handle, 'omStaticDataKey', $args['omStaticDataKey'] );
}
// We only need to output once.
break;
}
return $this;
}
/**
* Attempts to load the asset manifest.
*
* @since 2.0.0
*
* @param string $assetPath The asset path.
* @return null|Array
*/
protected function loadAssetFile( $assetPath ) {
static $resources = array();
if ( isset( $resources[ $assetPath ] ) ) {
return $resources[ $assetPath ];
}
$url = filter_var( $assetPath, FILTER_VALIDATE_URL );
$contents = '';
if ( $url ) {
$results = wp_remote_get(
$assetPath,
array(
'sslverify' => false,
)
);
$contents = wp_remote_retrieve_body( $results );
} elseif ( file_exists( $assetPath ) ) {
ob_start();
include_once $assetPath;
$contents = ob_get_clean();
}
if ( empty( $contents ) ) {
return null;
}
$resources[ $assetPath ] = json_decode( $contents, true );
return $resources[ $assetPath ];
}
/**
* Finds the asset manifest in the given directory.
*
* @since 2.0.0
*
* @param string $directory The directory to append to the manifest file.
* @return array The assets themselves or an array of parsed assets.
*/
public function getAssetsList( $directory ) {
$directory = trailingslashit( $directory );
$assets = $this->loadAssetFile( $directory . $this->manifestFile );
return ! empty( $assets ) ? array_values( $assets ) : array();
}
/**
* Infer a base web URL for a file system path.
*
* @since 2.0.0
*
* @param string $path Filesystem path for which to return a URL.
* @return string|null
*/
protected function inferBaseUrl( $path ) {
if ( strpos( $path, get_stylesheet_directory() ) === 0 ) {
return get_theme_file_uri( substr( $path, strlen( get_stylesheet_directory() ) ) );
}
if ( strpos( $path, get_template_directory() ) === 0 ) {
return get_theme_file_uri( substr( $path, strlen( get_template_directory() ) ) );
}
// Any path not known to exist within a theme is treated as a plugin path.
$pluginPath = plugin_dir_path( __FILE__ );
if ( strpos( $path, $pluginPath ) === 0 ) {
return plugin_dir_url( __FILE__ ) . substr( $path, strlen( $pluginPath ) );
}
return '';
}
/**
* Return web URIs or convert relative filesystem paths to absolute paths.
*
* @since 2.0.0
*
* @param string $assetPath A relative filesystem path or full resource URI.
* @param string $baseUrl A base URL to prepend to relative bundle URIs.
* @return string
*/
public function getAssetUri( $assetPath, $baseUrl ) {
if ( strpos( $assetPath, '://' ) !== false ) {
return $assetPath;
}
return trailingslashit( $baseUrl ) . $assetPath;
}
}
BaseRestApi.php 0000644 00000015227 15153673736 0007446 0 ustar 00 <?php
/**
* Base Rest API Class, extend this if implementing a RestApi class.
* Most of the code was migrated from OMAPI_RestApi.
*
* @since 2.8.0
*
* @package OMAPI
* @author Gabriel Oliveira and Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Base Rest Api class.
*
* @since 2.8.0
*/
abstract class OMAPI_BaseRestApi {
/**
* The Base OMAPI Object
*
* @since 2.8.0
*
* @var OMAPI
*/
protected $base;
/**
* The REST API Namespace
*
* @since 2.8.0
*
* @var string The namespace
*/
protected $namespace = 'omapp/v1';
/**
* Whether request was given a valid api key.
*
* @since 2.8.0
*
* @var null|bool
*/
protected $has_valid_api_key = null;
/**
* Build our object.
*
* @since 2.8.0
*/
public function __construct() {
$this->base = OMAPI::get_instance();
$this->register_rest_routes();
}
/**
* Registers the Rest API routes for this class
*
* @since 2.8.0
*
* @return void
*/
abstract public function register_rest_routes();
/**
* Determine if we can store settings.
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_Error|bool
*/
public function can_update_settings( $request ) {
try {
$this->verify_request_nonce( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
return OMAPI::get_instance()->can_access( 'settings_update' );
}
/**
* Determine if OM API key is provided and valid.
*
* @since 1.9.10
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_Error|bool
*/
public function has_valid_api_key( $request ) {
$header = $request->get_header( 'X-OptinMonster-ApiKey' );
// Use this API Key to validate.
if ( ! $this->validate_api_key( $header ) ) {
return new WP_Error(
'omapp_rest_forbidden',
esc_html__( 'Could not verify your API Key.', 'optin-monster-api' ),
array(
'status' => rest_authorization_required_code(),
)
);
}
return $this->has_valid_api_key;
}
/**
* Determine if logged in or OM API key is provided and valid.
*
* @since 1.9.10
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
*/
public function logged_in_or_has_api_key( $request ) {
return $this->logged_in_and_can_access_route( $request )
|| true === $this->has_valid_api_key( $request );
}
/**
* Determine if logged in user can access this route (calls current_user_can).
*
* @since 2.6.4
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
*/
public function logged_in_and_can_access_route( $request ) {
return OMAPI::get_instance()->can_access( $request->get_route() );
}
/**
* Determine if the passed connection token is valid.
*
* @since 2.16.6
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_Error|bool
*/
public function has_connection_token( $request ) {
$request_connection_token = $request->get_param( 'connectionToken' );
$connection_token = $this->base->get_option( 'connectionToken' );
if ( 'omwpoct_' . $connection_token !== $request_connection_token ) {
return new WP_Error(
'omapp_rest_forbidden',
esc_html__( 'Could not verify your connection token.', 'optin-monster-api' ),
array(
'status' => rest_authorization_required_code(),
)
);
}
return true;
}
/**
* Validate this API Key
* We validate an API Key by fetching the Sites this key can fetch
* And then confirming that this key has access to at least one of these sites
*
* @since 1.8.0
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param string $apikey The OM api key.
*
* @return bool True if the Key can be validated
*/
public function validate_api_key( $apikey ) {
$this->has_valid_api_key = OMAPI_ApiKey::validate( $apikey );
return $this->has_valid_api_key;
}
/**
* Convert an exception to a REST API WP_Error object.
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param Exception $e The exception.
*
* @return WP_Error
*/
protected function exception_to_response( Exception $e ) {
// Return WP_Error objects directly.
if ( $e instanceof OMAPI_WpErrorException && $e->getWpError() ) {
return $e->getWpError();
}
$code = $e->getCode();
if ( empty( $code ) || $code < 400 ) {
$code = 400;
}
$data = ! empty( $e->data ) ? $e->data : array();
$data = wp_parse_args(
$data,
array(
'status' => $code,
)
);
$error_code = rest_authorization_required_code() === $code
? 'omapp_rest_forbidden'
: 'omapp_rest_error';
return new WP_Error( $error_code, $e->getMessage(), $data );
}
/**
* Convert a WP_Error to a proper REST API WP_Error object.
*
* @since 2.6.5
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param WP_Error $e The WP_Error object.
* @param mixed $data Data to include in the error data.
*
* @return WP_Error
*/
protected function wp_error_to_response( WP_Error $e, $data = array() ) {
$api = OMAPI_Api::instance();
$data = is_array( $data ) || is_object( $data ) ? (array) $data : array();
$error_data = $e->get_error_data();
$error_message = $e->get_error_message();
$error_code = $e->get_error_code();
if ( empty( $error_data['status'] ) ) {
$status = is_numeric( $error_data ) ? $error_data : 400;
$error_code = (string) rest_authorization_required_code() === (string) $status
? 'omapp_rest_forbidden'
: 'omapp_rest_error';
$error_data = wp_parse_args(
array(
'status' => $status,
),
$data
);
} else {
$error_data = wp_parse_args( $error_data, $data );
}
return new WP_Error( $error_code, $error_message, $error_data );
}
/**
* Verify the request nonce and throw an exception if verification fails.
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi
*
* @param WP_REST_Request $request The REST request.
*
* @return void
*
* @throws Exception If the nonce is missing or invalid.
*/
public function verify_request_nonce( $request ) {
$nonce = $request->get_param( 'nonce' );
if ( empty( $nonce ) ) {
$nonce = $request->get_header( 'X-WP-Nonce' );
}
if ( empty( $nonce ) ) {
throw new Exception( esc_html__( 'Missing security token!', 'optin-monster-api' ), rest_authorization_required_code() );
}
// Check the nonce.
$result = wp_verify_nonce( $nonce, 'wp_rest' );
if ( ! $result ) {
throw new Exception( esc_html__( 'Security token invalid!', 'optin-monster-api' ), rest_authorization_required_code() );
}
}
}
Blocks.php 0000644 00000031476 15153673736 0006525 0 ustar 00 <?php
/**
* Gutenberg Blocks registration class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Gutenberg Blocks registration class.
*
* @since 1.9.10
*/
class OMAPI_Blocks {
/**
* Holds the class object.
*
* @since 1.9.10
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.9.10
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.9.10
*
* @var object
*/
public $base;
/**
* The data to be localized for JS.
*
* @since 2.2.0
*
* @var array
*/
protected $data_for_js = array();
/**
* The campaign options list array.
*
* @var null|array
*/
protected static $campaigns_list = null;
/**
* Primary class constructor.
*
* @since 1.9.10
*/
public function __construct() {
// Set our object.
$this->set();
if ( function_exists( 'register_block_type' ) ) {
// Register our blocks.
$this->register_blocks();
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
// Register the global post campaign switch meta.
register_meta(
'post',
'om_disable_all_campaigns',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'boolean',
)
);
}
}
/**
* Sets our object instance and base class instance.
*
* @since 1.9.10
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Register OptinMonster Gutenberg blocks on the backend.
*
* @since 1.9.10
*/
public function register_blocks() {
$use_blocks_json = version_compare( $GLOBALS['wp_version'], '5.8', '>=' );
$block_type = plugin_dir_path( OMAPI_FILE ) . 'assets/js/';
$args = array(
'render_callback' => array( $this, 'get_output' ),
);
if ( ! $use_blocks_json ) {
$block_type = 'optinmonster/campaign-selector';
$args['attributes'] = array(
'slug' => array(
'type' => 'string',
),
'followrules' => array(
'type' => 'boolean',
),
);
}
register_block_type( $block_type, $args );
}
/**
* Load OptinMonster Gutenberg block scripts.
*
* @since 1.9.10
*/
public function enqueue_block_editor_assets() {
global $pagenow;
$version = $this->base->asset_version();
$css_handle = $this->base->plugin_slug . '-blocks-admin';
wp_enqueue_style( $css_handle, $this->base->url . 'assets/dist/css/blocks-admin.min.css', array(), $version );
if ( function_exists( 'wp_add_inline_style' ) ) {
$data = get_post_type()
? get_post_type_object( get_post_type() )
: array();
$css = $this->base->get_min_css_view_contents( 'disable-warning-css.php', (object) $data );
wp_add_inline_style( $css_handle, $css );
}
$campaign_selector_handle = $this->base->plugin_slug . '-gutenberg-campaign-selector';
wp_enqueue_script(
$campaign_selector_handle,
$this->base->url . 'assets/dist/js/campaign-selector.min.js',
array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
$version,
true
);
OMAPI_Utils::add_inline_script( $campaign_selector_handle, 'OMAPI', $this->get_data_for_js() );
$is_widgets_page = 'widgets.php' === $pagenow;
// Prevent enqueueing sidebar settings on widgets screen...
if ( ! $is_widgets_page && ! is_customize_preview() ) {
wp_enqueue_script(
$this->base->plugin_slug . '-gutenberg-sidebar-settings',
$this->base->url . 'assets/dist/js/om-settings.min.js',
array( $campaign_selector_handle, 'wp-plugins', 'wp-edit-post', 'wp-element' ),
$version
);
}
if ( version_compare( $GLOBALS['wp_version'], '5.3', '>=' ) ) {
wp_enqueue_script(
$this->base->plugin_slug . '-gutenberg-format-button',
$this->base->url . 'assets/dist/js/om-format.min.js',
array(
$campaign_selector_handle,
'wp-rich-text',
'wp-element',
$is_widgets_page && version_compare( $GLOBALS['wp_version'], '5.8.0', '>=' )
? 'wp-edit-widgets'
: 'wp-editor',
),
$version
);
}
}
/**
* Get OptinMonster data for Gutenberg JS.
*
* @since 2.2.0
*
* @param string $key The js data to get, by key.
*
* @return array Array of data for JS.
*/
public function get_data_for_js( $key = null ) {
if ( empty( $this->data_for_js ) ) {
// For translation of strings.
$i18n = array(
'title' => esc_html__( 'OptinMonster', 'optin-monster-api' ),
'description' => esc_html__( 'Select and display one of your OptinMonster inline campaigns.', 'optin-monster-api' ),
'campaign_select' => esc_html__( 'Select Campaign...', 'optin-monster-api' ),
'campaign_select_display' => esc_html__( 'Select and display your email marketing call-to-action campaigns from OptinMonster', 'optin-monster-api' ),
'create_new_popup' => esc_html__( 'Create New Popup Campaign', 'optin-monster-api' ),
'create_new_inline' => esc_html__( 'Create New Inline Campaign', 'optin-monster-api' ),
'block_settings' => esc_html__( 'OptinMonster Block Settings', 'optin-monster-api' ),
'settings' => esc_html__( 'OptinMonster Settings', 'optin-monster-api' ),
'campaign_selected' => esc_html__( 'Campaign', 'optin-monster-api' ),
'followrules_label' => esc_html__( 'Use Output Settings', 'optin-monster-api' ),
/* translators: %s - Output Settings (linked).*/
'followrules_help' => esc_html__( 'Ensure this campaign follows any conditions you\'ve selected in its %s', 'optin-monster-api' ),
'output_settings' => esc_html__( 'Output Settings', 'optin-monster-api' ),
'no_sites' => esc_html__( 'Please create a free account or connect an existing account to use an OptinMonster block.', 'optin-monster-api' ),
'no_sites_button_create_account' => esc_html__( 'Create a Free Account', 'optin-monster-api' ),
'no_sites_button_connect_account' => esc_html__( 'Connect an Existing Account', 'optin-monster-api' ),
'no_inline_campaigns' => esc_html__( 'You don’t have any inline campaigns yet!', 'optin-monster-api' ),
'no_campaigns_help' => esc_html__( 'Create an inline campaign to display in your posts and pages.', 'optin-monster-api' ),
'create_inline_campaign' => esc_html__( 'Create Your First Inline Campaign', 'optin-monster-api' ),
'create_popup_campaign' => esc_html__( 'Create Your First Popup', 'optin-monster-api' ),
'no_campaigns_button_help' => esc_html__( 'Learn how to create your first campaign', 'optin-monster-api' ),
'found_error' => esc_html__( 'An error was encountered', 'optin-monster-api' ),
'disable_all' => esc_html__( 'Disable all OptinMonster campaigns.', 'optin-monster-api' ),
'view_all' => esc_html__( 'View All Campaigns', 'optin-monster-api' ),
'not_connected' => esc_html__( 'You Have Not Connected with OptinMonster', 'optin-monster-api' ),
'no_campaigns_yet' => esc_html__( 'You don’t have any campaigns created yet.', 'optin-monster-api' ),
'update_selected_popup' => esc_html__( 'Update Selected OptinMonster Campaign', 'optin-monster-api' ),
'open_popup' => esc_html__( 'Open an OptinMonster Popup', 'optin-monster-api' ),
'remove_popup' => esc_html__( 'Remove Campaign Link', 'optin-monster-api' ),
'upgrade_monsterlink' => esc_html__( 'Unlock access to the OptinMonster click-to-load feature called MonsterLinks by upgrading your subscription.', 'optin-monster-api' ),
'upgrade' => esc_html__( 'Upgrade Now', 'optin-monster-api' ),
);
$i18n['description'] = html_entity_decode( $i18n['description'], ENT_COMPAT, 'UTF-8' );
$campaigns = $this->get_campaign_options();
$site_ids = $this->base->get_site_ids();
$post = get_post();
$this->data_for_js = array(
'logoUrl' => $this->base->url . 'assets/css/images/icons/archie-icon.svg',
'i18n' => $i18n,
'campaigns' => array(
'inline' => ! empty( $campaigns['inline'] ) ? $campaigns['inline'] : array(),
'other' => ! empty( $campaigns['other'] ) ? $campaigns['other'] : array(),
),
'site_ids' => ! empty( $site_ids ) ? $site_ids : array(),
'post' => $post,
'omEnv' => defined( 'OPTINMONSTER_ENV' ) ? OPTINMONSTER_ENV : '',
'canMonsterlink' => $this->base->has_rule_type( 'monster-link' ),
'templatesUri' => OMAPI_Urls::templates(),
'playbooksUri' => OMAPI_Urls::playbooks(),
'campaignsUri' => OMAPI_Urls::campaigns(),
'settingsUri' => OMAPI_Urls::settings(),
'wizardUri' => OMAPI_Urls::wizard(),
'upgradeUri' => OMAPI_Urls::upgrade( 'gutenberg', '--FEATURE--' ),
'apiUrl' => esc_url_raw( OPTINMONSTER_APIJS_URL ),
'omUserId' => $this->base->get_option( 'accountUserId' ),
'outputSettingsUrl' => OMAPI_Urls::campaign_output_settings( '%s' ),
'editUrl' => OMAPI_Urls::om_app(
'campaigns/--CAMPAIGN_SLUG--/edit/',
rawurlencode( OMAPI_Urls::campaign_output_settings( '--CAMPAIGN_SLUG--' ) )
),
'monsterlink' => esc_url_raw( OPTINMONSTER_SHAREABLE_LINK ) . '/c/',
'wpVersion' => $GLOBALS['wp_version'],
'customFieldsSupported' => post_type_supports( get_post_type( $post ), 'custom-fields' ),
'pluginUrl' => esc_url_raw( plugin_dir_url( dirname( __FILE__ ) ) ),
);
}
if ( $key ) {
return isset( $this->data_for_js[ $key ] ) ? $this->data_for_js[ $key ] : null;
}
return $this->data_for_js;
}
/**
* Does the user have any associated OM sites registered?
*
* @since 2.2.0
*
* @return boolean
*/
public function has_sites() {
$site_ids = $this->base->get_site_ids();
return ! empty( $site_ids );
}
/**
* Get campaign options.
*
* @since 2.2.0
*
* @param boolean $titles_only Whether to include titles only, or separate data as array.
*
* @return array Array of campaign options.
*/
public function get_campaign_options( $titles_only = false ) {
if ( null === self::$campaigns_list ) {
$campaigns_list = array(
'inline' => array(),
'other' => array(),
);
if ( $this->has_sites() ) {
$campaigns = $this->base->get_campaigns();
if ( ! empty( $campaigns ) ) {
foreach ( $campaigns as $campaign ) {
$title = mb_strlen( $campaign->post_title, 'UTF-8' ) > 100
? mb_substr( $campaign->post_title, 0, 97, 'UTF-8' ) . '...'
: $campaign->post_title;
$title .= ' (' . $campaign->post_name . ')';
$type = 'inline' === $campaign->campaign_type ? 'inline' : 'other';
$campaigns_list[ $type ][ $campaign->post_name ] = array(
'title' => $title,
'pending' => empty( $campaign->enabled ),
);
}
}
}
self::$campaigns_list = $campaigns_list;
}
if ( $titles_only && ! empty( self::$campaigns_list ) ) {
$list = array();
foreach ( self::$campaigns_list as $type => $type_list ) {
foreach ( $type_list as $campaign_name => $args ) {
$list[ $type ][ $campaign_name ] = $args['title'] . ( $args['pending'] ? ' [Pending]' : '' );
}
}
return $list;
}
return self::$campaigns_list;
}
/**
* Get form HTML to display in a OptinMonster Gutenberg block.
*
* @param array $atts Attributes passed by OptinMonster Gutenberg block.
*
* @since 1.9.10
*
* @return string
*/
public function get_output( $atts ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$context = ! empty( $_REQUEST['context'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['context'] ) ) : '';
$is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
$is_gutes = $is_rest && 'edit' === $context;
// Our Guten-block handles the embed output manually.
if ( $is_gutes ) {
return;
}
// Unslash and sanitize the shortcode attributes.
$atts = array_map( 'sanitize_text_field', wp_unslash( $atts ) );
// Gutenberg block shortcodes default to following the rules.
// See assets/js/campaign-selector.js, attributes.followrules.
if ( ! isset( $atts['followrules'] ) ) {
$atts['followrules'] = true;
}
$atts['followrules'] = wp_validate_boolean( $atts['followrules'] );
$output = $this->base->shortcode->shortcode( $atts );
if (
! empty( $output )
&& ! wp_script_is( $this->base->plugin_slug . '-api-script', 'enqueued' )
) {
// Need to enqueue the base api script.
$this->base->output->api_script();
}
// Just return the shortcode output to the frontend.
return $output;
}
}
ClassicEditor.php 0000644 00000015655 15153673736 0010041 0 ustar 00 <?php
/**
* Classic Editor class.
*
* @since 2.3.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Classic Editor class.
*
* @since 2.3.0
*/
class OMAPI_ClassicEditor {
/**
* Holds the class object.
*
* @since 2.3.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 2.3.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 2.3.0
*
* @var OMAPI
*/
public $base;
/**
* Primary class constructor.
*
* @since 2.3.0
*/
public function __construct() {
// Set our object.
$this->set();
add_action( 'media_buttons', array( $this, 'media_button' ), 15 );
add_action( 'add_meta_boxes', array( $this, 'settings_meta_box' ) );
add_action( 'save_post', array( $this, 'save_metabox_data' ), 10, 2 );
}
/**
* Sets our object instance and base class instance.
*
* @since 2.3.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Allow easy shortcode insertion via a custom media button.
*
* @since 2.3.0
*
* @param string $editor_id Unique editor identifier, e.g. 'content'.
*/
public function media_button( $editor_id ) {
if ( ! $this->base->can_access( 'campaign_media_button' ) ) {
return;
}
// Provide the ability to conditionally disable the button, so it can be
// disabled for custom fields or front-end use such as bbPress. We default
// to only showing within the post editor page.
if ( ! apply_filters( 'optin_monster_display_media_button', $this->is_post_editor_page(), $editor_id ) ) {
return;
}
// Setup the icon.
$icon = '<span class="wp-media-buttons-icon optin-monster-menu-icon">' . $this->base->menu->icon_svg( 'currentColor', false ) . '</span>';
printf(
'<button type="button" class="button optin-monster-insert-campaign-button" data-editor="%s" title="%s">%s %s</button>',
esc_attr( $editor_id ),
esc_attr__( 'Add OptinMonster', 'optin-monster-api' ),
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, unable to escape SVG.
$icon,
esc_html__( 'Add OptinMonster', 'optin-monster-api' )
);
// If we have made it this far then load the JS.
$handle = $this->base->plugin_slug . '-editor';
wp_enqueue_script(
$handle,
$this->base->url . 'assets/dist/js/editor.min.js',
array( 'jquery' ),
$this->base->asset_version(),
true
);
$i18n = $this->base->blocks->get_data_for_js( 'i18n' );
$i18n['or_monsterlink'] = esc_html__( 'Or link to a popup campaign', 'optin-monster-api' );
OMAPI_Utils::add_inline_script(
$handle,
'OMAPI_Editor',
array(
'monsterlink' => esc_url_raw( OPTINMONSTER_SHAREABLE_LINK ) . '/c/',
'canMonsterlink' => $this->base->blocks->get_data_for_js( 'canMonsterlink' ),
'upgradeUri' => $this->base->blocks->get_data_for_js( 'upgradeUri' ),
'i18n' => $i18n,
)
);
add_action( 'admin_footer', array( $this, 'shortcode_modal' ) );
}
/**
* Check if we are on the post editor admin page.
*
* @since 2.3.0
*
* @returns boolean True if it is post editor admin page.
*/
public function is_post_editor_page() {
if ( ! is_admin() ) {
return false;
}
// get_current_screen() is loaded after 'admin_init' hook and may not exist yet.
if ( ! function_exists( 'get_current_screen' ) ) {
return false;
}
$screen = get_current_screen();
return null !== $screen && 'edit' === $screen->parent_base;
}
/**
* Modal window for inserting the optin-monster shortcode into TinyMCE.
*
* Thickbox is old and busted so we don't use that. Creating a custom view in
* Backbone would make me pull my hair out. So instead we offer a small clean
* modal that is based off of the WordPress insert link modal.
*
* @since 2.3.0
*/
public function shortcode_modal() {
$campaigns = $this->base->blocks->get_campaign_options( true );
$campaigns['inline'] = ! empty( $campaigns['inline'] )
? array_merge( array( '' => esc_html__( 'Select Campaign...', 'optin-monster-api' ) ), $campaigns['inline'] )
: array();
$campaigns['other'] = ! empty( $campaigns['other'] )
? array_merge( array( '' => esc_html__( 'Select Campaign...', 'optin-monster-api' ) ), $campaigns['other'] )
: array();
$this->base->output_view(
'shortcode-modal.php',
array(
'templatesUri' => $this->base->blocks->get_data_for_js( 'templatesUri' ),
'upgradeUri' => OMAPI_Urls::upgrade( 'gutenberg', 'monster-link' ),
'canMonsterlink' => $this->base->blocks->get_data_for_js( 'canMonsterlink' ),
'campaigns' => $campaigns,
)
);
$this->base->output_min_css( 'shortcode-modal-css.php' );
}
/**
* Register the global OptinMonster Settings metabox.
*
* @since 2.3.0
*/
public function settings_meta_box() {
$types = array_values( get_post_types( array( 'public' => true ) ) );
foreach ( $types as $type ) {
$supports_custom_fields = post_type_supports( $type, 'custom-fields' );
// If custom fields aren't supported, change title to avoid duplicate 'OptinMonster' sections.
// @see https://github.com/awesomemotive/optin-monster-wp-api/issues/391
$title = $supports_custom_fields ? 'OptinMonster Settings' : 'Campaign Settings';
add_meta_box(
'om-global-post-settings',
esc_html__( $title, 'optin-monster-api' ),
array( $this, 'settings_meta_box_output' ),
$type,
'side',
'default',
array( '__back_compat_meta_box' => $supports_custom_fields )
);
}
}
/**
* Output the markup for the global OptinMonster Settings metabox.
*
* @since 2.3.0
*
* @param WP_Post $post The post object.
*/
public function settings_meta_box_output( $post ) {
$disabled = get_post_meta( $post->ID, 'om_disable_all_campaigns', true );
wp_nonce_field( 'om_disable_all_campaigns', 'om_disable_all_campaigns_nonce' );
?>
<p>
<label for="om_disable_all_campaigns">
<input class="widefat" type="checkbox" <?php checked( ! empty( $disabled ) ); ?> name="om_disable_all_campaigns" id="om_disable_all_campaigns" value="1" />
<?php esc_html_e( 'Disable all OptinMonster campaigns.', 'optin-monster-api' ); ?>
</label>
</p>
<?php
}
/**
* Save the global OptinMonster settings.
*
* @since 2.3.0
*
* @param int $post_id Post Id.
* @param WP_Post $post Post object.
*/
public function save_metabox_data( $post_id, $post ) {
if (
empty( $_POST['om_disable_all_campaigns_nonce'] )
|| ! wp_verify_nonce( $_POST['om_disable_all_campaigns_nonce'], 'om_disable_all_campaigns' )
|| empty( $post->post_type )
) {
return;
}
$type = get_post_type_object( $post->post_type );
if (
empty( $type->cap->edit_post )
|| ! current_user_can( $type->cap->edit_post, $post_id )
) {
return;
}
$disabled = ! empty( $_POST['om_disable_all_campaigns'] );
update_post_meta( $post_id, 'om_disable_all_campaigns', $disabled );
}
}
ConstantContact.php 0000644 00000016662 15153673736 0010415 0 ustar 00 <?php
/**
* Constant Contact class.
*
* @since 1.6.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Constant Contact class.
*
* @since 1.6.0
*/
class OMAPI_ConstantContact {
/**
* Holds the class object.
*
* @since 1.6.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.6.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the review slug.
*
* @since 1.6.0
*
* @var string
*/
public $hook;
/**
* Holds the base class object.
*
* @since 1.6.0
*
* @var object
*/
public $base;
/**
* Sign up link.
*
* @since 1.6.0
* @var string
*/
public $sign_up = 'https://optinmonster.com/refer/constant-contact/';
/**
* Primary class constructor.
*
* @since 1.6.0
*/
public function __construct() {
// Set our object.
$this->set();
// Pages.
add_action( 'admin_menu', array( $this, 'register_cc_page' ) );
add_action( 'admin_notices', array( $this, 'constant_contact_cta_notice' ) );
add_action( 'wp_ajax_om_constant_contact_dismiss', array( $this, 'constant_contact_dismiss' ) );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.6.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Loads the OptinMonster admin menu.
*
* @since 1.6.0
*/
public function register_cc_page() {
$slug = 'optin-monster-constant-contact';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$is_current = isset( $_GET['page'] ) && $slug === sanitize_key( wp_unslash( $_GET['page'] ) );
$parent_slug = $this->base->menu->parent_slug();
if ( ! $is_current ) {
$parent_slug .= '-no-menu';
}
$this->hook = add_submenu_page(
$parent_slug, // parent slug
esc_html__( 'OptinMonster with Constant Contact', 'optin-monster-api' ), // page title,
esc_html__( 'OptinMonster + Constant Contact', 'optin-monster-api' ),
$this->base->access_capability( $slug ), // cap
$slug, // slug
array( $this, 'display_page' ) // callback
);
// Load settings page assets.
if ( $this->hook ) {
add_action( 'load-' . $this->hook, array( $this, 'assets' ) );
}
}
/**
* Add admin notices to connect to Constant Contact.
*
* @since 1.6.0
*/
public function constant_contact_cta_notice() {
// Only consider showing the notice when WPForms plugin is not active.
// Here WPForms_Constant_Contact class existence is checked which shows the notice in WPForms plugin.
if ( class_exists( 'WPForms_Constant_Contact' ) ) {
return;
}
// Only consider showing the review request to admin users.
if ( ! is_super_admin() ) {
return;
}
// Only display the notice if it has not been dismissed.
$dismissed = get_option( 'optinmonster_constant_contact_dismiss', false );
if ( $dismissed ) {
return;
}
// Only show on the main dashboard page (wp-admin/index.php)
// or any OptinMonster plugin-specific screen.
$can_show = $is_om_page = $this->base->menu->is_om_page();
if ( ! $can_show ) {
$can_show = function_exists( 'get_current_screen' ) && 'dashboard' === get_current_screen()->id;
}
if ( ! $can_show ) {
return;
}
$connect = OMAPI_Urls::onboarding();
$learn_more = OMAPI_Urls::admin( array( 'page' => 'optin-monster-constant-contact' ) );
// Output the notice message.
?>
<div class="notice notice-info is-dismissible om-constant-contact-notice">
<p>
<?php
echo wp_kses(
__( 'Get the most out of the <strong>OptinMonster</strong> plugin — use it with an active Constant Contact account.', 'optin-monster-api' ),
array(
'strong' => array(),
)
);
?>
</p>
<p>
<a href="<?php echo esc_url( $this->sign_up ); ?>" class="button-primary" target="_blank" rel="noopener noreferrer">
<?php esc_html_e( 'Try Constant Contact for Free', 'optin-monster-api' ); ?>
</a>
<?php if ( ! $is_om_page ) { ?>
<a href="<?php echo esc_url( $connect ); ?>" class="button-secondary">
<?php esc_html_e( 'Get Started', 'optin-monster-api' ); ?>
</a>
<?php } ?>
<?php
printf(
wp_kses(
/* translators: %s - OptinMonster Constant Contact internal URL. */
__( 'Learn More about the <a href="%s">power of email marketing</a>', 'optin-monster-api' ),
array(
'a' => array(
'href' => array(),
),
)
),
esc_url( $learn_more )
);
?>
</p>
<style type="text/css">
.om-constant-contact-notice p:first-of-type {
margin: 16px 0 8px;
}
.om-constant-contact-notice p:last-of-type {
margin: 8px 0 16px;
}
.om-constant-contact-notice .button-primary,
.om-constant-contact-notice .button-secondary {
display: inline-block;
margin: 0 10px 0 0;
}
</style>
<script type="text/javascript">
jQuery( function ( $ ) {
$( document ).on( 'click', '.om-constant-contact-notice button', function ( event ) {
event.preventDefault();
$.post(
ajaxurl,
{
action: 'om_constant_contact_dismiss',
_wpnonce: '<?php echo esc_js( wp_create_nonce( 'om_constant_contact_dismiss' ) ); ?>',
}
);
$( '.om-constant-contact-notice' ).remove();
} );
} );
</script>
</div>
<?php
}
/**
* Dismiss the Constant Contact admin notice.
*
* @since 1.6.0
*/
public function constant_contact_dismiss() {
// Verify the nonce.
check_ajax_referer( 'om_constant_contact_dismiss' );
// Make sure the user can dismiss the notice.
$can_dismiss = $this->base->access_capability( 'optin-monster-constant-contact' );
if ( ! $can_dismiss ) {
wp_send_json_error();
}
// Update the option to dismiss the notice.
update_option( 'optinmonster_constant_contact_dismiss', 1, false );
wp_send_json_success();
}
/**
* Loads assets for the settings page.
*
* @since 1.6.0
*/
public function assets() {
add_filter( 'admin_body_class', array( $this, 'add_body_classes' ) );
add_action( 'admin_enqueue_scripts', array( $this->base->menu, 'styles' ) );
add_filter( 'admin_footer_text', array( $this, 'footer' ) );
add_action( 'in_admin_header', array( $this->base->menu, 'output_plugin_screen_banner' ) );
}
/**
* Add body classes.
*
* @since 2.0.0
*
* @param string $classes Body classes.
*
* @return string Body classes.
*/
public function add_body_classes( $classes ) {
$classes .= ' omapi-constant-contact ';
return $classes;
}
/**
* Customizes the footer text on the OptinMonster settings page.
*
* @since 1.6.0
*
* @param string $text The default admin footer text.
* @return string $text Amended admin footer text.
*/
public function footer( $text ) {
$url = 'https://wordpress.org/support/plugin/optinmonster/reviews?filter=5#new-post';
$text = sprintf( __( 'Please rate <strong>OptinMonster</strong> <a href="%1$s" target="_blank" rel="noopener">★★★★★</a> on <a href="%1$s" target="_blank" rel="noopener noreferrer">WordPress.org</a> to help us spread the word. Thank you from the OptinMonster team!', 'optin-monster-api' ), $url );
return $text;
}
/**
* Outputs the Review Page.
*
* @since 1.6.0
*/
public function display_page() {
$this->base->output_view(
'constantcontact.php',
array(
'images_url' => esc_url( $this->base->url . 'assets/css/images/' ),
'signup_url' => esc_url( $this->sign_up ),
)
);
}
}
Debug.php 0000644 00000010545 15153673736 0006330 0 ustar 00 <?php
/**
* Output class.
*
* @since 2.6.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Output class.
*
* @since 2.6.0
*/
class OMAPI_Debug {
/**
* Check if rules debug can be output.
*
* @since 2.0.0
*
* @return bool
*/
public static function can_output_debug() {
$rules_debug = ! empty( $_GET['omwpdebug'] ) ? sanitize_text_field( wp_unslash( $_GET['omwpdebug'] ) ) : '';
if ( $rules_debug ) {
$omapi = OMAPI::get_instance();
$disable = 'off' === $rules_debug;
$decoded = base64_decode( base64_decode( $rules_debug ) );
$debug_enabled = $omapi->get_option( 'api', 'omwpdebug' );
$creds = $omapi->get_api_credentials();
if (
! empty( $creds['apikey'] )
&& ( $decoded === $creds['apikey'] || $disable )
) {
$option = $omapi->get_option();
if ( $disable ) {
unset( $option['api']['omwpdebug'] );
$debug_enabled = false;
} else {
$option['api']['omwpdebug'] = true;
$debug_enabled = true;
}
update_option( 'optin_monster_api', $option );
}
$rules_debug = $debug_enabled || is_user_logged_in() && $omapi->can_access( 'rules_debug' );
}
// If query var is set and user can manage OM, output debug data.
return apply_filters( 'optin_monster_api_should_output_rules_debug', ! empty( $rules_debug ) );
}
/**
* Outputs general debug rule data.
*
* Borrowed heavily from Query Monitor plugin.
*
* @see https://github.com/johnbillion/query-monitor/blob/develop/collectors/conditionals.php#L25-L100
*
* @since 2.6.0
*
* @return void
*/
public static function output_general() {
// Get all registered post types.
$all_post_types = get_post_types( array(), 'objects' );
// Initialize an array to store the results.
$results = array();
// Iterate through each post type.
foreach ( $all_post_types as $post_type_object ) {
// Check if the post type is viewable.
if ( is_post_type_viewable( $post_type_object ) ) {
$post_type = $post_type_object->name;
// Evaluate if the current post is singular and of this post type.
$results[ is_singular( $post_type ) ? 'TRUE' : 'FALSE' ][] = "is_singular('{$post_type}')";
}
}
$conditionals = array(
'is_404',
'is_admin',
'is_archive',
'is_attachment',
'is_author',
'is_blog_admin',
'is_category',
'is_comment_feed',
'is_customize_preview',
'is_date',
'is_day',
'is_embed',
'is_favicon',
'is_feed',
'is_front_page',
'is_home',
'is_main_network',
'is_main_site',
'is_month',
'is_network_admin',
'is_page',
'is_page_template',
'is_paged',
'is_post_type_archive',
'is_preview',
'is_privacy_policy',
'is_robots',
'is_rtl',
'is_search',
'is_single',
'is_singular',
'is_ssl',
'is_sticky',
'is_tag',
'is_tax',
'is_time',
'is_trackback',
'is_user_admin',
'is_year',
);
foreach ( $conditionals as $conditional ) {
if ( ! function_exists( $conditional ) ) {
$results['N/A'][] = $conditional;
break;
}
// Special case for is_sticky to prevent PHP notices.
$id = null;
if ( ( 'is_sticky' === $conditional ) && ! get_post( $id ) ) {
$results['FALSE'][] = $conditional;
break;
}
// Special case for multisite $conditionals to prevent them from.
// being annoying on single site installations.
if ( ! is_multisite() && in_array( $conditional, array( 'is_main_network', 'is_main_site' ), true ) ) {
$results['N/A'][] = $conditional;
break;
}
// The default case.
$results[ call_user_func( $conditional ) ? 'TRUE' : 'FALSE' ][] = $conditional;
}
$results[ OMAPI_Utils::is_front_or_search() ? 'TRUE' : 'FALSE' ][] = 'is_front_or_search';
sort( $results['FALSE'] );
sort( $results['TRUE'] );
?>
<hr style="padding-top:15px;border-top:10px double red;"/>
<div style="padding:20px;margin:20px;">
<button type="button" onclick="javascript:this.parentElement.remove();document.querySelectorAll('._om-debugging').forEach(el => el.style.display = 'block')" class="button btn">
Show Verbose Debugging Info
</button>
</div>
<?php // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export ?>
<pre class="_om-debugging _om-optin">$conditionals: <?php echo esc_html( var_export( $results, true ) ); ?></pre>
<?php
}
}
EasyDigitalDownloads/Output.php 0000644 00000004514 15153673736 0012653 0 ustar 00 <?php
/**
* EasyDigitalDownloads Output class.
*
* @since 2.8.0
*
* @package OMAPI
* @author Gabriel Oliveira
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* EasyDigitalDownloads Output class.
*
* @since 2.8.0
*/
class OMAPI_EasyDigitalDownloads_Output {
/**
* The OMAPI_EasyDigitalDownloads instance.
*
* @since 2.8.0
*
* @var OMAPI_EasyDigitalDownloads
*/
public $edd;
/**
* Constructor
*
* @since 2.13.0
*
* @param OMAPI_EasyDigitalDownloads $edd
*/
public function __construct( OMAPI_EasyDigitalDownloads $edd ) {
$this->edd = $edd;
}
/**
* Returns the payload EDD needs to use in its Display Rules.
*
* @since 2.8.0
*
* @return array The
*/
public function display_rules_data() {
$cart = $this->get_cart();
$user_id = get_current_user_id();
$purchased_products = edd_get_users_purchased_products( $user_id );
$cart['customer'] = null;
if ( ! empty( $purchased_products ) ) {
$customer_products = array_map(
function ( $product ) {
return $product->ID;
},
$purchased_products
);
$cart['customer'] = array(
'products' => $customer_products,
'stats' => edd_get_purchase_stats_by_user( $user_id ),
);
}
return $cart;
}
/**
* Retrieve the cart from EDD
*
* @since 2.8.0.
*
* @return array An array of EDD cart data.
*/
public function get_cart() {
// Bail if EDD isn't currently active.
if ( ! $this->edd->is_active() ) {
return array();
}
// Check if EDD is the minimum version.
if ( ! $this->edd->is_minimum_version() ) {
return array();
}
$edd_cart = EDD()->cart;
$cart = array();
$cart['discounts'] = $edd_cart->get_discounts();
$cart['quantity'] = $edd_cart->get_quantity();
$cart['subtotal'] = $edd_cart->get_subtotal();
$cart['total'] = $edd_cart->get_total();
// Filter out items by leaving only necessary fields
$cart['items'] = array_map(
function ( $edd_item ) {
return array(
'id' => $edd_item['id'],
'quantity' => $edd_item['quantity'],
'discount' => $edd_item['discount'],
'subtotal' => $edd_item['subtotal'],
'price' => $edd_item['price'],
'item_price' => $edd_item['item_price'],
);
},
$edd_cart->get_contents_details()
);
return $cart;
}
}
EasyDigitalDownloads/RestApi.php 0000644 00000015314 15153673736 0012722 0 ustar 00 <?php
/**
* EasyDigitalDownloads API routes for usage in WP's RestApi.
*
* @since 2.8.0
*
* @author Gabriel Oliveira
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rest Api class.
*
* @since 2.8.0
*/
class OMAPI_EasyDigitalDownloads_RestApi extends OMAPI_BaseRestApi {
/**
* The OMAPI_EasyDigitalDownloads instance.
*
* @since 2.8.0
*
* @var OMAPI_EasyDigitalDownloads
*/
public $edd;
/**
* Constructor
*
* @since 2.13.0
*
* @param OMAPI_EasyDigitalDownloads $edd
*/
public function __construct( OMAPI_EasyDigitalDownloads $edd ) {
$this->edd = $edd;
parent::__construct();
}
/**
* Registers the Rest API routes for EasyDigitalDownloads
*
* @since 2.8.0
*
* @return void
*/
public function register_rest_routes() {
register_rest_route(
$this->namespace,
'edd/autogenerate',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_manage_shop' ),
'callback' => array( $this, 'autogenerate' ),
)
);
register_rest_route(
$this->namespace,
'edd/save',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_update_settings' ),
'callback' => array( $this, 'save' ),
)
);
register_rest_route(
$this->namespace,
'edd/disconnect',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_update_settings' ),
'callback' => array( $this, 'disconnect' ),
)
);
register_rest_route(
$this->namespace,
'edd/settings',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_settings' ),
)
);
register_rest_route(
$this->namespace,
'edd/display-rules',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => '__return_true',
'callback' => array( $this, 'get_display_rules_info' ),
)
);
}
/**
* Determine if logged in user can manage the shop
*
* @since 2.8.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
*/
public function can_manage_shop( $request ) {
return $this->can_update_settings( $request ) && $this->edd->can_manage_shop();
}
/**
* Handles autogenerating and connecting EDD plugin with our app.
*
* Route: POST omapp/v1/edd/autogenerate
*
* @since 2.8.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function autogenerate( $request ) {
try {
$connected = $this->edd->save->autogenerate();
if ( is_wp_error( $connected ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $connected );
}
return $this->get_settings( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Handles connect the EDD API key/token to our app.
*
* Route: POST omapp/v1/edd/save
*
* @since 2.8.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function save( $request ) {
try {
$public_key = $request->get_param( 'publicKey' );
if ( empty( $public_key ) ) {
throw new Exception( esc_html__( 'Public Key is missing!', 'optin-monster-api' ), 400 );
}
$token = $request->get_param( 'token' );
if ( empty( $token ) ) {
throw new Exception( esc_html__( 'Token is missing!', 'optin-monster-api' ), 400 );
}
$connected = $this->edd->save->connect( $public_key, $token );
if ( is_wp_error( $connected ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $connected );
}
return $this->get_settings( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Handles disconnecting the EDD API key/token.
*
* Route: POST omapp/v1/edd/disconnect
*
* @since 2.8.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function disconnect( $request ) {
try {
$disconnected = $this->edd->save->disconnect();
if ( is_wp_error( $disconnected ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $disconnected );
}
return $this->get_settings( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Gets the associated EDD settings, such as is connected, key, token, etc.
*
* Route: GET omapp/v1/edd/settings
*
* @since 2.8.0
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function get_settings() {
try {
$truncated_key = substr( $this->base->get_option( 'edd', 'key' ), 0, 8 );
$truncated_token = substr( $this->base->get_option( 'edd', 'token' ), 0, 8 );
$user_id = absint( $this->base->get_option( 'edd', 'user', 0 ) );
$path = ! $user_id || $user_id === get_current_user_id()
? 'profile.php#publickey'
: sprintf( 'user-edit.php?user_id=%d#publickey', $user_id );
$description = __( 'token', 'optin-monster-api' );
if ( $user_id ) {
$user = get_user_by( 'ID', $user_id );
if ( ! empty( $user->user_login ) ) {
$description = sprintf(
esc_html__( '%s -', 'optin-monster-api' ),
$user->user_login
);
}
}
return new WP_REST_Response(
array(
'key' => $truncated_key,
'token' => $truncated_token,
'editUrl' => esc_url_raw( admin_url( $path ) ),
'description' => $description,
'isEddConnected' => OMAPI_EasyDigitalDownloads::is_connected(),
'isEddMinimumVersion' => OMAPI_EasyDigitalDownloads::is_minimum_version(),
'currentVersion' => OMAPI_EasyDigitalDownloads::version(),
'minimumVersion' => OMAPI_EasyDigitalDownloads::MINIMUM_VERSION,
),
200
);
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Gets the necessary information for Display Rules.
* This is used when there's an event on cart page to update information in the frontend.
*
* Route: GET omapp/v1/edd/display-rules
*
* @since 2.8.0
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function get_display_rules_info() {
$edd_output = new OMAPI_EasyDigitalDownloads_Output( $this->edd );
return new WP_REST_Response(
$edd_output->display_rules_data(),
200
);
}
}
EasyDigitalDownloads/Rules.php 0000644 00000006122 15153673736 0012442 0 ustar 00 <?php
/**
* EasyDigitalDownloads Rules class.
*
* @since 2.8.0
*
* @package OMAPI
* @author Gabriel Oliveira
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* EasyDigitalDownloads_Rules class.
*
* @since 2.8.0
*/
class OMAPI_EasyDigitalDownloads_Rules extends OMAPI_Rules_Base {
/**
* Holds the meta fields used for checking output statuses.
*
* @since 2.8.0
*
* @var array
*/
protected $fields = array(
'show_on_edd',
'is_edd_download',
'is_edd_checkout',
'is_edd_success_page',
'is_edd_failed_transaction_page',
'is_edd_purchase_history_page',
'is_edd_download_category',
'is_edd_download_tag',
);
/**
* Check for edd rules.
*
* @since 2.8.0
*
* @throws OMAPI_Rules_False If rule doesn't match.
* @throws OMAPI_Rules_True If rule matches.
* @return void
*/
public function run_checks() {
// If EDD is not connected we can ignore the EDD specific settings.
if ( ! OMAPI_EasyDigitalDownloads::is_connected() ) {
return;
}
try {
$edd_checks = array(
'is_edd_download' => array( $this, 'is_edd_download' ),
'is_edd_checkout' => 'edd_is_checkout',
'is_edd_success_page' => 'edd_is_success_page',
'is_edd_failed_transaction_page' => 'edd_is_failed_transaction_page',
'is_edd_purchase_history_page' => 'edd_is_purchase_history_page',
);
// If show_on_edd is selected, then we'll override the field_empty check for each page.
$show_on_all_edd_pages = ! $this->rules->field_empty( 'show_on_edd' );
if ( $show_on_all_edd_pages ) {
$this->rules
->set_global_override( false )
->set_advanced_settings_field( 'show_on_edd', $this->rules->get_field_value( 'show_on_edd' ) );
}
foreach ( $edd_checks as $field => $callback ) {
// If show on all edd pages is not selected, and field is empty, then we don't need to check this.
if ( ! $show_on_all_edd_pages && $this->rules->field_empty( $field ) ) {
continue;
}
$rule_value = $this->rules->get_field_value( $field );
if ( $rule_value ) {
$this->rules
->set_global_override( false )
->set_advanced_settings_field( $field, $rule_value );
}
if ( call_user_func( $callback ) ) {
// If it passes, send it back.
throw new OMAPI_Rules_True( $field );
}
}
} catch ( OMAPI_Rules_Exception $e ) {
if ( $e instanceof OMAPI_Rules_True ) {
throw new OMAPI_Rules_True( 'include edd', 0, $e );
}
$this->rules->add_reason( $e );
}
}
/**
* Check if the current page is a valid EDD Download page.
*
* @since 2.8.0
*
* @return boolean True if current page is EDD Download, false otherwise or if it was not able to determine.
*/
public function is_edd_download() {
// Get the current page/post id.
$post_id = get_the_ID();
if ( ! $post_id || ! function_exists( 'edd_get_download' ) ) {
return false;
}
// This method only returns the download if the post id passed through
// is a valid EDD download object. Null otherwhise.
$download = edd_get_download( $post_id );
return ! empty( $download );
}
}
EasyDigitalDownloads/Save.php 0000644 00000007327 15153673736 0012256 0 ustar 00 <?php
/**
* EasyDigitalDownloads Save class.
*
* @since 2.8.0
*
* @package OMAPI
* @author Gabriel Oliveira
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* EasyDigitalDownloads Save class.
*
* @since 2.8.0
*/
class OMAPI_EasyDigitalDownloads_Save {
/**
* Holds the class object.
*
* @since 2.8.0
*
* @var OMAPI_EasyDigitalDownloads_Save
*/
public static $instance;
/**
* Holds the base class object.
*
* @since 2.8.0
*
* @var OMAPI
*/
public $base;
/**
* The OMAPI_EasyDigitalDownloads instance.
*
* @since 2.8.0
*
* @var OMAPI_EasyDigitalDownloads
*/
public $edd;
/**
* Constructor
*
* @since 2.8.0
*
* @param OMAPI_EasyDigitalDownloads $edd
*/
public function __construct( OMAPI_EasyDigitalDownloads $edd ) {
$this->edd = $edd;
$this->base = OMAPI::get_instance();
self::$instance = $this;
}
/**
* Handles auto-connecting the EDD plugin with our app. First, get public key. If not present, then generate it, and then connect.
*
* @since 2.8.0
*
* @return WP_Error|array Array with public_key, private_key and token, or WP_Error if something happened.
*/
public function autogenerate() {
if ( ! $this->edd->can_manage_shop() ) {
return new WP_Error(
'omapi-error-user-permission',
esc_html__( 'You don\'t have the required permissions to retrieve or generate API Keys for EDD.', 'optin-monster-api' )
);
}
$user_id = get_current_user_id();
// We first try retrieving the public keys for the current user
$public_key = EDD()->api->get_user_public_key( $user_id );
// If it doesn't have, then let's generate it
if ( empty( $public_key ) ) {
EDD()->api->generate_api_key( $user_id );
// If we can't retrieve for the second time, then that's an error
$public_key = EDD()->api->get_user_public_key( $user_id );
if ( empty( $public_key ) ) {
return new WP_Error(
'omapi-error-generate-edd-keys',
esc_html__( 'An error happened while generating the keys for EDD user. Try again.', 'optin-monster-api' )
);
}
}
$token = EDD()->api->get_token( $user_id );
return $this->connect( $public_key, $token, $user_id );
}
/**
* Handles connecting the EDD plugin with our app.
*
* @since 2.8.0
*
* @param string $public_key The user public key
* @param string $token The user token
* @param string $user_id The user ID
*
* @return WP_Error|array Array with public_key, private_key and token, or WP_Error if something happened.
*/
public function connect( $public_key, $token, $user_id = 0 ) {
$url = esc_url_raw( site_url() );
$payload = array(
'public_key' => $public_key,
'token' => $token,
'url' => $url,
);
$response = $this->edd->connect( $payload );
if ( is_wp_error( $response ) ) {
return $response;
}
// Set up the connected EDD options.
// We only need to save the truncated public_key, so we can output to the user.
$option = $this->base->get_option();
$option['edd'] = array(
'key' => $public_key,
'token' => $token,
'shop' => $response->id,
'user' => $user_id,
);
// Save the option.
$this->base->save->update_option( $option );
return true;
}
/**
* Handles disconnecting EDD when the disconnect button is clicked.
*
* @since 2.8.0
*
* @return WP_Error|bool True if successful, or WP_Error if something happened.
*/
public function disconnect() {
$response = $this->edd->disconnect();
// Output an error or register a successful disconnection.
if ( is_wp_error( $response ) ) {
return $response;
}
$option = $this->base->get_option();
unset( $option['edd'] );
// Save the option.
$this->base->save->update_option( $option );
return true;
}
}
EasyDigitalDownloads.php 0000644 00000022521 15153673736 0011351 0 ustar 00 <?php
/**
* Easy Digital Downloads class.
*
* @since 2.6.13
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The Easy Digital Downloads class.
*
* @since 2.6.13
*/
class OMAPI_EasyDigitalDownloads extends OMAPI_Integrations_Base {
/**
* The minimum EDD version required.
*
* @since 2.8.0
*
* @var string
*/
const MINIMUM_VERSION = '2.1.0';
/**
* OMAPI_EasyDigitalDownloads_Save object
*
* @since 2.8.0
*
* @var OMAPI_EasyDigitalDownloads_Save
*/
public $save;
/**
* The OMAPI_EasyDigitalDownloads_RestApi instance.
*
* @since 2.13.0
*
* @var null|OMAPI_EasyDigitalDownloads_RestApi
*/
public $rest = null;
/**
* Primary class constructor.
*
* @since 2.6.13
*/
public function __construct() {
parent::__construct();
// Set our object.
$this->save = new OMAPI_EasyDigitalDownloads_Save( $this );
add_action( 'optin_monster_api_rest_register_routes', array( $this, 'maybe_init_rest_routes' ) );
add_filter( 'optin_monster_display_rules_data_output', array( $this, 'maybe_add_edd_data' ) );
// Revenue attribution support. We load on shutdown because we need access
// to the $_COOKIE data, which will not be available for any action triggered
// by cron. This attempts at the last possible moment to avoid interfering
// with anything else happening with the payment.
add_action( 'shutdown', array( $this, 'maybe_store_revenue_attribution' ) );
add_action( 'edd_update_payment_status', array( $this, 'maybe_store_revenue_attribution_on_payment_status_update' ), 10, 2 );
}
/**
* Maybe stores revenue attribution data when a purchase is successful.
*
* @since 2.6.13
*
* @param int $payment_id The EDD payment ID.
* @param bool $force Flag to force sending the revenue attribution data.
*
* @return void
*/
public function maybe_store_revenue_attribution( $payment_id = 0, $force = false ) {
if ( ! class_exists( 'EDD_Payment' ) ) {
return;
}
// If we don't have a payment ID passed, try to grab one.
if ( ! $payment_id ) {
// If we don't have the right EDD function to grab session data, return early.
if ( ! function_exists( 'edd_get_purchase_session' ) ) {
return;
}
// If we are not on the success page, return early.
if ( function_exists( 'edd_is_success_page' ) && ! edd_is_success_page() ) {
return;
}
// Grab the purchase session. If we can't find it, return early.
$session = edd_get_purchase_session();
if ( empty( $session['purchase_key'] ) ) {
return;
}
// Grab the payment ID from the purchase session. If we can't find
// it, return early.
$payment_id = edd_get_purchase_id_by_key( $session['purchase_key'] );
if ( ! $payment_id ) {
return;
}
}
// If we have already stored revenue attribution data before, return early.
$stored = get_post_meta( $payment_id, '_om_revenue_attribution_complete', true );
if ( $stored ) {
return;
}
// Grab the payment. If we can't, return early.
$payment = new EDD_Payment( $payment_id );
if ( ! $payment ) {
return;
}
// Grab some necessary data to send.
$data_on_payment = get_post_meta( $payment_id, '_om_revenue_attribution_data', true );
$data = wp_parse_args(
array(
'transaction_id' => absint( $payment_id ),
'value' => esc_html( $payment->total ),
'test' => 'live' !== $payment->mode,
),
! empty( $data_on_payment ) ? $data_on_payment : $this->base->revenue->get_revenue_data()
);
// If the status is not complete, return early.
// This will happen for payments where further
// work is required (such as checks, etc.). In those
// instances, we need to store the data to be processed
// at a later time.
if (
! in_array( $payment->status, array( 'complete', 'completed', 'publish' ), true )
&& ! $force
) {
update_post_meta( $payment_id, '_om_revenue_attribution_data', $data );
return;
}
// Attempt to make the revenue attribution request.
// It checks to determine if campaigns are set, etc.
$ret = $this->base->revenue->store( $data );
if ( ! $ret || is_wp_error( $ret ) ) {
return;
}
// Update the payment meta for storing revenue attribution data.
update_post_meta( $payment_id, '_om_revenue_attribution_complete', time() );
}
/**
* Maybe stores revenue attribution data when a purchase is successful.
*
* @since 2.6.13
*
* @param int $payment_id The EDD payment ID.
* @param string $new_status The new payment status.
*
* @return void
*/
public function maybe_store_revenue_attribution_on_payment_status_update( $payment_id, $new_status ) {
// If we don't have the proper new status, return early.
if ( 'publish' !== $new_status && 'complete' !== $new_status && 'completed' !== $new_status ) {
return;
}
// Maybe store the revenue attribution data.
return $this->maybe_store_revenue_attribution( $payment_id, true );
}
/**
* Connects EDD to OptinMonster.
*
* @param array $data The array of key / token.
*
* @since 2.8.0
*
* @return WP_Error|bool True if success, or WP_Error if any error was encountered.
*/
public function connect( $data ) {
if ( empty( $data['public_key'] ) || empty( $data['token'] ) ) {
return new WP_Error(
'omapi-invalid-edd-keys',
esc_html__( 'The EDD key or token appears to be invalid. Try again.', 'optin-monster-api' )
);
}
// Setup the request payload.
$payload = array_merge(
array(
'key' => $data['public_key'],
'token' => $data['token'],
'shop' => $data['url'],
'name' => esc_html( get_bloginfo( 'name' ) ),
),
OMAPI_Api::get_url_args()
);
// Get the OptinMonster API credentials.
$creds = $this->base->get_api_credentials();
// Initialize the API class.
$api = new OMAPI_Api( 'edd/shop', $creds, 'POST', 'v2' );
$body = $api->request( $payload );
if ( is_wp_error( $body ) ) {
$message = isset( $body->message )
? $body->message
: esc_html__( 'EDD could not be connected to OptinMonster. The OptinMonster API returned with the following response: ', 'optin-monster-api' ) . $body->get_error_message();
return new WP_Error( 'omapi-error-edd-api-connect', $message );
}
return $body;
}
/**
* Disconnects EDD from OptinMonster.
*
* @since 2.8.0
*
* @return WP_Error|string Empty string if success, or WP_Error if any error was encountered.
*/
public function disconnect() {
// Get the OptinMonster API credentials.
$creds = $this->base->get_api_credentials();
// Get the shop.
$shop = esc_attr( $this->base->get_option( 'edd', 'shop' ) );
if ( empty( $shop ) ) {
return true;
}
// Initialize the API class.
$api = new OMAPI_Api( 'edd/shop/' . rawurlencode( $shop ), $creds, 'DELETE', 'v2' );
$body = $api->request();
if ( is_wp_error( $body ) ) {
$message = isset( $body->message )
? $body->message
: esc_html__( 'EDD could not be disconnected to OptinMonster. The OptinMonster API returned with the following response: ', 'optin-monster-api' ) . $body->get_error_message();
return new WP_Error( 'omapi-error-api-disconnect', $message );
}
return empty( $body ) ? true : $body;
}
/**
* Checks if current user can manage the shop
*
* @since 2.8.0
*
* @return bool True if it can, false if not.
*/
public static function can_manage_shop() {
return current_user_can( 'manage_shop_settings' );
}
/**
* Return the EDD Plugin version string.
*
* @since 2.8.0
*
* @return string
*/
public static function version() {
return defined( 'EDD_VERSION' ) ? EDD_VERSION : '0.0.0';
}
/**
* Check if the EDD plugin is active.
*
* @since 2.8.0
*
* @return bool
*/
public static function is_active() {
return class_exists( 'Easy_Digital_Downloads', true ) && function_exists( 'EDD' );
}
/**
* Check if the EDD plugin is connected.
*
* @since 2.8.0
*
* @return bool If it is currently connected.
*/
public static function is_connected() {
// If not active, then it is not connected as well.
if ( ! self::is_active() ) {
return false;
}
// Get any options we have stored.
$option = OMAPI::get_instance()->get_option( 'edd' );
// If the option is empty, then it was never connected or it was disconnected.
if ( empty( $option ) ) {
return false;
}
$shop = isset( $option['shop'] ) ? $option['shop'] : '';
if ( empty( $shop ) ) {
return false;
}
// Check if the saved key and token are not empty.
$key = isset( $option['key'] ) ? $option['key'] : '';
if ( empty( $key ) ) {
return false;
}
// Finally, check if the public_key is still active in user.
$user_id = EDD()->api->get_user( $key );
return ! empty( $user_id );
}
/**
* Initiate our REST routes for EDD if EDD active.
*
* @since 2.13.0
*
* @return void
*/
public function maybe_init_rest_routes() {
if ( self::is_active() ) {
$this->rest = new OMAPI_EasyDigitalDownloads_RestApi( $this );
}
}
/**
* If EDD active, add our EDD cart data for use in Display Rules.
*
* @since 2.13.0
*
* @param array $output Array of data for use in Display Rules.
*
* @return array Array of data for use in Display Rules.
*/
public function maybe_add_edd_data( $output = array() ) {
if ( self::is_active() ) {
$edd = new OMAPI_EasyDigitalDownloads_Output( $this );
$output['edd'] = $edd->display_rules_data();
}
return $output;
}
}
Elementor/ButtonWidget.php 0000644 00000006310 15153673736 0011646 0 ustar 00 <?php
/**
* Elementor Widget class.
*
* @since 2.2.0
*
* @package OMAPI
* @author Justin Sternberg
*/
use Elementor\Plugin;
use Elementor\Widget_Button;
use Elementor\Controls_Manager;
/**
* OptinMonster widget for Elementor page builder.
*
* @since 2.2.0
*/
class OMAPI_Elementor_ButtonWidget extends Widget_Button {
/**
* Register button widget controls.
*
* Adds different input fields to allow the user to change and customize the widget settings.
*
* @since 1.0.0
* @access protected
*/
protected function register_controls() {
parent::register_controls();
$campaigns = OMAPI::get_instance()->blocks->get_campaign_options( true );
$campaigns = ! empty( $campaigns['other'] )
? array_merge( array( '' => esc_html__( 'Select Campaign...', 'optin-monster-api' ) ), $campaigns['other'] )
: array( '' => 'N/A' );
$this->add_control(
'om_button_campaign_id',
array(
'label' => esc_html__( 'Click to Load Popup', 'optin-monster-api' ),
'type' => Controls_Manager::SELECT,
'frontend_available' => true,
'label_block' => true,
'options' => $campaigns,
'default' => '',
),
array(
'position' => array(
'type' => 'control',
'of' => 'link',
),
)
);
$link_control = $this->get_controls( 'link' );
$link_control['condition'] = array(
'om_button_campaign_id' => '',
);
$this->add_control(
'link',
$link_control,
array(
'overwrite' => true,
)
);
}
/**
* Render button widget output in the editor.
*
* Written as a Backbone JavaScript template and used to generate the live preview.
*
* @since 2.9.0
* @access protected
*/
protected function content_template() {
?>
<#
if ( settings.om_button_campaign_id ) {
settings.link = settings.link || {}
settings.link.url = '<?php echo esc_url_raw( OPTINMONSTER_SHAREABLE_LINK ); ?>/c/' + settings.om_button_campaign_id + '/';
settings.link.is_external = 'on';
settings.link.nofollow = false;
settings.link.custom_attributes = 'rel|noopener noreferrer';
}
#>
<?php
return parent::content_template();
}
/**
* Get active settings.
*
* Retrieve the settings from all the active controls.
*
* @since 1.4.0
* @since 2.1.0 Added the `controls` and the `settings` parameters.
* @access public
*
* @param array $settings Optional. Controls settings. Default is null.
* @param array $controls Optional. An array of controls. Default is null.
*
* @return array Active settings.
*/
public function get_active_settings( $settings = null, $controls = null ) {
$settings = parent::get_active_settings( $settings, $controls );
if ( ! empty( $settings['om_button_campaign_id'] ) ) {
$settings['link'] = ! empty( $settings['link'] ) ? $settings['link'] : array();
$settings['link'] = wp_parse_args(
array(
'url' => OPTINMONSTER_SHAREABLE_LINK . '/c/' . sanitize_text_field( $settings['om_button_campaign_id'] ) . '/',
'is_external' => 'on',
'nofollow' => false,
'custom_attributes' => 'rel|noopener noreferrer',
),
$settings['link']
);
}
return $settings;
}
}
Elementor/Widget.php 0000644 00000037646 15153673736 0010472 0 ustar 00 <?php
/**
* Elementor Widget class.
*
* @since 2.2.0
*
* @package OMAPI
* @author Justin Sternberg
*/
use Elementor\Plugin;
use Elementor\Widget_Base;
use Elementor\Controls_Manager;
/**
* OptinMonster widget for Elementor page builder.
*
* @since 2.2.0
*/
class OMAPI_Elementor_Widget extends Widget_Base {
/**
* Widget constructor.
*
* Initializing the widget class.
*
* @see https://code.elementor.com/methods/elementor-widget_base-__construct/
*
* @since 2.2.0
*
* @throws \Exception If arguments are missing when initializing a full widget
* instance.
*
* @param array $data Widget data. Default is an empty array.
* @param array|null $args Optional. Widget default arguments. Default is null.
*/
public function __construct( $data = array(), $args = null ) {
parent::__construct( $data, $args );
// Load the base class object.
$this->base = OMAPI::get_instance();
/*
* Increased priority to get around: https://github.com/elementor/elementor/issues/19709
*/
add_action( 'wp_enqueue_scripts', array( $this, 'register_js' ), 999 );
}
/**
* Register widget JS.
*
* @since 2.16.2
*/
public function register_js() {
$script_id = $this->base->plugin_slug . '-elementor';
wp_register_script(
$script_id,
$this->base->url . 'assets/dist/js/elementor.min.js',
array( 'jquery' ),
$this->base->asset_version(),
true
);
OMAPI_Utils::add_inline_script( $script_id, 'OMAPI', $this->base->blocks->get_data_for_js() );
}
/**
* Get widget name.
*
* Retrieve shortcode widget name.
*
* @see https://code.elementor.com/methods/elementor-controls_stack-get_name/
*
* @since 2.2.0
*
* @return string Widget name.
*/
public function get_name() {
return 'optinmonster';
}
/**
* Get widget title.
*
* Retrieve widget title.
*
* @see https://code.elementor.com/methods/elementor-element_base-get_title/
*
* @since 2.2.0
*
* @return string Widget title.
*/
public function get_title() {
return __( 'OptinMonster', 'optin-monster-api' );
}
/**
* Get widget icon.
*
* Retrieve widget icon.
*
* @see https://code.elementor.com/methods/elementor-widget_base-get_icon/
*
* @since 2.2.0
*
* @return string Widget icon.
*/
public function get_icon() {
return 'icon-optinmonster';
}
/**
* Get widget keywords.
*
* Retrieve the list of keywords the widget belongs to.
*
* @see https://code.elementor.com/methods/elementor-widget_base-get_keywords/
*
* @since 2.2.0
*
* @return array Widget keywords.
*/
public function get_keywords() {
return array(
'popup',
'form',
'forms',
'campaign',
'email',
'conversion',
'contact form',
);
}
/**
* Get widget categories.
*
* @see https://code.elementor.com/methods/elementor-widget_base-get_categories/
*
* @since 2.2.0
*
* @return array Widget categories.
*/
public function get_categories() {
return array(
'basic',
);
}
/**
* Handle registering elementor editor JS assets.
*
* @see https://code.elementor.com/methods/elementor-element_base-get_script_depends/
*
* @since 2.2.0
*
* @return array
*/
public function get_script_depends() {
return Plugin::instance()->preview->is_preview_mode()
? array( $this->base->plugin_slug . '-elementor' )
: array();
}
/**
* Handle registering elementor editor CSS assets.
*
* @see https://code.elementor.com/methods/elementor-element_base-get_style_depends/
*
* @since 2.2.0
*
* @return array
*/
public function get_style_depends() {
$css_handle = $this->base->plugin_slug . '-elementor-frontend';
wp_register_style(
$css_handle,
$this->base->url . 'assets/dist/css/elementor-frontend.min.css',
array(),
$this->base->asset_version()
);
return array( $css_handle );
}
/**
* Register widget controls.
*
* Adds different input fields to allow the user to change and customize the widget settings.
*
* @see https://code.elementor.com/methods/elementor-controls_stack-_register_controls/
*
* @since 2.2.0
*/
protected function register_controls() {
$this->start_controls_section(
'section_om_campaign',
array(
'label' => esc_html__( 'OptinMonster Campaign', 'optin-monster-api' ),
'tab' => Controls_Manager::TAB_CONTENT,
)
);
if ( ! $this->base->blocks->has_sites() ) {
$this->no_sites_controls();
} elseif ( ! $this->has_inline_campaigns() ) {
$this->no_campaign_controls();
} else {
$this->campaign_controls();
}
$this->end_controls_section();
}
/**
* Register no-site controls.
*
* @since 2.2.0
*/
protected function no_sites_controls() {
$i18n = $this->base->blocks->get_data_for_js( 'i18n' );
$this->add_control(
'add_om_campaign_notice',
array(
'show_label' => false,
'type' => Controls_Manager::RAW_HTML,
'raw' => '
<p class="om-elementor-editor-no_sites-help">
<strong>' . esc_html__( 'You Have Not Connected to OptinMonster', 'optin-monster-api' ) . '</strong>
<br>
' . esc_html__( 'Please create a Free Account or Connect an Existing Account', 'optin-monster-api' ) . '
</p>
',
'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
)
);
$this->add_control(
'om_create_account',
array(
'show_label' => false,
'label_block' => false,
'type' => Controls_Manager::BUTTON,
'button_type' => 'default',
'text' => $i18n['no_sites_button_create_account'],
'event' => 'elementorOMAPICreateAccount',
)
);
$this->add_control(
'om_connect_account',
array(
'show_label' => false,
'label_block' => false,
'type' => Controls_Manager::BUTTON,
'button_type' => 'default',
'separator' => 'after',
'text' => $i18n['no_sites_button_connect_account'],
'event' => 'elementorOMAPIConnectAccount',
)
);
}
/**
* Register no-campaign controls.
*
* @since 2.2.0
*/
protected function no_campaign_controls() {
$this->add_control(
'add_om_campaign_notice',
array(
'show_label' => false,
'type' => Controls_Manager::RAW_HTML,
'raw' => wp_kses(
'<b>' . __( 'No inline campaigns available!', 'optin-monster-api' ) . '</b>',
array(
'b' => array(),
)
),
'content_classes' => 'elementor-panel-alert elementor-panel-alert-info',
)
);
$this->add_control(
'add_campaign_btn',
array(
'show_label' => false,
'label_block' => false,
'type' => Controls_Manager::BUTTON,
'button_type' => 'default',
'separator' => 'after',
'text' => '<b>+</b>' . esc_html__( 'Create New Inline Campaign', 'optin-monster-api' ),
'event' => 'elementorOMAPIAddInlineBtnClick',
)
);
}
/**
* Register campaign controls.
*
* @since 2.2.0
*/
protected function campaign_controls() {
$campaigns = $this->base->blocks->get_campaign_options( true );
$campaigns = array_merge( array( '' => esc_html__( 'Select Campaign...', 'optin-monster-api' ) ), $campaigns['inline'] );
$this->add_control(
'campaign_id',
array(
'label' => esc_html__( 'Select Campaign', 'optin-monster-api' ),
'type' => Controls_Manager::SELECT,
'frontend_available' => true,
'label_block' => true,
'options' => $campaigns,
'default' => '',
)
);
$this->add_control(
'followrules',
array(
'label' => esc_html__( 'Use Output Settings', 'optin-monster-api' ),
'type' => Controls_Manager::SWITCHER,
'frontend_available' => true,
'label_on' => esc_html__( 'Yes', 'optin-monster-api' ),
'label_off' => esc_html__( 'No', 'optin-monster-api' ),
'return_value' => 'yes',
'condition' => array(
'campaign_id!' => '0',
),
)
);
$this->add_control(
'edit_campaign',
array(
'show_label' => false,
'type' => Controls_Manager::RAW_HTML,
'raw' => sprintf(
wp_kses(
/* translators: %s - OptinMonster edit link. */
__( 'Need to make changes? <a href="%1s" class="skip-om-trigger" target="_blank" rel="noopener">Edit the selected campaign.</a>', 'optin-monster-api' ),
array(
'a' => array(
'href' => array(),
'class' => array(),
'target' => array(),
'rel' => array(),
),
)
),
esc_url( $this->base->blocks->get_data_for_js( 'editUrl' ) )
),
'condition' => array(
'campaign_id!' => '0',
),
)
);
$this->add_control(
'add_campaign_btn',
array(
'show_label' => false,
'label_block' => false,
'type' => Controls_Manager::BUTTON,
'button_type' => 'default',
'separator' => 'before',
'text' => '<b>+</b>' . esc_html__( 'Create New Inline Campaign', 'optin-monster-api' ),
'event' => 'elementorOMAPIAddInlineBtnClick',
)
);
}
/**
* Render widget output.
*
* @see https://code.elementor.com/methods/elementor-element_base-render/
*
* @since 2.2.0
*/
protected function render() {
if ( Plugin::instance()->editor->is_edit_mode() ) {
$this->render_edit_mode();
} else {
$this->render_frontend();
}
}
/**
* Get the editing-block render format.
*
* @since 2.2.0
*
* @return string Format html string.
*/
protected function get_render_format() {
return '
<div class="om-elementor-editor" data-slug="%1$s">
%2$s
<div class="om-elementor-holder">
%3$s
</div>
<div class="om-errors" style="display:none;">
<strong>' . esc_html__( 'OptinMonster Campaign Error:', 'optin-monster-api' ) . '</strong><br><span class="om-error-description"></span>
</div>
</div>
';
}
/**
* Get the campaign-selector html.
*
* @since 2.2.0
*
* @param bool $icon Whether to include Archie icon.
*
* @return string Html string.
*/
protected function get_campaign_select_html( $icon = true ) {
$data = $this->base->blocks->get_data_for_js();
if ( ! $this->base->blocks->has_sites() ) {
$guts = '
<div class="om-elementor-editor-no_sites">
' . ( $icon ? '<img src="' . $this->base->url . 'assets/css/images/icons/archie-color-icon.svg">' : '' ) . '
<p class="om-elementor-editor-no_sites-help">
<strong>' . esc_html__( 'You Have Not Connected to OptinMonster', 'optin-monster-api' ) . '</strong>
<br>
' . esc_html__( 'Please create a Free Account or Connect an Existing Account', 'optin-monster-api' ) . '
</p>
<p class="om-elementor-editor-no_sites-button">
<a class="om-help-button skip-om-trigger components-button is-primary" href="' . $data['wizardUri'] . '" target="_blank" rel="noopener">
' . $data['i18n']['no_sites_button_create_account'] . '
</a>
<span>or</span>
<a class="om-help-button skip-om-trigger components-button is-secondary" href="' . $data['settingsUri'] . '" target="_blank" rel="noopener">
' . $data['i18n']['no_sites_button_connect_account'] . '
</a>
</p>
</div>
';
} elseif ( ! $this->has_inline_campaigns() ) {
$guts = '
<div class="om-elementor-editor-no_campaigns">
' . ( $icon ? '<img src="' . $this->base->url . 'assets/css/images/icons/archie-color-icon.svg">' : '' ) . '
<p class="om-elementor-editor-no_campaigns-help">
<strong>' . $data['i18n']['no_campaigns'] . '</strong>
<br>
' . $data['i18n']['no_campaigns_help'] . '
</p>
<p class="om-elementor-editor-no_campaigns-button">
<a class="om-help-button skip-om-trigger components-button om-green omapi-link-arrow-after" href="' . $data['templatesUri'] . '&type=inline" target="_blank" rel="noopener">
' . $data['i18n']['create_inline_campaign'] . '
</a>
</p>
<p class="om-elementor-editor-no_campaigns-button-help">
<a class="om-help-button skip-om-trigger components-button is-secondary" href="https://optinmonster.com/docs/getting-started-optinmonster-wordpress-checklist/?utm_source=plugin&utm_medium=link&utm_campaign=gutenbergblock" target="_blank" rel="noopener">
' . esc_html__( 'Need some help? Check out our comprehensive guide.', 'optin-monster-api' ) . '
</a>
</p>
</div>
';
} else {
$guts = '
<div class="om-elementor-editor-select-label">
' . ( $icon ? '<img src="' . $this->base->url . 'assets/css/images/icons/archie-icon.svg">' : '' ) . '
OptinMonster
</div>
<p>' . esc_html__( 'Select and display your email marketing form or smart call-to-action campaigns from OptinMonster.', 'optin-monster-api' ) . '</p>
<div class="om-elementor-editor-select-controls">
<select></select>
<div class="om-elementor-editor-select-controls-button">
<a class="om-help-button skip-om-trigger components-button is-secondary" href="' . $data['templatesUri'] . '&type=inline" target="_blank" rel="noopener">
' . esc_html__( 'Create a New Inline Campaign', 'optin-monster-api' ) . '
</a>
<a class="om-help-button skip-om-trigger components-button is-secondary" href="' . $data['templatesUri'] . '&type=popup" target="_blank" rel="noopener">
' . esc_html__( 'Create a New Popup Campaign', 'optin-monster-api' ) . '
</a>
</div>
</div>
';
}
return '<div class="om-elementor-editor-select">' . $guts . '</div>';
}
/**
* Get the campaign holder html.
*
* @since 2.2.0
*
* @param string $campaign_id Campaign Id string.
*
* @return string Html.
*/
public function get_campaign_holder( $campaign_id ) {
return sprintf(
'
<div id="om-%1$s-holder">
<div class="om-elementor-editor-holder-loading om-elementor-editor-select-label">
<img src="' . $this->base->url . 'assets/css/images/icons/archie-icon.svg">
' . esc_html__( 'Loading Campaign...', 'optin-monster-api' ) . '
</div>
</div>
',
$campaign_id
);
}
/**
* Render widget output in edit mode.
*
* @since 2.2.0
*/
protected function render_edit_mode() {
$campaign_id = esc_attr( $this->get_settings_for_display( 'campaign_id' ) );
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
printf(
$this->get_render_format(),
$campaign_id,
! $campaign_id ? $this->get_campaign_select_html() : '',
$campaign_id ? $this->get_campaign_holder( $campaign_id ) : ''
);
// phpcs:enable
}
/**
* This method is used by the parent methods to output the backbone/underscore template.
*
* @see https://code.elementor.com/methods/elementor-element_base-_content_template/
*
* @since 2.2.0
*/
protected function content_template() {
// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
printf(
$this->get_render_format(),
'{{{ settings.campaign_id.replace(/[^a-zA-Z0-9]/g, "") }}}',
'<# if ( ! settings.campaign_id ) { #>' . $this->get_campaign_select_html() . '<# } #>',
'<# if ( settings.campaign_id ) { #>' . $this->get_campaign_holder( '{{{ settings.campaign_id.replace(/[^a-zA-Z0-9]/g, "") }}}' ) . '<# } #>'
);
// phpcs:enable
}
/**
* Render widget output on the frontend.
*
* @since 2.2.0
*/
protected function render_frontend() {
echo do_shortcode( $this->get_shortcode_output() );
}
/**
* Render widget as plain content.
*
* @see https://code.elementor.com/methods/elementor-widget_base-render_plain_content/
*
* @since 2.2.0
*/
public function render_plain_content() {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_shortcode_output();
}
/**
* Render shortcode.
*
* @since 2.2.0
*
* @return string Shortcode
*/
protected function get_shortcode_output() {
return sprintf(
'[optin-monster slug="%1$s"%2$s]',
esc_attr( $this->get_settings_for_display( 'campaign_id' ) ),
$this->get_settings_for_display( 'followrules' ) === 'yes' ? ' followrules="true"' : ''
);
}
/**
* Does the user have any inline campaigns created?
*
* @since 2.2.0
*
* @return boolean
*/
protected function has_inline_campaigns() {
$campaigns = $this->base->blocks->get_campaign_options();
return ! empty( $campaigns['inline'] );
}
}
Elementor.php 0000644 00000012565 15153673736 0007240 0 ustar 00 <?php
/**
* Elementor class.
*
* @since 2.2.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The Elementor class.
*
* @since 2.2.0
*/
class OMAPI_Elementor {
/**
* Holds the class object.
*
* @since 1.7.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.7.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.7.0
*
* @var object
*/
public $base;
/**
* The minimum Elementor version required.
*
* @since 2.11.2
*
* @var string
*/
const MINIMUM_VERSION = '3.1.0';
/**
* Primary class constructor.
*
* @since 1.7.0
*/
public function __construct() {
// Set our object.
$this->set();
// Skip if Elementor is not available.
if ( ! class_exists( '\Elementor\Plugin' ) ) {
return;
}
// Check if Elementor is the minimum version.
if ( ! self::is_minimum_version() ) {
return;
}
add_action( 'elementor/editor/after_enqueue_styles', array( $this, 'editor_assets' ) );
add_action( 'elementor/widgets/widgets_registered', array( $this, 'register_widget' ), 999 );
add_action( 'optin_monster_should_set_campaigns_as_preview', array( $this, 'maybe_set_campaigns_as_preview' ) );
add_action( 'optin_monster_display_media_button', array( $this, 'maybe_show_campaign_button' ), 10, 2 );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.7.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Load an integration css in the elementor document.
*
* @since 2.2.0
*/
public function editor_assets() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( empty( $_GET['action'] ) || 'elementor' !== $_GET['action'] ) {
return;
}
$css_handle = $this->base->plugin_slug . '-elementor-admin';
wp_enqueue_style(
$css_handle,
$this->base->url . 'assets/dist/css/elementor-admin.min.css',
array(),
$this->base->asset_version()
);
$this->maybe_enqueue_dark_mode( $css_handle );
}
/**
* Handle enqueueing the dark-mode css. Will be conditionally displayed based on the UI setting.
*
* We have to do this until Elementor has better handling for dark-mode via a body class
*
* @see https://github.com/elementor/elementor/issues/13419
*
* @since 2.2.0
*
* @param string $css_handle Non-dark mode handle.
*
* @return bool|string
*/
protected function maybe_enqueue_dark_mode( $css_handle ) {
$ui_theme = \Elementor\Core\Settings\Manager::get_settings_managers( 'editorPreferences' )->get_model()->get_settings( 'ui_theme' );
if ( 'light' === $ui_theme ) {
return false;
}
$ui_theme_media_queries = 'auto' === $ui_theme
? '(prefers-color-scheme: dark)'
: 'all';
wp_enqueue_style(
$css_handle . '-dark-mode',
$this->base->url . 'assets/dist/css/elementor-admin-dark.min.css',
array( $css_handle ),
$this->base->asset_version(),
$ui_theme_media_queries
);
}
/**
* Return the Elementor versions string.
*
* @since 2.11.2
*
* @return string
*/
public static function version() {
return defined( 'ELEMENTOR_VERSION' ) ? ELEMENTOR_VERSION : '0.0.0';
}
/**
* Determines if the passed version string passes the operator compare
* against the currently installed version of Elementor.
*
* Defaults to checking if the current Elementor version is greater than
* the passed version.
*
* @since 2.11.2
*
* @param string $version The version to check.
* @param string $operator The operator to use for comparison.
*
* @return string
*/
public static function version_compare( $version = '', $operator = '>=' ) {
return version_compare( self::version(), $version, $operator );
}
/**
* Determines if the current Elementor version meets the minimum version
* requirement.
*
* @since 2.11.2
*
* @return boolean
*/
public static function is_minimum_version() {
return self::version_compare( self::MINIMUM_VERSION );
}
/**
* Register WPForms Widget.
*
* @since 2.2.0
*
* @param \Elementor\Widgets_Manager $widget_manager Elementor widget manager object.
*/
public function register_widget( $widget_manager ) {
$widget_manager->register_widget_type( new OMAPI_Elementor_Widget() );
// We need to override the button widget with our extended version.
$widget_manager->register_widget_type( new OMAPI_Elementor_ButtonWidget() );
}
/**
* Set the preview flag if in the elementor preview mode.
*
* @since 2.2.0
*
* @param bool $is_preview Whether we're currently in preview mode.
*
* @return bool Whether we're in preview mode.
*/
public function maybe_set_campaigns_as_preview( $is_preview ) {
if ( ! $is_preview ) {
$is_preview = \Elementor\Plugin::instance()->preview->is_preview_mode();
}
return $is_preview;
}
/**
* Show the editor campaign media button if in the elementor editor.
*
* @since 2.3.0
*
* @param bool $show Whether button will show.
*
* @return bool Whether button will show.
*/
public function maybe_show_campaign_button( $show, $editor_id ) {
$edit_mode = \Elementor\Plugin::instance()->editor->is_edit_mode();
if ( $edit_mode ) {
$show = true;
add_action( 'elementor/editor/footer', array( $this->base->classicEditor, 'shortcode_modal' ) );
}
return $show;
}
}
Inserter.php 0000644 00000026452 15153673736 0007101 0 ustar 00 <?php
/**
* Content Inserter class.
*
* @since 2.0.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Content Inserter class.
*
* @since 2.0.0
*/
class OMAPI_Inserter {
/**
* Content to insert.
*
* @since 2.0.0
*
* @var string
*/
protected $to_insert = '';
/**
* Original content.
*
* @since 2.0.0
*
* @var string
*/
protected $content = '';
/**
* Any tags which we should not insert content in.
* The order is important ("a" tag should be after any other tags starting with "a") in order
* to avoid false positives (where "a" tag is "found", but it's really an "abbr" tag).
*
* @since 2.0.0
*
* @var string
*/
const INSERT_AFTER_TAGS = 'address,abbr,acronym,area,audio,a,bdo,big,blockquote,button,b,caption,cite,code,col,colgroup,del,dfn,dl,dt,em,figure,figcaption,font,h1,h2,h3,h4,h5,h6,hgroup,ins,i,kbd,label,legend,map,mark,menu,pre,samp,small,strike,strong,sub,sup,s,table,tbody,textarea,tfoot,thead,title,track,tt,tr,ul,u,ol,var,video';
/**
* Constructor.
*
* @since 2.0.0
*
* @param string $content The original content.
* @param string $to_insert The content to insert into the original content.
*/
public function __construct( $content, $to_insert ) {
$this->content = $content;
$this->to_insert = $to_insert;
}
/**
* Prepend the original content with the content to insert.
*
* @since 2.0.0
*
* @return string The combined content.
*/
public function prepend() {
return $this->to_insert . $this->content;
}
/**
* Append the original content with the content to insert.
*
* @since 2.0.0
*
* @return string The combined content.
*/
public function append() {
return $this->content . $this->to_insert;
}
/**
* Inserts the insert-content after X paragraphs in the original content.
*
* @since 2.0.0
*
* @param int $paragraph_number The paragraph number to insert after.
*
* @return string The combined content.
*/
public function after_paragraph( $paragraph_number ) {
// If "0", then prepend.
if ( empty( $paragraph_number ) ) {
return $this->prepend();
}
$closing_p = '</p>';
$paragraphs = explode( $closing_p, $this->content );
$count = count( $paragraphs );
// If the number of paragraphs in the content is less than
// the number we asked for, just append it to the end.
if ( $count < $paragraph_number ) {
return $this->append();
}
$valid_paragraphs = 0;
$invalid_tag = '';
$appended = false;
foreach ( $paragraphs as $index => $paragraph ) {
// Only add closing tag to non-empty paragraphs.
if ( trim( $paragraph ) ) {
// Adding closing markup now, rather than at implode, means insertion
// is outside of the paragraph markup, and not just inside of it.
$paragraphs[ $index ] .= $closing_p;
}
// If it has already appended, it doesn't need to keep checking.
// It needs to keep the loop to close the paragraph tag.
if ( $appended ) {
continue;
}
// If it has an invalid tag from the last index,
// search if this index has the closing tag.
// If it doesn't have, it need to continue to the next index.
if ( ! empty( $invalid_tag ) && self::find_tag( $invalid_tag, $paragraphs[ $index ] ) ) {
// Found it, let's clean it.
$invalid_tag = '';
}
// Check if it has any after_tags inside this block.
$invalid_tag = self::find_invalid_word_tag( $paragraphs[ $index ] );
if ( ! empty( $invalid_tag ) ) {
// If so, then it must continue counting to the next paragraph.
continue;
}
// This is a valid paragraph.
$valid_paragraphs++;
// If it has enough valid paragraphs, it can continue
if ( $valid_paragraphs === $paragraph_number ) {
// If it doesn't have an invalid tag inside this paragraph,
// it can append after this paragraph.
$paragraphs[ $index ] .= $this->to_insert;
$appended = true;
}
}
// If it appended, it can output all paragraphs.
if ( $appended ) {
return implode( '', $paragraphs );
}
// If it didn't append, then it didn't have enough valid paragraphs,
// so we'll append it to the end.
return $this->append();
}
/**
* Inserts the insert-content after X words in the original content.
*
* @since 2.0.0
*
* @param int $word_number The word number to insert after.
*
* @return string The combined content.
*/
public function after_words( $word_number ) {
// If "0", then prepend.
if ( empty( $word_number ) ) {
return $this->prepend();
}
// The following splitting into words code is copied from the wp_trim_words function.
$rawtext = wp_strip_all_tags( $this->content );
/*
* translators: If your word count is based on single characters (e.g. East Asian characters),
* enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
* Do not translate into your own language.
*/
if ( strpos( _x( 'words', 'Word count type. Do not translate!', 'optin-monster-api' ), 'characters' ) === 0 && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
$rawtext = trim( preg_replace( "/[\n\r\t ]+/", ' ', $rawtext ), ' ' );
preg_match_all( '/./u', $rawtext, $words_array );
$words_array = array_slice( $words_array[0], 0, $word_number + 1 );
} else {
$words_array = preg_split( "/[\n\r\t ]+/", $rawtext, $word_number + 1, PREG_SPLIT_NO_EMPTY );
}
// If the number of words in the content is less than
// the number we asked for, just append it to the end.
if ( count( $words_array ) <= $word_number ) {
return $this->append();
}
// Now we need to clean up the words, removing punctuation,
// so our chances of matching are greater.
foreach ( $words_array as $index => $word ) {
$words_array[ $index ] = preg_replace( '~[^\w\s]~', '', $word );
}
$after_word = $words_array[ $word_number - 1 ];
$number_occurrences = 0;
$rest = array_pop( $words_array );
foreach ( $words_array as $word ) {
if ( ! empty( $after_word ) && false !== strpos( $word, $after_word ) ) {
$number_occurrences++;
}
}
$to_replace = $this->content;
// We need to loop through the number of occurrences...
while ( $number_occurrences-- ) {
// Then find the word in the content to replace...
$pos = strpos( $to_replace, $after_word ) + strlen( $after_word );
// And split that content where the word was found...
$to_replace = substr( $to_replace, $pos );
// And keep doing that until we've reached our final occurrence.
}
// Ok, no we know where we want to insert, but we can't insert inside any
// of our self::INSERT_AFTER_TAGS tags, so we need to insert _after_ them.
$to_replace = self::after_tags( $to_replace );
// Now insert into the content.
$updated_content = str_replace( $to_replace, $this->to_insert . $to_replace, $this->content );
return $updated_content;
}
/**
* Takes given content and returns first acceptable content outside of any
* self::INSERT_AFTER_TAGS tags.
*
* @since 2.0.0
*
* @param string $content Content to replace/find.
*
* @return string Updated content.
*/
protected static function after_tags( $content ) {
foreach ( explode( ',', self::INSERT_AFTER_TAGS ) as $tag ) {
$positions = self::find_tag( $tag, $content );
if ( ! $positions ) {
continue;
}
// Ok... we found a tag that we should scoot behind.
return substr( $content, $positions['closing'] + strlen( $positions['closing_tag'] ) );
}
return $content;
}
/**
* Get the opening/closing positions of given tag.
*
* @since 2.10.0
*
* @param string $tag The html tag.
* @param string $content The html content to search.
*
* @return array Array of tag position data.
*/
public static function get_tag_positions( $tag, $content ) {
if ( self::is_void_element_tag( $tag ) ) {
$result = self::find_void_element_tag( $tag, $content );
return array(
'opening' => $result['position'],
'closing' => $result['position'],
'opening_tag' => $result['tag'],
'closing_tag' => $result['tag'],
);
}
$opening_tag = '<' . $tag . '>';
$opening_pos = stripos( $content, $opening_tag );
if ( false === $opening_pos ) {
$opening_tag = '<' . $tag . ' ';
$opening_pos = stripos( $content, $opening_tag );
}
$closing_tag = '</' . $tag . '>';
$closing_pos = stripos( $content, $closing_tag );
return array(
'opening' => $opening_pos,
'closing' => $closing_pos,
'opening_tag' => $opening_tag,
'closing_tag' => $closing_tag,
);
}
/**
* Find the opening/closing positions of given tag, if found.
*
* @since 2.10.0
*
* @param string $tag The html tag.
* @param string $content The html content to search.
*
* @return array|bool Array of tag position data, if found.
*/
public static function find_tag( $tag, $content ) {
$positions = self::get_tag_positions( $tag, $content );
// Not found, move along.
if ( false === $positions['closing'] ) {
return false;
}
// If we found an opening tag but it comes before the closing,
// then this is not the one we were looking at.
if (
false !== $positions['opening']
&& $positions['opening'] < $positions['closing']
) {
return false;
}
return $positions;
}
/**
* Search for invalid tags within given content.
*
* @since 2.10.0
*
* @param string $content html content to search.
*
* @return string Invalid tag, if found.
*/
public static function find_invalid_word_tag( $content ) {
// Check if it has any after_tags inside this block.
// If so, then it must continue counting to the next paragraph.
foreach ( explode( ',', self::INSERT_AFTER_TAGS ) as $tag ) {
$positions = self::get_tag_positions( $tag, $content );
// Not found, continue to the next
if ( false === $positions['opening'] ) {
continue;
}
// It has it, so it can ignore since it opens and closes inside the paragraph.
// It also needs to check if the closing comes after the opening.
if ( false !== $positions['closing'] && $positions['opening'] < $positions['closing'] ) {
continue;
}
// It doesn't have a closing tag, so this paragraph lives inside an invalid tag.
return $tag;
}
return '';
}
/**
* Find the given void element tag position/occurrence.
*
* @since 2.10.0
*
* @param string $tag The void element tag name.
* @param string $content html content to search.
*
* @return array Array of found element occurrence and string position.
*/
public static function find_void_element_tag( $tag, $content ) {
preg_match_all( '~<' . $tag . '.*?\/?>~i', $content, $matches );
if ( ! empty( $matches[0][0] ) ) {
return array(
'tag' => $matches[0][0],
'position' => strpos( $content, $matches[0][0] ),
);
}
return array(
'tag' => '<' . $tag . '/>',
'position' => false,
);
}
/**
* Check if given tag is a void element tag.
*
* @since 2.10.0
*
* @param string $tag HTML tag name.
*
* @return boolean
*/
public static function is_void_element_tag( $tag ) {
return in_array(
strtolower( $tag ),
array(
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr',
'command',
'keygen',
'menuitem',
),
true
);
}
}
InstallSkin.php 0000644 00000002552 15153673736 0007534 0 ustar 00 <?php
/**
* Install Skin class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WordPress class extended for on-the-fly addon installations.
*/
class OMAPI_InstallSkin extends WP_Upgrader_Skin {
/**
* Empty out the header of its HTML content and only check to see if it has
* been performed or not.
*
* @since 1.9.10
*/
public function header() {}
/**
* Empty out the footer of its HTML contents.
*
* @since 1.9.10
*/
public function footer() {}
/**
* Instead of outputting HTML for errors, json_encode the errors and send them
* back to the Ajax script for processing.
*
* @since 1.9.10
*
* @param array $errors Array of errors with the install process.
*/
public function error( $errors ) {
if ( ! empty( $errors ) ) {
wp_send_json_error( $errors );
}
}
/**
* Empty out the feedback method to prevent outputting HTML strings as the install
* is progressing.
*
* @since 1.9.10
*
* @param string $string The feedback string.
*/
public function feedback( $string, ...$args ) {}
/**
* Empty out JavaScript output that calls function to decrement the update counts.
*
* @since 1.9.10
*
* @param string $type Type of update count to decrement.
*/
public function decrement_update_count( $type ) {}
}
InstallSkinCompat.php 0000644 00000002563 15153673736 0010702 0 ustar 00 <?php
/**
* Install Skin Compatibility class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WordPress class extended for on-the-fly addon installations.
*/
class OMAPI_InstallSkinCompat extends WP_Upgrader_Skin {
/**
* Empty out the header of its HTML content and only check to see if it has
* been performed or not.
*
* @since 1.9.10
*/
public function header() {}
/**
* Empty out the footer of its HTML contents.
*
* @since 1.9.10
*/
public function footer() {}
/**
* Instead of outputting HTML for errors, json_encode the errors and send them
* back to the Ajax script for processing.
*
* @since 1.9.10
*
* @param array $errors Array of errors with the install process.
*/
public function error( $errors ) {
if ( ! empty( $errors ) ) {
wp_send_json_error( $errors );
}
}
/**
* Empty out the feedback method to prevent outputting HTML strings as the install
* is progressing.
*
* @since 1.9.10
*
* @param string $string The feedback string.
*/
public function feedback( $string ) {}
/**
* Empty out JavaScript output that calls function to decrement the update counts.
*
* @since 1.9.10
*
* @param string $type Type of update count to decrement.
*/
public function decrement_update_count( $type ) {}
}
Integrations/Base.php 0000644 00000003433 15153673736 0010620 0 ustar 00 <?php
/**
* Base Plugin Integration Class, extend this if implementing a plugin integration class.
*
* @since 2.13.0
*
* @package OMAPI
* @author Gabriel Oliveira and Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Base Plugin Integration class.
*
* @since 2.13.0
*/
abstract class OMAPI_Integrations_Base {
/**
* Holds the class object.
*
* @since 2.13.0
*
* @var static
*/
public static $instance;
/**
* The Base OMAPI Object
*
* @since 2.13.0
*
* @var OMAPI
*/
protected $base;
/**
* The minimum Plugin version required.
*
* @since 2.13.0
*
* @var string
*/
const MINIMUM_VERSION = '0.0.0';
/**
* Build our object.
*
* @since 2.13.0
*/
public function __construct() {
$this->base = OMAPI::get_instance();
static::$instance = $this;
}
/**
* Return the plugin version string.
*
* @since 2.13.0
*
* @return string
*/
abstract public static function version();
/**
* Determines if the passed version string passes the operator compare
* against the currently installed version of plugin.
*
* Defaults to checking if the current plugin version is greater than
* the passed version.
*
* @since 2.13.0
*
* @param string $version The version to check.
* @param string $operator The operator to use for comparison.
*
* @return string
*/
public static function version_compare( $version = '', $operator = '>=' ) {
return version_compare( static::version(), $version, $operator );
}
/**
* Determines if the current WooCommerce version meets the minimum version
* requirement.
*
* @since 2.13.0
*
* @return boolean
*/
public static function is_minimum_version() {
return static::version_compare( static::MINIMUM_VERSION );
}
}
MailPoet.php 0000644 00000032560 15153673736 0007015 0 ustar 00 <?php
/**
* Mailpoet integration class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Mailpoet integration class.
*
* @since 1.9.10
*/
class OMAPI_MailPoet {
/**
* Check to see if Mailpoet is active.
*
* @since 1.2.3
*
* @return bool
*/
public static function is_active() {
return class_exists( 'WYSIJA_object' ) || class_exists( 'MailPoet\\API\\API' );
}
/**
* Returns the available MailPoet lists.
*
* @since 1.0.0
*
* @return array An array of MailPoet lists.
*/
public function get_lists() {
// Prepare variables.
$mailpoet = null;
$lists = array();
$ret = array();
$list_id_key = 'id';
// Get lists. Check for MailPoet 3 first. Default to legacy.
if ( class_exists( '\\MailPoet\\Config\\Initializer' ) ) {
/**
* Get the MailPoet lists.
*
* @see https://github.com/mailpoet/mailpoet/blob/c8fa9f007fd6fa39c17cd45e919566726a0578d7/doc/api_methods/GetLists.md
*/
$lists = \MailPoet\API\API::MP( 'v1' )->getLists();
} else {
$mailpoet = WYSIJA::get( 'list', 'model' );
$lists = $mailpoet->get( array( 'name', 'list_id' ), array( 'is_enabled' => 1 ) );
$list_id_key = 'list_id';
}
// Add default option.
$ret[] = array(
'name' => esc_html__( 'Select your MailPoet list...', 'optin-monster-api' ),
'value' => 'none',
);
// Loop through the list data and add to array.
foreach ( (array) $lists as $list ) {
$ret[] = array(
'name' => $list['name'],
'value' => $list[ $list_id_key ],
);
}
/**
* Filters the MailPoet lists.
*
* @param array $ret The MailPoet lists array.
* @param array $lists The raw MailPoet lists array. Format differs by plugin verison.
* @param WYSIJA|null $mailpoet The MailPoet object if using legacy. Null otherwise.
*/
return apply_filters( 'optin_monster_api_mailpoet_lists', $ret, $lists, $mailpoet );
}
/**
* Returns the available MailPoet subscriber fields.
*
* @see https://github.com/mailpoet/mailpoet/blob/c8fa9f007fd6fa39c17cd45e919566726a0578d7/doc/api_methods/GetSubscriberFields.md
*
* @since 1.9.8
*
* @return array An array of MailPoet subscriber fields.
*/
public function get_subscriber_fields() {
// Get lists. Check for MailPoet 3.
return class_exists( '\\MailPoet\\Config\\Initializer' )
? \MailPoet\API\API::MP( 'v1' )->getSubscriberFields()
: array();
}
/**
* Returns the available MailPoet custom fields formatted for dropdown.
*
* @since 1.9.8
*
* @return array An array of MailPoet custom fields.
*/
public function get_custom_fields_dropdown_values() {
// Prepare variables.
$ret = array();
$default_fields = array( 'email', 'first_name', 'last_name' );
// Add default option.
$ret[] = array(
'name' => esc_html__( 'Select the custom field...', 'optin-monster-api' ),
'value' => '',
);
$subscriber_fields = $this->get_subscriber_fields();
// Loop through the list data and add to array.
foreach ( (array) $subscriber_fields as $subscriber_field ) {
if ( in_array( $subscriber_field['id'], $default_fields, true ) ) {
continue;
}
$ret[] = array(
'name' => $subscriber_field['name'],
'value' => $subscriber_field['id'],
);
}
/**
* Filters the MailPoet custom fields.
*
* @param array. $ret The MailPoet custom fields array, except
* first name, last name and email
* @param array. $subscriber_fields The raw MailPoet subscriber fields array.
* Format differs by plugin verson.
*/
return apply_filters( 'optin_monster_api_mailpoet_custom_fields', $ret, $subscriber_fields );
}
/**
* Opts the user into MailPoet.
*
* @since 1.0.0
*/
public function handle_ajax_call() {
/*
* Check the nonce is correct first.
*
* As this is a front end form to store the visitor's data in a mailing
* list no capability check is required.
*/
check_ajax_referer( 'omapi', 'nonce' );
// Prepare variables.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$optin_data = ! empty( $_REQUEST['optinData'] ) && is_array( $_REQUEST['optinData'] ) ? wp_unslash( $_REQUEST['optinData'] ) : array();
$data = array_merge( $_REQUEST, $optin_data );
unset( $data['optinData'] );
$optin = OMAPI::get_instance()->get_optin_by_slug( stripslashes( $data['optin'] ) );
$list = get_post_meta( $optin->ID, '_omapi_mailpoet_list', true );
$user = $this->prepare_subscriber_data( $optin, $data );
// Store the data.
$data = array(
'user' => $user,
'user_list' => array( 'list_ids' => array( $list ) ),
);
/**
* Filters the data before saving to MailPoet.
*
* @param array $data The data to save.
* @param array $request Deprecated. Use $_REQUEST instead.
* @param string $list The list to add the lead.
*/
$data = apply_filters( 'optin_monster_pre_optin_mailpoet', $data, array(), $list );
// Save the subscriber. Check for MailPoet 3 first. Default to legacy.
if ( class_exists( 'MailPoet\\API\\API' ) ) {
// Customize the lead data for MailPoet 3.
if ( isset( $user['firstname'] ) ) {
$user['first_name'] = $user['firstname'];
unset( $user['firstname'] );
}
if ( isset( $user['lastname'] ) ) {
$user['last_name'] = $user['lastname'];
unset( $user['lastname'] );
}
try {
/**
* Try to find the subscriber by email.
*
* @see https://github.com/mailpoet/mailpoet/blob/c8fa9f007fd6fa39c17cd45e919566726a0578d7/doc/api_methods/GetSubscriber.md
*/
$subscriber = \MailPoet\API\API::MP( 'v1' )->getSubscriber( $user['email'] );
} catch ( Exception $e ) {
$subscriber = false;
}
try {
if ( $subscriber ) {
/**
* Subscribe the existing subscriber to the list.
*
* @see https://github.com/mailpoet/mailpoet/blob/c8fa9f007fd6fa39c17cd45e919566726a0578d7/doc/api_methods/SubscribeToList.md
*/
\MailPoet\API\API::MP( 'v1' )->subscribeToList( $subscriber['email'], $list );
} else {
/**
* Add a new subscriber.
*
* @see https://github.com/mailpoet/mailpoet/blob/c8fa9f007fd6fa39c17cd45e919566726a0578d7/doc/api_methods/AddSubscriber.md
*/
\MailPoet\API\API::MP( 'v1' )->addSubscriber( $user, array( $list ) );
}
} catch ( Exception $e ) {
return wp_send_json_error( $e->getMessage(), 400 );
}
} else {
$user_helper = WYSIJA::get( 'user', 'helper' );
$user_helper->addSubscriber( $data );
}
// Send back a response.
wp_send_json_success();
}
/**
* Prepares the subscriber data that will be inserted/updated into the Mailpoet's API.
*
* @since 2.16.3
*
* @param WP_Post $optin The optin object.
* @param array $data The lead data from the optin request.
*
* @return array The updated subscriber data.
*/
protected function prepare_subscriber_data( $optin, $data ) {
$email = ! empty( $data['email'] ) ? stripslashes( $data['email'] ) : false;
$name = ! empty( $data['name'] ) ? stripslashes( $data['name'] ) : false;
$user = array();
// Possibly split name into first and last.
if ( $name ) {
$names = explode( ' ', $name );
if ( isset( $names[0] ) ) {
$user['firstname'] = $names[0];
}
if ( isset( $names[1] ) ) {
$user['lastname'] = $names[1];
}
}
// Save the email address.
$user['email'] = $email;
// Save the custom fields data into user array.
$user += $this->prepare_lead_custom_fields( $data, $optin->ID );
return $user;
}
/**
* Prepares and returns the custom fields to be sent to Mailpoet.
*
* @since 2.16.3
*
* @param array $data The request data.
* @param int $optin_id The optin ID.
*
* @return array The updated user data containing the custom fields and values as they will be sent to Mailpoet.
*/
protected function prepare_lead_custom_fields( $data, $optin_id ) {
$custom_fields = $this->get_lead_custom_fields( $optin_id, $data );
if ( empty( $custom_fields ) ) {
// If there's nothing to map, return an empty array.
return array();
}
$parsed_custom_fields = array();
$mapped_custom_fields = get_post_meta( $optin_id, '_omapi_mailpoet_mapped_fields', true );
$should_auto_create_fields = get_post_meta( $optin_id, '_omapi_mailpoet_fields_auto_create', true );
// Save a copy of the mapped fields as they saved in the database.
$saved_mapped_fields = $mapped_custom_fields;
$mp_fields = $this->get_subscriber_fields();
$mp_fields_by_id = array();
foreach ( $mp_fields as $field ) {
$mp_fields_by_id[ $field['id'] ] = $field;
}
if ( empty( $mapped_custom_fields ) ) {
$mapped_custom_fields = array();
}
// Keep old phone field mapping for backward compatibility.
$legacy_phone_field_mapping = get_post_meta( $optin_id, '_omapi_mailpoet_phone_field', true );
if ( ! empty( $legacy_phone_field_mapping ) ) {
// If there's a mapping for the phone field that means we already set the legacy mapping or the user manually
// set a different mapping. That means we don't need to set the legacy mapping.
if ( empty( $mapped_custom_fields['lead.phoneInput'] ) ) {
$mapped_custom_fields['lead.phoneInput'] = $legacy_phone_field_mapping;
}
}
foreach ( $custom_fields as $field_id => $field ) {
if ( empty( $mapped_custom_fields[ $field_id ] ) && ! $should_auto_create_fields ) {
// If the field is not mapped and auto field creation is disabled, skip it.
continue;
}
$field_key = '';
if ( ! empty( $mapped_custom_fields[ $field_id ] ) && ! empty( $mp_fields_by_id[ $mapped_custom_fields[ $field_id ] ] ) ) {
// If the field is already mapped and the mapped field exists, use the mapped field.
$field_key = $mapped_custom_fields[ $field_id ];
} elseif ( $should_auto_create_fields ) {
// If the field is not mapped, but auto field creation is enabled, create and map the field.
// Create the custom field in MailPoet.
$created_field = $this->create_custom_field( $field['label'], $field_id );
$field_key = $created_field['id'];
// Add the new created custom field to the mapped fields meta.
$mapped_custom_fields[ $field_id ] = $field_key;
}
// If the field key is empty, skip it. Safety check.
if ( empty( $field_key ) ) {
continue;
}
// If the value is an array (e.g. for checkboxes), we are converting it to a string.
$parsed_custom_fields[ $field_key ] = is_array( $field['value'] ) ?
implode( ', ', $field['value'] ) :
$field['value'];
}
if ( $saved_mapped_fields !== $mapped_custom_fields ) {
// Updating the mapped fields meta if new fields are added.
update_post_meta( $optin_id, '_omapi_mailpoet_mapped_fields', $mapped_custom_fields );
}
return $parsed_custom_fields;
}
/**
* Returns the custom fields for the lead.
*
* @since 2.16.3
*
* @param int $optin_id The optin ID.
* @param array $data The request data.
*
* @return array The custom fields for the lead.
*/
protected function get_lead_custom_fields( $optin_id, $data ) {
$meta_fields = ! empty( $data['meta'] ) ? stripslashes_deep( $data['meta'] ) : array();
$smart_tags = ! empty( $data['tags'] ) ? stripslashes_deep( $data['tags'] ) : array();
$optin_fields_config = get_post_meta( $optin_id, '_omapi_mailpoet_optin_fields_config', true );
$optin_meta_fields_by_name = array();
if ( ! empty( $optin_fields_config['meta'] ) ) {
foreach ( $optin_fields_config['meta'] as $field_config ) {
$optin_meta_fields_by_name[ $field_config['name'] ] = $field_config;
}
}
$custom_fields = array();
foreach ( $meta_fields as $key => $value ) {
$field_config = ! empty( $optin_meta_fields_by_name[ $key ] ) ? $optin_meta_fields_by_name[ $key ] : array();
$custom_fields[ 'meta.' . $key ] = array(
'value' => $value,
'label' => ! empty( $field_config['label'] ) ? $field_config['label'] : $key,
);
}
// Add the coupon code and label to the meta fields, if exists.
if ( ! empty( $smart_tags['coupon_code'] ) ) {
$custom_fields['tags.couponCode'] = array(
'value' => $smart_tags['coupon_code'],
'label' => 'Coupon Code',
);
$custom_fields['tags.couponLabel'] = array(
'value' => $smart_tags['coupon_label'],
'label' => 'Coupon Label',
);
}
// Add the privacy consent to the meta fields, if exists.
if ( ! empty( $data['FieldsElement--privacyText-checkbox'] ) ) {
$custom_fields['lead.privacyText'] = array(
'value' => $data['FieldsElement--privacyText-checkbox'],
'label' => 'Privacy Consent',
);
}
// Add the phone number to the meta fields, if exists.
if ( ! empty( $data['phone'] ) ) {
$custom_fields['lead.phoneInput'] = array(
'value' => $data['phone'],
'label' => 'Phone Number',
);
}
return $custom_fields;
}
/**
* Create a custom field in MailPoet.
*
* @see https://github.com/mailpoet/mailpoet/blob/trunk/doc/api_methods/AddSubscriberField.md
*
* @since 2.16.3
*
* @param string $name The field name.
* @param string $label The field label.
*
* @return array The created custom field.
*/
protected function create_custom_field( $name, $label ) {
$data = array(
'name' => $name,
'type' => 'TEXT',
'label' => $label,
);
// We are creating the custom fields only for Mailpoet 3 and later.
return \MailPoet\API\API::MP( 'v1' )->addSubscriberField( $data );
}
}
MemberPress/Courses.php 0000644 00000005101 15153673736 0011141 0 ustar 00 <?php
/**
* MemberPress Courses Addon class.
*
* @since 2.13.0
*
* @package OMAPI
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* MemberPress Courses Addon class.
*
* @since 2.13.0
*/
class OMAPI_MemberPress_Courses {
/**
* Holds the OMAPI_MemberPress_Courses class object.
*
* @since 2.13.0
*
* @var OMAPI_MemberPress_Courses
*/
public static $instance;
/**
* Holds the base class object.
*
* @since 2.13.0
*
* @var OMAPI
*/
public $base;
/**
* The OMAPI_MemberPress instance.
*
* @since 2.13.0
*
* @var OMAPI_MemberPress
*/
public $mp;
/**
* Primary class constructor.
*
* @since 2.13.0
*/
public function __construct( OMAPI_MemberPress $mp ) {
$this->mp = $mp;
}
/**
* Adds the addon data to the `memberpress` object.
*
* @since 2.13.0
*
* @return array
*/
public function get_args() {
return array(
'courses' => $this->get_entities( 'mpcs-course' ),
'lessons' => $this->get_entities( 'mpcs-lesson' ),
'quizzes' => $this->get_entities( 'mpcs-quiz' ),
'wpFooterDisabled' => $this->is_memberpress_wp_footer_disabled(),
);
}
/**
* Check to see if MemberPress courses are active and, if so, check if
* the WP footer hook is enabled.
*
* @since 2.13.0
*
* @return bool
*/
public function is_memberpress_wp_footer_disabled() {
return class_exists( '\\memberpress\\courses\\helpers\\App' )
? ! \memberpress\courses\helpers\App::is_classroom_wp_footer()
: false;
}
/**
* Check if the MemberPress Courses addon is active.
*
* @since 2.13.0
*
* @return bool
*/
public static function is_active() {
return defined( 'memberpress\courses\PLUGIN_SLUG' )
&& class_exists( '\\memberpress\\courses\\models\\Course', true );
}
/**
* Retrieve MemberPress data.
*
* @since 2.13.0
*
* @param string $type ['mpcs-course', 'mpcs-lesson', 'mpcs-quiz']
* @return array Retrieved data array
*/
private function retrieve_data( $type ) {
// Bail if MemberPress and addons aren't currently active.
if ( ! $this->mp->is_active() || ! self::is_active() ) {
return array();
}
$args = array(
'post_type' => $type,
'post_status' => array( 'publish', 'draft', 'future' ),
);
$query = new \WP_Query( $args );
$data = $query->get_posts();
return $data;
}
/**
* Get the given MemberPress entities.
*
* @since 2.13.0
*
* @return array
*/
public function get_entities( $slug ) {
return $this->mp->format_data( $this->retrieve_data( $slug ) );
}
}
MemberPress/ProductEducation.php 0000644 00000010724 15153673736 0013001 0 ustar 00 <?php
/**
* MemberPress Product Education class.
*
* @since 2.13.5
*
* @package OMAPI
* @author Matt Sparks
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* MemberPress Product Education class.
*
* @since 2.13.5
*/
class OMAPI_MemberPress_ProductEducation {
/**
* The post types that we want to add the meta box to.
*
* @since 2.13.5
*
* @var array
*/
public $post_types = array(
'memberpressproduct',
'memberpressgroup',
'memberpressrule',
'memberpresscoupon',
'mp-reminder',
);
/**
* The path to the SVGs.
*
* @since 2.13.5
*
* @var string
*/
public $svg_path;
/**
* Primary class constructor.
*
* @since 2.13.5
*/
public function __construct() {
$this->svg_path = plugin_dir_path( OMAPI_FILE ) . '/assets/images/memberpress/';
}
/**
* Registers the add_meta_box hook.
*
* @since 2.13.5
*
* @return void
*/
public function add_meta_box() {
add_action( 'add_meta_boxes', array( $this, 'meta_box' ) );
}
/**
* Adds the meta box to the post types.
*
* @since 2.13.5
*
* @return void
*/
public function meta_box() {
$output = OMAPI_ApiKey::has_credentials() ? 'meta_box_output_connected' : 'meta_box_output_not_connected';
foreach ( $this->post_types as $type ) {
add_meta_box(
'om-mp-education',
esc_html__( 'Create a Popup', 'optin-monster-api' ),
array( $this, $output ),
$type,
'side',
'default'
);
}
}
/**
* Outputs the meta box content when connected.
*
* @since 2.13.5
*
* @return void
*/
public function meta_box_output_connected() {
$explore_text = __( 'Explore Templates', 'optin-monster-api' );
$type_buttons = array(
'popup' => __( 'Create a Popup', 'optin-monster-api' ),
'floating' => __( 'Create a Floating Bar', 'optin-monster-api' ),
'slide' => __( 'Create a Slide-in', 'optin-monster-api' ),
'full' => __( 'Create a Fullscreen', 'optin-monster-api' ),
'inline' => __( 'Create a Inline', 'optin-monster-api' ),
);
?>
<div class="om-mp-education">
<div class="om-mp-education-love">
<?php include $this->svg_path . 'love.svg'; ?>
</div>
<p class="om-mp-education-description">
<?php esc_html_e( 'Create a Targeted Offer', 'optin-monster-api' ); ?>
</p>
<div class="om-mp-education-body">
<nav>
<ul>
<?php foreach ( $type_buttons as $type => $text ) : ?>
<li>
<a href="<?php echo esc_url( OMAPI_Urls::templates( array( 'type' => $type ) ) ); ?>" title="<?php echo esc_attr( $text ); ?>" class="om-mp-cta">
<?php
include $this->svg_path . $type . '.svg';
echo esc_html( $text );
?>
</a>
</li>
<?php endforeach; ?>
</ul>
</nav>
</div>
<a href="<?php echo esc_url( OMAPI_Urls::templates() ); ?>" title="<?php echo esc_attr( $explore_text ); ?>" class="om-mp-button">
<?php echo esc_html( $explore_text ); ?>
</a>
</div>
<?php
}
/**
* Outputs the meta box content when not connected.
*
* @since 2.13.5
*
* @return void
*/
public function meta_box_output_not_connected() {
$get_started_text = __( 'Get Started For Free', 'optin-monster-api' );
?>
<div class="om-mp-education">
<div class="om-mp-education-love">
<?php include $this->svg_path . 'love.svg'; ?>
</div>
<p class="om-mp-education-description not-connected">
<?php esc_html_e( 'Show Popups, Floating Bars, and More to Members and Visitors with OptinMonster', 'optin-monster-api' ); ?>
</p>
<div class="om-mp-education-body">
<ul class="om-mp-education-benefits">
<li><?php echo esc_html_x( '...for active members of specific memberships or groups.', 'benefits of using OptinMonster with MemberPress', 'optin-monster-api' ); ?></li>
<li><?php echo esc_html_x( '...on MemberPress pages such as Register, Checkout, and Thank You.', 'benefits of using OptinMonster with MemberPress', 'optin-monster-api' ); ?></li>
<li><?php echo esc_html_x( '...on Group pages, Membership pages, Courses, Lessons, and Quizzes', 'benefits of using OptinMonster with MemberPress', 'optin-monster-api' ); ?></li>
<li><?php echo esc_html_x( 'And tons more!', 'benefits of using OptinMonster with MemberPress', 'optin-monster-api' ); ?></li>
</ul>
</div>
<a href="<?php echo esc_url( OMAPI_Urls::onboarding() ); ?>" title="<?php echo esc_attr( $get_started_text ); ?>" class="om-mp-button">
<?php echo esc_html( $get_started_text ); ?>
</a>
</div>
<?php
}
}
MemberPress/Rules.php 0000644 00000023317 15153673736 0010621 0 ustar 00 <?php
/**
* MemberPress Rules class.
*
* @since 2.13.0
*
* @package OMAPI
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* MemberPress_Rules class.
*
* @since 2.13.0
*/
class OMAPI_MemberPress_Rules extends OMAPI_Rules_Base {
/**
* Holds the meta fields used for checking output statuses.
*
* @since 2.13.0
*
* @var array
*/
protected $fields = array(
'show_to_mbp_membership_member',
'show_not_to_mbp_membership_member',
'show_to_mbp_group_member',
'show_not_to_mbp_group_member',
'show_on_mbp_group_pages',
'show_on_mbp_membership_pages',
'show_on_mbp_register_pages',
'show_on_mbp_checkout_pages',
'is_mbp_register',
'is_mbp_checkout',
'is_mbp_thank_you',
'show_on_mbp_course_pages',
'show_on_mbp_lesson_pages',
'show_on_mbp_quiz_pages',
);
/**
* MeprUser object
*
* @since 2.13.0
*
* @var MeprUser
*/
protected $mp_user;
/**
* Whether user is on the checkout page.
*
* @since 2.13.0
*
* @var bool
*/
protected $is_checkout = false;
/**
* Check for MP rules.
*
* @since 2.13.0
*
* @throws OMAPI_Rules_True If rule matches.
* @return void
*/
public function run_checks() {
// If MP is not active we can ignore the MP specific settings.
if ( ! OMAPI_MemberPress::is_active() ) {
return;
}
if( ! empty( $_GET['action'] ) && 'checkout' === $_GET['action'] ) {
$this->set_is_checkout( true );
}
if ( is_user_logged_in() ) {
$this->mp_user = new MeprUser( get_current_user_id() );
$this->exclude_if_member_not_allowed();
}
$mp_checks = array(
'show_on_mbp_group_pages' => array( $this, 'is_on_page' ),
'show_on_mbp_membership_pages' => array( $this, 'is_on_page' ),
'show_on_mbp_register_pages' => array( $this, 'is_on_membership_register_page' ),
'show_on_mbp_checkout_pages' => array( $this, 'is_on_membership_checkout_page' ),
'is_mbp_register' => array( $this, 'is_on_register_page' ),
'is_mbp_checkout' => array( $this, 'is_on_checkout_page' ),
'is_mbp_thank_you' => array( $this, 'is_on_thank_you_page' ),
'show_on_mbp_course_pages' => array( $this, 'is_on_page' ),
'show_on_mbp_lesson_pages' => array( $this, 'is_on_page' ),
'show_on_mbp_quiz_pages' => array( $this, 'is_on_page' ),
);
foreach ( $mp_checks as $field => $callback ) {
// If field is empty, then we don't need to check this.
if ( $this->rules->field_empty( $field ) ) {
continue;
}
$rule_value = $this->rules->get_field_value( $field );
$this->rules
->set_global_override( false )
->set_advanced_settings_field( $field, $rule_value );
if ( call_user_func( $callback, $rule_value ) ) {
throw new OMAPI_Rules_True( $field );
}
}
}
/**
* Check if campaign prevented from showing based on applicable membership/groups.
*
* @since 2.13.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function exclude_if_member_not_allowed() {
// * Does user exist? If no, Nothing to do.
if ( empty( $this->mp_user->ID ) ) {
return false;
}
// * Are there selected memberships?
$required_memberships = $this->rules->field_not_empty_array( 'show_to_mbp_membership_member' );
if ( $required_memberships ) {
// * Is user NOT ACTIVE in one of the selected memberships?
if ( ! $this->is_active_on_memberships( $required_memberships ) ) {
// * then fail!
throw new OMAPI_Rules_False( 'show_to_mbp_membership_member' );
}
}
// * Are there selected excluded memberships?
$excluded_memberships = $this->rules->field_not_empty_array( 'show_not_to_mbp_membership_member' );
if ( $excluded_memberships ) {
// * Is user ACTIVE in one of the selected excluded memberships?
if ( $this->is_active_on_memberships( $excluded_memberships ) ) {
// * then fail!
throw new OMAPI_Rules_False( 'show_not_to_mbp_membership_member' );
}
}
// * Are there selected groups?
$required_groups = $this->rules->field_not_empty_array( 'show_to_mbp_group_member' );
if ( $required_groups ) {
// * Is user NOT ACTIVE in one of the selected groups?
if ( ! $this->is_active_on_groups( $required_groups ) ) {
// * then fail!
throw new OMAPI_Rules_False( 'show_to_mbp_group_member' );
}
}
// * Are there selected excluded groups?
$excluded_groups = $this->rules->field_not_empty_array( 'show_not_to_mbp_group_member' );
if ( $excluded_groups ) {
// * Is user ACTIVE in one of the selected excluded groups?
if ( $this->is_active_on_groups( $excluded_groups ) ) {
// * then fail!
throw new OMAPI_Rules_False( 'show_not_to_mbp_group_member' );
}
}
}
/**
* Check if the user is logged in and subscribed to any product in the group.
*
* @since 2.13.0
*
* @param array $group_ids Array of group IDs.
*
* @return bool
*/
public function is_active_on_groups( $group_ids ) {
// Stash our checks in a static variable
// So we only do the fetch/checks once per page-load.
static $cached = array();
foreach ( $group_ids as $group_id ) {
if ( isset( $cached[ $group_id ] ) ) {
if ( $cached[ $group_id ] ) {
return true;
}
continue;
}
$cached[ $group_id ] = false;
$group = new MeprGroup( $group_id );
// If we can't find this group, then it's not considered active.
if ( ! empty( $group->ID ) ) {
// Check if user is subscribed to any products of a group.
foreach ( $group->products() as $group_product ) {
if ( $this->mp_user->is_already_subscribed_to( $group_product->ID ) ) {
$cached[ $group_id ] = true;
return true;
}
}
}
}
return false;
}
/**
* Check if the If the page is a Membership.
*
* @since 2.13.0
*
* @return bool
*/
public function is_on_register_page() {
return $this->is_product( $this->rules->post_id )
? ! $this->is_checkout
: false;
}
/**
* Check if the current user is active on any of the given memberships.
*
* @since 2.13.0
*
* @param array $membership_ids Array of membership IDs.
*
* @return bool
*/
public function is_active_on_memberships( $membership_ids ) {
foreach ( $membership_ids as $membership ) {
if ( $this->mp_user->is_active_on_membership( $membership ) ) {
return true;
}
}
return false;
}
/**
* Check if the current user is on a thank you page.
*
* @since 2.13.0
*
* @return bool
*/
public function is_on_thank_you_page() {
$options = MeprOptions::fetch();
if (
! empty( $options->thankyou_page_id )
&& $this->is_current_post_id( $options->thankyou_page_id )
) {
return true;
}
$product = new MeprProduct();
$all_memberpress_posts = $product->get_all();
foreach ( $all_memberpress_posts as $post ) {
if (
! empty( $post->thankyou_page_id )
&& $this->is_current_post_id( $post->thankyou_page_id )
) {
return true;
}
}
return false;
}
/**
* Check if the current user is on a checkout page.
*
* @since 2.13.0
*
* @return bool
*/
public function is_on_checkout_page() {
return $this->is_product( $this->rules->post_id )
? $this->is_checkout
: false;
}
/**
* Check if the current user is on a membership register page.
*
* @since 2.13.0
*
* @param array $memberships Array of membership IDs.
*
* @return bool
*/
public function is_on_membership_register_page( $memberships ) {
foreach ( $memberships as $membership ) {
if ( ! $this->is_current_post_id( $membership ) ) {
continue;
}
// If page is not a Membership, go to the next loop.
if ( ! $this->is_product( $this->rules->post_id ) ) {
continue;
}
return ! $this->is_checkout;
}
return false;
}
/**
* Check if the current user is on a membership checkout page.
*
* @since 2.13.0
*
* @param array $memberships Array of membership IDs.
*
* @return bool
*/
public function is_on_membership_checkout_page( $memberships ) {
foreach ( $memberships as $membership ) {
if ( ! $this->is_current_post_id( $membership ) ) {
continue;
}
// If page is not a Membership, go to the next loop.
if ( ! $this->is_product( $membership ) ) {
continue;
}
return $this->is_checkout;
}
return false;
}
/**
* Check if the current user is on a page of given IDs.
*
* @param array $page_ids Array of page IDs.
*
* @return bool
*/
public function is_on_page( $page_ids ) {
foreach ( $page_ids as $page_id ) {
if ( $this->is_current_post_id( $page_id ) ) {
return true;
}
}
return false;
}
/**
* Check if given id is a product.
*
* @since 2.13.0
*
* @param int $product_id Product/post ID.
*
* @return bool Whether given id is a product.
*/
public function is_product( $product_id ) {
$product = $this->get_product( $product_id );
return ! empty( $product->ID );
}
/**
* Get memberpress product.
*
* @since 2.13.0
*
* @param int $product_id Product/post ID.
*
* @return MeprProduct Memberpress product object.
*/
public function get_product( $product_id ) {
static $products = array();
if ( empty( $products[ $product_id ] ) ) {
$products[ $product_id ] = new MeprProduct( $product_id );
}
return $products[ $product_id ];
}
/**
* Determines if current post is the same one being passed in.
*
* @since 2.13.0
*
* @param int $post_id Current post id.
*
* @return boolean Whether current post is the same one being passed in.
*/
public function is_current_post_id( $post_id ) {
return intval( $this->rules->post_id ) === intval( $post_id );
}
/**
* Set the is_checkout property.
*
* @since 2.13.0
*
* @param boolean $is_checkout The property value.
*
* @return void
*/
public function set_is_checkout( $is_checkout ) {
$this->is_checkout = (bool) $is_checkout;
}
}
MemberPress.php 0000644 00000010170 15153673736 0007520 0 ustar 00 <?php
/**
* MemberPress class.
*
* @since 2.13.0
*
* @package OMAPI
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The MemberPress class.
*
* @since 2.13.0
*/
class OMAPI_MemberPress extends OMAPI_Integrations_Base {
/**
* Holds the OMAPI_MemberPress_Courses class instance.
*
* @since 2.13.0
*
* @var OMAPI_MemberPress_Courses
*/
public $courses;
/**
* Holds the OMAPI_MemberPress_ProductEducation class instance.
*
* @since 2.13.5
*
* @var OMAPI_MemberPress_ProductEducation
*/
public $education;
/**
* The minimum MemberPress version required.
*
* @since 2.13.0
*
* @var string
*/
const MINIMUM_VERSION = '1.9.39';
/**
* Primary class constructor.
*
* @since 2.13.0
*/
public function __construct() {
parent::__construct();
// Set our object.
$this->courses = new OMAPI_MemberPress_Courses( $this );
$this->education = new OMAPI_MemberPress_ProductEducation();
if ( self::is_active() && self::is_minimum_version() ) {
add_filter( 'optin_monster_campaigns_js_api_args', array( $this, 'add_args' ) );
add_filter( 'optin_monster_api_setting_ui_data', array( $this, 'add_args' ) );
wp_enqueue_style(
$this->base->plugin_slug . '-memberpress',
$this->base->url . 'assets/dist/css/memberpress.min.css',
array(),
$this->base->asset_version()
);
$this->education->add_meta_box();
}
}
/**
* Check if the MemberPress plugin is active.
*
* @since 2.13.0
*
* @return boolean
*/
public static function is_active() {
return defined( 'MEPR_PLUGIN_SLUG' ) && class_exists( 'MeprCptModel', true );
}
/**
* Return the MemberPress Plugin version string.
*
* @since 2.13.0
*
* @return string
*/
public static function version() {
return defined( 'MEPR_VERSION' ) ? MEPR_VERSION : '0.0.0';
}
/**
* Adds the `memberpress` object to payload, which is passed to the JS frontend.
*
* @since 2.13.0
*
* @param array $args This is the array of parameters that will be passed to the JS file.
* @return array $args The array with the `memberpress` payload.
*/
public function add_args( $args ) {
$args['memberpress'] = array(
'groups' => self::format_data( $this->retrieve_mp_data( 'MeprGroup' ) ),
'memberships' => self::format_data( $this->retrieve_mp_data( 'MeprProduct' ) ),
'isActive' => self::is_active(),
'isCoursesActive' => OMAPI_MemberPress_Courses::is_active(),
'checkoutTemplateEnabled' => self::isProTemplateEnabled( 'checkout' ),
);
$args['memberpress'] = array_merge( $args['memberpress'], $this->courses->get_args() );
return $args;
}
/**
* Format data to be consumed by the front-end admin output settings.
*
* @since 2.13.0
*
* @param array $payload The data to be formatted.
* @return array The formatted data
*/
public static function format_data( $payload ) {
$data = array();
if ( empty( $payload ) || ! is_array( $payload ) ) {
return $data;
}
foreach ( $payload as $entity ) {
$data[] = array(
'value' => $entity->ID,
'label' => $entity->post_title,
'name' => $entity->post_title,
);
}
return $data;
}
/**
* Retrieve MemberPress model data.
*
* @since 2.13.0
*
* @param string $model The entity model name.
* @return array The array model data.
*/
private function retrieve_mp_data( $model ) {
// Bail if MemberPress isn't currently active.
if ( ! self::is_active() || ! self::is_minimum_version() ) {
return array();
}
$data = MeprCptModel::all( $model );
if ( empty( $data ) ) {
return array();
}
return $data;
}
/**
* Determine if a "pro" template is enabled.
*
* @param string $name The template name.
* @return boolean True if enabled.
*/
public static function isProTemplateEnabled( $name ) {
if ( ! class_exists( 'MeprOptions', true ) ) {
return false;
}
$options = MeprOptions::fetch();
$attribute = 'design_enable_' . $name . '_template';
return ! empty( $options->$attribute ) && filter_var( $options->$attribute, FILTER_VALIDATE_BOOLEAN );
}
}
Menu.php 0000644 00000036653 15153673736 0006216 0 ustar 00 <?php
/**
* Menu class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Menu class.
*
* @since 1.0.0
*/
class OMAPI_Menu {
/**
* The admin page slug.
*
* @since 2.0.0
*/
const SLUG = 'optin-monster-dashboard';
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var OMAPI
*/
public $base;
/**
* The OMAPI_Pages object.
*
* @since 1.9.10
*
* @var OMAPI_Pages
*/
public $pages = null;
/**
* Panel slugs/names.
*
* @since 1.9.0
*
* @var array
*/
public $panels = array();
/**
* Registered page hooks.
*
* @since 1.9.10
*
* @var array
*/
public $hooks = array();
/**
* The OM landing page url.
*
* @since 1.8.4
*/
const LANDING_URL = 'https://optinmonster.com/wp/?utm_source=orgplugin&utm_medium=link&utm_campaign=wpdashboard';
/**
* Primary class constructor.
*
* @since 1.0.0
*
* @param bool $is_testing Whether we are doing integration testing.
*/
public function __construct( $is_testing = false ) {
if ( ! $is_testing ) {
// Set our object.
$this->set();
// Load actions and filters.
add_action( 'admin_menu', array( $this, 'menu' ) );
add_action( 'admin_menu', array( $this, 'after_menu_registration' ), 999 );
// Load custom admin bar menu items.
add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 999 );
// Load helper body classes.
add_filter( 'admin_body_class', array( $this, 'admin_body_classes' ) );
add_filter( 'plugin_action_links_' . plugin_basename( OMAPI_FILE ), array( $this, 'output_plugin_links' ) );
// Add upgrade link to plugin page.
add_filter( 'plugin_row_meta', array( $this, 'maybe_add_upgrade_link' ), 10, 2 );
}
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Loads the OptinMonster admin menu.
*
* @since 1.0.0
*/
public function menu() {
if ( ! current_user_can( $this->base->access_capability( self::SLUG ) ) ) {
return;
}
$this->pages = new OMAPI_Pages();
$this->pages->setup();
// Filter to change the menu position if there is any conflict with another menu on the same position.
$menu_position = apply_filters( 'optin_monster_api_menu_position', 26 );
$this->hooks[] = add_menu_page(
'OptinMonster',
'OptinMonster' . $this->notifications_count(),
$this->base->access_capability( self::SLUG ),
self::SLUG,
array( $this->pages, 'render_app_loading_page' ),
$this->icon_svg(),
$menu_position
);
// Just add a placeholder secondary page.
$this->hooks[] = add_submenu_page(
self::SLUG, // parent slug.
__( 'Dashboard', 'optin-monster-api' ), // page title.
__( 'Dashboard', 'optin-monster-api' ), // menu title.
$this->base->access_capability( self::SLUG ),
self::SLUG,
array( $this->pages, 'render_app_loading_page' )
);
$this->hooks = array_merge( $this->hooks, $this->pages->register_submenu_pages( self::SLUG ) );
// Register our old api page and redirect to the new dashboard.
$hook = add_submenu_page(
self::SLUG . '-hidden',
'OptinMonster',
'OptinMonster',
$this->base->access_capability( self::SLUG ),
'optin-monster-api-settings',
'__return_null'
);
add_action( 'load-' . $hook, array( $this, 'redirect_to_dashboard' ) );
global $submenu;
if ( $submenu ) {
// Register link under the "Dashboard" menu for "Marketing Education" which directs to the "University".
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$submenu['index.php'][] = array(
esc_html__( 'Marketing Education', 'optin-monster-api' ),
$this->base->access_capability( self::SLUG ),
esc_url_raw( OMAPI_Urls::university() ),
);
// Register link under the appearance menu for "Popup Builder".
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$submenu['themes.php'][] = array(
esc_html__( 'Popup Builder', 'optin-monster-api' ),
$this->base->access_capability( self::SLUG ),
esc_url_raw( OMAPI_Urls::templates() ),
);
}
// Maybe add custom CSS for our menu upgrade link.
if ( $this->base->can_show_upgrade() ) {
add_action( 'admin_footer', array( $this, 'add_upgrade_link_css' ) );
}
}
/**
* Loads custom items in the WP admin bar menu.
*
* @since 2.6.12
*
* @param object $admin_bar The WP admin bar object.
*/
public function admin_bar_menu( $admin_bar ) {
if ( ! current_user_can( $this->base->access_capability( self::SLUG ) ) ) {
return;
}
$admin_bar->add_node(
array(
'id' => 'om-new-campaign',
'title' => esc_html__( 'Popup', 'optin-monster-api' ),
'href' => esc_url_raw( OMAPI_Urls::templates() ),
'parent' => 'new-content',
)
);
}
/**
* Get the Archie SVG, and maybe encode it.
*
* @since 1.0.0
*
* @param string $fill Color of Archie.
* @param bool $return_encoded Whether the svg shoud be base_64 encoded.
*
* @return string Archie SVG.
*/
public function icon_svg( $fill = '#a0a5aa', $return_encoded = true ) {
$icon = file_get_contents( plugin_dir_path( OMAPI_FILE ) . '/assets/css/images/icons/archie-icon.svg' );
$icon = str_replace( 'fill="currentColor"', 'fill="' . $fill . '"', $icon );
if ( $return_encoded ) {
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$icon = 'data:image/svg+xml;base64,' . base64_encode( $icon );
}
return $icon;
}
/**
* Handles enqueueing assets for registered pages, and ensuring about page is at bottom.
*
* @since 1.9.10
*
* @return void
*/
public function after_menu_registration() {
if ( ! current_user_can( $this->base->access_capability( self::SLUG ) ) ) {
return;
}
global $submenu;
// Make sure the about page is still the last page.
if ( isset( $submenu[ self::SLUG ] ) ) {
$after = array();
$at_end = array( 'optin-monster-about', 'optin-monster-upgrade', 'optin-monster-bfcm' );
foreach ( $submenu[ self::SLUG ] as $key => $menu ) {
// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
if ( isset( $menu[2] ) && in_array( $menu[2], $at_end ) ) {
$after[] = $menu;
unset( $submenu[ self::SLUG ][ $key ] );
}
}
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$submenu[ self::SLUG ] = array_values( $submenu[ self::SLUG ] );
foreach ( $after as $menu ) {
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$submenu[ self::SLUG ][] = $menu;
}
}
// Load settings page assets.
foreach ( $this->hooks as $hook ) {
if ( ! empty( $hook ) ) {
add_action( 'load-' . $hook, array( $this, 'assets' ) );
}
}
}
/**
* Add pages to plugin action links in the Plugins table.
*
* @since 1.9.10
*
* @param array $links Default plugin action links.
*
* @return array $links Amended plugin action links.
*/
public function output_plugin_links( $links ) {
// Maybe add an upgrade link to the plugin links.
$upgrade_links = array();
if ( $this->base->can_show_upgrade() ) {
$upgrade_links[] = sprintf( '<a class="om-plugin-upgrade-link" href="%s">%s</a>', OMAPI_Urls::upgrade( 'plugin_action_link' ), 'vbp_pro' === $this->base->get_level() ? __( 'Upgrade to Growth', 'optin-monster-api' ) : __( 'Upgrade to Pro', 'optin-monster-api' ) );
}
$new_links = $this->base->get_api_credentials()
? array(
sprintf( '<a href="%s">%s</a>', OMAPI_Urls::campaigns(), __( 'Campaigns', 'optin-monster-api' ) ),
sprintf( '<a href="%s">%s</a>', OMAPI_Urls::settings(), __( 'Settings', 'optin-monster-api' ) ),
)
: array(
sprintf( '<a href="%s">%s</a>', OMAPI_Urls::onboarding(), __( 'Get Started', 'optin-monster-api' ) ),
);
$links = array_merge( $upgrade_links, $new_links, $links );
return $links;
}
/**
* Add upgrade link to the plugin row.
*
* @since 2.4.0
*
* @param array $links Default plugin row links.
* @param string $file The plugin file.
*
* @return array The links array.
*/
public function maybe_add_upgrade_link( $links, $file ) {
if ( plugin_basename( OMAPI_FILE ) === $file ) {
// If user upgradeable or not registered yet, let's put an upgrade link.
if ( $this->base->can_show_upgrade() ) {
$label = 'vbp_pro' === $this->base->get_level()
? __( 'Upgrade to Growth', 'optin-monster-api' )
: __( 'Upgrade to Pro', 'optin-monster-api' );
$upgrade_link = sprintf(
'<a class="om-plugin-upgrade-link" href="%s" aria-label="%s" target="_blank" rel="noopener">%s</a>',
esc_url_raw( OMAPI_Urls::upgrade( 'plugin_row_meta' ) ),
$label,
$label
);
// Maybe show the the BF item in the plugins description.
$pages = new OMAPI_Pages();
$bfcm_item = $pages->should_show_bfcf_menu_item();
if ( $bfcm_item ) {
$bflink = sprintf(
'<a class="om-plugin-bf-link" href="%s" style="font-weight: 700; color: #ff0000;">%s</a>',
esc_url( $bfcm_item['redirect'] ),
esc_html( $bfcm_item['alternate-name'] )
);
$links[] = $bflink;
}
array_splice( $links, 1, 0, array( $upgrade_link ) );
}
}
return $links;
}
/**
* Adds om admin body classes
*
* @since 1.3.4
*
* @param array $classes Body classes.
*
* @return array
*/
public function admin_body_classes( $classes ) {
$classes .= ' omapi-screen ';
if ( $this->base->get_api_key_errors() ) {
$classes .= ' omapi-has-api-errors ';
}
return $classes;
}
/**
* Check if we're on one of the OM menu/sub-menu pages.
*
* @since 1.9.0
*
* @return boolean
*/
public function is_om_page() {
if ( ! is_admin() ) {
return false;
}
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
$page = $screen->id;
if ( false !== strpos( $page, 'toplevel_page_optin-monster-' ) ) {
return true;
}
if ( ! empty( $screen->parent_base ) && false !== strpos( $screen->parent_base, 'optin-monster-' ) ) {
return true;
}
} else {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
}
return false !== strpos( $page, 'optin-monster' );
}
/**
* Loads assets for the settings page.
*
* @since 1.0.0
*/
public function assets() {
add_action( 'admin_enqueue_scripts', array( $this, 'styles' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
add_filter( 'admin_footer_text', array( $this, 'footer' ) );
add_action( 'in_admin_header', array( $this, 'output_plugin_screen_banner' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'fix_plugin_js_conflicts' ), 100 );
add_action( 'admin_print_footer_scripts', array( $this, 'fix_plugin_js_conflicts' ), 100 );
}
/**
* Register and enqueue settings page specific CSS.
*
* @since 1.0.0
*/
public function styles() {
$version = $this->base->asset_version();
$prefix = $this->base->plugin_slug . '-';
wp_enqueue_style( $prefix . 'font-awesome', $this->base->url . 'assets/css/font-awesome.min.css', array(), $version );
wp_enqueue_style( $prefix . 'common', $this->base->url . 'assets/dist/css/common.min.css', array( $prefix . 'font-awesome' ), $version );
// Run a hook to load in custom styles.
do_action( 'optin_monster_api_admin_styles' );
}
/**
* Register and enqueue settings page specific JS.
*
* @since 1.0.0
*/
public function scripts() {
$version = $this->base->asset_version();
wp_register_script(
$this->base->plugin_slug . '-admin',
$this->base->url . 'assets/dist/js/admin.min.js',
array( 'jquery' ),
$version,
true
);
wp_enqueue_script( $this->base->plugin_slug . '-admin' );
// Run a hook to load in custom styles.
do_action( 'optin_monster_api_admin_scripts' );
}
/**
* Deque specific scripts that cause conflicts on settings page. E.g.
* - optimizely
* - bigcommerce
* - learnpress
*
* @since 1.1.5.9
*/
public function fix_plugin_js_conflicts() {
if ( $this->is_om_page() ) {
global $wp_scripts;
$remove = array(
'lp-',
'optimizely',
'bigcommerce-',
);
foreach ( $wp_scripts->queue as $script ) {
foreach ( $remove as $search ) {
if ( 0 === strpos( $script, $search ) ) {
// Dequeue scripts that might cause our settings not to work properly.
wp_dequeue_script( $script );
}
}
}
}
}
/**
* Customizes the footer text on the OptinMonster settings page.
*
* @since 1.0.0
*
* @param string $text The default admin footer text.
* @return string $text Amended admin footer text.
*/
public function footer( $text ) {
$url = 'https://wordpress.org/support/plugin/optinmonster/reviews?filter=5#new-post';
/* translators: %1$s - OptinMonster plugin support url */
$text = sprintf( __( 'Please rate <strong>OptinMonster</strong> <a href="%1$s" target="_blank" rel="noopener">★★★★★</a> on <a href="%1$s" target="_blank" rel="noopener noreferrer">WordPress.org</a> to help us spread the word. Thank you from the OptinMonster team!', 'optin-monster-api' ), $url );
return $text;
}
/**
* Echo out plugin header banner
*
* @since 1.1.5.2
*/
public function output_plugin_screen_banner() {
$path = 'vue/dist';
$dir = trailingslashit( dirname( $this->base->file ) ) . $path;
$loader = new OMAPI_AssetLoader( $dir );
$logo = '#';
$help = '#';
$list = $loader->getAssetsList( $dir );
foreach ( $list as $item ) {
if (
false !== strpos( $item, 'logo-om' )
&& preg_match( '/\.svg$/', $item )
) {
$logo = $loader->getAssetUri( trim( $item, '/' ), $this->base->url . $path );
}
if ( false !== strpos( $item, '/help-circle.' ) ) {
$help = $loader->getAssetUri( trim( $item, '/' ), $this->base->url . $path );
}
}
$this->base->output_view( 'plugin-banner.php', compact( 'logo', 'help' ) );
}
/**
* Get the parent slug (contextual based on beta being enabled).
*
* @since 1.9.10
*
* @return string
*/
public function parent_slug() {
return self::SLUG;
}
/**
* Redirects to main OM page.
*
* @since 1.9.10
*
* @param array $args Array of query args.
*
* @return void
*/
public function redirect_to_dashboard( $args = array() ) {
$url = OMAPI_Urls::dashboard( $args );
wp_safe_redirect( esc_url_raw( $url ) );
exit;
}
/**
* Add the notifications bubble to the menu.
*
* @since 2.0.0
*
* @return string Notifications bubble markup.
*/
public function notifications_count() {
$count = apply_filters( 'optin_monster_api_notifications_count', 0, $this );
$count = absint( $count );
$html = '';
if ( $count ) {
$html .= sprintf(
' <span class="om-notifications-count update-plugins count-%1$d"><span class="plugin-count">%2$s</span></span>',
$count,
esc_html( number_format_i18n( $count ) )
);
}
add_action( 'admin_footer', array( $this, 'add_jiggle_css' ) );
return $html;
}
/**
* Output the css that jiggles the OM notification count bubble.
*
* @since 2.0.0
*/
public function add_jiggle_css() {
$this->base->output_min_css( 'jiggle-css.php' );
}
/**
* Output the css that highlights the OM upgrade menu link.
*
* @since 2.6.12
*/
public function add_upgrade_link_css() {
$this->base->output_min_css( 'upgrade-link-css.php' );
}
}
Notifications.php 0000644 00000044736 15153673736 0010124 0 ustar 00 <?php
/**
* Notifications class.
*
* @since 2.0.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Notifications class.
*
* @since 2.0.0
*/
class OMAPI_Notifications {
/**
* Holds the class object.
*
* @since 2.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 2.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 2.0.0
*
* @var object
*/
public $base;
/**
* Source of notifications content.
*
* @since 2.0.0
*
* @var string
*/
const SOURCE_URL = 'https://a.omwpapi.com/production/wp/notifications.json';
/**
* The option where the notifications are stored.
*
* @since 2.0.0
*
* @var string
*/
const OPTION_NAME = 'om_notifications';
/**
* Option value.
*
* @since 2.0.0
*
* @var bool|array
*/
protected $option = null;
/**
* Primary class constructor.
*
* @since 2.0.0
*/
public function __construct() {
// Set our object.
$this->set();
$this->hooks();
}
/**
* Sets our object instance and base class instance.
*
* @since 2.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Register hooks.
*
* @since 2.0.0
*/
public function hooks() {
add_action( 'optin_monster_api_rest_loaded', array( $this, 'schedule_next_update' ) );
add_action( 'optin_monster_api_admin_loaded', array( $this, 'schedule_next_update' ) );
add_action( 'optin_monster_api_admin_notifications_update', array( $this, 'update' ) );
add_filter( 'optin_monster_api_notifications_count', array( $this, 'get_count' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
}
/**
* Schedule the next notifications fetch.
*
* @since 2.11.1
*
* @return void
*/
public function schedule_next_update() {
$hook = 'optin_monster_api_admin_notifications_update';
$args = array( 'wpcron' );
$scheduled = wp_next_scheduled( $hook, $args );
if ( $scheduled ) {
// Nothing to do here.
return;
}
$timezone = new DateTimeZone( 'America/New_York' );
$now = new DateTime( 'now', $timezone );
$today_am = DateTime::createFromFormat( 'H:iA', '10:10am', $timezone );
$date = $today_am;
// If past 10am already...
if ( $now > $today_am ) {
// Try to schedule for 10pm instead.
$date = DateTime::createFromFormat( 'H:iA', '10:10pm', $timezone );
// If past 10pm already...
if ( $now > $date ) {
// Schedule for 10am tomorrow.
$date = $today_am->modify( '+1 day' );
}
}
wp_schedule_single_event( $date->getTimestamp(), $hook, $args );
}
/**
* Check if user has access and is enabled.
*
* @since 2.0.0
*
* @return bool
*/
public function has_access() {
$access = (
$this->base->can_access( 'notifications' )
&& ! $this->base->get_option( 'hide_announcements' )
);
return apply_filters( 'optin_monster_api_admin_notifications_has_access', $access );
}
/**
* Get option value.
*
* @since 2.0.0
*
* @param string $key The option value to get for given key.
* @param bool $cache Reference property cache if available.
*
* @return mixed The notification option array, or requsted value.
*/
public function get_option( $key = '', $cache = true ) {
if ( ! $this->option || ! $cache ) {
$option = get_option( self::OPTION_NAME, array() );
$this->option = array(
'updated' => ! empty( $option['updated'] ) ? $option['updated'] : 0,
'events' => ! empty( $option['events'] ) ? $option['events'] : array(),
'feed' => ! empty( $option['feed'] ) ? $option['feed'] : array(),
'dismissed' => ! empty( $option['dismissed'] ) ? $option['dismissed'] : array(),
);
}
if ( ! empty( $key ) ) {
return isset( $this->option[ $key ] ) ? $this->option[ $key ] : false;
}
return $this->option;
}
/**
* Fetch notifications from feed.
*
* @since 2.0.0
*
* @return array
*/
public function fetch_feed() {
$url = add_query_arg( 't', strtotime( 'today' ), self::SOURCE_URL );
$args = array(
'sslverify' => false,
);
add_filter( 'https_ssl_verify', '__return_false', 98765 );
$response = wp_remote_get( $url, $args );
remove_filter( 'https_ssl_verify', '__return_false', 98765 );
if ( is_wp_error( $response ) ) {
return $response;
}
$body = wp_remote_retrieve_body( $response );
if ( empty( $body ) ) {
return array();
}
return $this->verify( json_decode( $body, true ) );
}
/**
* Verify notification data before it is saved.
*
* @since 2.0.0
*
* @param array $notifications Array of notifications items to verify.
* @param array $dismissed Array of dismissed notification ids.
* Defaults to fetching them from option.
*
* @return array
*/
public function verify( $notifications, $dismissed = null ) {
$data = array();
if ( ! empty( $notifications ) && is_array( $notifications ) ) {
$dismissed = null !== $dismissed ? $dismissed : $this->get_option( 'dismissed' );
$installed = $this->base->get_option( 'installed', '', time() );
foreach ( $notifications as $notification ) {
$notification = $this->verify_notification( $notification, $dismissed, $installed );
if ( ! empty( $notification ) ) {
$data[] = $notification;
}
}
}
return $data;
}
/**
* Verify a notification before it is saved.
*
* @since 2.0.0
*
* @param array $notification Array of notification data.
* @param array $dismissed Array of dismissed notifications.
* @param int $installed The installation timestamp.
*
* @return bool|array The notification if verified, false if not.
*/
public function verify_notification( $notification, $dismissed, $installed = null ) {
$installed = null !== $installed ? $installed : $this->base->get_option( 'installed', '', time() );
if ( empty( $notification['content'] ) ) {
// The message should never be empty. If they are, ignore.
return false;
}
/*
* - Empty levels means show to everyone, regardless of plan/connected status.
* - `none` is to all who are not connected
* - `all` is to all who are connected regardless of license
*/
if ( ! empty( $notification['levels'] ) ) {
if ( ! $this->verify_notification_level( (array) $notification['levels'] ) ) {
// If notification level verification fails, stop here.
return false;
}
// Otherwise, proceed to the next checks.
}
if ( ! empty( $notification['end'] ) && time() > strtotime( $notification['end'] ) ) {
// Ignore if expired.
return false;
}
if (
! empty( $notification['min'] )
&& version_compare( $this->base->version, $notification['min'], '<' )
) {
// Ignore if below minimum plugin version.
return false;
}
if (
! empty( $notification['max'] )
&& version_compare( $this->base->version, $notification['max'], '>' )
) {
// Ignore if above maximum plugin version.
return false;
}
// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
if ( ! empty( $dismissed ) && in_array( $notification['id'], $dismissed ) ) {
// Ignore if notification has already been dismissed.
return false;
}
// Ignore if notification existed before installing the plugin and
// the end date has not been set.
if (
! empty( $installed ) &&
! empty( $notification['start'] ) &&
empty( $notification['end'] ) &&
$installed > strtotime( $notification['start'] )
) {
return false;
}
return $notification;
}
/**
* Verify if the notification levels match.
*
* @since 2.0.0
*
* @param array $levels Array of notification levels.
*
* @return bool Whether notification passes level verification.
*/
public function verify_notification_level( $levels ) {
$account_level = $this->base->get_level();
$is_connected = ! empty( $account_level );
$for_all_connected = in_array( 'all', $levels, true );
$for_not_connected = in_array( 'none', $levels, true );
$for_custom = in_array( 'vbp_custom', $levels, true );
$level_matches = in_array( $account_level, $levels, true );
if ( $for_not_connected ) {
return ! $is_connected;
}
if ( $for_all_connected ) {
return $is_connected;
}
if ( $for_custom ) {
return $this->base->is_custom_plan();
}
return $level_matches;
}
/**
* Verify saved notification data for active notifications.
*
* @since 2.0.0
*
* @param array $notifications Array of notifications items to verify.
*
* @return array
*/
public function verify_active( $notifications ) {
if ( ! is_array( $notifications ) || empty( $notifications ) ) {
return array();
}
$notifications = $this->verify( $notifications );
if ( empty( $notifications ) ) {
return array();
}
$now = time();
// Remove notfications that are not active.
foreach ( $notifications as $key => $notification ) {
if ( ! empty( $notification['start'] ) && $now < strtotime( $notification['start'] ) ) {
// Notification is not yet active.
unset( $notifications[ $key ] );
}
if ( ! empty( $notification['end'] ) && $now > strtotime( $notification['end'] ) ) {
// Notification is expired.
unset( $notifications[ $key ] );
}
if (
! empty( $notification['min'] )
&& version_compare( $this->base->version, $notification['min'], '<' )
) {
// Ignore if below minimum plugin version.
unset( $notifications[ $key ] );
}
if (
! empty( $notification['max'] )
&& version_compare( $this->base->version, $notification['max'], '>' )
) {
// Ignore if above maximum plugin version.
unset( $notifications[ $key ] );
}
}
return $notifications;
}
/**
* Get notification data.
*
* @since 2.0.0
*
* @param bool $can_update Whether we can fetch/update the feed/options.
*
* @return array
*/
public function get( $can_update = false ) {
if ( ! $this->has_access() ) {
return array();
}
// Update notifications using async task.
if ( $this->should_update() && $can_update ) {
$this->update();
}
$option = $this->get_option();
$events = ! empty( $option['events'] ) ? $this->verify_active( $option['events'] ) : array();
$feed = ! empty( $option['feed'] ) ? $this->verify_active( $option['feed'] ) : array();
$notifications = array_merge( $feed, $events );
set_transient( 'om_notification_count', count( $notifications ), ( 12 * HOUR_IN_SECONDS ) );
if ( ! $this->base->get_api_credentials() ) {
$notifications = array_merge(
$notifications,
array(
array(
'type' => 'action',
'title' => esc_html__( 'You haven\'t finished setting up your site.', 'optin-monster-api' ),
'content' => esc_html__( 'You\'re losing subscribers, leads and sales! Click on the button below to get started with OptinMonster.', 'optin-monster-api' ),
'btns' => array(
'main' => array(
'text' => esc_html__( 'Connect Your Site', 'optin-monster-api' ),
'url' => '?page=optin-monster-settings&action=connect-your-site',
),
),
'canDismiss' => false,
),
)
);
}
return $notifications;
}
/**
* Get notification count.
*
* @since 2.0.0
*
* @return int
*/
public function get_count() {
$count = get_transient( 'om_notification_count' );
if ( ! is_numeric( $count ) ) {
$this->get();
$count = get_transient( 'om_notification_count' );
}
$count = absint( $count );
if ( ! $this->base->get_api_credentials() ) {
$count++;
}
return $count;
}
/**
* Add a manual notification event.
*
* @since 2.0.0
*
* @param array $notification Notification data.
*
* @return bool Whether update occurred.
*/
public function add_event( $notification ) {
$notification = self::sanitize_notification( (array) $notification );
$update = ! empty( $notification['update'] );
// ID (string) is required!
if ( empty( $notification['id'] ) && $update ) {
return new WP_Error(
'omapp_notification_event_error',
esc_html__( 'Event notification update requires the "id" parameter', 'optin-monster-api' )
);
}
if ( empty( $notification['id'] ) ) {
$notification['id'] = uniqid( 'event-' );
}
$notification['id'] = (string) $notification['id'];
// ID is required to be a string!
if ( ctype_digit( $notification['id'] ) ) {
return new WP_Error(
'omapp_notification_event_error',
esc_html__( 'Event notification requires an "id" parameter which is a unique string.', 'optin-monster-api' )
);
}
$events = (array) $this->get_option( 'events' );
$dismissed = (array) $this->get_option( 'dismissed' );
if ( $update ) {
$index = array_search( $notification['id'], $dismissed, true );
if ( false !== $index ) {
unset( $dismissed[ $index ] );
}
unset( $notification['update'] );
} else {
// Already dismissed.
if ( in_array( $notification['id'], $dismissed, true ) ) {
return false;
}
foreach ( $events as $item ) {
if ( $item['id'] === $notification['id'] ) {
return false;
}
}
}
$notification = $this->verify_notification( $notification, $dismissed );
if ( empty( $notification ) ) {
return new WP_Error(
'omapp_notification_event_error',
esc_html__( 'Event notification verification failed.', 'optin-monster-api' )
);
}
$notification = self::set_created_timestamp( $notification );
$updated = false;
if ( $update ) {
foreach ( $events as $key => $item ) {
if ( $item['id'] === $notification['id'] ) {
$updated = true;
foreach ( $notification as $name => $val ) {
$events[ $key ][ $name ] = $val;
}
$events[ $key ]['updated'] = time();
}
}
}
if ( ! $updated ) {
$events[] = $notification;
}
$this->handle_update(
array(
'events' => $events,
'dismissed' => $dismissed,
)
);
return true;
}
/**
* Update notification data from feed.
*
* @param string $context The context for this update. Used by cron event.
*
* @since 2.0.0
*/
public function update( $context = 'default' ) {
$feed = $this->fetch_feed();
if ( 'wpcron' === $context ) {
$this->schedule_next_update();
}
// If there was an error with the fetch, do not update the option.
if ( is_wp_error( $feed ) ) {
return;
}
foreach ( $feed as $key => $notification ) {
$feed[ $key ] = self::set_created_timestamp( $notification );
}
delete_transient( 'om_notification_count' );
$this->handle_update(
array(
'updated' => time(),
'feed' => $feed,
)
);
}
/**
* Dismiss notification(s).
*
* @since 2.0.0
*
* @param array|string|int $ids Array of ids or single id.
*
* @return bool Whether dismiss update occurred.
*/
public function dismiss( $ids ) {
// Check for access and required param.
if ( ! $this->has_access() || empty( $ids ) ) {
return false;
}
$ids = self::sanitize_string( (array) $ids );
$option = $this->get_option();
foreach ( $ids as $id ) {
if ( ! is_scalar( $id ) ) {
continue;
}
$id = (string) $id;
$type = ctype_digit( $id ) ? 'feed' : 'events';
$option['dismissed'][] = $id;
// Remove notification.
if ( is_array( $option[ $type ] ) && ! empty( $option[ $type ] ) ) {
foreach ( $option[ $type ] as $key => $notification ) {
if ( (string) $notification['id'] === $id ) {
unset( $option[ $type ][ $key ] );
break;
}
}
}
}
$option['dismissed'] = array_unique( $option['dismissed'] );
$option['dismissed'] = array_values( $option['dismissed'] );
$option['dismissed'] = array_filter( $option['dismissed'] );
$this->handle_update( $option );
return true;
}
/**
* Sanitize notification data.
*
* @since 2.0.0
*
* @param array|string|int $data The notification data.
*
* @return mixed The sanitized id(s).
*/
public static function sanitize_notification( array $data ) {
foreach ( $data as $key => $value ) {
$data[ $key ] = 'content' === $key
? wp_kses_post( $value )
: self::sanitize_string( $value );
}
return $data;
}
/**
* Sanitize string(s).
*
* @since 2.0.0
*
* @param array|string|int $string The notification string(s).
*
* @return mixed The sanitized string(s).
*/
public static function sanitize_string( $string ) {
if ( is_array( $string ) ) {
return array_map( array( __CLASS__, __FUNCTION__ ), $string );
}
return sanitize_text_field( wp_unslash( $string ) );
}
/**
* Updates our notification option in the DB (disable option autoload).
*
* @since 2.0.0
*
* @param array $option Option value.
*
* @return mixed Result from update_option.
*/
protected function handle_update( $option ) {
$required_keys = array(
'updated',
'feed',
'events',
'dismissed',
);
foreach ( $required_keys as $key ) {
if ( ! isset( $option[ $key ] ) ) {
$option[ $key ] = $this->get_option( $key );
}
}
$result = update_option( self::OPTION_NAME, $option, false );
if ( false !== $result ) {
// Re-cache value.
$this->get_option( '', false );
}
return $result;
}
/**
* Set the created timestamp.
*
* Will add it if it doesn't exist, and will convert to timestamp if applicable.
*
* @since 2.0.0
*
* @param array $notification Notification array.
*/
protected function set_created_timestamp( array $notification ) {
// Set created timestamp if it's not already set.
if ( empty( $notification['created'] ) ) {
$notification['created'] = time();
}
// Convert to timestamp, if it's not already.
if ( ! ctype_digit( (string) $notification['created'] ) ) {
$notification['created'] = strtotime( $notification['created'] );
}
return $notification;
}
/**
* Checks if our notifications should be updated.
*
* @since 2.6.1
*
* @return bool Whether notifications should be updated.
*/
public function should_update() {
$updated = $this->get_option( 'updated' );
return empty( $updated ) || time() > ( $updated + ( 12 * HOUR_IN_SECONDS ) );
}
/**
* Register and enqueue admin specific JS.
*
* @since 2.1.1
*/
public function scripts() {
$handle = $this->base->plugin_slug . '-global';
wp_enqueue_script(
$handle,
$this->base->url . 'assets/dist/js/global.min.js',
array( 'jquery' ),
$this->base->asset_version(),
true
);
OMAPI_Utils::add_inline_script(
$handle,
'OMAPI_Global',
array(
'url' => esc_url_raw( rest_url( 'omapp/v1/notifications' ) ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'fetchNotifications' => $this->should_update(),
)
);
}
}
OmuApi.php 0000644 00000010032 15153673736 0006463 0 ustar 00 <?php
/**
* OMU API class.
*
* @since 2.6.6
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* OMU API class.
*
* @since 2.6.6
*/
class OMAPI_OmuApi extends OMAPI_Api {
/**
* Holds the last instantiated instance of this class.
*
* @var OMAPI_Api
*/
protected static $instance = null;
/**
* Base API route.
*
* @since 2.6.6
*
* @var string
*/
public $base = OPTINMONSTER_URL;
/**
* The OMU routes map.
*
* @since 2.6.6
*
* @var string
*/
protected static $routes = array(
'courses' => 'wp-json/ldlms/v1/courses?per_page=8&orderby=menu_order&order=desc&_embed=1',
'guides' => 'wp-json/wp/v2/omu-guides?per_page=8&orderby=menu_order&order=desc&_embed=1',
);
/**
* The cached-request TTL.
*/
public $cache_ttl = DAY_IN_SECONDS;
/**
* Primary class constructor.
*
* @since 2.6.6
*
* @param string $route The API route to target.
* @param string $method The API method.
*/
public function __construct( $route, $method = 'GET' ) {
// Set class properties.
$this->route = $route;
$this->method = $method;
self::$instance = $this;
}
/**
* Processes the OMU REST API request.
*
* @since 2.6.6
*
* @param string $route The API route to target.
* @param string $method The API method.
* @param array $args Request args.
* @param bool $refresh Whether to refresh the cache.
*
* @return mixed $value The response to the API call.
*/
public static function cached_request( $route, $method = 'GET', $args = array(), $refresh = false ) {
$key_args = $args;
$key_args[] = $method;
$key_args[] = $route;
$cache_key = 'omapp_omu_cached' . md5( serialize( $key_args ) );
$result = get_transient( $cache_key );
if ( empty( $result ) || $refresh ) {
$api = new self( $route, $method );
$result = $api->request( $args );
if ( ! is_wp_error( $result ) ) {
$headers = wp_remote_retrieve_headers( $api->response );
$result = array(
'data' => $result,
'total' => isset( $headers['x-wp-total'] )
? (int) $headers['x-wp-total']
: 0,
'totalpages' => isset( $headers['x-wp-totalpages'] )
? (int) $headers['x-wp-totalpages']
: 0,
);
set_transient( $cache_key, $result, $api->cache_ttl );
}
}
return $result;
}
/**
* Processes the OMU REST API request.
*
* @since 2.6.6
*
* @param array $args Request args.
*
* @return mixed $value The response to the API call.
*/
public function request( $args = array() ) {
$url = in_array( $this->method, array( 'GET', 'DELETE' ), true )
? add_query_arg( array_map( 'urlencode', $args ), $this->get_url() )
: $this->get_url();
$url = esc_url_raw( $url );
// Build the headers of the request.
$headers = array(
'Content-Type' => 'application/json',
);
// Setup data to be sent to the API.
$data = array(
'headers' => $headers,
'timeout' => 3000,
'sslverify' => false,
'method' => $this->method,
);
// Perform the query and retrieve the response.
$this->handle_response( wp_remote_request( $url, $data ) );
// Bail out early if there are any errors.
if ( is_wp_error( $this->response ) ) {
return $this->response;
}
$error = $this->check_response_error();
// Bail out early if there are any errors.
if ( is_wp_error( $error ) ) {
return $error;
}
// Return the json decoded content.
return $this->response_body;
}
/**
* The gets the URL based on our base, endpoint and version
*
* @since 2.6.6
*
* @return string The API url.
*/
public function get_url() {
if ( empty( self::$routes[ $this->route ] ) ) {
throw new Exception( sprintf( 'Missing route information for %s', $this->route ), 400 );
}
return trailingslashit( $this->base ) . self::$routes[ $this->route ];
}
/**
* Returns the last instantiated instance of this class.
*
* @since 2.6.6
*
* @return A single instance of this class.
*/
public static function instance() {
return self::$instance;
}
}
Output.php 0000644 00000060434 15153673736 0006604 0 ustar 00 <?php
/**
* Output class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Output class.
*
* @since 1.0.0
*/
class OMAPI_Output {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Holds the meta fields used for checking output statuses.
*
* @since 1.0.0
*
* @var array
*/
public $fields = array();
/**
* Flag for determining if localized JS variable is output.
*
* @since 1.0.0
*
* @var bool
*/
public $localized = false;
/**
* Flag for determining if localized JS variable is output.
*
* @since 1.0.0
*
* @var bool
*/
public $data_output = false;
/**
* Holds JS slugs for maybe parsing shortcodes.
*
* @since 1.0.0
*
* @var array
*/
public $slugs = array();
/**
* Holds shortcode output.
*
* @since 1.0.0
*
* @var array
*/
public $shortcodes = array();
/**
* Whether we are in a live campaign preview.
*
* @since 2.2.0
*
* @var boolean
*/
protected static $live_preview = false;
/**
* Whether we are in a live campaign rules preview.
*
* @since 2.2.0
*
* @var boolean
*/
protected static $live_rules_preview = false;
/**
* Whether we are in a site verification request.
*
* @since 2.2.0
*
* @var boolean
*/
protected static $site_verification = false;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
add_filter( 'optinmonster_pre_campaign_should_output', array( $this, 'enqueue_helper_js_if_applicable' ), 999, 2 );
// If no credentials have been provided, do nothing.
if ( ! $this->base->get_api_credentials() ) {
return;
}
// Add the hook to allow OptinMonster to process.
add_action( 'pre_get_posts', array( $this, 'load_optinmonster_inline' ), 9999 );
add_action( 'wp', array( $this, 'maybe_load_optinmonster' ), 9999 );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
$rules = new OMAPI_Rules();
if ( OMAPI_Debug::can_output_debug() ) {
add_action( 'wp_footer', array( 'OMAPI_Debug', 'output_general' ), 99 );
}
// Keep these around for back-compat.
$this->fields = $rules->fields;
// phpcs:disable WordPress.Security.NonceVerification.Recommended
self::$live_preview = ! empty( $_GET['om-live-preview'] )
? sanitize_text_field( wp_unslash( $_GET['om-live-preview'] ) )
: false;
self::$live_rules_preview = ! empty( $_GET['om-live-rules-preview'] )
? sanitize_text_field( wp_unslash( $_GET['om-live-rules-preview'] ) )
: false;
self::$site_verification = ! empty( $_GET['om-verify-site'] )
? sanitize_text_field( wp_unslash( $_GET['om-verify-site'] ) )
: false;
// phpcs:enable
}
/**
* Conditionally loads the OptinMonster optin based on the query filter detection.
*
* @since 1.0.0
*/
public function maybe_load_optinmonster() {
/**
* Check if there are any campaigns for the site
*/
$optins = $this->base->get_optins();
if ( empty( $optins ) ) {
return;
}
// Checking if AMP is enabled.
if ( OMAPI_Utils::is_amp_enabled() ) {
return;
}
// Load actions and filters.
add_action( 'wp_enqueue_scripts', array( $this, 'api_script' ) );
add_action( 'wp_footer', array( $this, 'localize' ), 9999 );
add_action( 'wp_footer', array( $this, 'display_rules_data' ), 9999 );
add_action( 'wp_footer', array( $this, 'maybe_parse_shortcodes' ), 11 );
// Add the hook to allow OptinMonster to process.
add_action( 'wp_footer', array( $this, 'load_optinmonster' ) );
if ( self::$live_preview || self::$live_rules_preview ) {
add_filter( 'optin_monster_api_final_output', array( $this, 'load_previews' ), 10, 2 );
add_filter( 'optin_monster_api_empty_output', array( $this, 'load_previews' ), 10, 2 );
}
if ( self::$live_preview || self::$site_verification ) {
add_action( 'wp_footer', array( $this, 'load_global_optinmonster' ) );
}
}
/**
* Enqueues the OptinMonster API script.
*
* @since 1.0.0
*/
public function api_script() {
// A hook to change the API location. Using this hook, we can force to load in header; default location is footer.
$in_footer = apply_filters( 'optin_monster_api_loading_location', true );
wp_enqueue_script(
$this->base->plugin_slug . '-api-script',
OMAPI_Urls::om_api(),
array(),
$this->base->asset_version(),
$in_footer
);
if ( version_compare( get_bloginfo( 'version' ), '4.1.0', '>=' ) ) {
add_filter( 'script_loader_tag', array( $this, 'filter_api_script' ), 10, 2 );
} else {
add_filter( 'clean_url', array( $this, 'filter_api_url' ) );
}
}
/**
* Filters the API script tag to output the JS version embed and to add a custom ID.
*
* @since 1.0.0
*
* @param string $tag The HTML script output.
* @param string $handle The script handle to target.
* @return string $tag Amended HTML script with our ID attribute appended.
*/
public function filter_api_script( $tag, $handle ) {
// If the handle is not ours, do nothing.
if ( $this->base->plugin_slug . '-api-script' !== $handle ) {
return $tag;
}
// Adjust the output to the JS version embed and to add our custom script ID.
return self::om_script_tag(
array(
'id' => 'omapi-script',
)
);
}
/**
* Filters the API script tag to add a custom ID.
*
* @since 1.0.0
*
* @param string $url The URL to filter.
* @return string $url Amended URL with our ID attribute appended.
*/
public function filter_api_url( $url ) {
// If the handle is not ours, do nothing.
if ( false === strpos( $url, str_replace( 'https://', '', OMAPI_Urls::om_api() ) ) ) {
return $url;
}
// Adjust the URL to add our custom script ID.
return "$url' async='async' id='omapi-script";
}
/**
* Loads an inline optin form (sidebar and after post) by checking against the current query.
*
* @since 1.0.0
*
* @param object $query The current main WP query object.
*/
public function load_optinmonster_inline( $query ) {
// If we are not on the main query or if in an rss feed, do nothing.
if ( ! $query->is_main_query() || $query->is_feed() ) {
return;
}
$priority = apply_filters( 'optin_monster_post_priority', 999 ); // Deprecated.
$priority = apply_filters( 'optin_monster_api_post_priority', 999 );
add_filter( 'the_content', array( $this, 'load_optinmonster_inline_content' ), $priority );
}
/**
* Filters the content to output a campaign form.
*
* @since 1.0.0
*
* @param string $content The current HTML string of main content.
* @return string $content Amended content with possibly a campaign.
*/
public function load_optinmonster_inline_content( $content ) {
global $post;
// Checking if AMP is enabled.
if ( OMAPI_Utils::is_amp_enabled() ) {
return $content;
}
// If the global $post is not set or the post status is not published, return early.
if ( empty( $post ) || isset( $post->ID ) && 'publish' !== get_post_status( $post->ID ) ) {
return $content;
}
// Don't do anything for excerpts.
// This prevents the optin accidentally being output when get_the_excerpt() or wp_trim_excerpt() is
// called by a theme or plugin, and there is no excerpt, meaning they call the_content and break us.
if (
doing_filter( 'get_the_excerpt' ) ||
doing_filter( 'wp_trim_excerpt' )
) {
return $content;
}
// Prepare variables.
$post_id = self::current_id();
$optins = $this->base->get_optins();
// If no optins are found, return early.
if ( empty( $optins ) ) {
return $content;
}
// Loop through each optin and optionally output it on the site.
foreach ( $optins as $optin ) {
if ( OMAPI_Rules::check_inline( $optin, $post_id, true ) ) {
$this->set_slug( $optin );
// Prepare the optin campaign.
$prepared = $this->prepare_campaign( $optin );
$position = get_post_meta( $optin->ID, '_omapi_auto_location', true );
$inserter = new OMAPI_Inserter( $content, $prepared );
switch ( $position ) {
case 'paragraphs':
$paragraphs = get_post_meta( $optin->ID, '_omapi_auto_location_paragraphs', true );
$content = $inserter->after_paragraph( absint( $paragraphs ) );
break;
case 'words':
$words = get_post_meta( $optin->ID, '_omapi_auto_location_words', true );
$content = $inserter->after_words( absint( $words ) );
break;
case 'above_post':
$content = $inserter->prepend();
break;
case 'below_post':
default:
$content = $inserter->append();
break;
}
}
}
// Return the content.
return $content;
}
/**
* Possibly loads a campaign on a page.
*
* @since 1.0.0
*/
public function load_optinmonster() {
/**
* Check if there are any campaigns for the site
*/
$optins = $this->base->get_optins();
if ( empty( $optins ) ) {
return;
}
$post_id = self::current_id();
$prevented = is_singular() && $post_id && get_post_meta( $post_id, 'om_disable_all_campaigns', true );
$prevented = apply_filters( 'optinmonster_prevent_all_campaigns', $prevented, $post_id );
if ( $prevented ) {
add_action( 'wp_footer', array( $this, 'prevent_all_campaigns' ), 11 );
}
$optins = $prevented ? array() : $optins;
$campaigns = array();
if ( empty( $optins ) ) {
// If no optins are found, send through filter to potentially add preview data.
$campaigns = apply_filters( 'optin_monster_api_empty_output', $campaigns, $post_id );
} else {
// Loop through each optin and optionally output it on the site.
foreach ( $optins as $campaign ) {
$rules = new OMAPI_Rules( $campaign, $post_id );
if ( $rules->should_output() ) {
$this->set_slug( $campaign );
// Prepare the optin campaign.
$campaigns[ $campaign->post_name ] = $this->prepare_campaign( $campaign );
continue;
}
$fields = $rules->field_values;
// Allow devs to filter the final output for more granular control over optin targeting.
// Devs should return the value for the slug key as false if the conditions are not met.
$campaigns = apply_filters( 'optinmonster_output', $campaigns ); // Deprecated.
$campaigns = apply_filters( 'optin_monster_output', $campaigns, $campaign, $fields, $post_id ); // Deprecated.
$campaigns = apply_filters( 'optin_monster_api_output', $campaigns, $campaign, $fields, $post_id );
}
// Run a final filter for all items.
$campaigns = apply_filters( 'optin_monster_api_final_output', $campaigns, $post_id );
}
// If the init code is empty, do nothing.
if ( empty( $campaigns ) ) {
return;
}
// Load the optins.
foreach ( (array) $campaigns as $campaign ) {
if ( $campaign ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, trusted data from post_content
echo $campaign;
}
}
$is_preview = apply_filters(
'optin_monster_should_set_campaigns_as_preview',
is_preview() || is_customize_preview()
);
if ( $is_preview ) {
remove_action( 'wp_footer', array( $this, 'prevent_all_campaigns' ), 11 );
add_action( 'wp_footer', array( $this, 'set_campaigns_as_preview' ), 99 );
}
}
/**
* Possibly loads a campaign preview on a page.
*
* @since 2.2.0
*
* @param array $campaigns Array of campaign objects to output.
* @param int $post_id The current post id.
*
* @return array Array of campaign objects to output.
*/
public function load_previews( $campaigns, $post_id ) {
if ( self::$live_preview || self::$live_rules_preview ) {
$campaign_id = sanitize_title_with_dashes( self::$live_preview ? self::$live_preview : self::$live_rules_preview );
$embed = self::om_script_tag(
array(
'id' => 'omapi-script-preview-' . $campaign_id,
'campaignId' => $campaign_id,
'accountUserId' => $this->base->get_option( 'accountUserId' ),
)
);
$embed = apply_filters( 'optin_monster_api_preview_output', $embed, $campaign_id, $post_id );
$this->set_preview_slug( $campaign_id );
$campaigns[ $campaign_id ] = $embed;
}
return $campaigns;
}
/**
* Loads the global OM code on this page.
*
* @since 1.8.0
*/
public function load_global_optinmonster() {
$option = $this->base->get_option();
// If we don't have the data we need, return early.
if ( empty( $option['accountUserId'] ) || empty( $option['accountId'] ) ) {
return;
}
$option['id'] = 'omapi-script-global';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, escaped function.
echo self::om_script_tag( $option );
}
/**
* Sets the slug for possibly parsing shortcodes.
*
* @since 1.0.0
*
* @param object $optin The optin object.
*/
public function set_slug( $optin ) {
$slug = str_replace( '-', '_', $optin->post_name );
// Set the slug.
$this->slugs[ $slug ] = array(
'slug' => $slug,
'mailpoet' => ! empty( $optin->ID ) && (bool) get_post_meta( $optin->ID, '_omapi_mailpoet', true ),
);
// Maybe set shortcode.
if ( ! empty( $optin->ID ) && get_post_meta( $optin->ID, '_omapi_shortcode', true ) ) {
$this->shortcodes[] = get_post_meta( $optin->ID, '_omapi_shortcode_output', true );
}
if ( ! empty( $this->slugs[ $slug ]['mailpoet'] ) ) {
$this->wp_mailpoet();
}
return $this;
}
/**
* Sets the preview slug for possibly parsing shortcodes.
*
* @since 2.2.0
*
* @param object $slug The campaign Id slug.
*/
public function set_preview_slug( $slug ) {
$optin = $this->base->get_optin_by_slug( $slug );
if ( empty( $optin ) ) {
$optin = (object) array(
'post_name' => $slug,
'ID' => 0,
);
}
$this->set_slug( $optin );
// Request the shortcodes from the campaign preview object.
$user_id = $this->base->get_option( 'accountUserId' );
$route = "embed/{$user_id}/{$slug}/preview/shortcodes";
$body = OMAPI_Api::build( 'v2', $route, 'GET' )->request();
if ( ! empty( $body->{$slug} ) ) {
$this->shortcodes[] = OMAPI_Save::get_shortcodes_string( $body->{$slug} );
}
return $this;
}
/**
* Maybe outputs the JS variables to parse shortcodes.
*
* @since 1.0.0
*/
public function maybe_parse_shortcodes() {
// If no slugs have been set, do nothing.
if ( empty( $this->slugs ) ) {
return;
}
// Loop through any shortcodes and output them.
foreach ( $this->shortcodes as $shortcode_string ) {
if ( empty( $shortcode_string ) ) {
continue;
}
if ( strpos( $shortcode_string, '|||' ) !== false ) {
$all_shortcode = explode( '|||', $shortcode_string );
} else { // Backwards compat.
$all_shortcode = explode( ',', $shortcode_string );
}
foreach ( $all_shortcode as $shortcode ) {
if ( empty( $shortcode ) ) {
continue;
}
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<script type="text/template" class="omapi-shortcode-helper">' . html_entity_decode( $shortcode, ENT_COMPAT, 'UTF-8' ) . '</script>';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<script type="text/template" class="omapi-shortcode-parsed omapi-encoded">' . htmlentities( do_shortcode( html_entity_decode( $shortcode, ENT_COMPAT, 'UTF-8' ) ), ENT_COMPAT, 'UTF-8' ) . '</script>';
}
}
// Output the JS variables to signify shortcode parsing is needed.
?>
<script type="text/javascript">
<?php
foreach ( $this->slugs as $slug => $data ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo 'var ' . sanitize_title_with_dashes( $slug ) . '_shortcode = true;';
}
?>
</script>
<?php
}
/**
* Sets all OM campaigns to preview mode, which disables their form fields.
*
* @since 2.2.0
*/
public function set_campaigns_as_preview() {
?>
<script type="text/javascript">
// Disable OM analytics.
window._omdisabletracking = true;
document.addEventListener('om.Optin.init', function(evt) {
// Disables form submission.
evt.detail.Optin.preview = true;
} );
</script>
<?php
}
/**
* Prevents any OM campaigns from loading if we're on a singular post
* with the `om_disable_all_campaigns` meta set.
*
* @since 2.3.0
*/
public function prevent_all_campaigns() {
?>
<script type="text/javascript">
document.addEventListener('om.Shutdown.init', function(evt) {
evt.detail.Shutdown.preventAll = true;
});
</script>
<?php
}
/**
* Possibly localizes a JS variable for output use.
*
* @since 1.0.0
*/
public function localize() {
// If no slugs have been set, do nothing.
if ( empty( $this->slugs ) ) {
return;
}
// If already localized, do nothing.
if ( $this->localized ) {
return;
}
// Set flag to true.
$this->localized = true;
// Output JS variable.
?>
<script type="text/javascript">var omapi_localized = {
ajax: '<?php echo esc_url_raw( add_query_arg( 'optin-monster-ajax-route', true, admin_url( 'admin-ajax.php' ) ) ); ?>',
nonce: '<?php echo esc_js( wp_create_nonce( 'omapi' ) ); ?>',
slugs:
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, method is escaping.
echo OMAPI_Utils::json_encode( $this->slugs );
?>
};</script>
<?php
}
/**
* Enqueues the WP mailpoet script for storing local optins.
*
* @since 1.8.2
*/
public function wp_mailpoet() {
// Only try to use the MailPoet integration if it is active.
if ( $this->base->is_mailpoet_active() ) {
wp_enqueue_script(
$this->base->plugin_slug . '-wp-mailpoet',
$this->base->url . 'assets/js/mailpoet.js',
array( 'jquery' ),
$this->base->asset_version(),
true
);
}
}
/**
* Enqueues the WP helper script for the API.
*
* @since 1.0.0
*/
public function wp_helper() {
wp_enqueue_script(
$this->base->plugin_slug . '-wp-helper',
$this->base->url . 'assets/dist/js/helper.min.js',
array(),
$this->base->asset_version(),
true
);
}
/**
* Outputs a JS variable, in the footer of the site, with information about
* the current page, and the terms in use for the display rules.
*
* @since 1.6.5
*
* @return void
*/
public function display_rules_data() {
global $wp_query;
/**
* Check if there are any campaigns for the site
*/
$optins = $this->base->get_optins();
if ( empty( $optins ) ) {
return;
}
// If already localized, do nothing.
if ( $this->data_output ) {
return;
}
// Set flag to true.
$this->data_output = true;
$tax_terms = array();
$object = get_queried_object();
$object_id = self::current_id();
$object_class = is_object( $object ) ? get_class( $object ) : '';
$object_type = '';
$object_key = '';
$post = null;
if ( 'WP_Post' === $object_class ) {
$post = $object;
$object_type = 'post';
$object_key = $object->post_type;
} elseif ( 'WP_Term' === $object_class ) {
$object_type = 'term';
$object_key = $object->taxonomy;
}
// Get the current object's terms, if applicable. Defaults to public taxonomies only.
if ( ! empty( $post->ID ) && is_singular() || ( $wp_query->is_category() || $wp_query->is_tag() || $wp_query->is_tax() ) ) {
// Should we only check public taxonomies?
$only_public = apply_filters( 'optinmonster_only_check_public_taxonomies', true, $post );
$taxonomies = get_object_taxonomies( $post, false );
if ( ! empty( $taxonomies ) && is_array( $taxonomies ) ) {
foreach ( $taxonomies as $taxonomy ) {
// Private ones should remain private and not output in the JSON blob.
if ( $only_public && ! $taxonomy->public ) {
continue;
}
$terms = get_the_terms( $post, $taxonomy->name );
if ( ! empty( $terms ) && is_array( $terms ) ) {
$tax_terms = array_merge( $tax_terms, wp_list_pluck( $terms, 'term_id' ) );
}
}
$tax_terms = wp_parse_id_list( $tax_terms );
}
}
$output = array(
'object_id' => $object_id,
'object_key' => $object_key,
'object_type' => $object_type,
'term_ids' => $tax_terms,
'wp_json' => untrailingslashit( get_rest_url() ),
'wc_active' => OMAPI_WooCommerce::is_active(),
'edd_active' => OMAPI_EasyDigitalDownloads::is_active(),
'nonce' => wp_create_nonce( 'wp_rest' ),
);
$output = apply_filters( 'optin_monster_display_rules_data_output', $output );
// Output JS variable.
?>
<script type="text/javascript">var omapi_data = <?php echo OMAPI_Utils::json_encode( $output ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;</script>
<?php
}
/**
* Prepare the optin campaign html.
*
* @since 1.5.0
*
* @param object $optin The optin post object.
*
* @return string The optin campaign html.
*/
public function prepare_campaign( $optin ) {
$optin = $this->base->validate_is_campaign_type( $optin );
$campaign_embed = ! empty( $optin->post_content )
? trim( html_entity_decode( stripslashes( $optin->post_content ), ENT_QUOTES, 'UTF-8' ), '\'' )
: '';
return apply_filters( 'optin_monster_campaign_embed_output', $campaign_embed, $optin );
}
/**
* Enqueues the WP helper script if relevant optin fields are found.
*
* @since 1.5.0
*
* @param bool $should_output Whether it should output.
* @param OMAPI_Rules $rules OMAPI_Rules object.
*
* @return array
*/
public function enqueue_helper_js_if_applicable( $should_output, $rules ) {
// Check to see if we need to load the WP API helper script.
if ( $should_output ) {
if ( ! $rules->field_empty( 'mailpoet' ) ) {
$this->wp_mailpoet();
}
$this->wp_helper();
}
return $should_output;
}
/**
* Get the current page/post's post id.
*
* @since 1.6.9
*
* @return int
*/
public static function current_id() {
$object = get_queried_object();
if ( is_object( $object ) && ! $object instanceof WP_Post ) {
return 0;
}
$post_id = get_queried_object_id();
if ( ! $post_id ) {
if ( 'page' === get_option( 'show_on_front' ) ) {
$post_id = get_option( 'page_for_posts' );
}
}
return $post_id;
}
/**
* AJAX callback for returning WooCommerce cart information.
*
* @since 1.7.0
* @since 2.8.0 All the logic was moved to OMAPI_WooCommerce class.
*
* @deprecated 2.8.0 Use `OMAPI_WooCommerce->get_cart()` instead.
*
* @return array An array of WooCommerce cart data.
*/
public function woocommerce_cart() {
_deprecated_function( __FUNCTION__, '2.8.0', 'OMAPI_WooCommerce->get_cart()' );
return $this->base->woocommerce->get_cart();
}
/**
* Get the OptinMonster embed script JS.
*
* @since 1.9.8
*
* @param array $args Array of arguments for the script, including
* optional user id, account id, and script id.
*
* @return string The embed script JS.
*/
public static function om_script_tag( $args = array() ) {
// Set up the script variables.
$src = OMAPI_Urls::om_api();
$script_id = empty( $args['id'] ) ? '' : $args['id'];
$account_id = empty( $args['accountId'] ) ? '' : $args['accountId'];
$campaign_id = empty( $account_id ) && ! empty( $args['campaignId'] ) ? $args['campaignId'] : '';
$user_id = empty( $args['accountUserId'] ) ? '' : $args['accountUserId'];
$api_cname = OMAPI::get_instance()->get_option( 'apiCname' );
$env = defined( 'OPTINMONSTER_ENV' ) ? OPTINMONSTER_ENV : '';
$tag = '<script>';
$tag .= '(function(d){';
$tag .= 'var s=d.createElement("script");';
$tag .= 's.type="text/javascript";';
$tag .= 's.src="%1$s";';
$tag .= 's.async=true;';
$tag .= empty( $script_id ) ? '' : 's.id="%2$s";';
$tag .= empty( $account_id ) ? '' : 's.dataset.account="%3$s";';
$tag .= empty( $campaign_id ) ? '' : 's.dataset.campaign="%4$s";';
$tag .= empty( $user_id ) ? '' : 's.dataset.user="%5$s";';
$tag .= empty( $api_cname ) ? '' : 's.dataset.api="%6$s";';
$tag .= empty( $env ) ? '' : 's.dataset.env="%7$s";';
$tag .= 'd.getElementsByTagName("head")[0].appendChild(s);';
$tag .= '})(document);';
$tag .= '</script>';
$tag = sprintf(
$tag,
esc_url_raw( $src ),
esc_attr( $script_id ),
esc_attr( $account_id ),
esc_attr( $campaign_id ),
esc_attr( $user_id ),
esc_attr( $api_cname ),
esc_attr( $env )
);
return apply_filters( 'optin_monster_embed_script_tag', $tag, $args );
}
}
Pages.php 0000644 00000041424 15153673736 0006341 0 ustar 00 <?php
/**
* Pages class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Erik Jonasson
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Pages class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Erik Jonasson
*/
class OMAPI_Pages {
/**
* Holds the class object.
*
* @since 1.9.10
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.9.10
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.9.10
*
* @var OMAPI
*/
public $base;
/**
* The admin title tag format.
*
* @since 2.0.0
*
* @var string
*/
public $title_tag = '';
/**
* The registered pages.
*
* @since 2.0.0
*
* @var array
*/
protected $pages = array();
/**
* Sets our object instance and base class instance.
*
* @since 1.9.10
*/
public function __construct() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Setup any hooks.
*
* @since 2.0.0
*/
public function setup() {
add_filter( 'admin_title', array( $this, 'store_admin_title' ), 999999, 2 );
add_filter( 'admin_body_class', array( $this, 'admin_body_classes' ) );
}
/**
* Stores the admin title tag format to be used in JS.
*
* @since 2.0.0
*
* @param string $admin_title The admin title.
* @param string $title The title.
*
* @return string The admin title.
*/
public function store_admin_title( $admin_title, $title ) {
$this->title_tag = str_replace( $title, '{replaceme}', $admin_title );
return $admin_title;
}
/**
* Returns an array of our registered pages.
* If we need more pages, add them to this array
*
* @return array Array of page objects.
*/
public function get_registered_pages() {
if ( empty( $this->pages ) ) {
$this->pages['optin-monster-campaigns'] = array(
'name' => __( 'Campaigns', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-templates'] = array(
'name' => __( 'Templates', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-playbooks'] = array(
'name' => __( 'Playbooks', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-monsterleads'] = array(
'name' => __( 'Subscribers', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-integrations'] = array(
'name' => __( 'Integrations', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-trustpulse'] = array(
'name' => __( 'TrustPulse', 'optin-monster-api' ),
);
$this->pages['optin-monster-settings'] = array(
'name' => __( 'Settings', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-personalization'] = array(
'name' => __( 'Personalization', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-university'] = array(
'name' => __( 'University', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$this->pages['optin-monster-about'] = array(
'name' => __( 'About Us', 'optin-monster-api' ),
'app' => true,
'callback' => array( $this, 'render_app_loading_page' ),
);
$bfcmitem = $this->should_show_bfcf_menu_item();
// If user upgradeable, add an upgrade link to menu.
if ( $this->base->can_show_upgrade() ) {
$this->pages['optin-monster-upgrade'] = array(
'name' => 'vbp_pro' === $this->base->get_level()
? esc_html__( 'Upgrade to Growth', 'optin-monster-api' )
: esc_html__( 'Upgrade to Pro', 'optin-monster-api' ),
'redirect' => esc_url_raw( OMAPI_Urls::upgrade( 'pluginMenu' ) ),
// Only highlight if we don't have a bfcm item to highlight.
'highlight' => ! $bfcmitem,
'callback' => '__return_null',
);
add_filter( 'om_add_inline_script', array( $this, 'add_upgrade_url_to_js' ), 10, 2 );
}
if ( $bfcmitem ) {
$this->pages['optin-monster-bfcm'] = $bfcmitem;
}
foreach ( $this->pages as $slug => $page ) {
$this->pages[ $slug ]['slug'] = $slug;
}
}
return $this->pages;
}
/**
* Should we show the Black Friday menu item.
*
* @since 2.11.0
*
* @return bool|array
*/
public function should_show_bfcf_menu_item() {
$now = new DateTime( 'now', new DateTimeZone( 'America/New_York' ) );
$thanksgiving = strtotime( 'fourth Thursday of November' );
$promo_start = gmdate( 'Y-m-d 10:00:00', $thanksgiving - ( 3 * DAY_IN_SECONDS ) );
$bf_end = gmdate( 'Y-m-d 23:59:59', strtotime( 'first Tuesday of December' ) );
$is_bf_window = OMAPI_Utils::date_within( $now, $promo_start, $bf_end );
$year = $now->format( 'Y' );
if ( $is_bf_window ) {
$url = OMAPI_Urls::marketing(
'pricing',
array(
'utm_medium' => 'pluginMenu',
'utm_campaign' => "BF{$year}",
)
);
if ( OMAPI_ApiKey::has_credentials() ) {
$url = OMAPI_Urls::upgrade(
'pluginMenu',
'',
'',
array(
'utm_campaign' => "BF{$year}",
'feature' => false,
)
);
}
$cyber_monday = $thanksgiving + ( 4 * DAY_IN_SECONDS );
$cm_start = gmdate( 'Y-m-d 10:00:00', $cyber_monday );
$is_cm_window = ! OMAPI_Utils::date_before( $now, $cm_start );
return array(
'name' => $is_cm_window
? esc_html__( 'Cyber Monday!', 'optin-monster-api' )
: esc_html__( 'Black Friday!', 'optin-monster-api' ),
'alternate-name' => $is_cm_window
? esc_html__( 'Cyber Monday Sale!', 'optin-monster-api' )
: esc_html__( 'Black Friday Sale!', 'optin-monster-api' ),
'redirect' => esc_url_raw( $url ),
'callback' => '__return_null',
'highlight' => true,
);
}
$green_monday = strtotime( 'second Monday of December' );
$is_gm_window = OMAPI_Utils::date_within(
$now,
gmdate( 'Y-m-d 10:00:00', $green_monday ),
gmdate( 'Y-m-d 23:59:59', $green_monday )
);
if ( $is_gm_window ) {
$url = OMAPI_Urls::marketing(
'pricing-wp/',
array(
'utm_medium' => 'pluginMenu',
'utm_campaign' => "BF{$year}",
)
);
if ( OMAPI_ApiKey::has_credentials() && ! $this->base->is_lite_user() ) {
$url = OMAPI_Urls::upgrade(
'pluginMenu',
'',
'',
array(
'utm_campaign' => "BF{$year}",
'feature' => false,
)
);
}
return array(
'name' => esc_html__( 'Green Monday!', 'optin-monster-api' ),
'redirect' => esc_url_raw( $url ),
'callback' => '__return_null',
'highlight' => true,
);
}
return false;
}
/**
* Add the menu upgrade url to the data sento to the global JS file.
*
* @since 2.4.0
*
* @param array $data Array of data for JS.
* @param string $handle The script handle.
*
* @return $data Array of data for JS.
*/
public function add_upgrade_url_to_js( $data, $handle ) {
if ( $this->base->plugin_slug . '-global' === $handle ) {
$data['upgradeUrl'] = esc_url_raw( OMAPI_Urls::upgrade( 'pluginMenu' ) );
}
return $data;
}
/**
* Returns an array of our registered JS app pages.
*
* @return array Array of page objects.
*/
public function get_registered_app_pages() {
return wp_list_filter( $this->get_registered_pages(), array( 'app' => true ) );
}
/**
* Whether given page slug is one of our registered JS app pages.
*
* @param string $page_slug Page slug.
*
* @return boolean
*/
public function is_registered_app_page( $page_slug ) {
$pages = wp_list_pluck( $this->get_registered_app_pages(), 'slug' );
$pages[] = 'optin-monster-api-settings';
$pages[] = 'optin-monster-dashboard';
return in_array( $page_slug, $pages, true );
}
/**
* Registers our submenu pages
*
* @param string $parent_page_name The Parent Page Name.
*
* @return array Array of hook ids.
*/
public function register_submenu_pages( $parent_page_name ) {
$pages = $this->get_registered_pages();
$hooks = array();
foreach ( $pages as $page ) {
if ( ! empty( $page['callback'] ) ) {
$parent_slug = $parent_page_name;
if ( ! empty( $page['hidden'] ) ) {
$parent_slug .= '-hidden';
}
$menu_title = ! empty( $page['menu'] ) ? $page['menu'] : $page['name'];
if ( $this->maybe_add_new_badge( $page ) ) {
$menu_title .= ' <span class="omapi-menu-new">New!<span>';
} elseif ( ! empty( $page['highlight'] ) ) {
$menu_title = '<span class="om-menu-highlight">' . $menu_title . '</span>';
}
$hook = add_submenu_page(
$parent_slug, // $parent_slug
$page['name'], // $page_title
$menu_title,
$this->base->access_capability( $page['slug'] ),
$page['slug'],
$page['callback']
);
$hooks[] = $hook;
if ( ! empty( $page['redirect'] ) ) {
add_action( 'load-' . $hook, array( $this, 'handle_redirect' ), 999 );
}
}
}
return $hooks;
}
/**
* Handle redirect for registered page.
*
* @since 2.0.0
*
* @return void
*/
public function handle_redirect() {
global $plugin_page;
$pages = $this->get_registered_pages();
if (
empty( $pages[ $plugin_page ]['redirect'] )
|| is_bool( $pages[ $plugin_page ]['redirect'] )
) {
$this->base->menu->redirect_to_dashboard();
return;
}
add_filter( 'allowed_redirect_hosts', 'OMAPI_Urls::allowed_redirect_hosts' );
wp_safe_redirect( esc_url_raw( $pages[ $plugin_page ]['redirect'] ) );
exit;
}
/**
* Adds om app admin body classes
*
* @since 2.0.0
*
* @param string $classes The admin body classes.
*
* @return string The admin body classes.
*/
public function admin_body_classes( $classes ) {
global $plugin_page;
$classes = explode( ' ', $classes );
$classes = array_filter( $classes );
$classes = array_map( 'trim', $classes );
if ( $this->is_registered_app_page( $plugin_page ) ) {
$classes[] = 'omapi-app';
$classes[] = 'omapi-app-' . str_replace( 'optin-monster-', '', $plugin_page );
}
$classes = implode( ' ', $classes );
return $classes;
}
/**
* Registers our submenu pages, but redirects to main page when navigating to them.
*
* @since 1.9.10
*
* @param string $parent_page_name The Parent Page Name.
* @return void
*/
public function register_submenu_redirects( $parent_page_name ) {
$hooks = $this->register_submenu_pages( $parent_page_name . '-hidden' );
foreach ( $hooks as $hook ) {
add_action( 'load-' . $hook, array( $this->base->menu, 'redirect_to_dashboard' ) );
}
}
/**
* Outputs the OptinMonster about-us page.
*
* @since 1.9.10
*/
public function render_app_loading_page() {
$this->load_scripts();
echo '<div id="om-app">';
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->base->output_view( 'archie-loading.php' );
echo '</div>';
}
/**
* Loads the OptinMonster app scripts.
*
* @since 2.0.0
*
* @param array $args Array of arguments to pass to the app.
*
* @return OMAPI_AssetLoader|false
*/
public function load_scripts( $args = array() ) {
$path = 'vue/dist';
$loader = new OMAPI_AssetLoader( trailingslashit( dirname( $this->base->file ) ) . $path );
try {
add_filter( 'optin_monster_should_enqueue_asset', array( $this, 'should_enqueue' ), 10, 2 );
$loader->enqueue(
array(
'base_url' => $this->base->url . $path,
'version' => $this->base->asset_version(),
)
);
$pages = array(
'optin-monster-dashboard' => __( 'Dashboard', 'optin-monster-api' ),
);
foreach ( $this->get_registered_pages() as $page ) {
$pages[ $page['slug'] ] = ! empty( $page['title'] ) ? $page['title'] : $page['name'];
}
$creds = $this->base->get_api_credentials();
$admin_parts = wp_parse_url( admin_url( 'admin.php' ) );
$url_parts = wp_parse_url( $this->base->url );
$current_user = wp_get_current_user();
$plugins = new OMAPI_Plugins();
$defaults = array(
'key' => ! empty( $creds['apikey'] ) ? $creds['apikey'] : '',
'nonce' => wp_create_nonce( 'wp_rest' ),
'siteId' => $this->base->get_site_id(),
'siteIds' => $this->base->get_site_ids(),
'wpUrl' => trailingslashit( site_url() ),
'adminUrl' => OMAPI_Urls::admin(),
'upgradeParams' => OMAPI_Urls::upgrade_params( null, null ),
'restUrl' => rest_url(),
'adminPath' => $admin_parts['path'],
'apijsUrl' => OPTINMONSTER_APIJS_URL,
'omAppUrl' => untrailingslashit( OPTINMONSTER_APP_URL ),
'marketing' => untrailingslashit( OPTINMONSTER_URL ),
'omAppApiUrl' => untrailingslashit( OPTINMONSTER_API_URL ),
'omAppCdnURL' => untrailingslashit( OPTINMONSTER_CDN_URL ),
'newCampaignUrl' => untrailingslashit( esc_url_raw( admin_url( 'admin.php?page=optin-monster-templates' ) ) ),
'shareableUrl' => untrailingslashit( OPTINMONSTER_SHAREABLE_LINK ),
'pluginPath' => $url_parts['path'],
'omStaticDataKey' => 'omWpApi',
'isItWp' => true,
// 'scriptPath' => $path,
'pages' => $pages,
'titleTag' => html_entity_decode( $this->title_tag, ENT_QUOTES, 'UTF-8' ),
'isWooActive' => OMAPI_WooCommerce::is_active(),
'isWooConnected' => OMAPI_WooCommerce::is_connected(),
'isEddActive' => OMAPI_EasyDigitalDownloads::is_active(),
'isEddConnected' => OMAPI_EasyDigitalDownloads::is_connected(),
'isWPFormsActive' => OMAPI_WPForms::is_active(),
'blogname' => esc_attr( get_option( 'blogname' ) ),
'userEmail' => esc_attr( $current_user->user_email ),
'userFirstName' => esc_attr( $current_user->user_firstname ),
'userLastName' => esc_attr( $current_user->user_lastname ),
'betaVersion' => $this->base->beta_version(),
'pluginVersion' => $this->base->version,
'pluginsInfo' => $plugins->get_active_plugins_header_value(),
'partnerId' => OMAPI_Partners::get_id(),
'partnerUrl' => OMAPI_Partners::has_partner_url(),
'referredBy' => OMAPI_Partners::referred_by(),
'showReview' => $this->base->review->should_show_review(),
'timezone' => wp_timezone_string(),
);
// Add the onboarding connection token if it exists.
$connection_token = $this->base->get_option( 'connectionToken' );
if ( ! empty( $connection_token ) ) {
$args['connectionToken'] = $connection_token;
}
$onboarding_plugins = $this->base->get_option( 'onboardingPlugins', '', array() );
if ( ! empty( $onboarding_plugins ) ) {
$args['onboardingPlugins'] = $onboarding_plugins;
}
$args['isWpmlActive'] = OMAPI_Utils::is_wpml_active();
$args['wpmlDomains'] = OMAPI_Utils::get_wpml_language_domains();
$js_args = wp_parse_args( $args, $defaults );
$js_args = apply_filters( 'optin_monster_campaigns_js_api_args', $js_args );
$loader->localize( $js_args );
return $loader;
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
} catch ( \Exception $e ) {
// We don't want to throw an exception here, since it will break the output.
}
return false;
}
/**
* Determine if given asset should be enqueued.
*
* We only want app/common, since remaining assets are chunked/lazy-loaded.
*
* @since 2.0.0
*
* @param bool $should Whether asset should be enqueued.
* @param string $handle The asset handle.
*
* @return bool Whether asset should be enqueued.
*/
public function should_enqueue( $should, $handle ) {
$allowed = array(
'wp-om-app',
'wp-om-common',
);
foreach ( $allowed as $search ) {
if ( 0 === strpos( $handle, $search ) ) {
return true;
}
}
return false;
}
/**
* Determine if a page should have a "new" badge.
*
* Example:
*
* $this->pages['optin-monster-playbooks'] = [
* 'name' => __( 'Playbooks', 'optin-monster-api' ),
* 'app' => true,
* 'callback' => [ $this, 'render_app_loading_page' ],
* 'new_badge_period' => [
* 'start' => '2023-02-02 00:00:00',
* 'end' => '2023-03-03 59:59:59',
* ],
* ];
*
* @param array $page The page data.
*
* @return boolean True if the given page should have a new badge
*/
public function maybe_add_new_badge( $page ) {
if ( empty( $page['new_badge_period']['start'] ) ) {
return false;
}
$now = new DateTime( 'now', new DateTimeZone( 'America/New_York' ) );
return OMAPI_Utils::date_within(
$now,
$page['new_badge_period']['start'],
$page['new_badge_period']['end']
);
}
}
Partners.php 0000644 00000011305 15153673736 0007073 0 ustar 00 <?php
/**
* Partners class.
*
* @since 2.0.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The Partners class.
*
* @since 2.0.0
*/
class OMAPI_Partners {
/**
* The OM landing page url.
*
* @since 1.8.4
*/
const LANDING_URL = 'https://optinmonster.com/wp/?utm_source=orgplugin&utm_medium=link&utm_campaign=wpdashboard';
/**
* The SaS affiliate url.
*
* @since 2.0.0
*/
const SAS_URL = 'https://www.shareasale.com/r.cfm?u=%1$s&b=601672&m=49337&afftrack=&urllink=optinmonster.com';
/**
* Get the SAS Partner ID.
*
* 3 ways to specify an ID, ordered by highest to lowest priority:
* - add_filter( 'optinmonster_sas_id', function() { return 1234; } );
* - define( 'OPTINMONSTER_SAS_ID', 1234 );
* - get_option( 'optinmonster_sas_id' ); (with the option being in the
* wp_options table) If an ID is present, returns the affiliate link
* with the affiliate ID.
*
* @since 2.0.0
*
* @return string
*/
public static function get_sas_id() {
$sas_id = '';
// Check if sas ID is a constant.
if ( defined( 'OPTINMONSTER_SAS_ID' ) ) {
$sas_id = OPTINMONSTER_SAS_ID;
}
// Now run any filters that may be on the sas ID.
$sas_id = apply_filters( 'optinmonster_sas_id', $sas_id );
/**
* If we still don't have a sas ID by this point
* check the DB for an option
*/
if ( empty( $sas_id ) ) {
$sas_id = get_option( 'optinmonster_sas_id', $sas_id );
}
return $sas_id;
}
/**
* Get the trial Partner ID.
*
* 3 ways to specify an ID, ordered by highest to lowest priority:
* - add_filter( 'optinmonster_trial_id', function() { return 1234; } );
* - define( 'OPTINMONSTER_TRIAL_ID', 1234 );
* - get_option( 'optinmonster_trial_id' ); (with the option being in the
* wp_options table) If an ID is present, returns the affiliate link
* with the affiliate ID.
*
* @since 2.0.0
*
* @return string
*/
public static function get_trial_id() {
$trial_id = '';
// Check if trial ID is a constant.
if ( defined( 'OPTINMONSTER_TRIAL_ID' ) ) {
$trial_id = OPTINMONSTER_TRIAL_ID;
}
// Now run any filters that may be on the trial ID.
$trial_id = apply_filters( 'optinmonster_trial_id', $trial_id );
/**
* If we still don't have a trial ID by this point
* check the DB for an option
*/
if ( empty( $trial_id ) ) {
$trial_id = get_option( 'optinmonster_trial_id', $trial_id );
}
return $trial_id;
}
/**
* Get the affiliate url for given id.
*
* @since 1.8.4
*
* @param mixed $partner_id The Partner ID.
*
* @return string The affilaite url.
*/
protected static function get_affiliate_url( $partner_id ) {
return sprintf( self::SAS_URL, rawurlencode( trim( $partner_id ) ) );
}
/**
* Get the partner url.
*
* Not used directly, but parsed for query args.
*
* @since 2.0.0
*
* @return boolean
*/
protected static function get_partner_url() {
$id = self::get_trial_id();
$type = 'trial';
if ( empty( $id ) ) {
$id = self::get_sas_id();
$type = 'sas';
}
// Return the regular WP landing page by default.
$url = self::LANDING_URL;
// Return the trial link if we have a trial ID.
if ( ! empty( $id ) ) {
$url = self::get_affiliate_url( $id );
}
return apply_filters(
'optin_monster_action_link',
$url,
array(
'type' => $type,
'id' => $id,
)
);
}
/**
* Returns partner url, if it exists.
*
* Not used directly, but parsed for query args.
*
* @since 2.0.0
*
* @return string|boolean
*/
public static function has_partner_url() {
$url = self::get_partner_url();
return false === strpos( $url, 'optinmonster.com/wp' )
? $url
: false;
}
/**
* Get the Partner ID.
*
* @since 2.0.0
* @since 2.15.0 Fallback to parsing the partner url for the ID.
*
* @return string
*/
public static function get_id() {
$id = self::get_trial_id();
if ( empty( $id ) ) {
$id = self::get_sas_id();
}
if ( empty( $id ) ) {
// Try to get the ID from the partner url.
$url = self::has_partner_url();
if ( $url ) {
$parsed = wp_parse_url( $url );
if (
! empty( $parsed['host'] )
// Only get the ID if it's a shareasale url.
&& false !== stripos( $parsed['host'], 'shareasale.com' )
&& ! empty( $parsed['query'] )
) {
$args = wp_parse_args( $parsed['query'] );
if ( ! empty( $args['u'] ) ) {
$id = $args['u'];
}
}
}
}
return $id;
}
/**
* Get the referrer, if stored.
*
* @since 2.10.0
*
* @return string Referrer
*/
public static function referred_by() {
return sanitize_text_field( get_option( 'optinmonster_referred_by', '' ) );
}
}
Plugins/Plugin.php 0000644 00000013763 15153673736 0010166 0 ustar 00 <?php
/**
* AM Plugins Plugin class.
*
* @since 2.10.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* AM Plugins class.
*
* @since 2.10.0
*/
class OMAPI_Plugins_Plugin implements JsonSerializable, ArrayAccess {
/**
* Plugin registry.
*
* @since 2.10.0
*
* @var OMAPI_Plugins_Plugin[]
*/
protected static $plugins = array();
/**
* The plugin id.
*
* @since 2.10.0
*
* @var string
*/
protected $id = '';
/**
* Array of plugin data.
*
* @since 2.10.0
*
* @var array
*/
protected $plugin_data = array();
/**
* Constructor
*
* @since 2.10.0
*
* @param string $plugin_id The plugin id.
* @param OMAPI_Plugins $plugins The Plugins object instance.
*
* @return void
* @throws Exception When ID is not found in plugins data.
*/
protected function __construct( $plugin_id, OMAPI_Plugins $plugins ) {
$this->id = $plugin_id;
$data = $plugins->get_list();
if ( empty( $data[ $this->id ] ) ) {
throw new Exception( 'Plugin info not found.' );
}
$this->plugin_data = $data[ $this->id ];
$this->get_data();
self::$plugins[ $this->id ] = $this;
}
/**
* Get instance of the Plugin and store it.
*
* @since 2.10.0
*
* @param string $id The plugin ID.
*
* @return OMAPI_Plugins_Plugin
*/
public static function get( $id ) {
static $plugins = null;
if ( null === $plugins ) {
$plugins = new OMAPI_Plugins();
}
if ( ! isset( self::$plugins[ $id ] ) ) {
new self( $id, $plugins );
}
return self::$plugins[ $id ];
}
/**
* Gets the info for this AM plugin.
*
* @since 2.10.0
*
* @return array plugin data.
*/
public function get_data() {
if ( ! isset( $this->plugin_data['status'] ) ) {
$this->add_status_data();
}
return $this->plugin_data;
}
/**
* Check if plugin is active/installed.
*
* @since 2.10.0
*
* @return self
*/
public function add_status_data() {
list( $installed, $active, $which ) = $this->exists_checks();
$this->plugin_data['status'] = ! $installed
? __( 'Not Installed', 'optin-monster-api' )
: (
$active
? __( 'Active', 'optin-monster-api' )
: __( 'Inactive', 'optin-monster-api' )
);
$this->plugin_data['installed'] = $installed;
$this->plugin_data['active'] = $installed && $active;
$this->plugin_data['which'] = $which;
return $this;
}
/**
* Check if plugin is active/installed.
*
* @since 2.10.0
*
* @return array
*/
protected function exists_checks() {
// Check if plugin is active by checking if class/function/constant exists.
// This gets around limitations with the normal `is_plugin_active` checks.
// Those limitations include:
// - The install path could be modified (e.g. using -beta version, or version downloaded from github)
// - The plugin is considered "active", but the actual plugin has been deleted from the server.
$active = $this->plugin_code_exists_checks();
// Otherwise, check if it exists in the list of plugins.
$which = $this->is_installed();
$installed = ! empty( $which );
return array( $installed, $active, $which );
}
/**
* Check if plugin is active via code checks.
*
* @since 2.10.0
*
* @return bool
*/
protected function plugin_code_exists_checks() {
// Loop through all checks.
foreach ( $this->plugin_data['check'] as $check_type => $to_check ) {
// Now loop through all the things to checks.
foreach ( (array) $to_check as $thing_to_check ) {
switch ( $check_type ) {
case 'function':
if ( function_exists( $thing_to_check ) ) {
return true;
}
break;
case 'class':
if ( class_exists( $thing_to_check ) ) {
return true;
}
break;
case 'constant':
if ( defined( $thing_to_check ) ) {
return true;
}
break;
}
}
}
return false;
}
/**
* Check if plugin is installed (exists in array of plugin data).
*
* @since 2.10.0
*
* @return bool|string
*/
protected function is_installed() {
static $all_plugins = null;
if ( null === $all_plugins ) {
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$all_plugins = get_plugins();
}
if ( ! empty( $this->plugin_data['pro']['plugin'] ) ) {
if ( false !== strpos( $this->plugin_data['pro']['plugin'], '~' ) ) {
foreach ( $all_plugins as $key => $data ) {
if ( preg_match( $this->plugin_data['pro']['plugin'], $key ) ) {
return $key;
}
}
} elseif ( array_key_exists( $this->plugin_data['pro']['plugin'], $all_plugins ) ) {
return $this->plugin_data['pro']['plugin'];
}
}
if ( array_key_exists( $this->plugin_data['id'], $all_plugins ) ) {
return 'default';
}
return false;
}
/**
* Whether an offset exists.
*
* @since 2.10.0
*
* @param mixed $offset An offset to check for.
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists( $offset ) {
return isset( $this->plugin_data[ $offset ] );
}
/**
* Offset to retrieve.
*
* @since 2.10.0
*
* @param mixed $offset The offset to retrieve.
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet( $offset ) {
return isset( $this->plugin_data[ $offset ] ) ? $this->plugin_data[ $offset ] : null;
}
/**
* Assign a value to the specified offset (N/A).
*
* @since 2.10.0
*
* @param mixed $offset The offset to assign the value to (N/A).
* @param mixed $value The value to set (N/A).
*
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet( $offset, $value ) {}
/**
* Unset an offset
*
* @since 2.10.0
*
* @param mixed $offset The offset to unset (N/A).
*
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetUnset( $offset ) {}
/**
* Specify data which should be serialized to JSON
*
* @since 2.10.0
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return $this->plugin_data;
}
}
Plugins.php 0000644 00000060533 15153673736 0006725 0 ustar 00 <?php
/**
* AM Plugins class.
*
* @since 1.9.10
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* AM Plugins class.
*
* @since 1.9.10
*/
class OMAPI_Plugins {
/**
* The Base OMAPI Object
*
* @since 1.8.0
*
* @var OMAPI
*/
protected $base;
/**
* Plugins array.
*
* @since 2.10.0
*
* @var array
*/
protected static $plugins = array();
/**
* Constructor.
*
* @since 1.8.0
*/
public function __construct() {
$this->base = OMAPI::get_instance();
}
/**
* Gets the list of AM plugins.
*
* @since 2.0.0
*
* @return array List of AM plugins.
*/
public function get_list() {
if ( empty( self::$plugins ) ) {
self::$plugins = array(
'wpforms-lite/wpforms.php' => array(
'slug' => 'wpforms',
'icon' => $this->base->url . 'assets/images/about/plugin-wp-forms.png',
'class' => 'wpforms-litewpformsphp',
'check' => array( 'function' => 'wpforms' ),
'name' => 'WPForms',
'desc' => __( 'The best drag & drop WordPress form builder. Easily create beautiful contact forms, surveys, payment forms, and more with our 1300+ form templates. Trusted by over 6 million websites as the best forms plugin.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/wpforms-lite.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'wpforms/wpforms.php',
'name' => 'WPForms Pro',
'url' => 'https://wpforms.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'google-analytics-for-wordpress/googleanalytics.php' => array(
'slug' => 'monsterinsights',
'icon' => $this->base->url . 'assets/images/about/plugin-mi.png',
'class' => 'google-analytics-for-wordpressgoogleanalyticsphp',
'check' => array( 'function' => 'MonsterInsights' ),
'name' => 'MonsterInsights',
'desc' => __( 'The leading WordPress analytics plugin that shows you how people find and use your website, so you can make data driven decisions to grow your business. Properly set up Google Analytics without writing code.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'google-analytics-premium/googleanalytics-premium.php',
'name' => 'MonsterInsights Pro',
'url' => 'https://www.monsterinsights.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'wp-mail-smtp/wp_mail_smtp.php' => array(
'slug' => 'wpmailsmtp',
'icon' => $this->base->url . 'assets/images/about/plugin-wp-mail-smtp.png',
'class' => 'wp-mail-smtpwp-mail-smtpphp',
'check' => array( 'function' => 'wp_mail_smtp' ),
'name' => 'WP Mail SMTP',
'desc' => __( 'Improve your WordPress email deliverability and make sure that your website emails reach user’s inbox with the #1 SMTP plugin for WordPress. Over 3 million websites use it to fix WordPress email issues.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/wp-mail-smtp.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'wp-mail-smtp-pro/wp_mail_smtp.php',
'name' => 'WP Mail SMTP',
'url' => 'https://wpmailsmtp.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'all-in-one-seo-pack/all_in_one_seo_pack.php' => array(
'slug' => 'aioseo',
'icon' => $this->base->url . 'assets/images/about/plugin-aioseo.png',
'class' => 'all-in-one-seo-packall-in-one-seo-packphp',
'check' => array(
'constant' => array(
'AIOSEOP_VERSION',
'AIOSEO_VERSION',
),
),
'name' => 'AIOSEO',
'desc' => __( 'The original WordPress SEO plugin and toolkit that improves your website’s search rankings. Comes with all the SEO features like Local SEO, WooCommerce SEO, sitemaps, SEO optimizer, schema, and more.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/all-in-one-seo-pack.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
'name' => 'All-in-One SEO Pack',
'url' => 'https://aioseo.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'coming-soon/coming-soon.php' => array(
'slug' => 'seedprod',
'icon' => $this->base->url . 'assets/images/about/plugin-seedprod.png',
'class' => 'coming-sooncoming-soonphp',
'check' => array(
'constant' => array(
'SEEDPROD_PRO_VERSION',
'SEEDPROD_VERSION',
),
),
'name' => 'SeedProd',
'desc' => __( 'The fastest drag & drop landing page builder for WordPress. Create custom landing pages without writing code, connect them with your CRM, collect subscribers, and grow your audience. Trusted by 1 million sites.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/coming-soon.zip',
'action' => 'install',
'pro' => array(
'plugin' => '~seedprod-coming-soon-pro*~',
'name' => 'SeedProd',
'url' => 'https://www.seedprod.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'rafflepress/rafflepress.php' => array(
'slug' => 'rafflepress',
'icon' => $this->base->url . 'assets/images/about/plugin-rafflepress.png',
'class' => 'rafflepressrafflepressphp',
'check' => array(
'constant' => array(
'RAFFLEPRESS_VERSION',
'RAFFLEPRESS_PRO_VERSION',
),
),
'name' => 'RafflePress',
'desc' => __( 'Turn your website visitors into brand ambassadors! Easily grow your email list, website traffic, and social media followers with the most powerful giveaways & contests plugin for WordPress.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/rafflepress.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'rafflepress-pro/rafflepress-pro.php',
'name' => 'RafflePress',
'url' => 'https://rafflepress.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'trustpulse-api/trustpulse.php' => array(
'slug' => 'trustpulse',
'icon' => $this->base->url . 'assets/images/about/plugin-trustpulse.png',
'class' => 'trustpulseapitrustpulsephp',
'check' => array( 'class' => 'TPAPI' ),
'name' => 'TrustPulse',
'desc' => __( 'TrustPulse is the honest marketing platform that leverages and automates the real power of social proof to instantly increase trust, conversions and sales.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/trustpulse-api.zip',
'action' => 'install',
'pro' => array(
'plugin' => '',
'name' => 'TrustPulse',
'url' => 'https://trustpulse.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'pushengage/main.php' => array(
'slug' => 'pushengage',
'icon' => $this->base->url . 'assets/images/about/plugin-pushengage.png',
'class' => 'pushengagemainphp',
'check' => array( 'constant' => 'PUSHENGAGE_VERSION' ),
'name' => 'PushEngage',
'desc' => __( 'Connect with your visitors after they leave your website with the leading web push notification software. Over 10,000+ businesses worldwide use PushEngage to send 15 billion notifications each month.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/pushengage.zip',
'action' => 'install',
),
'instagram-feed/instagram-feed.php' => array(
'slug' => 'sbinstagram',
'icon' => $this->base->url . 'assets/images/about/plugin-sb-instagram.png',
'check' => array( 'constant' => 'SBIVER' ),
'name' => 'Smash Balloon Instagram Feeds',
'desc' => __( 'Easily display Instagram content on your WordPress site without writing any code. Comes with multiple templates, ability to show content from multiple accounts, hashtags, and more. Trusted by 1 million websites.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/instagram-feed.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'instagram-feed-pro/instagram-feed.php',
'name' => 'Smash Balloon Instagram Feeds Pro',
'url' => 'https://smashballoon.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'custom-facebook-feed/custom-facebook-feed.php' => array(
'slug' => 'sbfacebook',
'icon' => $this->base->url . 'assets/images/about/plugin-sb-fb.png',
'check' => array( 'constant' => 'CFFVER' ),
'name' => 'Smash Balloon Facebook Feeds',
'desc' => __( 'Easily display Facebook content on your WordPress site without writing any code. Comes with multiple templates, ability to embed albums, group content, reviews, live videos, comments, and reactions.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/custom-facebook-feed.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'custom-facebook-feed-pro/custom-facebook-feed.php',
'name' => 'Smash Balloon Facebook Feeds Pro',
'url' => 'https://smashballoon.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'feeds-for-youtube/youtube-feed.php' => array(
'slug' => 'sbyoutube',
'icon' => $this->base->url . 'assets/images/about/plugin-sb-youtube.png',
'check' => array( 'constant' => 'SBYVER' ),
'name' => 'Smash Balloon YouTube Feeds',
'desc' => __( 'Easily display YouTube videos on your WordPress site without writing any code. Comes with multiple layouts, ability to embed live streams, video filtering, ability to combine multiple channel videos, and more.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/feeds-for-youtube.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'youtube-feed-pro/youtube-feed.php',
'name' => 'Smash Balloon YouTube Feeds Pro',
'url' => 'https://smashballoon.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'custom-twitter-feeds/custom-twitter-feed.php' => array(
'slug' => 'sbtwitter',
'icon' => $this->base->url . 'assets/images/about/plugin-sb-twitter.png',
'check' => array( 'constant' => 'CTF_VERSION' ),
'name' => 'Smash Balloon Twitter Feeds',
'desc' => __( 'Easily display Twitter content in WordPress without writing any code. Comes with multiple layouts, ability to combine multiple Twitter feeds, Twitter card support, tweet moderation, and more.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/custom-twitter-feeds.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'custom-twitter-feeds-pro/custom-twitter-feed.php',
'name' => 'Smash Balloon Twitter Feeds Pro',
'url' => 'https://smashballoon.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'reviews-feed/sb-reviews.php' => array(
'slug' => 'sbreviewsfeed',
'icon' => $this->base->url . 'assets/images/about/plugin-sb-rf.png',
'check' => array( 'constant' => 'SBRVER' ),
'name' => 'Smash Balloon Reviews Feed',
'desc' => __( 'Easily display Google reviews, Yelp reviews, and more in a clean customizable feed. Comes with multiple layouts, customizable review content, leave-a-review link, review moderation, and more.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/reviews-feed.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'reviews-feed-pro/sb-reviews-pro.php',
'name' => 'Smash Balloon Reviews Feed Pro',
'url' => 'https://smashballoon.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'searchwp/index.php' => array(
'slug' => 'searchwp',
'icon' => $this->base->url . 'assets/images/about/plugin-searchwp.png',
'check' => array( 'constant' => 'SEARCHWP_VERSION' ),
'name' => 'SearchWP',
'desc' => __( 'The most advanced WordPress search plugin. Customize your WordPress search algorithm, reorder search results, track search metrics, and everything you need to leverage search to grow your business.', 'optin-monster-api' ),
'url' => 'https://searchwp.com/',
'action' => 'redirect',
),
'affiliate-wp/affiliate-wp.php' => array(
'slug' => 'affiliatewp',
'icon' => $this->base->url . 'assets/images/about/plugin-affwp.png',
'check' => array( 'function' => 'affiliate_wp' ),
'name' => 'AffiliateWP',
'desc' => __( 'The #1 affiliate management plugin for WordPress. Easily create an affiliate program for your eCommerce store or membership site within minutes and start growing your sales with the power of referral marketing.', 'optin-monster-api' ),
'url' => 'https://affiliatewp.com',
'action' => 'redirect',
),
'stripe/stripe-checkout.php' => array(
'slug' => 'wpsimplepay',
'icon' => $this->base->url . 'assets/images/about/plugin-wp-simple-pay.png',
'check' => array( 'constant' => 'SIMPLE_PAY_VERSION' ),
'name' => 'WP Simple Pay',
'desc' => __( 'The #1 Stripe payments plugin for WordPress. Start accepting one-time and recurring payments on your WordPress site without setting up a shopping cart. No code required.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/stripe.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'wp-simple-pay-pro-3/simple-pay.php',
'name' => 'WP Simple Pay Pro',
'url' => 'https://wpsimplepay.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'easy-digital-downloads/easy-digital-downloads.php' => array(
'slug' => 'edd',
'icon' => $this->base->url . 'assets/images/about/plugin-edd.png',
'check' => array( 'function' => 'EDD' ),
'name' => 'Easy Digital Downloads',
'desc' => __( 'The best WordPress eCommerce plugin for selling digital downloads. Start selling eBooks, software, music, digital art, and more within minutes. Accept payments, manage subscriptions, advanced access control, and more.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/easy-digital-downloads.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'easy-digital-downloads-pro/easy-digital-downloads.php',
'name' => 'Easy Digital Downloads Pro',
'url' => 'https://easydigitaldownloads.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'sugar-calendar-lite/sugar-calendar-lite.php' => array(
'slug' => 'sugarcalendar',
'icon' => $this->base->url . 'assets/images/about/plugin-sugarcalendar.png',
'check' => array( 'class' => 'Sugar_Calendar\\Requirements_Check' ),
'name' => 'Sugar Calendar',
'desc' => __( 'A simple & powerful event calendar plugin for WordPress that comes with all the event management features including payments, scheduling, timezones, ticketing, recurring events, and more.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/sugar-calendar-lite.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'sugar-calendar/sugar-calendar.php',
'name' => 'Sugar Calendar Professional',
'url' => 'https://sugarcalendar.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'charitable/charitable.php' => array(
'slug' => 'charitable',
'icon' => $this->base->url . 'assets/images/about/plugin-charitable.png',
'check' => array( 'class' => 'Charitable' ),
'name' => 'WP Charitable',
'desc' => __( 'Top-rated WordPress donation and fundraising plugin. Over 10,000+ non-profit organizations and website owners use Charitable to create fundraising campaigns and raise more money online.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/charitable.zip',
'action' => 'install',
),
'insert-headers-and-footers/ihaf.php' => array(
'slug' => 'wpcode',
'icon' => $this->base->url . 'assets/images/about/plugin-wpcode.png',
'check' => array( 'function' => 'WPCode' ),
'name' => 'WPCode',
'desc' => __( 'Future proof your WordPress customizations with the most popular code snippet management plugin for WordPress. Trusted by over 1,500,000+ websites for easily adding code to WordPress right from the admin area.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/insert-headers-and-footers.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'wpcode-premium/wpcode.php',
'name' => 'WPCode Premium',
'url' => 'https://wpcode.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'duplicator/duplicator.php' => array(
'slug' => 'duplicator',
'icon' => $this->base->url . 'assets/images/about/plugin-duplicator.png',
'check' => array(
'constant' => array(
'DUPLICATOR_VERSION',
'DUPLICATOR_PRO_PHP_MINIMUM_VERSION',
),
),
'name' => 'Duplicator',
'desc' => __( 'Leading WordPress backup & site migration plugin. Over 1,500,000+ smart website owners use Duplicator to make reliable and secure WordPress backups to protect their websites. It also makes website migration really easy.', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/duplicator.zip',
'action' => 'install',
'pro' => array(
'plugin' => 'duplicator-pro/duplicator-pro.php',
'name' => 'Duplicator Pro',
'url' => 'https://duplicator.com/?utm_source=WordPress&utm_medium=Plugin&utm_campaign=OptinMonsterAboutUs',
),
),
'uncanny-automator/uncanny-automator.php' => array(
'slug' => 'uncanny-automator',
'icon' => $this->base->url . 'assets/images/about/plugin-uncanny-automator.png',
'class' => 'uncanny-automatoruncanny-automatorphp',
'check' => array( 'constant' => 'AUTOMATOR_PLUGIN_VERSION' ),
'name' => 'Uncanny Automator',
'desc' => __( 'Connect your WordPress plugins, sites and apps together with powerful automations. Save time and money with the #1 automation plugin for WordPress!', 'optin-monster-api' ),
'url' => 'https://downloads.wordpress.org/plugin/uncanny-automator.zip',
'action' => 'install',
'data' => array(
'dashboard_url' => add_query_arg(
array(
'post_type' => 'uo-recipe',
'page' => 'uncanny-automator-dashboard',
),
admin_url( 'edit.php' )
),
),
),
);
foreach ( self::$plugins as $plugin_id => $plugin ) {
self::$plugins[ $plugin_id ]['id'] = $plugin_id;
}
}
return self::$plugins;
}
/**
* Gets the list of AM plugins which include plugin status (installed/activated).
*
* @since 2.10.0
*
* @return array List of AM plugins.
*/
public function get_list_with_status() {
$this->get_list();
foreach ( self::$plugins as $plugin_id => $data ) {
if ( ! isset( $data['status'] ) ) {
self::$plugins[ $plugin_id ] = $this->get( $plugin_id )->get_data();
}
}
return self::$plugins;
}
/**
* Get given Plugin instance.
*
* @since 2.10.0
*
* @param string $plugin_id The plugin ID.
*
* @return OMAPI_Plugins_Plugin
*/
public function get( $plugin_id ) {
return OMAPI_Plugins_Plugin::get( $plugin_id );
}
/**
* Installs and activates a plugin for a given url (if user is allowed).
*
* @since 2.0.0
*
* @param OMAPI_Plugins_Plugin $plugin The Plugin instance.
* @return array On success.
* @throws Exception On error.
*/
public function install_plugin( OMAPI_Plugins_Plugin $plugin ) {
$not_allowed_exception = new Exception( esc_html__( 'Sorry, not allowed!', 'optin-monster-api' ), rest_authorization_required_code() );
// Check for permissions.
if ( ! current_user_can( 'install_plugins' ) ) {
throw $not_allowed_exception;
}
require_once ABSPATH . 'wp-admin/includes/file.php';
$creds = request_filesystem_credentials( admin_url( 'admin.php' ), '', false, false, null );
// Check for file system permissions.
if ( false === $creds ) {
throw $not_allowed_exception;
}
// We do not need any extra credentials if we have gotten this far, so let's install the plugin.
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$skin = version_compare( $GLOBALS['wp_version'], '5.3', '>=' )
? new OMAPI_InstallSkin()
: new OMAPI_InstallSkinCompat();
// Create the plugin upgrader with our custom skin.
$installer = new Plugin_Upgrader( $skin );
// Error check.
if ( ! method_exists( $installer, 'install' ) ) {
throw new Exception( esc_html__( 'Missing required installer!', 'optin-monster-api' ), 500 );
}
$result = $installer->install( esc_url_raw( $plugin['url'] ) );
if ( ! $installer->plugin_info() ) {
throw new Exception( esc_html__( 'Plugin failed to install!', 'optin-monster-api' ), 500 );
}
update_option(
sanitize_text_field( $plugin['slug'] . '_referred_by' ),
'optinmonster'
);
$plugin_basename = $installer->plugin_info();
// Activate the plugin silently.
try {
$this->activate_plugin( $plugin_basename );
return array(
'message' => esc_html__( 'Plugin installed & activated.', 'optin-monster-api' ),
'is_activated' => true,
'basename' => $plugin_basename,
);
} catch ( \Exception $e ) {
return array(
'message' => esc_html__( 'Plugin installed.', 'optin-monster-api' ),
'is_activated' => false,
'basename' => $plugin_basename,
);
}
}
/**
* Activates a plugin with a given plugin name (if user is allowed).
*
* @since 2.0.0
*
* @param string $plugin_id ID of the plugin.
*
* @return array On success.
* @throws Exception On error.
*/
public function activate_plugin( $plugin_id ) {
// Check for permissions.
if ( ! current_user_can( 'activate_plugins' ) ) {
throw new Exception( esc_html__( 'Sorry, not allowed!', 'optin-monster-api' ), rest_authorization_required_code() );
}
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$activate = activate_plugin( sanitize_text_field( $plugin_id ), '', false, true );
if ( is_wp_error( $activate ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $activate );
}
// Prevent the various welcome/onboarding redirects that may occur when activating plugins.
switch ( $plugin_id ) {
case 'google-analytics-for-wordpress/googleanalytics.php':
delete_transient( '_monsterinsights_activation_redirect' );
break;
case 'wpforms-lite/wpforms.php':
update_option( 'wpforms_activation_redirect', true );
break;
case 'all-in-one-seo-pack/all_in_one_seo_pack.php':
update_option( 'aioseo_activation_redirect', true );
break;
case 'trustpulse-api/trustpulse.php':
delete_option( 'trustpulse_api_plugin_do_activation_redirect' );
break;
case 'duplicator/duplicator.php':
update_option( 'duplicator_redirect_to_welcome', false );
break;
}
return array(
'message' => esc_html__( 'Plugin activated.', 'optin-monster-api' ),
'basename' => $plugin_id,
);
}
/**
* Get a active plugins header value for OM app requests.
*
* @since 2.9.0
*
* @return string The plugins header value.
*/
public function get_active_plugins_header_value() {
$wpf_active = $this->base->wpforms->is_active();
// Set initial values.
$plugins = array(
// We want to know information about WPForms regardless
// of if it's active. We'll use this to "disconnect" WPForms if it's not active.
'wpf' => array(
'a' => $wpf_active,
'v' => $wpf_active ? $this->base->wpforms->version() : 0,
),
);
return http_build_query( $plugins );
}
}
Promos/Base.php 0000644 00000006170 15153673736 0007432 0 ustar 00 <?php
/**
* Base Promos class.
*
* @since 2.10.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Base Promos class.
*
* @since 2.10.0
*/
abstract class OMAPI_Promos_Base {
/**
* Holds the class object.
*
* @since 2.10.0
*
* @var object
*/
public static $instance;
/**
* Holds the base class object.
*
* @since 2.10.0
*
* @var object
*/
public $base;
/**
* Holds the welcome slug.
*
* @since 2.10.0
*
* @var string
*/
public $hook;
/**
* SeedProd plugin data.
*
* @since 2.10.0
*
* @var array
*/
public $plugin_data = array();
/**
* The promo id.
*
* @since 2.10.0
*
* @var string
*/
protected $promo = '';
/**
* The plugin id (from OMAPI_Plugins).
*
* @since 2.10.0
*
* @var string
*/
protected $plugin_id = '';
/**
* OMAPI_Plugins_Plugin instance.
*
* @since 2.10.0
*
* @var OMAPI_Plugins_Plugin
*/
public $plugin;
/**
* Primary class constructor.
*
* @since 2.10.0
*/
public function __construct() {
// If we are not in admin or admin ajax, return.
if ( ! is_admin() ) {
return;
}
// If user is in admin ajax or doing cron, return.
if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
return;
}
// If user is not logged in, return.
if ( ! is_user_logged_in() ) {
return;
}
// return;
// If user cannot manage_options, return.
if ( ! OMAPI::get_instance()->can_access( $this->promo ) ) {
return;
}
// Set our object.
$this->set();
// Register the menu item.
add_action( 'admin_menu', array( $this, '_register_page' ) );
}
/**
* Sets our object instance and base class instance.
*
* @since 2.10.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
$this->plugin = OMAPI_Plugins_Plugin::get( $this->plugin_id );
}
/**
* Loads the OptinMonster admin menu and page.
*
* @since 2.10.0
*/
abstract protected function register_page();
/**
* Loads the OptinMonster admin menu and page.
*
* @since 2.10.0
*/
public function _register_page() {
$hook = $this->register_page();
// If SeedProd is active, we want to redirect to its own landing page.
if ( ! empty( $this->plugin['active'] ) ) {
add_action( 'load-' . $hook, array( $this, 'redirect_plugin' ) );
}
// Load settings page assets.
add_action( 'load-' . $hook, array( $this, 'assets' ) );
}
/**
* Redirects to the seedprod admin page.
*
* @since 2.10.0
*/
abstract public function redirect_plugin();
/**
* Outputs the OptinMonster settings page.
*
* @since 2.10.0
*/
abstract public function display_page();
/**
* Loads assets for the settings page.
*
* @since 2.10.0
*/
public function assets() {
add_filter( 'admin_body_class', array( $this, 'add_body_classes' ) );
$this->base->menu->styles();
$this->base->menu->scripts();
}
/**
* Add body classes.
*
* @since 2.10.0
*/
public function add_body_classes( $classes ) {
$classes .= " omapi-{$this->promo} ";
return $classes;
}
}
Promos/SeedProd.php 0000644 00000004471 15153673736 0010267 0 ustar 00 <?php
/**
* SeedProd class.
*
* @since 2.10.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* SeedProd class.
*
* @since 2.10.0
*/
class OMAPI_Promos_SeedProd extends OMAPI_Promos_Base {
/**
* The promo id.
*
* @var string
*/
protected $promo = 'seedprod';
/**
* The plugin id.
*
* @var string
*/
protected $plugin_id = 'coming-soon/coming-soon.php';
/**
* Loads the OptinMonster admin menu.
*
* @since 2.10.0
*/
protected function register_page() {
return add_submenu_page(
$this->base->menu->parent_slug(), // Parent slug
esc_html__( 'SeedProd', 'optin-monster-api' ), // Page title
esc_html__( 'Landing Pages', 'optin-monster-api' ),
$this->base->access_capability( 'optin-monster-seedprod' ), // Cap
'optin-monster-seedprod', // Slug
array( $this, 'display_page' ) // Callback
);
}
/**
* Redirects to the seedprod admin page.
*
* @since 2.10.0
*/
public function redirect_plugin() {
$slug = ! empty( $this->plugin['which'] ) && 'default' !== $this->plugin['which']
? 'seedprod_pro'
: 'seedprod_lite';
$url = add_query_arg( 'page', $slug, admin_url( 'admin.php' ) );
wp_safe_redirect( esc_url_raw( $url ) );
exit;
}
/**
* Outputs the OptinMonster settings page.
*
* @since 2.10.0
*/
public function display_page() {
$plugin_search_url = is_multisite()
? network_admin_url( 'plugin-install.php?tab=search&type=term&s=seedprod' )
: admin_url( 'plugin-install.php?tab=search&type=term&s=seedprod' );
$this->base->output_view(
'seedprod-settings-page.php',
array(
'plugin' => $this->plugin,
'plugin_search_url' => $plugin_search_url,
'button_activate' => __( 'Start Creating Landing Pages', 'optin-monster-api' ),
'button_install' => __( 'Start Creating Landing Pages', 'optin-monster-api' ),
)
);
}
/**
* Loads assets for the settings page.
*
* @since 2.10.0
*/
public function assets() {
parent::assets();
wp_enqueue_style( 'om-tp-admin-css', $this->base->url . 'assets/dist/css/seedprod.min.css', false, $this->base->asset_version() );
}
/**
* Add body classes.
*
* @since 2.10.0
*/
public function add_body_classes( $classes ) {
$classes .= ' omapi-seedprod ';
return $classes;
}
}
Promos/TrustPulse.php 0000644 00000006270 15153673736 0010713 0 ustar 00 <?php
/**
* TrustPulse class.
*
* @since 1.9.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* TrustPulse class.
*
* @since 1.9.0
*/
class OMAPI_Promos_TrustPulse extends OMAPI_Promos_Base {
/**
* The promo id.
*
* @var string
*/
protected $promo = 'trustpulse';
/**
* The plugin id.
*
* @var string
*/
protected $plugin_id = 'trustpulse-api/trustpulse.php';
/**
* Whether the TrustPulse plugin has been setup.
*
* @since 1.9.0
*
* @var bool
*/
public $trustpulse_setup;
/**
* Primary class constructor.
*
* @since 1.9.0
*/
public function __construct() {
if ( ! defined( 'TRUSTPULSE_APP_URL' ) ) {
define( 'TRUSTPULSE_APP_URL', 'https://app.trustpulse.com/' );
}
if ( ! defined( 'TRUSTPULSE_URL' ) ) {
define( 'TRUSTPULSE_URL', 'https://trustpulse.com/' );
}
parent::__construct();
}
/**
* Sets our object instance and base class instance.
*
* @since 1.9.0
*/
public function set() {
parent::set();
$account_id = get_option( 'trustpulse_script_id', null );
$this->trustpulse_setup = ! empty( $account_id );
}
/**
* Loads the OptinMonster admin menu.
*
* @since 1.9.0
*/
protected function register_page() {
return add_submenu_page(
// If trustpulse is active/setup, don't show the TP sub-menu item under OM.
! empty( $this->plugin['active'] ) && $this->trustpulse_setup
? $this->base->menu->parent_slug() . '-no-menu'
: $this->base->menu->parent_slug(), // Parent slug
esc_html__( 'TrustPulse', 'optin-monster-api' ), // Page title
esc_html__( 'Social Proof Widget', 'optin-monster-api' ),
$this->base->access_capability( 'optin-monster-trustpulse' ), // Cap
'optin-monster-trustpulse', // Slug
array( $this, 'display_page' ) // Callback
);
}
/**
* Redirects to the trustpulse admin page.
*
* @since 1.9.0
*/
public function redirect_plugin() {
$url = esc_url_raw( admin_url( 'admin.php?page=trustpulse' ) );
wp_safe_redirect( $url );
exit;
}
/**
* Outputs the OptinMonster settings page.
*
* @since 1.9.0
*/
public function display_page() {
$plugin_search_url = is_multisite()
? network_admin_url( 'plugin-install.php?tab=search&type=term&s=trustpulse' )
: admin_url( 'plugin-install.php?tab=search&type=term&s=trustpulse' );
$this->base->output_view(
'trustpulse-settings-page.php',
array(
'plugin' => $this->plugin,
'plugin_search_url' => $plugin_search_url,
'button_activate' => __( 'Activate the TrustPulse Plugin', 'optin-monster-api' ),
'button_install' => __( 'Install & Activate the TrustPulse Plugin', 'optin-monster-api' ),
)
);
}
/**
* Loads assets for the settings page.
*
* @since 1.9.0
*/
public function assets() {
parent::assets();
wp_enqueue_style( 'om-tp-admin-css', $this->base->url . 'assets/dist/css/trustpulse.min.css', false, $this->base->asset_version() );
add_action( 'in_admin_header', array( $this, 'render_banner' ) );
}
/**
* Renders TP banner in the page header
*
* @return void
*/
public function render_banner() {
$this->base->output_view( 'trustpulse-banner.php' );
}
}
Promos.php 0000644 00000002154 15153673736 0006556 0 ustar 00 <?php
/**
* Promos class.
*
* @since 2.10.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Promos class.
*
* @since 2.10.0
*/
class OMAPI_Promos {
/**
* OMAPI_Promos_TrustPulse object (loaded only in the admin)
*
* @var OMAPI_Promos_TrustPulse
*/
public $trustpulse;
/**
* OMAPI_Promos_SeedProd object (loaded only in the admin)
*
* @var OMAPI_Promos_SeedProd
*/
public $seedprod;
/**
* OMAPI_ConstantContact object (loaded only in the admin)
*
* @var OMAPI_ConstantContact
*/
public $constant_contact;
/**
* Constructor
*
* @since 2.10.0
*/
public function __construct() {
add_action( 'optin_monster_api_admin_loaded', array( $this, 'init_promos' ) );
}
/**
* Initiate the promos objects.
*
* @since 2.10.0
*
* @return void
*/
public function init_promos() {
$this->trustpulse = new OMAPI_Promos_TrustPulse();
$this->seedprod = new OMAPI_Promos_SeedProd();
if ( OMAPI_Partners::has_partner_url() ) {
$this->constant_contact = new OMAPI_ConstantContact();
}
}
}
Refresh.php 0000644 00000013403 15153673736 0006674 0 ustar 00 <?php
/**
* Refresh class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Refresh class.
*
* @since 1.0.0
*/
class OMAPI_Refresh {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Arguments for the API requests.
*
* @since 1.6.5
*
* @var array
*/
protected $api_args = array(
'limit' => 100,
'status' => 'all',
);
/**
* OMAPI_Api object
*
* @var null|OMAPI_Api
*/
public $api = null;
/**
* WP_Error object if refresh fails.
*
* @var null|WP_Error
*/
public $error = null;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
// If WPML is enabled, add the language domains to the parameter.
if ( OMAPI_Utils::is_wpml_active() ) {
$this->api_args['wpml_domains'] = json_encode( OMAPI_Utils::get_wpml_language_domains() );
}
}
/**
* Refresh the optins.
*
* @since 1.0.0
*
* @param string $api_key API key.
*
* @return WP_Error|bool True if successful.
*/
public function refresh( $api_key = null ) {
$this->api = OMAPI_Api::build( 'v1', 'optins', 'GET', $api_key ? array( 'apikey' => $api_key ) : $api_key );
$args = $this->setup_api( $api_key, $this->api_args );
$results = array();
$body = $this->api->request( $args );
// Loop through paginated requests until we have fetched all the campaigns.
while ( ! is_wp_error( $body ) || empty( $body ) ) {
$limit = absint( wp_remote_retrieve_header( $this->api->response, 'limit' ) );
$page = absint( wp_remote_retrieve_header( $this->api->response, 'page' ) );
$total = absint( wp_remote_retrieve_header( $this->api->response, 'total' ) );
$total_pages = ceil( $total / $limit );
$results = array_merge( $results, (array) $body );
// If we've reached the end, prevent any further requests.
if ( $page >= $total_pages || 0 === $limit ) {
break;
}
$args['page'] = $page + 1;
// Request the next page.
$body = $this->api->request( $args );
}
if ( is_wp_error( $body ) ) {
$this->handle_error( $body );
} else {
// Store the optin data.
$this->base->save->store_optins( $results );
// Update our sites as well.
$result = $this->base->sites->fetch( $api_key );
// Update the option to remove stale error messages.
$option = $this->base->get_option();
$option['is_invalid'] = false;
$option['is_expired'] = false;
$option['is_disabled'] = false;
$option['connected'] = time();
if ( is_wp_error( $result ) ) {
$this->error = $result;
} else {
$option = array_merge( $option, $result );
}
$this->base->save->update_option( $option );
}
return $this->error ? $this->error : true;
}
/**
* Trigger a refresh for given campaign.
*
* @since 1.9.10
*
* @param string $campaign_id The campaign id (slug).
*
* @return WP_Error|bool True if successful.
*/
public function sync( $campaign_id ) {
$time = time();
$path = "for-wp/{$campaign_id}?t={$time}";
$this->api = OMAPI_Api::build( 'v1', $path, 'GET' );
$body = $this->api->request( $this->setup_api() );
if ( is_wp_error( $body ) ) {
// If campaign is gone, delete the optin.
if (
'campaign-error' === $body->get_error_code()
&& (string) '404' === (string) $body->get_error_data()
) {
$result = $this->base->save->delete_optin( $campaign_id, true );
}
$this->handle_error( $body );
} else {
// Store the optin data.
$this->base->save->add_optins( (array) $body, false );
}
return $this->error ? $this->error : true;
}
/**
* Gets contextual info for API requests.
*
* @since 1.9.10
*
* @param array $args Array of args.
*
* @return arry Modified array of args.
*/
public function get_info_args( $args = array() ) {
// Set additional flags.
$args['wp'] = $GLOBALS['wp_version'];
$args['av'] = $this->base->asset_version();
$args['v'] = $this->base->version;
if ( OMAPI_WooCommerce::is_active() ) {
$args['wc'] = OMAPI_WooCommerce::version();
}
$args = array_merge( $args, OMAPI_Api::get_url_args() );
return $args;
}
/**
* Handles setting up the API request.
*
* @since 1.9.10
*
* @param string $api_key API key.
* @param array $args Array of request args.
*
* @return arry Modified array of request args.
*/
protected function setup_api( $api_key = null, $args = array() ) {
if ( $api_key ) {
$this->api->set( 'apikey', $api_key );
}
$this->api->clear_additional_data();
// Set additional flags.
return $this->get_info_args( $args );
}
/**
* Handles errors occurring during refresh.
*
* @since 1.9.10
*
* @param WP_Error $error WP_Error object.
*
* @return OMAPI_Refresh
*/
protected function handle_error( $error ) {
switch ( $error->get_error_code() ) {
// If no optins available, make sure they get deleted.
case 'optins':
case 'no-campaigns-error':
$this->base->save->store_optins( array() );
break;
case 'referrer-error':
$api = OMAPI_Api::instance();
$result = $this->base->sites->check_existing_site( $api->get_creds() );
if ( is_wp_error( $result ) ) {
$error = $result;
}
break;
}
// Set an error message.
$this->error = $error;
return $this;
}
}
RestApi.php 0000644 00000115122 15153673736 0006646 0 ustar 00 <?php
/**
* Rest API Class, where we register/execute any REST API Routes
*
* @since 1.8.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rest Api class.
*
* @since 1.8.0
*/
class OMAPI_RestApi extends OMAPI_BaseRestApi {
/**
* Whether Access-Control-Allow-Headers header was set/updated by us.
*
* @since 1.9.12
*
* @var bool
*/
protected $allow_header_set = false;
/**
* Registers our Rest Routes for this App
*
* @since 1.8.0
*
* @return void
*/
public function register_rest_routes() {
// Filter only available in WP 5.5.
add_filter( 'rest_allowed_cors_headers', array( $this, 'set_allow_headers' ), 999 );
// Fall-through to check if we still need to set header (WP < 5.5).
add_filter( 'rest_send_nocache_headers', array( $this, 'fallback_set_allow_headers' ), 999 );
// Fetch some quick info about this WP installation.
register_rest_route(
$this->namespace,
'info',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'output_info' ),
)
);
// Fetch in-depth support info about this WP installation.
register_rest_route(
$this->namespace,
'support',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'support_info' ),
)
);
// Toggles rule debug.
register_rest_route(
$this->namespace,
'support/debug/enable',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'rule_debug_enable' ),
)
);
register_rest_route(
$this->namespace,
'support/debug/disable',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'rule_debug_disable' ),
)
);
// Proxy route for getting the /me data from the app.
register_rest_route(
$this->namespace,
'me',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_me' ),
)
);
// Route for triggering refreshing/syncing of all campaigns.
register_rest_route(
$this->namespace,
'campaigns/refresh',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'refresh_campaigns' ),
)
);
// Route for fetching the campaign data for specific campaign.
register_rest_route(
$this->namespace,
'campaigns/(?P<id>\w+)',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_campaign_data' ),
)
);
// Route for updating the campaign data.
register_rest_route(
$this->namespace,
'campaigns/(?P<id>\w+)',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'update_campaign_data' ),
)
);
// Route for triggering refreshing/syncing of a single campaign.
register_rest_route(
$this->namespace,
'campaigns/(?P<id>[\w-]+)/sync',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'sync_campaign' ),
)
);
// Route for fetching data/resources needed for the campaigns.
register_rest_route(
$this->namespace,
'resources',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_wp_resources' ),
)
);
register_rest_route(
$this->namespace,
'notifications',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_notifications' ),
)
);
register_rest_route(
$this->namespace,
'notifications/dismiss',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'dismiss_notification' ),
)
);
register_rest_route(
$this->namespace,
'notifications/create',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'create_event_notification' ),
)
);
register_rest_route(
$this->namespace,
'plugins',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_am_plugins_list' ),
)
);
register_rest_route(
$this->namespace,
'plugins',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'handle_plugin_action' ),
)
);
register_rest_route(
$this->namespace,
'api',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_store_api_key' ),
'callback' => array( $this, 'init_api_key_connection' ),
)
);
// Only register the regenerate route when we have a token in the DB.
if ( OMAPI_ApiAuth::has_token() ) {
register_rest_route(
$this->namespace,
'api/regenerate',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_store_regenerated_api_key' ),
'callback' => array( $this, 'store_regenerated_api_key' ),
)
);
}
register_rest_route(
$this->namespace,
'api',
array(
'methods' => WP_REST_Server::DELETABLE,
'permission_callback' => array( $this, 'can_delete_api_key' ),
'callback' => array( $this, 'disconnect' ),
)
);
register_rest_route(
$this->namespace,
'settings',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_settings' ),
)
);
register_rest_route(
$this->namespace,
'settings',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_update_settings' ),
'callback' => array( $this, 'update_settings' ),
)
);
register_rest_route(
$this->namespace,
'review/dismiss',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_dismiss_review' ),
'callback' => array( $this, 'dismiss_review' ),
)
);
register_rest_route(
$this->namespace,
'omu/courses',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'get_courses' ),
)
);
register_rest_route(
$this->namespace,
'omu/guides',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'get_guides' ),
)
);
register_rest_route(
$this->namespace,
'account/sync',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'sync_account' ),
)
);
register_rest_route(
$this->namespace,
'account/connect',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'has_connection_token' ),
'callback' => array( $this, 'connect_account' ),
)
);
do_action( 'optin_monster_api_rest_register_routes', $this );
}
/**
* Filters the list of request headers that are allowed for CORS requests,
* and ensures our API key is allowed.
*
* @since 1.9.12
*
* @param string[] $allow_headers The list of headers to allow.
*
* @return string[]
*/
public function set_allow_headers( $allow_headers ) {
$allow_headers[] = 'X-OptinMonster-ApiKey';
$this->allow_header_set = true;
// remove fall-through.
remove_filter( 'rest_send_nocache_headers', array( $this, 'fallback_set_allow_headers' ), 999 );
return $allow_headers;
}
/**
* Fallback to make sure we set the allow headers.
*
* @since 1.9.12
*
* @param bool $rest_send_nocache_headers Whether to send no-cache headers.
* We ignore this, because we're simply using this
* as an action hook.
*
* @return bool Unchanged result.
*/
public function fallback_set_allow_headers( $rest_send_nocache_headers ) {
if ( ! $this->allow_header_set && ! headers_sent() ) {
foreach ( headers_list() as $header ) {
if ( 0 === strpos( $header, 'Access-Control-Allow-Headers: ' ) ) {
list( $key, $value ) = explode( 'Access-Control-Allow-Headers: ', $header );
if ( false === strpos( $value, 'X-OptinMonster-ApiKey' ) ) {
header( 'Access-Control-Allow-Headers: ' . $value . ', X-OptinMonster-ApiKey' );
}
$this->allow_header_set = true;
break;
}
}
}
return $rest_send_nocache_headers;
}
/**
* Gets the /me data from the app.
*
* Route: GET omapp/v1/me
*
* @since 2.6.6
*
* @return WP_REST_Response The API Response
*/
public function get_me() {
$data = OMAPI_Api::fetch_me_cached( true );
return is_wp_error( $data )
? $this->wp_error_to_response( $data, OMAPI_Api::instance()->response_body )
: new WP_REST_Response( $data, 200 );
}
/**
* Triggers refreshing our campaigns.
*
* Route: POST omapp/v1/campaigns/refresh
*
* @since 1.9.10
*
* @return WP_REST_Response The API Response
*/
public function refresh_campaigns() {
$result = $this->base->refresh->refresh();
return is_wp_error( $result )
? $this->wp_error_to_response( $result, OMAPI_Api::instance()->response_body )
: new WP_REST_Response(
array( 'message' => esc_html__( 'OK', 'optin-monster-api' ) ),
200
);
}
/**
* Fetch some quick info about this WP installation
* (WP version, plugin version, rest url, home url, WooCommerce version)
*
* Route: GET omapp/v1/info
*
* @since 1.9.10
*
* @return WP_REST_Response
*/
public function output_info() {
return new WP_REST_Response( $this->base->refresh->get_info_args(), 200 );
}
/**
* Fetch in-depth support info about this WP installation.
* Used for the debug PDF, but can also be requested by support staff with the right api key.
*
* Route: GET omapp/v1/support
*
* @since 1.9.10
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response
*/
public function support_info( $request ) {
$support = new OMAPI_Support();
$format = $request->get_param( 'format' );
if ( empty( $format ) ) {
$format = 'raw';
}
return new WP_REST_Response( $support->get_support_data( $format ), 200 );
}
/**
* Enables the rules debug output for this site.
* (Still requires the omwpdebug query var on the frontend)
*
* Route: GET omapp/v1/support/debug/enable
*
* @since 2.4.0
*
* @return WP_REST_Response
*/
public function rule_debug_enable() {
return $this->toggle_rule_debug( true );
}
/**
* Disables the rules debug output for this site.
*
* Route: GET omapp/v1/support/debug/disable
*
* @since 2.4.0
*
* @return WP_REST_Response
*/
public function rule_debug_disable() {
return $this->toggle_rule_debug( false );
}
/**
* Toggles the rules debug setting.
*
* @since 2.4.0
*
* @param boolean $enable Whether to enable/disable the rules debug setting.
*
* @return WP_REST_Response
*/
protected function toggle_rule_debug( $enable ) {
$options = $this->base->get_option();
if ( $enable ) {
$options['api']['omwpdebug'] = true;
} else {
unset( $options['api']['omwpdebug'] );
}
$updated = $this->base->save->update_option( $options );
return new WP_REST_Response(
array(
'message' => $updated
? esc_html__( 'OK', 'optin-monster-api' )
: esc_html__( 'Not Modified', 'optin-monster-api' ),
),
$updated ? 200 : 202
);
}
/**
* Triggering refreshing/syncing of a single campaign.
*
* Route: POST omapp/v1/campaigns/(?P<id>[\w-]+)/sync
*
* @since 1.9.10
*
* @param WP_REST_Request $request The REST Request.
* @return WP_REST_Response The API Response
*/
public function sync_campaign( $request ) {
$campaign_id = $request->get_param( 'id' );
if ( empty( $campaign_id ) ) {
return new WP_REST_Response(
array( 'message' => esc_html__( 'No campaign ID given.', 'optin-monster-api' ) ),
400
);
}
$this->base->refresh->sync( $campaign_id );
return new WP_REST_Response(
array( 'message' => esc_html__( 'OK', 'optin-monster-api' ) ),
200
);
}
/**
* Gets all the data needed for the campaign dashboard for a given campaign.
*
* Route: GET omapp/v1/campaigns/(?P<id>\w+)
*
* @since 1.9.10
*
* @param WP_REST_Request $request The REST Request.
* @return WP_REST_Response The API Response
*
* @throws OMAPI_WpErrorException If there is a WordPress error during the process.
*/
public function get_campaign_data( $request ) {
try {
$campaign_id = $request->get_param( 'id' );
if ( empty( $campaign_id ) ) {
return new WP_REST_Response(
array( 'message' => esc_html__( 'No campaign ID given.', 'optin-monster-api' ) ),
400
);
}
$campaign = $this->base->get_optin_by_slug( $campaign_id );
if ( empty( $campaign->ID ) ) {
$this->base->refresh->sync( $campaign_id );
if ( is_wp_error( $this->base->refresh->error ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $this->base->refresh->error );
}
$campaign = $this->base->get_optin_by_slug( $campaign_id );
}
if ( empty( $campaign->ID ) ) {
return new WP_REST_Response(
array(
/* translators: %s: the campaign post id. */
'message' => sprintf( esc_html__( 'Could not find campaign by given ID: %s. Are you sure campaign is associated with this site?', 'optin-monster-api' ), $campaign_id ),
),
404
);
}
// Get Campaigns Data.
$data = $this->base->collect_campaign_data( $campaign );
$data = apply_filters( 'optin_monster_api_setting_ui_data_for_campaign', $data, $campaign );
return new WP_REST_Response( $data, 200 );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Updates data for given campaign.
*
* Route: POST omapp/v1/campaigns/(?P<id>\w+)
*
* @since 1.9.10
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function update_campaign_data( $request ) {
$campaign_id = $request->get_param( 'id' );
// If no campaign_id, return error.
$campaign = $this->base->get_optin_by_slug( $campaign_id );
// If no campaign, return 404.
// Get the Request Params.
$fields = json_decode( $request->get_body(), true );
if ( ! empty( $fields['taxonomies'] ) ) {
if ( isset( $fields['taxonomies']['categories'] ) ) {
$fields['categories'] = $fields['taxonomies']['categories'];
}
// Save the data from the regular taxonomies fields into the WC specific tax field.
// For back-compatibility.
$fields['is_wc_product_category'] = isset( $fields['taxonomies']['product_cat'] )
? $fields['taxonomies']['product_cat']
: array();
$fields['is_wc_product_tag'] = isset( $fields['taxonomies']['product_tag'] )
? $fields['taxonomies']['product_tag']
: array();
}
// Escape Parameters as needed.
// Update Post Meta.
foreach ( $fields as $key => $value ) {
$value = $this->sanitize( $value );
switch ( $key ) {
default:
update_post_meta( $campaign->ID, '_omapi_' . $key, $value );
}
}
return new WP_REST_Response(
array( 'message' => esc_html__( 'OK', 'optin-monster-api' ) ),
200
);
}
/**
* Gets all the data/resources needed for the campaigns.
*
* Route: GET omapp/v1/resources
*
* @since 1.9.10
*
* @param WP_REST_Request $request The REST Request.
* @return WP_REST_Response The API Response
*/
public function get_wp_resources( $request ) {
global $wpdb;
$excluded = $request->get_param( 'excluded' );
$excluded = ! empty( $excluded )
? explode( ',', $excluded )
: array();
if ( $request->get_param( 'refresh' ) ) {
$result = $this->refresh_campaigns();
if ( is_wp_error( $result ) ) {
$error_data = $result->get_error_data();
if ( empty( $error_data['type'] ) || 'no-campaigns-error' !== $error_data['type'] ) {
return $result;
}
}
}
$campaign_data = array();
if ( ! in_array( 'campaigns', $excluded, true ) ) {
// Get Campaigns Data.
$campaigns = $this->base->get_optins( array( 'post_status' => 'any' ) );
$campaigns = ! empty( $campaigns ) ? $campaigns : array();
foreach ( $campaigns as $campaign ) {
$campaign_data[] = $this->base->collect_campaign_data( $campaign );
}
}
$mailpoet = $this->base->is_mailpoet_active();
$taxonomy_map = array();
if ( ! in_array( 'taxonomies', $excluded, true ) ) {
// Get Taxonomies Data.
$taxonomies = get_taxonomies( array( 'public' => true ), 'objects' );
$taxonomies = apply_filters( 'optin_monster_api_setting_ui_taxonomies', $taxonomies );
foreach ( $taxonomies as $taxonomy ) {
if ( 'category' === $taxonomy->name ) {
$cats = get_categories();
$taxonomy_map['category'] = array(
'name' => 'category',
'label' => ucwords( $taxonomy->label ),
'terms' => is_array( $cats ) ? array_values( $cats ) : array(),
'for' => $taxonomy->object_type,
);
continue;
}
$terms = get_terms(
array(
'taxonomy' => $taxonomy->name,
'get' => 'all',
)
);
$taxonomy_map[ $taxonomy->name ] = array(
'name' => $taxonomy->name,
'label' => ucwords( $taxonomy->label ),
'terms' => is_array( $terms ) ? array_values( $terms ) : array(),
'for' => $taxonomy->object_type,
);
}
}
$posts = array();
if ( ! in_array( 'posts', $excluded, true ) ) {
// Posts query.
$post_types = implode( '","', esc_sql( get_post_types( array( 'public' => true ) ) ) );
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Already prepared.
$posts = $wpdb->get_results( "SELECT ID AS `value`, post_title AS `name` FROM {$wpdb->prefix}posts WHERE post_type IN (\"{$post_types}\") AND post_status IN('publish', 'future') ORDER BY post_title ASC", ARRAY_A );
}
$post_types = ! in_array( 'post_types', $excluded, true )
? array_values( get_post_types( array( 'public' => true ), 'object' ) )
: array();
// Get "Config" data.
$config = array(
'hasMailPoet' => $mailpoet,
'isWooActive' => OMAPI_WooCommerce::is_active(),
'isWooConnected' => OMAPI_WooCommerce::is_connected(),
'isWPFormsActive' => OMAPI_WPForms::is_active(),
'isEddActive' => OMAPI_EasyDigitalDownloads::is_active(),
'isEddConnected' => OMAPI_EasyDigitalDownloads::is_connected(),
'mailPoetLists' => $mailpoet && ! in_array( 'mailPoetLists', $excluded, true )
? $this->base->mailpoet->get_lists()
: array(),
'mailPoetFields' => $mailpoet && ! in_array( 'mailPoetFields', $excluded, true )
? $this->base->mailpoet->get_custom_fields_dropdown_values()
: array(),
);
$omapi_plugins = new OMAPI_Plugins();
$response_data = apply_filters(
'optin_monster_api_setting_ui_data',
array(
'config' => $config,
'campaigns' => $campaign_data,
'taxonomies' => $taxonomy_map,
'posts' => $posts,
'post_types' => $post_types,
'siteId' => $this->base->get_site_id(),
'siteIds' => $this->base->get_site_ids(),
'pluginsInfo' => $omapi_plugins->get_active_plugins_header_value(),
)
);
return new WP_REST_Response( $response_data, 200 );
}
/**
* Gets the list of AM notifications.
*
* Route: GET omapp/v1/notifications
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function get_notifications( $request ) {
add_filter( 'optin_monster_api_admin_notifications_has_access', array( $this, 'maybe_allow' ) );
// Make sure we have all the required user parameters.
$this->base->actions->maybe_fetch_missing_data();
if ( ! $this->base->notifications->has_access() ) {
return new WP_REST_Response( array(), 206 );
}
return new WP_REST_Response( $this->base->notifications->get( true ), 200 );
}
/**
* Dismiss a given notifications.
*
* Route: POST omapp/v1/notifications/dismiss
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function dismiss_notification( $request ) {
add_filter( 'optin_monster_api_admin_notifications_has_access', array( $this, 'maybe_allow' ) );
$ids = $request->get_json_params();
if ( $this->base->notifications->dismiss( $ids ) ) {
return new WP_REST_Response( $this->base->notifications->get( true ), 200 );
}
return new WP_REST_Response(
array(
'message' => sprintf(
/* translators: %s: the notification id(s). */
esc_html__( 'Could not dismiss: %s', 'optin-monster-api' ),
implode( ', ', $ids )
),
),
400
);
}
/**
* Dismiss a given notifications.
*
* Route: POST omapp/v1/notifications/create
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function create_event_notification( $request ) {
add_filter( 'optin_monster_api_admin_notifications_has_access', array( $this, 'maybe_allow' ) );
$payload = $request->get_json_params();
$errors = array();
foreach ( $payload as $notification ) {
$added = $this->base->notifications->add_event( $notification );
if ( is_wp_error( $added ) ) {
$errors[] = $added;
}
}
$updated = $this->base->notifications->get( true );
if ( ! empty( $errors ) ) {
$message = count( $payload ) > 1
? sprintf(
/* translators: %s: "Some" or "one". */
esc_html__( 'Could not create %s of the event notifications!', 'optin-monster-api' ),
count( $errors ) > 1 ? esc_html__( 'some', 'optin-monster-api' ) : esc_html__( 'one', 'optin-monster-api' )
)
: esc_html__( 'Could not create event notification!', 'optin-monster-api' );
foreach ( $errors as $error ) {
$message .= '<br>- ' . $error->get_error_message();
}
return new WP_REST_Response(
array(
'message' => $message,
'notifications' => $updated,
),
400
);
}
return new WP_REST_Response( $updated, 200 );
}
/**
* Maybe allow api-key authenticated user to see notifications.
*
* @since 2.0.0
*
* @param bool $access If current user has access to notifications.
*
* @return bool Maybe modified access.
*/
public function maybe_allow( $access ) {
if ( ! $access && $this->has_valid_api_key ) {
$access = ! $this->base->get_option( 'hide_announcements' );
}
return $access;
}
/**
* Gets the list of AM plugins.
*
* Route: GET omapp/v1/plugins
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function get_am_plugins_list( $request ) {
$plugins = new OMAPI_Plugins();
$data = $plugins->get_list_with_status();
$action_nonce = wp_create_nonce( 'om_plugin_action_nonce' );
foreach ( $data as $plugin_id => $plugin ) {
$data[ $plugin_id ]['actionNonce'] = $action_nonce;
}
return new WP_REST_Response( array_values( $data ), 200 );
}
/**
* Handles installing or activating an AM plugin.
*
* Route: POST omapp/v1/plugins
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function handle_plugin_action( $request ) {
try {
$nonce = $request->get_param( 'actionNonce' );
// Check the nonce.
if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'om_plugin_action_nonce' ) ) {
throw new Exception( esc_html__( 'Security token invalid!', 'optin-monster-api' ), rest_authorization_required_code() );
}
$id = $request->get_param( 'id' );
if ( empty( $id ) ) {
throw new Exception( esc_html__( 'Plugin Id required.', 'optin-monster-api' ), 400 );
}
$plugins = new OMAPI_Plugins();
$plugin = $plugins->get( $id );
if ( empty( $plugin['installed'] ) ) {
if ( empty( $plugin['url'] ) ) {
throw new Exception( esc_html__( 'Plugin install URL required.', 'optin-monster-api' ), 400 );
}
return new WP_REST_Response( $plugins->install_plugin( $plugin ), 200 );
}
$which = 'default' === $plugin['which'] ? $id : $plugin['which'];
return new WP_REST_Response( $plugins->activate_plugin( $which ), 200 );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Handles storing the API key and initiating the API connection.
*
* Route: POST omapp/v1/api
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception|OMAPI_WpErrorException If plugin action fails or API Key is missing.
*/
public function init_api_key_connection( $request ) {
try {
$apikey = $request->get_param( 'key' );
if ( empty( $apikey ) ) {
throw new Exception( esc_html__( 'API Key Missing!', 'optin-monster-api' ), 400 );
}
$result = OMAPI_ApiKey::init_connection( $apikey );
if ( is_wp_error( $result ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $result );
}
return new WP_REST_Response( $result, 200 );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Determine if we can store the given api key.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
* @throws Exception|OMAPI_WpErrorException If plugin action fails.
*/
public function can_store_api_key( $request ) {
try {
$this->verify_request_nonce( $request );
$apikey = $request->get_param( 'key' );
if ( empty( $apikey ) ) {
throw new Exception( esc_html__( 'API Key Missing!', 'optin-monster-api' ), 400 );
}
$result = OMAPI_ApiKey::verify( $apikey );
if ( is_wp_error( $result ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $result );
}
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
return OMAPI::get_instance()->can_access( 'store_api_key' );
}
/**
* Handles storing the regenerated API key.
*
* Route: POST omapp/v1/api/regenerate
*
* @since 2.6.5
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function store_regenerated_api_key( $request ) {
try {
$apikey = $request->get_param( 'key' );
if ( empty( $apikey ) ) {
throw new Exception( esc_html__( 'API Key Missing!', 'optin-monster-api' ), 400 );
}
$options = $this->base->get_option();
$options['api']['apikey'] = $apikey;
$this->base->save->update_option( $options );
OMAPI_ApiAuth::delete_token();
return $this->output_info();
} catch ( Exception $e ) {
OMAPI_ApiAuth::delete_token();
return $this->exception_to_response( $e );
}
}
/**
* Determine if we can store given regenerated api key.
*
* @since 2.6.5
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
* @throws Exception If plugin action fails.
*/
public function can_store_regenerated_api_key( $request ) {
try {
$tt = $request->get_param( 'tt' );
$apikey = $request->get_param( 'key' );
if ( empty( $tt ) || empty( $apikey ) ) {
throw new Exception( esc_html__( 'Required Credentials Missing!', 'optin-monster-api' ), rest_authorization_required_code() );
}
$validated = OMAPI_ApiAuth::validate_token( $tt );
if ( empty( $validated ) ) {
throw new Exception( esc_html__( 'Invalid token!', 'optin-monster-api' ), 403 );
}
return true;
} catch ( Exception $e ) {
OMAPI_ApiAuth::delete_token();
return $this->exception_to_response( $e );
}
}
/**
* Handles disconnecting the API key.
*
* Route: DELETE omapp/v1/api
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function disconnect( $request ) {
try {
OMAPI_ApiKey::disconnect();
return new WP_REST_Response(
array( 'message' => esc_html__( 'OK', 'optin-monster-api' ) ),
204
);
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Determine if we can disconnect the api key.
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
* @throws Exception If plugin action fails.
*/
public function can_delete_api_key( $request ) {
try {
$this->verify_request_nonce( $request );
if ( ! OMAPI_ApiKey::has_credentials() ) {
throw new Exception( esc_html__( 'API Key Missing!', 'optin-monster-api' ), 400 );
}
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
return OMAPI::get_instance()->can_access( 'delete_api_key' );
}
/**
* Handles getting the misc. settings.
*
* Route: GET omapp/v1/settings
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function get_settings( $request ) {
$defaults = $this->base->default_options();
$options = $this->base->get_option();
$misc_settings = array();
foreach ( array( 'auto_updates', 'usage_tracking', 'hide_announcements' ) as $key ) {
$misc_settings[ $key ] = isset( $options[ $key ] )
? $options[ $key ]
: $defaults[ $key ];
}
return new WP_REST_Response( $misc_settings, 200 );
}
/**
* Handles updating settings.
*
* Route: POST omapp/v1/settings
*
* @since 2.0.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function update_settings( $request ) {
try {
$settings = $request->get_param( 'settings' );
if ( empty( $settings ) ) {
throw new Exception( esc_html__( 'Settings Missing!', 'optin-monster-api' ), 400 );
}
$allowed_settings = array(
'auto_updates' => array(
'validate' => 'is_string',
),
'usage_tracking' => array(
'validate' => 'is_bool',
),
'hide_announcements' => array(
'validate' => 'is_bool',
),
'accountId' => array(
'validate' => 'is_string',
),
'currentLevel' => array(
'validate' => 'is_string',
),
'plan' => array(
'validate' => 'is_string',
),
'customApiUrl' => array(
'validate' => 'is_string',
),
'apiCname' => array(
'validate' => 'is_string',
),
'resetOnboardingPlugins' => array(
'validate' => 'is_bool',
),
);
$options = $this->base->get_option();
$has_settings = false;
foreach ( $settings as $setting => $value ) {
if ( empty( $allowed_settings[ $setting ] ) ) {
continue;
}
$has_settings = true;
if ( isset( $options[ $setting ] ) && $value === $options[ $setting ] ) {
continue;
}
$validator = $allowed_settings[ $setting ]['validate'];
if ( call_user_func( $validator, $value ) ) {
switch ( $validator ) {
case 'is_bool':
$options[ $setting ] = ! ! $value;
break;
case 'is_string':
$options[ $setting ] = sanitize_text_field( $value );
break;
}
switch ( $setting ) {
case 'customApiUrl':
$options[ $setting ] = $value
? 0 === strpos( $value, 'https://' )
? $value
: 'https://' . $value . '/app/js/api.min.js'
: '';
break;
case 'resetOnboardingPlugins':
unset( $options['onboardingPlugins'] );
unset( $options['resetOnboardingPlugins'] );
break;
}
}
}
// Looks like we want to toggle the omwpdebug setting.
if ( isset( $settings['omwpdebug'] ) ) {
$enabled = wp_validate_boolean( $settings['omwpdebug'] );
if ( empty( $enabled ) ) {
unset( $option['api']['omwpdebug'] );
} else {
$options['api']['omwpdebug'] = true;
}
$has_settings = true;
}
// Looks like we want to toggle the beta setting.
if ( isset( $settings['omwpbeta'] ) ) {
$enabled = wp_validate_boolean( $settings['omwpdebug'] );
$options['beta'] = ! empty( $enabled );
$has_settings = true;
}
if ( ! $has_settings ) {
throw new Exception( esc_html__( 'Invalid Settings!', 'optin-monster-api' ), 400 );
}
// Save the updated option.
$this->base->save->update_option( $options );
return $this->get_settings( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Sanitize value recursively.
*
* @since 1.9.10
*
* @param mixed $value The value to sanitize.
*
* @return mixed The sanitized value.
*/
public function sanitize( $value ) {
if ( empty( $value ) ) {
return $value;
}
if ( is_scalar( $value ) ) {
return sanitize_text_field( $value );
}
if ( is_array( $value ) ) {
return array_map( array( $this, 'sanitize' ), $value );
}
}
/**
* Determine if user can dismiss review.
*
* @since 2.6.1
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
*/
public function can_dismiss_review( $request ) {
try {
$this->verify_request_nonce( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
return is_user_logged_in() &&
OMAPI::get_instance()->can_access( 'review' );
}
/**
* Dismisses review.
*
* Route: POST omapp/v1/review/dismiss
*
* @since 2.6.1
*
* @param WP_REST_Request $request The REST Request.
*
* @return bool
*/
public function dismiss_review( $request ) {
$this->base->review->dismiss_review( $request->get_param( 'later' ) );
return new WP_REST_Response(
array( 'message' => esc_html__( 'OK', 'optin-monster-api' ) ),
200
);
}
/**
* Fetch courses from OMU.
*
* Route: GET omapp/v1/omu/courses
*
* @since 2.6.6
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response|WP_Error The API Response or WP_Error object.
*/
public function get_courses( $request ) {
return $this->handle_omu_request( 'courses' );
}
/**
* Fetch guides from OMU.
*
* Route: GET omapp/v1/omu/guides
*
* @since 2.6.6
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response|WP_Error The API Response or WP_Error object.
*/
public function get_guides( $request ) {
return $this->handle_omu_request( 'guides' );
}
/**
* Fetch object from OMU.
*
* @since 2.6.6
*
* @param string $object The API object to fetch.
*
* @return WP_REST_Response|WP_Error The API Response or WP_Error object.
*/
protected function handle_omu_request( $object ) {
try {
$result = OMAPI_OmuApi::cached_request( $object );
$api = OMAPI_OmuApi::instance();
if ( is_wp_error( $result ) ) {
return $this->wp_error_to_response( $result, $api->response_body );
}
$result = wp_parse_args(
$result,
array(
'data' => array(),
'total' => 0,
'totalpages' => 0,
)
);
$response = new WP_REST_Response( $result['data'], 200 );
$response->header( 'X-WP-Total', $result['total'] );
$response->header( 'X-WP-TotalPages', $result['totalpages'] );
return $response;
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Triggering refreshing/syncing of account settings.
*
* Route: POST omapp/v1/account/sync
*
* @since 2.6.13
*
* @param WP_REST_Request $request The REST Request.
* @return WP_REST_Response The API Response
*/
public function sync_account( $request ) {
$data = OMAPI_Api::fetch_me_cached( true );
if ( is_wp_error( $data ) ) {
return new WP_REST_Response(
array( 'message' => esc_html__( 'Sync failed!', 'optin-monster-api' ) ),
400
);
}
return new WP_REST_Response(
array( 'message' => esc_html__( 'Sync succeeded!', 'optin-monster-api' ) ),
200
);
}
/**
* Triggering connection of plugin to OptinMonster app.
*
* Route: POST omapp/v1/account/connect
*
* @since 2.16.6
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function connect_account( $request ) {
// Get the connection token from the request body.
$connection_token = sanitize_text_field( $request->get_param( 'connectionToken' ) );
// Get the array of potential plugins to install.
$plugins = array_filter( wp_parse_slug_list( $request->get_param( 'plugins' ) ) );
// Store the array of plugins the user requested to be installed. These
// will be installed the next time the user loads the plugin dashboard.
if ( ! empty( $plugins ) ) {
$options = $this->base->get_option();
$options['onboardingPlugins'] = $plugins;
update_option( 'optin_monster_api', $options );
}
// Set up the credentials array.
$creds = array( 'onboardingApiKey' => $connection_token );
// Fetch the /me data.
$data = OMAPI_Api::fetch_me_onboarding( $creds );
if ( is_wp_error( $data ) ) {
return new WP_REST_Response(
array( 'message' => esc_html__( 'Account connection failed!', 'optin-monster-api' ) ),
400
);
}
// Remove the connection token from the options. We no longer need it.
$options = $this->base->get_option();
unset( $options['connectionToken'] );
update_option( 'optin_monster_api', $options );
return new WP_REST_Response(
array( 'message' => esc_html__( 'Account connection succeeded!', 'optin-monster-api' ) ),
200
);
}
}
RevenueAttribution.php 0000644 00000005735 15153673736 0011145 0 ustar 00 <?php
/**
* Revenue attribution class.
*
* @since 2.6.13
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The Revenue Attribution class.
*
* @since 2.6.13
*/
class OMAPI_RevenueAttribution {
/**
* Holds the class object.
*
* @since 2.6.13
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 2.6.13
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 2.6.13
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 2.6.13
*/
public function __construct() {
// Set our object.
$this->set();
}
/**
* Sets our object instance and base class instance.
*
* @since 2.6.13
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Maybe stores revenue attribution data when a purchase is successful.
*
* @since 2.6.13
*
* @param array $data An array of revenue attribution data to store.
*
* @return bool|WP_Error True if successful, WP_Error or false otherwise.
*/
public function store( $data = array() ) {
// If revenue attribution is not turned on, return early.
$ra = $this->base->get_revenue_attribution();
if ( empty( $ra['enabled'] ) || empty( $ra['currency'] ) ) {
return false;
}
// If we can't find the account ID, return early.
$accountId = $this->base->get_option( 'accountId' );
if ( empty( $accountId ) ) {
return false;
}
// Build and send the request.
$api = OMAPI_Api::build( 'v2', 'revenue/' . $accountId, 'POST' );
return $api->request( $data );
}
/**
* Returns revenue attribution data.
*
* @since 2.6.13
*
* @return array An array of revenue attribution data.
*/
public function get_revenue_data() {
// If we don't have any cookies set for OM campaigns, return early.
if ( empty( $_COOKIE['_omra'] ) ) {
return array();
}
// If revenue attribution is not turned on, return early.
$ra = $this->base->get_revenue_attribution();
if ( empty( $ra['enabled'] ) || empty( $ra['currency'] ) ) {
return array();
}
// Loop through and prepare the campaign data. If it is empty, return early.
$campaign_data = json_decode( stripslashes( rawurldecode( $_COOKIE['_omra'] ) ), true );
if ( empty( $campaign_data ) ) {
return array();
}
// Sanitize the campaign data before sending it back.
$sanitized_campaigns = array();
foreach ( $campaign_data as $campaign_id => $action ) {
$sanitized_campaigns[ esc_html( $campaign_id ) ] = esc_html( $action );
}
// Return the default revenue attribution data. Additional revenue
// data should be returned from the integration itself (such as
// the total, transaction ID, etc.).
return array(
'campaigns' => $sanitized_campaigns,
'currency' => esc_html( $ra['currency'] ),
'device' => wp_is_mobile() ? 'mobile' : 'desktop',
'type' => 'sale',
);
}
}
Review.php 0000644 00000002712 15153673736 0006540 0 ustar 00 <?php
/**
* Review class.
*
* @since 1.1.4.5
*
* @package OMAPI
* @author Devin Vinson
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Review class.
*
* @since 1.1.4.5
*/
class OMAPI_Review {
/**
* Determine if review message should be shown
* based on backend rules.
*
* @since 2.6.1
*
* @return bool If it should show the review bar
*/
public function should_show_review() {
$review = get_option( 'omapi_review' );
if ( ! is_user_logged_in() || ! OMAPI::get_instance()->can_access( 'review' ) ) {
return false;
}
// If already dismissed...
if ( ! empty( $review['dismissed'] ) ) {
if ( empty( $review['later'] ) ) {
// Dismissed and no later, so do not show.
return false;
}
$delayed_less_than_month_ago = ! empty( $review['later'] ) && $review['time'] + ( 30 * DAY_IN_SECONDS ) > time();
if ( $delayed_less_than_month_ago ) {
// Delayed less than a month ago, so do not show.
return false;
}
}
return true;
}
/**
* Dismiss the review bar
*
* @param bool $later If delay the review for later.
*
* @since 1.1.6.1
* @since 2.6.1 Avoid using any request variables and receive later as parameter
*/
public function dismiss_review( $later = false ) {
$option = array(
'time' => time(),
'dismissed' => true,
'later' => ! empty( $later ),
);
$option['updated'] = update_option( 'omapi_review', $option );
return $option;
}
}
Rules/Base.php 0000644 00000003323 15153673736 0007242 0 ustar 00 <?php
/**
* Rules Base class.
*
* @since 2.13.0
*
* @package OMAPI
* @author Jutin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* OMAPI_Rules_Base class.
*
* @since 2.13.0
*/
abstract class OMAPI_Rules_Base {
/**
* Holds the meta fields used for checking output statuses.
*
* @since 2.13.0
*
* @var array
*/
protected $fields = array();
/**
* Holds the main Rules class instance.
*
* @since 2.13.0
*
* @var OMAPI_Rules
*/
public $rules;
/**
* Initiates hooks
*
* @since 2.13.0
*/
public function init_hooks() {
add_filter( 'optin_monster_api_output_fields', array( $this, 'merge_fields' ), 9 );
add_action( 'optinmonster_campaign_should_output_plugin_checks', array( $this, 'set_rules_and_run_checks' ), 9 );
}
/**
* Getter for fields property
*
* @since 2.13.0
*
* @return array
*/
public function get_fields() {
return $this->fields;
}
/**
* Merge fields array with the fields for this rules object.
*
* @since 2.13.0
*
* @param array $fields The meta fields used for checking output statuses.
*
* @return array
*/
public function merge_fields( $fields = array() ) {
return array_merge( $fields, $this->get_fields() );
}
/**
* Sets the rules object, then runs rule checks.
*
* @since 2.13.0
*
* @param OMAPI_Rules $rules The OMAPI_Rules object.
*
* @throws OMAPI_Rules_False|OMAPI_Rules_True
* @return void
*/
public function set_rules_and_run_checks( $rules ) {
$this->rules = $rules;
$this->run_checks();
}
/**
* Runs rule checks.
*
* @since 2.13.0
*
* @throws OMAPI_Rules_False|OMAPI_Rules_True
* @return void
*/
abstract public function run_checks();
}
Rules/Exception.php 0000644 00000003272 15153673736 0010331 0 ustar 00 <?php
/**
* OMAPI_Rules_Exception class.
*
* @since 1.5.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rules exception base class.
*
* @since 1.5.0
*/
class OMAPI_Rules_Exception extends Exception {
/**
* Whether the exception is a boolean.
*
* @var bool
*/
protected $bool = null;
/**
* An array of exceptions.
*
* @var array
*/
protected $exceptions = array();
/**
* Constructor.
*
* @param string $message Exception message.
* @param int $code Exception code.
*
* @param Exception $previous Previous exception.
*/
public function __construct( $message = null, $code = 0, Exception $previous = null ) {
if ( is_bool( $message ) ) {
$this->bool = $message;
$message = null;
}
if ( $previous ) {
$this->add_exceptions( $previous );
}
parent::__construct( $message, $code, $previous );
}
/**
* Get boolean.
*
* @return bool
*/
public function get_bool() {
return $this->bool;
}
/**
* Add exceptions.
*
* @param array|object $exceptions The array exceptions.
*
* @return void
*/
public function add_exceptions( $exceptions ) {
$this->exceptions = array_merge(
$this->exceptions,
is_array( $exceptions ) ? $exceptions : array( $exceptions )
);
}
/**
* Get exceptions.
*
* @return array
*/
public function get_exceptions() {
return (array) $this->exceptions;
}
/**
* Get exception messages.
*
* @return array
*/
public function get_exception_messages() {
$messages = array();
foreach ( $this->get_exceptions() as $e ) {
$messages[] = $e->getMessage();
}
return $messages;
}
}
Rules/False.php 0000644 00000000507 15153673736 0007423 0 ustar 00 <?php
/**
* OMAPI_Rules_False class.
*
* @since 1.5.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rules exception false class.
*
* @since 1.5.0
*/
class OMAPI_Rules_False extends OMAPI_Rules_Exception {
protected $bool = false;
}
Rules/True.php 0000644 00000000503 15153673736 0007304 0 ustar 00 <?php
/**
* OMAPI_Rules_True class.
*
* @since 1.5.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rules exception true class.
*
* @since 1.5.0
*/
class OMAPI_Rules_True extends OMAPI_Rules_Exception {
protected $bool = true;
}
Rules.php 0000644 00000056703 15153673736 0006402 0 ustar 00 <?php
/**
* Rules exception base class.
*
* @since 1.5.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rules class.
*
* @since 1.5.0
*
* @package OMAPI
* @author Justin Sternberg
*/
class OMAPI_Rules {
/**
* Holds the meta fields used for checking output statuses.
*
* @since 1.5.0
*
* @var array
*/
protected $fields = array(
'enabled',
'automatic',
'users',
'never',
'only',
'categories',
'taxonomies',
'show',
'type',
'test',
);
/**
* Holds the meta field values for optin.
*
* @since 1.5.0
*
* @var array
*/
protected $field_values = array();
/**
* Whether we're checking for inline display.
*
* @since 1.5.0
*
* @var boolean
*/
protected $is_inline_check = false;
/**
* The advanced settings for the campaign that should override inline.
*
* @since 1.6.2
*
* @var array
*/
protected $advanced_settings = array();
/**
* The current post id.
*
* @since 1.5.0
*
* @var int
*/
protected $post_id = 0;
/**
* The campaign post object.
*
* @since 1.5.0
*
* @var object
*/
protected $optin = null;
/**
* The OMAPI_Rules_Exception if applicable.
*
* @var OMAPI_Rules_Exception
*/
protected $caught = null;
/**
* Whether campaign in loop should be output globally.
*
* @since 1.5.0
*
* @var bool
*/
protected $global_override = true;
/**
* The OMAPI_WooCommerce_Rules instance.
*
* @var OMAPI_WooCommerce_Rules
*/
protected static $woocommerce = null;
/**
* The OMAPI_EasyDigitalDownloads_Rules instance.
*
* @var OMAPI_EasyDigitalDownloads_Rules
*/
protected static $edd = null;
/**
* The OMAPI_MemberPress_Rules instance.
*
* @var OMAPI_MemberPress_Rules
*/
protected static $mp = null;
/**
* The last instance called of this class.
*
* @var OMAPI_Rules
*/
public static $last_instance = null;
/**
* Exceptions which were not caught/were ignored and may be the reason for the final exclusion.
*
* @var array
*/
public $reasons = array();
/**
* Primary class constructor.
*
* @since 1.5.0
*
* @param null|object $optin The campaign post object.
* @param int $post_id The current post id.
* @param bool $is_inline_check Whether we're checking for inline display.
*/
public function __construct( $optin = null, $post_id = 0, $is_inline_check = false ) {
// Default to allowing global override if not an inline check.
$this->optin = $optin;
$this->post_id = $post_id;
$this->is_inline_check = $is_inline_check;
$this->global_override = ! $is_inline_check;
// Set our object.
$this->set();
}
/**
* Sets our object instance and base class instance.
*
* @since 2.8.0
*/
public function set() {
self::$last_instance = $this;
self::init_extensions();
$this->fields = apply_filters( 'optin_monster_api_output_fields', $this->fields );
}
/**
* Initiates the 3rd party extension rules.
*
* @since 2.13.0
*
* @return void
*/
public static function init_extensions() {
if ( self::$woocommerce ) {
return;
}
self::$woocommerce = new OMAPI_WooCommerce_Rules();
self::$edd = new OMAPI_EasyDigitalDownloads_Rules();
self::$mp = new OMAPI_MemberPress_Rules();
self::$woocommerce->init_hooks();
self::$edd->init_hooks();
self::$mp->init_hooks();
}
/**
* Determines if given campaign should show inline.
*
* @since 1.5.0
*
* @param int $post_id The current post id.
* @param object $optin The campaign post object.
*
* @return boolean|array
*/
public static function check_inline( $optin, $post_id = 0 ) {
return self::check( $optin, $post_id, true );
}
/**
* Determines if given campaign should show for a shortcode.
*
* @since 1.5.0
*
* @param int $post_id The current post id.
* @param object $optin The campaign post object.
*
* @return boolean|array
*/
public static function check_shortcode( $optin, $post_id = 0 ) {
return self::check( $optin, $post_id, 'shortcode' );
}
/**
* Determines if given campaign should show.
*
* @since 1.5.0
*
* @param int $post_id The current post id.
* @param object $optin The campaign post object.
* @param bool $is_inline_check Whether we're checking for inline display.
*
* @return boolean|array
*/
public static function check( $optin, $post_id = 0, $is_inline_check = false ) {
$rules = new self( $optin, $post_id, $is_inline_check );
return $rules->should_output();
}
public function get_field_value( $field ) {
return isset( $this->field_values[ $field ] )
? $this->field_values[ $field ]
: null;
}
public function field_empty( $field ) {
return empty( $this->field_values[ $field ] );
}
public function field_not_empty_array( $field ) {
return OMAPI_Utils::field_not_empty_array( $this->field_values, $field );
}
public function item_in_field( $item, $field ) {
return OMAPI_Utils::item_in_field( $item, $this->field_values, $field );
}
public function field_is( $field, $value ) {
return isset( $this->field_values[ $field ] ) && $value === $this->field_values[ $field ];
}
public function is_inline_type() {
return OMAPI_Utils::is_inline_type( $this->get_field_value( 'type' ) );
}
/**
* Checks if current campaign is a non-shortcode inline type on a post.
*
* @since 1.6.2
*
* @return bool
*/
public function is_inline_post_type() {
return $this->is_inline_check
&& 'shortcode' !== $this->is_inline_check
&& is_singular(
apply_filters( 'optinmonster_automatic_inline_post_types', 'post', $this )
);
}
/**
* Determines if given campaign should show.
*
* @since 1.5.0
*
* @return boolean|array
*/
public function should_output() {
// Collect all the fields to check against.
$this->collect_optin_fields();
$should_output = $this->check_should_output();
return apply_filters( 'optinmonster_pre_campaign_should_output', $should_output, $this );
}
/**
* Runs all checks to determine if given campaign should show.
*
* @since 1.5.0
*
* @return boolean|array
*/
protected function check_should_output() {
try {
$this->exclude_if_not_enabled();
$this->exclude_on_campaign_types();
$this->exclude_on_user_logged_in_checks();
$this->exclude_if_inline_and_not_automatic();
$this->default_checks();
$this->plugin_checks();
$this->include_if_inline_and_automatic_and_no_advanced_settings();
$this->include_if_shortcode_and_no_advanced_settings();
$this->output_if_global_override();
$e = new OMAPI_Rules_False( 'default no show' );
if ( ! empty( $this->reasons ) ) {
$e->add_exceptions( $this->reasons );
}
throw $e;
} catch ( OMAPI_Rules_Exception $e ) {
$e = apply_filters( 'optinmonster_check_should_output', $e, $this );
$this->caught = $e;
$should_output = $e instanceof OMAPI_Rules_True;
}
// If query var is set and user can manage OM, output debug data.
if ( OMAPI_Debug::can_output_debug() ) {
$this->output_rules_debug();
}
return $should_output;
}
/**
* Collect all the field values for an optin.
*
* @since 1.5.0
*
* @return OMAPI_Rules
*/
public function collect_optin_fields() {
// Bail early if the optin id is empty.
if ( empty( $this->optin->ID ) ) {
return $this;
}
foreach ( $this->fields as $field ) {
$this->field_values[ $field ] = get_post_meta( $this->optin->ID, '_omapi_' . $field, true );
}
return $this;
}
/**
* Excludes campaign from showing if its 'enabled' field is falsey or missing.
*
* @since 1.5.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function exclude_if_not_enabled() {
if (
// Ensure the optin is enabled and should output.
$this->field_empty( 'enabled' )
// Unless it's a shortcode, in which case, we don't want to check
// if the campaign in the shortcode is "enabled".
// This is for legacy reasons. ¯\_(ツ)_/¯
&& 'shortcode' !== $this->is_inline_check
) {
throw new OMAPI_Rules_False( 'not enabled' );
}
}
/**
* Excludes campaign from showing if its 'type' field is not an inline type
* and it's an inline request, or if it IS an inline type but NOT an inline request.
*
* @since 1.5.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function exclude_on_campaign_types() {
// If inline check
if ( $this->is_inline_check ) {
// And if the type is not an inline type...
if ( ! $this->is_inline_type() ) {
// exclude it from outputting.
throw new OMAPI_Rules_False( 'only inline for inline check' );
}
} else {
// Ok, it's not an inline check
// So check if the type is an inline or sidebar type
if ( $this->field_is( 'type', 'sidebar' ) || $this->is_inline_type() ) {
// and exclude it from outputting.
throw new OMAPI_Rules_False( 'no inline for global check' );
}
}
}
/**
* Checks if campaign should be shown based on its field and if user is logged in.
*
* @since 1.5.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function exclude_on_user_logged_in_checks() {
$is_logged_in = is_user_logged_in();
// If in legacy test mode but not logged in, skip over the optin.
if ( ! $this->field_empty( 'test' ) && ! $is_logged_in ) {
throw new OMAPI_Rules_False( 'test mode' );
}
// If the optin is to be shown only to logged in users but is not logged in, pass over it.
if ( $this->field_is( 'users', 'in' ) && ! $is_logged_in ) {
throw new OMAPI_Rules_False( 'exclude for logged out' );
}
// If the optin is to be shown only to visitors but is logged in, pass over it.
if ( $this->field_is( 'users', 'out' ) && $is_logged_in ) {
throw new OMAPI_Rules_False( 'exclude for logged in' );
}
}
/**
* The default checks to see if given campaign should show.
*
* @since 1.5.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function default_checks() {
// Check for global disable.
if ( get_post_meta( $this->post_id, 'om_disable_all_campaigns', true ) ) {
$this->set_global_override( false );
throw new OMAPI_Rules_False( "all campaigns disabled for this post ($this->post_id)" );
}
// Exclude posts/pages from optin display
// Set flag for possibly not loading globally.
if ( $this->field_not_empty_array( 'only' ) ) {
$this->set_global_override( false );
$this->set_advanced_settings_field( 'show', $this->get_field_value( 'only' ) );
// If the optin is only to be shown on specific post IDs...
if ( $this->item_in_field( $this->post_id, 'only' ) ) {
throw new OMAPI_Rules_True( "include on only $this->post_id" );
}
}
// Exclude posts/pages from optin display
if ( $this->item_in_field( $this->post_id, 'never' ) ) {
// No global check on purpose. Global is still true if only this setting is populated.
throw new OMAPI_Rules_False( "exclude on never $this->post_id" );
}
try {
// If the optin is only to be shown on particular categories...
$this->check_categories_field();
} catch ( OMAPI_Rules_Exception $e ) {
if ( $e instanceof OMAPI_Rules_True ) {
throw new OMAPI_Rules_True( 'include on categories', 0, $e );
}
$this->reasons[] = $e;
}
try {
// If the optin is only to be shown on particular taxonomies...
$this->check_taxonomies_field();
} catch ( OMAPI_Rules_Exception $e ) {
if ( $e instanceof OMAPI_Rules_True ) {
throw new OMAPI_Rules_True( 'include on taxonomies', 0, $e );
}
$this->reasons[] = $e;
}
if ( $this->field_not_empty_array( 'show' ) ) {
// Set flag for not loading globally.
$this->set_global_override( false );
$this->set_advanced_settings_field( 'show', $this->get_field_value( 'show' ) );
}
if (
! $this->is_inline_check
&& $this->item_in_field( 'index', 'show' ) && OMAPI_Utils::is_front_or_search()
) {
throw new OMAPI_Rules_True( 'is front or search and show on index' );
}
// Check if we should show on a selected post type.
if ( $this->item_in_field( get_post_type(), 'show' ) && ! OMAPI_Utils::is_front_or_search() ) {
throw new OMAPI_Rules_True( 'include on post type but not front/search' );
}
// Check if we should show on a selected singular post type.
if ( $this->field_not_empty_array( 'show' ) ) {
foreach ( $this->get_field_value( 'show' ) as $show_value ) {
if ( 0 === strpos( $show_value, 'singular___' ) ) {
$post_type = str_replace( 'singular___', '', $show_value );
if ( is_singular( $post_type ) ) {
throw new OMAPI_Rules_True( 'include on singular post type: ' . $post_type );
}
}
}
}
}
/**
* Run checks for for external plugins rules.
*
* @since 2.8.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function plugin_checks() {
do_action( 'optinmonster_campaign_should_output_plugin_checks', $this );
}
/**
* Disable campaign from showing if it is being checked inline and is NOT set to automatic.
*
* @since 1.5.3
*
* @throws OMAPI_Rules_False
* @return void
*/
public function exclude_if_inline_and_not_automatic() {
if (
$this->is_inline_post_type()
&& $this->field_empty( 'automatic' )
) {
throw new OMAPI_Rules_False( 'exclude inline if not automatic on singular post' );
}
}
/**
* Enable campaign to show if it is being checked inline and is set to automatic.
*
* @since 1.6.2
*
* @throws OMAPI_Rules_True
* @return void
*/
public function include_if_inline_and_automatic_and_no_advanced_settings() {
if (
empty( $this->advanced_settings )
&& $this->is_inline_post_type()
&& ! $this->field_empty( 'automatic' )
) {
throw new OMAPI_Rules_True( 'include inline automatic on singular post' );
}
}
/**
* Enable campaign to show if it is a shortcode and there are no advanced settings.
*
* @since 2.6.8
*
* @throws OMAPI_Rules_True
* @return void
*/
public function include_if_shortcode_and_no_advanced_settings() {
if (
empty( $this->advanced_settings )
&& 'shortcode' === $this->is_inline_check
) {
throw new OMAPI_Rules_True( 'include if shortcode and no advanced settings' );
}
}
/**
* Enable campaign to show if it's global override to show is still true.'
*
* @since 1.5.0
*
* @throws OMAPI_Rules_False
* @return void
*/
public function output_if_global_override() {
if ( $this->global_override ) {
// TODO: Track how often this occurs to determine the importance
// of the $this->global_override logic.
throw new OMAPI_Rules_True( 'include with global override' );
}
}
protected function check_categories_field() {
$categories = $this->get_field_value( 'categories' );
if ( empty( $categories ) ) {
throw new OMAPI_Rules_False( 'no categories' );
}
if ( $this->field_not_empty_array( 'categories' ) ) {
// Set flag for possibly not loading globally.
$this->set_global_override( false );
$this->set_advanced_settings_field( 'categories', $categories );
}
// If this is the home page, check to see if they have decided to load on certain archive pages.
// Run a check for archive-type pages.
// If showing on home and we are on an index page, show the optin.
// TODO: this originally checked is_front_page() || is_home() || is_archive() || is_search()
// but only is_home() would work originally (https://github.com/awesomemotive/optin-monster-wp-api/blame/948b74254284a57f1a338dfc70bc6db59ed3ce8b/OMAPI/Output.php#L407).
// Maybe reintroduce other conditions?
$this->check_is_home_and_show_on_index();
// TODO: Add check for: "Check if we should show on a selected post type."
// This was in the old logic (https://github.com/awesomemotive/optin-monster-wp-api/blame/948b74254284a57f1a338dfc70bc6db59ed3ce8b/OMAPI/Output.php#L385-L392)
// But has not worked for 3+ years as the is_home() condition was precluding it.
// Also applies to logic in check_taxonomies_field
// if ( in_array( 'post', (array) $fields['show'] ) && ! ( is_front_page() || is_home() || is_archive() || is_search() ) ) {
// $omapi_output->set_slug( $optin );
// return $html;
// }
if ( $this->post_id ) {
$all_cats = get_the_category( $this->post_id );
if ( ! empty( $all_cats ) ) {
foreach ( $all_cats as $term ) {
$has_term = in_array( $term->term_id, $categories );
if ( ! $has_term ) {
continue;
}
if ( ! $this->is_inline_check ) {
throw new OMAPI_Rules_True( "post has category $term->name" );
}
if ( ! is_archive() ) {
throw new OMAPI_Rules_True( "post has category $term->name & is not archive" );
}
}
}
}
if ( ! $this->is_inline_check && is_category( $categories ) ) {
throw new OMAPI_Rules_True( 'post on category' );
}
$tax = get_taxonomy( 'category' );
if (
! empty( $tax->object_type )
&& ! in_array( get_post_type( $this->post_id ), $tax->object_type, true )
) {
unset( $this->advanced_settings['categories'] );
throw new OMAPI_Rules_False(
sprintf(
'categories not associated with this post-type (%s).',
get_post_type( $this->post_id )
)
);
}
throw new OMAPI_Rules_False( 'no category matches found' );
}
protected function check_taxonomies_field() {
$taxonomies = $this->get_field_value( 'taxonomies' );
if ( empty( $taxonomies ) ) {
throw new OMAPI_Rules_False( 'no taxonomies' );
}
$values = $this->field_not_empty_array( 'taxonomies' );
if ( $values ) {
foreach ( $values as $i => $value ) {
if ( OMAPI_Utils::field_not_empty_array( $values, $i ) ) {
$this->set_global_override( false );
$this->set_advanced_settings_field( 'taxonomies', $taxonomies );
break;
}
}
}
// If the optin is only to be shown on particular taxonomies...
if ( $this->is_inline_check && ! is_singular() ) {
throw new OMAPI_Rules_False( 'not singular template' );
}
// If this is the home page, check to see if they have decided to load on certain archive pages.
// Run a check for archive-type pages.
// If showing on index pages and we are on an index page, show the optin.
// TODO: potentially move this above check_taxonomies_field && check_categories_field
$this->check_is_home_and_show_on_index();
foreach ( $taxonomies as $taxonomy => $ids_to_check ) {
// Tags are saved differently.
// https://github.com/awesomemotive/optin-monster-wp-api/issues/104
if ( ! empty( $ids_to_check[0] ) && false !== strpos( $ids_to_check[0], ',' ) ) {
$ids_to_check = explode( ',', (string) $ids_to_check[0] );
}
$ids_to_check = (array) $ids_to_check;
if ( $this->post_id ) {
$tax = get_taxonomy( $taxonomy );
$post_type = get_post_type( $this->post_id );
$valid = ! empty( $tax->object_type ) && in_array( $post_type, $tax->object_type, true );
if ( $valid ) {
$all_terms = get_the_terms( $this->post_id, $taxonomy );
if ( ! empty( $all_terms ) ) {
foreach ( $all_terms as $term ) {
// TODO: determine why this logic is different than in check_categories_field.
if ( in_array( $term->term_id, $ids_to_check ) ) {
throw new OMAPI_Rules_True( "{$post_type} has {$taxonomy} {$term->name}" );
}
}
}
} else {
unset( $this->advanced_settings['taxonomies'][ $taxonomy ] );
}
}
if ( ! $this->is_inline_check ) {
foreach ( $ids_to_check as $tax_id ) {
if ( OMAPI_Utils::is_term_archive( $tax_id, $taxonomy ) ) {
throw new OMAPI_Rules_True( "not inline and is on $taxonomy archive" );
}
}
}
}
if ( isset( $this->advanced_settings['taxonomies'] ) ) {
$taxonomies = array_filter( $this->advanced_settings['taxonomies'] );
if ( empty( $taxonomies ) ) {
unset( $this->advanced_settings['taxonomies'] );
throw new OMAPI_Rules_False(
sprintf(
'taxonomies not associated with this post-type (%s).',
get_post_type( $this->post_id )
)
);
}
}
throw new OMAPI_Rules_False( 'no taxonomy matches found' );
}
protected function check_is_home_and_show_on_index() {
if ( is_home() && $this->item_in_field( 'index', 'show' ) ) {
throw new OMAPI_Rules_True( 'is_home and show on index' );
}
}
/**
* Magic getter for our object.
*
* @since 1.5.0
*
* @param string $property
* @throws Exception Throws an exception if the field is invalid.
*
* @return mixed
*/
public function __get( $property ) {
switch ( $property ) {
case 'is_inline_check':
case 'post_id':
case 'optin':
case 'fields':
case 'field_values':
case 'caught':
case 'global_override':
case 'advanced_settings':
return $this->$property;
case 'woocommerce':
case 'edd':
case 'mp':
return self::$$property;
default:
break;
}
throw new Exception( sprintf( esc_html__( 'Invalid %1$s property: %2$s', 'optin-monster-api' ), __CLASS__, $property ) );
}
/**
* Add a new reason to the reasons array
*
* @since 2.8.0
*
* @param mixed $reason The reason to add
*
* @return self
*/
public function add_reason( $reason ) {
$this->reasons[] = $reason;
return $this;
}
/**
* Setter for $advanced_settings attribute
*
* @since 2.8.0
*
* @param string $field The advanced settings field to set
* @param string $value The field value
*
* @return self
*/
public function set_advanced_settings_field( $field, $value ) {
$this->advanced_settings[ $field ] = $value;
return $this;
}
/**
* Setter for $global_override attribute
*
* @since 2.8.0
*
* @param bool $value The new value
*
* @return self
*/
public function set_global_override( $value ) {
$this->global_override = $value;
return $this;
}
/**
* Outputs some debug data for the current campaign object.
*
* @since 1.6.2
*
* @return void
*/
protected function output_rules_debug() {
$show = $this->caught instanceof OMAPI_Rules_True;
$reasons = $this->caught->get_exception_messages();
// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_var_export
?>
<pre class="_om-debugging _om-campaign-sep"><?php echo esc_html( str_repeat( '-', 10 ) . $this->optin->post_name . str_repeat( '-', 10 ) ); ?></pre>
<pre class="_om-debugging _om-post-id">$post_id: <?php echo esc_html( var_export( $this->post_id, true ) ); ?></pre>
<pre class="_om-debugging _om-post-id">$debug_setting_enabled: <?php echo esc_html( var_export( OMAPI::get_instance()->get_option( 'api', 'omwpdebug' ), true ) ); ?></pre>
<pre class="_om-debugging _om-campaign-status" style="color: <?php echo ( $show ? 'green' : 'red' ); ?>;"><?php echo esc_html( $this->optin->post_name . ":\n" . var_export( $this->caught->getMessage(), true ) ); ?><?php echo ! empty( $reasons ) ? ":\n\t- " . implode( "\n\t- ", array_map( 'esc_html', $reasons ) ) : ''; ?>
</pre>
<?php if ( ! empty( $this->advanced_settings ) ) { ?>
<pre class="_om-debugging _om-advanced-settings">$advanced_settings: <?php echo esc_html( var_export( $this->advanced_settings, true ) ); ?></pre>
<?php } ?>
<?php if ( ! empty( $this->field_values ) ) { ?>
<pre class="_om-debugging _om-field-values" style="display:none;">$field_values: <?php echo esc_html( var_export( $this->field_values, true ) ); ?></pre>
<?php } ?>
<pre class="_om-debugging _om-is-inline-check" style="display:none;">$is_inline_check?: <?php echo esc_html( var_export( $this->is_inline_check, true ) ); ?></pre>
<pre class="_om-debugging _om-global-override" style="display:none;">$global_override?: <?php echo esc_html( var_export( $this->global_override, true ) ); ?></pre>
<pre class="_om-debugging _om-optin" style="display:none;">$optin: <?php echo esc_html( var_export( $this->optin, true ) ); ?></pre>
<?php
// phpcs:enable WordPress.PHP.DevelopmentFunctions.error_log_var_export
}
}
Save.php 0000644 00000025461 15153673736 0006203 0 ustar 00 <?php
/**
* Save class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Save class.
*
* @since 1.0.0
*/
class OMAPI_Save {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds save error.
*
* @since 1.0.0
*
* @var mixed
*/
public $error = null;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Store the optin data locally on the site.
*
* @since 1.0.0
*
* @param array $optins Array of optin objects to store.
* @param bool $enabled Whether newly-added campaigns are auto-enabled. Default is true.
*/
public function store_optins( $optins, $enabled = true ) {
/**
* Allows the filtering of what campaigns are stored locally.
*
* @since 1.6.3
*
* @param array $optins An array of `WP_Post` objects.
* @param object $this The OMAPI object.
*
* @return array The filtered `WP_Post` objects array.
*/
$optins = apply_filters( 'optin_monster_pre_store_options', $optins, $this );
// Do nothing if this is just a success message.
if ( isset( $optins->success ) ) {
return;
}
// Loop through all of the local optins so we can try to match and update.
$local_optins = $this->base->get_optins( array( 'post_status' => 'any' ) );
if ( ! empty( $local_optins ) ) {
$this->sync_optins( $local_optins, $optins, $enabled );
} else {
$this->add_optins( $optins, $enabled );
}
}
/**
* Add the retrieved optins as new optin post objects in the DB.
*
* @since 1.3.5
*
* @param array $optins Array of optin objects to store.
* @param bool $enabled Whether newly-added campaigns are auto-enabled. Default is true.
*/
public function add_optins( $optins, $enabled = true ) {
foreach ( (array) $optins as $slug => $optin ) {
// Maybe update an optin rather than add a new one.
$local = $this->base->get_optin_by_slug( $slug );
if ( $local ) {
$this->update_optin( $local, $optin );
} else {
$this->new_optin( $slug, $optin, $enabled );
}
}
}
/**
* Sync the retrieved optins with our stored optins.
*
* @since 1.3.5
*
* @param array $local_optins Array of local optin objects to sync.
* @param array $remote_optins Array of optin objects to store.
* @param bool $enabled Whether newly-added campaigns are auto-enabled. Default is true.
*/
public function sync_optins( $local_optins, $remote_optins, $enabled = true ) {
foreach ( $local_optins as $local ) {
if ( isset( $remote_optins[ $local->post_name ] ) ) {
$this->update_optin( $local, $remote_optins[ $local->post_name ] );
unset( $remote_optins[ $local->post_name ] );
} else {
// Delete the local optin. It does not exist remotely.
$this->delete_optin( $local );
unset( $remote_optins[ $local->post_name ] );
}
}
// If we still have optins, they are new and we need to add them.
if ( ! empty( $remote_optins ) ) {
foreach ( (array) $remote_optins as $slug => $optin ) {
$local = $this->base->get_optin_by_slug( $slug );
if ( $local ) {
$this->update_optin( $local, $optin );
} else {
$this->new_optin( $slug, $optin, $enabled );
}
}
}
}
/**
* Update an existing optin post object in the DB with the one fetched from the API.
*
* @since 1.3.5
*
* @param object $local The local optin post object.
* @param object $optin The optin object.
*
* @return void
*/
public function update_optin( $local, $optin ) {
$status = 'publish';
if ( ! empty( $optin->status ) && 'active' !== $optin->status ) {
$status = 'draft';
}
if (
$optin->title !== $local->post_title
|| $optin->output !== $local->post_content
|| $status !== $local->post_status
) {
$this->optin_to_db(
array(
'ID' => $local->ID, // Existing ID.
'post_title' => $optin->title,
'post_content' => $optin->output,
'post_status' => $status,
)
);
}
$this->update_optin_meta( $local->ID, $optin );
}
/**
* Generate a new optin post object in the DB.
*
* @since 1.3.5
*
* @param string $slug The campaign slug.
* @param object $optin The optin object.
* @param bool $enabled Whether the new campaigns are auto-enabled. Default is true.
*
* @return void
*/
public function new_optin( $slug, $optin, $enabled = true ) {
$status = 'publish';
if ( ! empty( $optin->status ) && 'active' !== $optin->status ) {
$status = 'draft';
}
$post_id = $this->optin_to_db(
array(
'post_name' => $slug,
'post_title' => $optin->title,
'post_excerpt' => $optin->id,
'post_content' => $optin->output,
'post_status' => $status,
'post_type' => OMAPI_Type::SLUG,
)
);
if ( 'post' === $optin->type ) {
update_post_meta( $post_id, '_omapi_automatic', 1 );
}
$enabled = apply_filters( 'optin_monster_auto_enable_campaign', $enabled );
if ( $enabled ) {
update_post_meta( $post_id, '_omapi_enabled', true );
}
$this->update_optin_meta( $post_id, $optin );
}
/**
* Adds/updates the optin post-object in the DB.
*
* @since 1.9.10
*
* @param array $args Array of args for post object.
*
* @return mixed Result
*/
protected function optin_to_db( $args ) {
$priority = has_filter( 'content_save_pre', 'wp_filter_post_kses' );
if ( false !== $priority ) {
remove_filter( 'content_save_pre', 'wp_filter_post_kses', $priority );
}
if ( ! empty( $args['ID'] ) ) {
$result = wp_update_post( $args );
} else {
$result = wp_insert_post( $args );
}
if ( false !== $priority ) {
add_filter( 'content_save_pre', 'wp_filter_post_kses', $priority );
}
return $result;
}
/**
* Deletes the optin post-type object from the DB.
*
* @since 1.9.10
*
* @param mixed $id WP_Post object, or post ID, or campaign slug (post_name).
* @param boolean $by_slug Whether id passed in was the campaign slug.
*
* @return mixed Result of wp_delete_post.
*/
public function delete_optin( $id, $by_slug = false ) {
if ( $by_slug ) {
$id = $this->base->get_optin_by_slug( $id );
}
return wp_delete_post( absint( ! empty( $id->ID ) ? $id->ID : $id ), true );
}
/**
* Update the optin post object's post-meta with an API object's values.
*
* @since 1.3.5
*
* @param int $post_id The post (optin) ID.
* @param object $optin The optin object.
*
* @return void
*/
public function update_optin_meta( $post_id, $optin ) {
update_post_meta( $post_id, '_omapi_type', $optin->type );
update_post_meta( $post_id, '_omapi_ids', $optin->ids );
$shortcodes = ! empty( $optin->shortcodes ) ? $optin->shortcodes : null;
$this->update_shortcodes_meta( $post_id, $shortcodes );
}
/**
* Store the raw shortcodes to the optin's meta for later retrieval/parsing.
*
* @since 1.3.5
*
* @param int $post_id The post (optin) ID.
* @param string|array|null $shortcodes The shortcodes to store to meta, or delete from meta if null.
*
* @return void
*/
protected function update_shortcodes_meta( $post_id, $shortcodes = null ) {
if ( ! empty( $shortcodes ) ) {
update_post_meta( $post_id, '_omapi_shortcode_output', self::get_shortcodes_string( $shortcodes ) );
update_post_meta( $post_id, '_omapi_shortcode', true );
} else {
delete_post_meta( $post_id, '_omapi_shortcode_output' );
delete_post_meta( $post_id, '_omapi_shortcode' );
}
}
/**
* Updated the `optin_monster_api` option in the database.
*
* @since 1.9.8
*
* @param array $option The full `optin_monster_api` option array.
* @param array $data Optional. The parameters passed in via POST request.
*
* @return mixed The results of update_option.
*/
public function update_option( $option, $data = array() ) {
// Allow storing the timestamp of when the API is connected for "first time".
// We are not changing it if the user disconnects and reconnects.
$connected = $this->base->get_option( 'connected' );
if ( ! empty( $connected ) ) {
unset( $option['connected'] );
}
/**
* Filters the `optin_monster_api` option before being saved to the database.
*
* @since 1.0.0
*
* @param array $option The full `optin_monster_api` option array.
* @param array $data The parameters passed in via POST request.
*/
$option = apply_filters( 'optin_monster_api_save', $option, $data );
// Save the option.
return update_option( 'optin_monster_api', $option );
}
/**
* Handles auto-generating WooCommerce API keys for use with OM.
*
* @since 1.7.0
* @since 2.8.0 All the logic was moved to OMAPI_WooCommerce_Save class.
*
* @deprecated 2.8.0 Use `OMAPI_WooCommerce_Save->autogenerate()` instead.
*
* @return array
*/
public function woocommerce_autogenerate() {
_deprecated_function( __FUNCTION__, '2.8.0', 'OMAPI_WooCommerce_Save->autogenerate()' );
return $this->base->woocommerce->save->autogenerate();
}
/**
* Handles connecting WooCommerce when the connect button is clicked.
*
* @since 1.7.0
* @since 2.8.0 All the logic was moved to OMAPI_WooCommerce_Save class.
*
* @deprecated 2.8.0 Use `OMAPI_WooCommerce_Save->connect()` instead.
*
* @param array $data The data passed in via POST request.
*
* @return void
*/
public function woocommerce_connect( $data ) {
_deprecated_function( __FUNCTION__, '2.8.0', 'OMAPI_WooCommerce_Save->connect()' );
return $this->base->woocommerce->save->connect( $data );
}
/**
* Handles disconnecting WooCommerce when the disconnect button is clicked.
*
* @since 1.7.0
* @since 2.8.0 All the logic was moved to OMAPI_WooCommerce_Save class.
*
* @deprecated 2.8.0 Use `OMAPI_WooCommerce_Save->disconnect()` instead.
*
* @param array $data The data passed in via POST request.
*
* @return void
*/
public function woocommerce_disconnect( $data ) {
_deprecated_function( __FUNCTION__, '2.8.0', 'OMAPI_WooCommerce_Save->disconnect()' );
return $this->base->woocommerce->save->disconnect( $data );
}
/**
* Parse shortcodes into a string.
*
* @since 2.2.0
*
* @param mixed $shortcodes Convert shortcodes array to a concatenated string.
*
* @return string
*/
public static function get_shortcodes_string( $shortcodes ) {
return is_array( $shortcodes )
? '|||' . implode( '|||', array_map( 'htmlentities', $shortcodes ) )
: '|||' . htmlentities( $shortcodes, ENT_COMPAT, 'UTF-8' );
}
}
Shortcode.php 0000644 00000007124 15153673736 0007233 0 ustar 00 <?php
/**
* Shortcode class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Shortcode class.
*
* @since 1.0.0
*/
class OMAPI_Shortcode {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Holds the OMAPI_Shortcodes_Shortcode object.
*
* @since 2.6.9
*
* @var object
*/
public $shortcode;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
// Load actions and filters.
add_shortcode( 'optin-monster', array( $this, 'shortcode' ) );
add_shortcode( 'optin-monster-shortcode', array( $this, 'shortcode_v1' ) );
add_shortcode( 'optin-monster-inline', array( $this, 'inline_campaign_shortcode_with_rules' ) );
add_filter( 'widget_text', 'shortcode_unautop' );
add_filter( 'widget_text', 'do_shortcode' );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Creates the shortcode for the plugin.
*
* @since 1.0.0
*
* @global object $post The current post object.
*
* @param array $atts Array of shortcode attributes.
*
* @return string The shortcode HTML output.
*/
public function shortcode( $atts ) {
global $post;
$this->shortcode = new OMAPI_Shortcodes_Shortcode( $atts, $post );
try {
return $this->shortcode->handle();
} catch ( Exception $e ) {
}
return '';
}
/**
* Backwards compat shortcode for v1.
*
* @since 1.0.0
*
* @param array $atts Array of shortcode attributes.
*
* @return string The shortcode HTML output.
*/
public function shortcode_v1( $atts ) {
// Run the v2 implementation.
if ( ! empty( $atts['id'] ) ) {
$atts['slug'] = $atts['id'];
unset( $atts['id'] );
}
return $this->shortcode( $atts );
}
/**
* Creates the inline campaign shortcode, with followrules defaulted to true.
*
* @since 2.6.8
*
* @param array $atts Array of shortcode attributes.
*
* @return string The shortcode HTML output.
*/
public function inline_campaign_shortcode_with_rules( $atts = array() ) {
global $post;
$html = '';
$this->shortcode = new OMAPI_Shortcodes_Shortcode( $atts, $post );
try {
add_filter( 'optinmonster_check_should_output', array( $this, 'reject_non_inline_campaigns' ), 10, 2 );
$html = $this->shortcode->handle_inline();
} catch ( Exception $e ) {
}
remove_filter( 'optinmonster_check_should_output', array( $this, 'reject_non_inline_campaigns' ), 10, 2 );
return $html;
}
/**
* Checks if optin type is inline, and rejects (returns html comment) if not.
*
* @since 2.6.8
*
* @param OMAPI_Rules_Exception $e A rules exception object.
* @param OMAPI_Rules $rules The rules object.
*
* @return OMAPI_Rules_Exception A rules exception object.
*/
public function reject_non_inline_campaigns( $e, $rules ) {
if (
! empty( $rules->optin->campaign_type )
&& ! empty( $rules->optin->ID )
&& ! empty( $this->shortcode->optin->ID )
&& (int) $this->shortcode->optin->ID === (int) $rules->optin->ID
&& 'inline' !== $rules->optin->campaign_type
) {
$e = new OMAPI_Rules_False( 'campaign not inline for optin-monster-inline shortcode' );
}
return $e;
}
}
Shortcodes/Exception.php 0000644 00000000451 15153673736 0011350 0 ustar 00 <?php
/**
* Shortcode exception class.
*
* @since 2.6.9
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Shortcode exception class.
*
* @since 2.6.9
*/
class OMAPI_Shortcodes_Exception extends Exception {}
Shortcodes/Shortcode.php 0000644 00000013273 15153673736 0011352 0 ustar 00 <?php
/**
* Shortcode class.
*
* @since 2.6.9
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Shortcode class.
*
* @since 2.6.9
*/
class OMAPI_Shortcodes_Shortcode {
/**
* Holds the base class object.
*
* @since 2.6.9
*
* @var object
*/
public $base;
/**
* Shortcode attributes.
*
* @since 2.6.9
*
* @var array
*/
public $atts = array();
/**
* The shortcode campaign identifier, slug (or ID for legacy/back-compat).
*
* @since 2.6.9
*
* @var string|int
*/
public $identifier = '';
/**
* The global post object.
*
* @since 2.6.9
*
* @var WP_Post
*/
public $post = null;
/**
* The OM campaign post object.
*
* @since 2.6.9
*
* @var WP_Post
*/
public $campaign = null;
/**
* Class constructor.
*
* @param array $atts Array of shortcode attributes.
* @param object $post The post object to check against.
*
* @since 2.6.9
*/
public function __construct( $atts, $post ) {
$this->atts = $atts;
$this->post = $post;
$this->base = OMAPI::get_instance();
}
/**
* Sends the shortcode html.
*
* @uses wp_validate_boolean
*
* @since 2.6.9
*
* @return string The shortcode HTML output.
* @throws OMAPI_Shortcodes_Exception
*/
public function handle() {
return $this
->check_amp()
->set_atts(
array(
'slug' => '',
'followrules' => 'false',
// id attribute is deprecated.
'id' => '',
),
'optin-monster'
)
->set_identifier()
->set_campaign_object()
->validate_rules()
->get_campaign_html();
}
/**
* Sends the inline-campaign shortcode html.
*
* @since 2.6.9
*
* @return string The shortcode HTML output.
* @throws OMAPI_Shortcodes_Exception
*/
public function handle_inline() {
return $this
->check_amp()
->set_atts(
array(
'slug' => '',
),
'optin-monster-inline'
)
->set_identifier()
->set_campaign_object()
->validate_rules( true )
->get_campaign_html();
}
/**
* Checking if AMP is enabled.
*
* @since 2.6.9
*
* @return OMAPI_Shortcodes_Shortcode
* @throws OMAPI_Shortcodes_Exception
*/
public function check_amp() {
if ( OMAPI_Utils::is_amp_enabled() ) {
throw new OMAPI_Shortcodes_Exception( 'Amp enabled' );
}
return $this;
}
/**
* Set the attributes array using shortcode_atts function.
*
* @since 2.6.9
*
* @uses shortcode_atts
*
* @param array $defaults Array of default attributes.
* @param string $shortcode_name The shortcode name.
*
* @return OMAPI_Shortcodes_Shortcode
*/
public function set_atts( $defaults, $shortcode_name ) {
// Merge default attributes with passed attributes.
$this->atts = shortcode_atts( $defaults, $this->atts, $shortcode_name );
return $this;
}
/**
* Set the campaign identifier from the given attributes, either ID or slug.
*
* @since 2.6.9
*
* @return OMAPI_Shortcodes_Shortcode
* @throws OMAPI_Shortcodes_Exception
*/
public function set_identifier() {
$identifier = false;
if ( ! empty( $this->atts['slug'] ) ) {
$identifier = $this->atts['slug'];
}
if ( ! empty( $this->atts['id'] ) ) {
$identifier = $this->atts['id'];
}
if ( empty( $identifier ) ) {
// A custom attribute must have been passed. Allow it to be filtered to grab the campaign ID from a custom source.
$identifier = apply_filters( 'optin_monster_api_custom_optin_id', false, $this->atts, $this->post );
}
// Allow the campaign ID to be filtered before it is stored and used to create the campaign output.
$identifier = apply_filters( 'optin_monster_api_pre_optin_id', $identifier, $this->atts, $this->post );
// If there is no identifier, do nothing.
if ( empty( $identifier ) ) {
throw new OMAPI_Shortcodes_Exception( 'Missing identifier in attributes' );
}
$this->identifier = $identifier;
return $this;
}
/**
* Set the campaign object, from the ID/slug.
*
* @since 2.6.9
*
* @return OMAPI_Shortcodes_Shortcode
* @throws OMAPI_Shortcodes_Exception
*/
public function set_campaign_object() {
$campaign = ctype_digit( (string) $this->identifier )
? $this->base->get_optin( absint( $this->identifier ) )
: $this->base->get_optin_by_slug( sanitize_text_field( $this->identifier ) );
// If no campaign found, do nothing.
if ( empty( $campaign ) ) {
throw new OMAPI_Shortcodes_Exception( 'Could not find campaign object for identifier' );
}
$this->campaign = $campaign;
return $this;
}
/**
* Checks the given campaign against the output settings rules.
*
* @since 2.6.9
*
* @return OMAPI_Shortcodes_Shortcode
* @throws OMAPI_Shortcodes_Exception
*/
public function validate_rules( $force = false ) {
$should_check = $force || wp_validate_boolean( $this->atts['followrules'] );
if (
$should_check
// Do OMAPI Output rules check.
&& ! OMAPI_Rules::check_shortcode( $this->campaign, $this->post->ID )
) {
throw new OMAPI_Shortcodes_Exception( 'Failed the WordPress rules' );
}
return $this;
}
/**
* Sends the campaign html, passed through optin_monster_shortcode_output filter.
*
* @since 2.6.9
*
* @return string Campaign html.
* @throws OMAPI_Shortcodes_Exception
*/
public function get_campaign_html() {
// Try to grab the stored HTML.
$html = $this->base->output->prepare_campaign( $this->campaign );
if ( ! $html ) {
throw new OMAPI_Shortcodes_Exception( 'Optin object missing campaign html in post_content' );
}
// Make sure to apply shortcode filtering.
$this->base->output->set_slug( $this->campaign );
// Return the HTML.
return apply_filters( 'optin_monster_shortcode_output', $html, $this->campaign, $this->atts );
}
}
Sites.php 0000644 00000020552 15153673736 0006370 0 ustar 00 <?php
/**
* Rest API Class, where we register/execute any REST API Routes
*
* @since 1.8.0
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rest Api class.
*
* @since 1.8.0
*/
class OMAPI_Sites {
/**
* Holds the class object.
*
* @since 2.3.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 2.3.0
*
* @var string
*/
public $file = __FILE__;
/**
* The Base OMAPI Object
*
* @since 1.8.0
*
* @var OMAPI
*/
protected $base;
/**
* Sets our object instance and base class instance.
*
* @since 1.8.0
*/
public function __construct() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Refresh the site data.
*
* @since 1.8.0
*
* @param string $api_key If we want to use a custom API Key, pass it in.
* @param bool $get_cached Whether to get the cached response. Defaults to false.
*
* @return array|null|WP_Error $sites An array of sites if the request is successful.
*/
public function fetch( $api_key = '', $get_cached = false ) {
$cache_key = 'om_sites' . md5( $api_key ? $api_key : '' );
if ( $get_cached ) {
$results = get_transient( $cache_key );
if ( ! empty( $results ) ) {
return $results;
}
}
// Delete any cached sites.
delete_transient( $cache_key );
$creds = ! empty( $api_key ) ? array( 'apikey' => $api_key ) : array();
$body = OMAPI_Api::build( 'v2', 'sites/origin', 'GET', $creds )->request();
if ( is_wp_error( $body ) ) {
return $this->handle_payment_required_error( $body );
}
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$results = array(
'siteId' => '',
'siteIds' => array(),
'customApiUrl' => '',
'apiCname' => '',
);
$domain = $this->get_domain();
$tld = $this->get_tld( $domain );
if ( ! empty( $body->data ) ) {
$check_cnames = true;
foreach ( $body->data as $site ) {
if ( empty( $site->domain ) ) {
continue;
}
$matches = $domain === (string) $site->domain;
$wildcard_domain = '*.' === substr( $site->domain, 0, 2 ) && $tld === $this->get_tld( $site->domain );
// Doesn't match, and not a wildcard? Bail.
if ( ! $matches && ! $wildcard_domain ) {
continue;
}
$results['siteIds'][] = (string) $site->siteId;
// If we don't have a siteId yet, set it to this one.
// If we DO already have a siteId and this one is NOT a wildcard,
// we want to overwrite with this one.
if ( empty( $results['siteId'] ) || ! $wildcard_domain ) {
$results['siteId'] = (string) $site->siteId;
}
// Do we have a custom cnamed api url to use?
if ( $check_cnames && $site->settings->enableCustomCnames ) {
$found = false;
if ( $site->settings->cdnCname && $site->settings->cdnCnameVerified ) {
// If we have a custom CNAME, let's enable it and add the data to the output array.
$results['customApiUrl'] = 'https://' . $site->settings->cdnUrl . '/app/js/api.min.js';
$found = true;
if (
! empty( $site->settings->apiCname )
&& ! empty( $site->settings->apiCnameVerified )
) {
$results['apiCname'] = $site->settings->apiCname;
}
}
// If this isn't a wildcard domain, and we found a custom api url, we don't
// need to continue checking cnames.
if ( $found && ! $wildcard_domain ) {
$check_cnames = false;
}
}
}
}
if ( empty( $results['siteId'] ) ) {
$result = $this->check_existing_site( $creds );
if ( is_wp_error( $result ) ) {
return $result;
}
$site = $this->attempt_create_site( $creds );
if ( is_wp_error( $site ) ) {
return $this->handle_payment_required_error( $site );
}
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
if ( ! empty( $site->siteId ) ) {
$results['siteId'] = (string) $site->siteId;
// phpcs:enable
}
}
if ( ! is_wp_error( $results ) && ! empty( $results['siteIds'] ) ) {
set_transient( $cache_key, $results, 5 * MINUTE_IN_SECONDS );
}
// phpcs:enable
return $results;
}
/**
* Attempt to create the associated site in the app.
*
* @since 1.9.10
*
* @param array $creds Array of credentials for request.
*
* @return mixed Site-created response or WP_Error.
*/
public function attempt_create_site( $creds ) {
$settings = OMAPI_Api::get_url_args();
$settings['wordpress'] = 1;
$site_args = array(
'domain' => esc_url_raw( site_url() ),
'name' => esc_attr( get_option( 'blogname' ) ),
'settings' => $settings,
);
// Create/update the site for this WordPress site.
$result = OMAPI_Api::build( 'v2', 'sites', 'POST', $creds )
->request( $site_args );
return 201 === (int) OMAPI_Api::instance()->response_code
? OMAPI_Api::instance()->response_body
: $result;
}
/**
* Get the domain for this WP site.
* Borrowed heavily from AwesomeMotive\OptinMonsterApp\Utils\Url
*
* @since 1.9.10
*
* @return string
*/
public function get_domain() {
$url = site_url();
$parsed = wp_parse_url( $url );
$hostname = ! empty( $parsed['host'] ) ? $parsed['host'] : $url;
$domain = preg_replace( '/^www\./', '', $hostname );
return $domain;
}
/**
* Get the top-level-domain for the given domain.
*
* @since 2.0.1
*
* @param string $domain Domain to get tld for.
*
* @return string The tld.
*/
public function get_tld( $domain ) {
$parts = explode( '.', $domain );
$count = count( $parts );
$tld = array_slice( $parts, max( 0, $count - 2 ) );
return implode( '.', $tld );
}
/**
* Updates the error text when we try to auto-create this WP site, but it fails.
*
* @since 1.9.10
*
* @param WP_Error $error The error object.
*
* @return WP_Error
*/
public function handle_payment_required_error( $error ) {
$instance = OMAPI_Api::instance();
if ( 402 === (int) $error->get_error_data() && ! empty( $instance->response_body->siteAmount ) ) {
$message = sprintf(
/* translators: %1$s - Link to account upgrade page, %2$s Link to account page to purchase additional licenses */
__( 'We tried to register your WordPress site with OptinMonster, but You have reached the maximum number of registered sites for your current OptinMonster plan.<br>Additional sites can be added to your account by <a href="%1$s" target="_blank" rel="noopener">upgrading</a> or <a href="%2$s" target="_blank" rel="noopener">purchasing additional site licenses</a>.', 'optin-monster-api' ),
esc_url_raw( OPTINMONSTER_APP_URL . '/account/upgrade/?utm_source=app&utm_medium=upsell&utm_campaign=header&feature=sites/' ),
esc_url_raw( OPTINMONSTER_APP_URL . '/account/billing/#additional-licenses' )
);
$error = new WP_Error( $error->get_error_code(), $message, array( 'status' => 402 ) );
}
return $error;
}
/**
* Check if user has already connected existing site, and return error.
*
* @since 2.3.0
*
* @param array $creds Array of credentials for request.
*
* @return WP_Error|bool WP_Error if user already has connected site.
*/
public function check_existing_site( $creds ) {
// Check if they already have a registered site.
$site_id = $this->base->get_site_id();
if ( empty( $site_id ) ) {
return false;
}
// Now check for that previously-registered site in our API.
$body = OMAPI_Api::build( 'v2', "sites/{$site_id}", 'GET', $creds )->request();
if ( empty( $body->name ) ) {
return false;
}
$site_edit_url = OMAPI_Urls::om_app( "sites/{$site_id}/edit/" );
// 'This domain does not match your registered site, %s (%s)'
$message = sprintf(
/* translators: %s - Current site domain, Link to registered OptinMonster site, name of registered OptinMonster site, domain for registered OptinMonster site */
__( 'This domain (%1$s) does not match your registered site — <a href="%2$s" target="_blank" rel="noopener">%3$s (%4$s)</a>', 'optin-monster-api' ),
$this->get_domain(),
esc_url_raw( $site_edit_url ),
sanitize_text_field( $body->name ),
sanitize_text_field( $body->domain )
);
$args = array(
'status' => 404,
'site' => array(
'name' => $body->name,
'domain' => $body->domain,
'editUrl' => $site_edit_url,
),
);
return new WP_Error( 'omapp_wrong_site', $message, $args );
}
}
Support.php 0000644 00000020376 15153673736 0006761 0 ustar 00 <?php
/**
* Support Class, handles generating info for support.
*
* @since 1.9.10
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rest Api class.
*
* @since 1.9.10
*/
class OMAPI_Support {
/**
* The Base OMAPI Object
*
* @since 1.9.10
*
* @var OMAPI
*/
protected $base;
/**
* Class constructor.
*
* @since 1.9.10
*/
public function __construct() {
$this->base = OMAPI::get_instance();
}
/**
* Combine Support data together.
*
* @since 1.9.10
*
* @param string $format The format to return the data in.
*
* @return array
*/
public function get_support_data( $format = 'raw' ) {
return array(
'server' => $this->get_server_data( $format ),
'settings' => $this->get_settings_data( $format ),
'campaigns' => $this->get_campaign_data( $format ),
);
}
/**
* Build Current Optin data array to localize
*
* @since 1.9.10
*
* @param string $format The format to return the data in.
*
* @return array
*/
public function get_campaign_data( $format = 'raw' ) {
$campaigns = $this->base->get_optins( array( 'post_status' => 'any' ) );
$data = array();
if ( empty( $campaigns ) ) {
return $data;
}
foreach ( (array) $campaigns as $campaign ) {
if ( empty( $campaign->ID ) ) {
continue;
}
$slug = $campaign->post_name;
$design_type = get_post_meta( $campaign->ID, '_omapi_type', true );
$data[ $slug ] = array(
'Campaign Type' => $design_type,
'WordPress ID' => $campaign->ID,
'Associated IDs' => get_post_meta( $campaign->ID, '_omapi_ids', true ),
'Current Status' => get_post_meta( $campaign->ID, '_omapi_enabled', true ) ? 'Enabled' : 'Disabled',
'User Settings' => get_post_meta( $campaign->ID, '_omapi_users', true ),
'Pages to Never show on' => get_post_meta( $campaign->ID, '_omapi_never', true ),
'Pages to Only show on' => get_post_meta( $campaign->ID, '_omapi_only', true ),
'Categories' => get_post_meta( $campaign->ID, '_omapi_categories', true ),
'Taxonomies' => get_post_meta( $campaign->ID, '_omapi_taxonomies', true ),
'Template types to Show on' => get_post_meta( $campaign->ID, '_omapi_show', true ),
'Shortcodes Synced and Recognized' => get_post_meta( $campaign->ID, '_omapi_shortcode', true ) ? htmlspecialchars_decode( get_post_meta( $campaign->ID, '_omapi_shortcode_output', true ) ) : 'None recognized',
);
if ( OMAPI_Utils::is_inline_type( $design_type ) ) {
$data[ $slug ]['Automatic Output Status'] = get_post_meta( $campaign->ID, '_omapi_automatic', true ) ? 'Enabled' : 'Disabled';
$data[ $slug ]['Automatic Output Location'] = get_post_meta( $campaign->ID, '_omapi_auto_location', true );
}
if ( 'raw' === $format ) {
$data[ $slug ]['raw'] = $this->base->collect_campaign_data( $campaign );
}
}
return $data;
}
/**
* Build array of server information to localize
*
* @since 1.9.10
*
* @param string $format The format to return the data in.
*
* @return array
*/
public function get_server_data( $format = 'raw' ) {
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$theme_data = wp_get_theme();
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$theme = 'raw' === $format
? array(
'Name' => $theme_data->Name,
'Author' => $theme_data->Author,
'Author Name' => $theme_data->{'Author Name'},
'Author URI' => $theme_data->{'Author URI'},
'Description' => $theme_data->Description,
'Version' => $theme_data->Version,
'Template' => $theme_data->Template,
'Stylesheet' => $theme_data->Stylesheet,
'Template Files' => $theme_data->{'Template Files'},
'Stylesheet Files' => $theme_data->{'Stylesheet Files'},
'Template Dir' => $theme_data->{'Template Dir'},
'Stylesheet Dir' => $theme_data->{'Stylesheet Dir'},
'Screenshot' => $theme_data->Screenshot,
'Tags' => $theme_data->Tags,
'Theme Root' => $theme_data->{'Theme Root'},
'Theme Root URI' => $theme_data->{'Theme Root URI'},
'Parent Theme' => $theme_data->{'Parent Theme'},
)
: $theme_data->Name . ' ' . $theme_data->Version;
// phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$active_plugins = get_option( 'active_plugins', array() );
$plugins = 'raw' === $format ? array() : "\n";
foreach ( get_plugins() as $plugin_path => $plugin ) {
if ( ! in_array( $plugin_path, $active_plugins, true ) ) {
continue;
}
if ( 'raw' === $format ) {
$plugins[] = $plugin;
} else {
$plugins .= $plugin['Name'] . ': ' . $plugin['Version'] . "\n";
}
}
$api_ping = wp_remote_request( OPTINMONSTER_API_URL . '/v2/ping' );
$array = array(
'Plugin Version' => esc_html( $this->base->version ),
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
'Server Info' => esc_html( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ),
'PHP Version' => function_exists( 'phpversion' ) ? esc_html( phpversion() ) : 'Unable to check.',
'Error Log Location' => function_exists( 'ini_get' ) ? ini_get( 'error_log' ) : 'Unable to locate.',
'Default Timezone' => date_default_timezone_get(),
'Site Timezone' => wp_timezone_string(),
'Site Name' => esc_html( get_option( 'blogname' ) ),
'Admin Email' => esc_html( get_site_option( 'admin_email' ) ),
'WordPress Home URL' => esc_url_raw( get_home_url() ),
'WordPress Site URL' => esc_url_raw( get_site_url() ),
'WordPress REST URL' => esc_url_raw( get_rest_url() ),
'WordPress Admin URL' => esc_url_raw( OMAPI_Urls::admin() ),
'WordPress Version' => $GLOBALS['wp_version'],
'Multisite' => is_multisite(),
'Language' => get_locale(),
'API Ping Response' => wp_remote_retrieve_response_code( $api_ping ),
'Active Theme' => $theme,
'Active Plugins' => $plugins,
);
if ( 'raw' !== $format ) {
$array['Multisite'] = $array['Multisite'] ? 'Multisite Enabled' : 'Not Multisite';
}
return $array;
}
/**
* Includes the plugin settings.
*
* @since 2.4.0
*
* @return array Array of plugin settings.
*/
public function get_settings_data() {
$options = $this->base->get_option();
// Remove the optins key. We don't need this in the settings data.
unset( $options['optins'] );
// List of keys to mask in the settings array.
$sensitive_keys = array(
array( 'api', 'apikey' ),
array( 'api', 'key' ),
array( 'api', 'user' ),
array( 'edd', 'key' ),
array( 'edd', 'token' ),
array( 'woocommerce', 'key_id' ),
);
/**
* Filters the extra keys array, allowing additional keys to be added.
*
* @since 2.16.3
*
* @param array $extra_keys The list of sensitive keys. Defaults to an empty array.
*/
$extra_keys = (array) apply_filters( 'optin_monster_redacted_sensitive_keys', array() );
$this->mask_sensitive_data_recursive( $options, array_merge( $sensitive_keys, $extra_keys ) );
return $options;
}
/**
* Recursively mask sensitive data in an array.
*
* @since 2.16.3
*
* @param array $data The data array.
* @param array $sensitive_keys The list of sensitive keys.
*
* @return void
*/
public function mask_sensitive_data_recursive( &$data, $sensitive_keys = array() ) {
foreach ( $sensitive_keys as $path ) {
$ref = &$data;
$path_count = 0;
foreach ( (array) $path as $key ) {
$path_count++;
// If the key doesn't exist, break out of the loop.
if ( ! isset( $ref[ $key ] ) ) {
break;
}
// Set a reference to the next level of the array.
$ref = &$ref[ $key ];
// If we're at the end of the path array, mask the value.
if ( count( $path ) === $path_count && ! empty( $ref ) ) {
$ref = substr( (string) $ref, 0, 2 )
. str_repeat( '*', strlen( (string) $ref ) - 4 )
. substr( (string) $ref, -2 );
}
}
}
}
}
Type.php 0000644 00000004700 15153673736 0006217 0 ustar 00 <?php
/**
* Type class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Type class.
*
* @since 1.0.0
*/
class OMAPI_Type {
/**
* The Post-type slug.
*/
const SLUG = 'omapi';
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
// Load actions and filters.
$this->type();
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Loads the OptinMonster API post type.
*
* @since 1.0.0
*/
public function type() {
register_post_type(
self::SLUG,
array(
'labels' => apply_filters(
'optin_monster_api_post_type_labels',
array(
'name' => _x( 'Campaigns', 'post type general name', 'optin-monster-api' ),
'singular_name' => _x( 'Campaign', 'post type singular name', 'optin-monster-api' ),
'add_new' => esc_html__( 'Add New', 'optin-monster-api' ),
'add_new_item' => esc_html__( 'Add New Campaign', 'optin-monster-api' ),
'edit_item' => esc_html__( 'Edit Campaign', 'optin-monster-api' ),
'new_item' => esc_html__( 'New Campaign', 'optin-monster-api' ),
'all_items' => esc_html__( 'Campaigns', 'optin-monster-api' ),
'view_item' => esc_html__( 'View Campaign', 'optin-monster-api' ),
'search_items' => esc_html__( 'Search Campaigns', 'optin-monster-api' ),
'not_found' => esc_html__( 'No Campaigns found', 'optin-monster-api' ),
'not_found_in_trash' => esc_html__( 'No Campaigns found in trash', 'optin-monster-api' ),
'parent_item_colon' => '',
'menu_name' => esc_html__( 'Campaigns', 'optin-monster-api' ),
)
),
'public' => false,
'rewrite' => false,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'supports' => array( 'title' ),
)
);
}
}
Urls.php 0000644 00000021444 15153673736 0006227 0 ustar 00 <?php
/**
* Urls class.
*
* @since 2.2.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Urls class.
*
* @since 2.2.0
*/
class OMAPI_Urls {
/**
* Get the settings url.
*
* @since 2.2.0
*
* @param array $args Array of query args.
*
* @return string
*/
public static function settings( $args = array() ) {
return self::om_admin( 'settings', $args );
}
/**
* Get the campaigns url.
*
* @since 2.2.0
*
* @param array $args Array of query args.
*
* @return string
*/
public static function campaigns( $args = array() ) {
return self::om_admin( 'campaigns', $args );
}
/**
* Get the templates url.
*
* @since 2.2.0
*
* @param array $args Array of query args.
*
* @return string
*/
public static function templates( $args = array() ) {
return self::om_admin( 'templates', $args );
}
/**
* Get the playbooks url.
*
* @since 2.12.0
*
* @param array $args Array of query args.
*
* @return string
*/
public static function playbooks( $args = array() ) {
return self::om_admin( 'playbooks', $args );
}
/**
* Get the OM wizard url.
*
* @since 2.2.0
*
* @return string
*/
public static function wizard() {
return self::dashboard( array( 'onboarding' => true ) );
}
/**
* Get the contextual OM dashboard url.
*
* @since 2.2.0
*
* @param array $args Array of query args.
*
* @return string
*/
public static function dashboard( $args = array() ) {
return self::om_admin( 'dashboard', $args );
}
/**
* Get the contextual OM university url.
*
* @since 2.13.8
*
* @param array $args Array of query args.
*
* @return string
*/
public static function university( $args = array() ) {
return self::om_admin( 'university', $args );
}
/**
* Get the campaign output settings edit url.
*
* @since 2.2.0
*
* @param string $campaign_slug The campaign slug to edit.
* @param array $args Array of query args.
*
* @return string
*/
public static function campaign_output_settings( $campaign_slug, $args = array() ) {
$args = array_merge( $args, array( 'campaignId' => $campaign_slug ) );
return self::campaigns( $args );
}
/**
* Get the OM onboarding dashboard url.
*
* @since 2.2.0
*
* @return string
*/
public static function onboarding() {
return self::wizard();
}
/**
* Get a link to an OM admin page.
*
* @since 2.2.0
*
* @param string $page Page shortened slug.
* @param array $args Array of query args.
*
* @return string
*/
public static function om_admin( $page, $args ) {
$defaults = array(
'page' => 'optin-monster-' . $page,
);
return self::admin( wp_parse_args( $args, $defaults ) );
}
/**
* Get an admin page url.
*
* @since 2.2.0
*
* @param array $args Array of query args.
*
* @return string
*/
public static function admin( $args = array() ) {
$url = add_query_arg( $args, admin_url( 'admin.php' ) );
return esc_url_raw( $url );
}
/**
* Get app url, with proper query args set to ensure going to correct account, and setting return
* query arg to come back (if relevant on the destination page).
*
* @since 2.2.0
*
* @param string $path The path on the app.
* @param string $return_url Url to return. Will default to wp_get_referer().
*
* @return string The app url.
*/
public static function om_app( $path, $return_url = '' ) {
$app_url = OPTINMONSTER_APP_URL . '/';
$final_destination = $app_url . $path;
if ( empty( $return_url ) ) {
$return_url = wp_get_referer();
if ( empty( $return_url ) ) {
$return_url = self::dashboard();
}
}
$return_url = rawurlencode( $return_url );
$final_destination = add_query_arg( 'return', $return_url, $final_destination );
$url = add_query_arg( 'redirect_to', rawurlencode( $final_destination ), $app_url );
$account_id = OMAPI::get_instance()->get_option( 'accountUserId' );
if ( ! empty( $account_id ) ) {
$url = add_query_arg( 'accountId', $account_id, $url );
}
return $url;
}
/**
* Get upgrade url, with utm_medium param and optional feature.
*
* @since 2.4.0
*
* @param string $utm_medium The utm_medium query param.
* @param string $feature The feature to pass to the upgrade page.
* @param string $return_url Url to return. Will default to wp_get_referer().
* @param array $args Additional query args.
*
* @return string The upgrade url.
*/
public static function upgrade( $utm_medium, $feature = 'none', $return_url = '', $args = array() ) {
$args = self::upgrade_params( $utm_medium, $feature, $args );
$path = add_query_arg( $args, 'account/wp-upgrade/' );
return self::om_app( $path, $return_url );
}
/**
* Get the query args for the upgrade url.
*
* @since 2.15.0
*
* @param string $utm_medium The utm_medium query param.
* @param string $feature The feature to pass to the upgrade page.
* @param array $args Additional query args.
*
* @return array The query args.
*/
public static function upgrade_params( $utm_medium, $feature = 'none', $args = array() ) {
$defaults = wp_parse_args(
self::get_partner_params( OPTINMONSTER_APP_URL . '/account/wp-upgrade/' ),
array(
'utm_source' => 'WordPress',
'utm_medium' => $utm_medium,
'utm_campaign' => 'Plugin',
'feature' => $feature,
)
);
foreach ( $defaults as $key => $value ) {
if ( null === $value ) {
unset( $defaults[ $key ] );
}
}
return wp_parse_args( $args, $defaults );
}
/**
* Get marketing url, with utm_medium params.
*
* @since 2.11.0
*
* @param string $path The path on the app.
* @param array $args Additional query args.
*
* @return string The marketing url.
*/
public static function marketing( $path = '', $args = array() ) {
$url = sprintf( OPTINMONSTER_URL . '/%1$s', $path );
$defaults = wp_parse_args(
self::get_partner_params( $url ),
array(
'utm_source' => 'WordPress',
'utm_medium' => '',
'utm_campaign' => 'Plugin',
)
);
$args = wp_parse_args( $args, $defaults );
return add_query_arg( $args, $url );
}
/**
* Returns the API credentials for OptinMonster.
*
* @since 2.2.0
*
* @return string The API url to use for embedding on the page.
*/
public static function om_api() {
$custom_api_url = OMAPI::get_instance()->get_option( 'customApiUrl' );
return ! empty( $custom_api_url ) ? $custom_api_url : OPTINMONSTER_APIJS_URL;
}
/**
* Sets the partner id param if found, and parses the partner url for additional args to set.
*
* @since 2.15.0
*
* @param string $destination_url The destination url to compare against.
*
* @return array The additional args.
*/
protected static function get_partner_params( $destination_url = '' ) {
$args = array();
// Add the partner id attribution query arg, if it exists.
$id = OMAPI_Partners::get_id();
if ( ! empty( $id ) ) {
$args['sscid'] = $id;
}
// Next, let's parse the partner url for additional query args
// stuffed on the urllink query arg redirect url.
$partner_url = OMAPI_Partners::has_partner_url();
if (
! $partner_url
|| false === strpos( $partner_url, 'urllink' )
) {
return $args;
}
// No params, no problem.
$parsed = wp_parse_url( $partner_url );
if ( empty( $parsed['query'] ) ) {
return $args;
}
// No urllink param, do not pass go, do not collect $200.
$query = wp_parse_args( $parsed['query'] );
if ( empty( $query['urllink'] ) ) {
return $args;
}
// Normalize the url.
$url = urldecode( $query['urllink'] );
$url = false === strpos( $url, 'http' )
? 'https://' . $url
: str_replace( 'http://', 'https://', $url );
// Now let's make sure the url matches the destination url,
// before we go attaching its query args.
if (
$destination_url
&& rtrim( $destination_url, '/' ) && 0 !== stripos( $url, $destination_url )
) {
return $args;
}
// No args, do not pass go, do not collect $200.
$bits = wp_parse_url( $url );
if ( empty( $bits['query'] ) ) {
return $args;
}
$query = wp_parse_args( $bits['query'] );
if ( ! empty( $query ) ) {
// Ok, let's add the found query args to the args array.
$args = wp_parse_args( $query, $args );
}
return $args;
}
/**
* Filters the `allowed_redirect_hosts`.
*
* Adds the OptinMonster app and OptinMonster site to the allowed hosts.
*
* @since 2.16.3
*
* @param array $hosts Array of allowed hosts.
*
* @return array The allowed hosts.
*/
public static function allowed_redirect_hosts( $hosts = array() ) {
if ( ! is_array( $hosts ) ) {
$hosts = array();
}
$hosts[] = str_replace( 'https://', '', OPTINMONSTER_APP_URL );
$hosts[] = str_replace( 'https://', '', OPTINMONSTER_URL );
return $hosts;
}
}
Utils.php 0000644 00000016646 15153673736 0006412 0 ustar 00 <?php
/**
* Utils class.
*
* @since 1.3.6
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Utils class.
*
* @since 1.3.6
*/
class OMAPI_Utils {
/**
* Determines if given type is an inline type.
*
* @since 1.3.6
*
* @param string $type Type to check.
*
* @return boolean
*/
public static function is_inline_type( $type ) {
return 'post' === $type || 'inline' === $type;
}
/**
* Check if an item is in field.
*
* @since 2.16.17
*
* @param mixed $item The item to check.
* @param array $fields The fields to check.
* @param string $field The field to check.
*
* @return bool True if the item is in the field, false otherwise.
*/
public static function item_in_field( $item, $fields, $field ) {
return $item
&& is_array( $fields )
&& ! empty( $fields[ $field ] )
&& in_array( $item, (array) $fields[ $field ] );
}
/**
* Check if a field is not empty and has values.
*
* @since 2.16.17
*
* @param array $fields The fields to check.
* @param string $field The field to check.
*
* @return bool True if the field is not empty and has values, false otherwise.
*/
public static function field_not_empty_array( $fields, $field ) {
if ( empty( $fields[ $field ] ) ) {
return false;
}
$values = array_values( (array) $fields[ $field ] );
$values = array_filter( $values );
return ! empty( $values ) ? $values : false;
}
/**
* WordPress utility functions.
*/
/**
* Check if the current page is the front page, home page, or search page.
*
* @since 2.16.17
*
* @return bool True if the current page is the front page, home page, or search page, false otherwise.
*/
public static function is_front_or_search() {
return is_front_page() || is_home() || is_search();
}
/**
* Check if a term archive is enabled.
*
* @since 2.16.17
*
* @param int $term_id The term ID.
* @param string $taxonomy The taxonomy.
*
* @return bool True if the term archive is enabled, false otherwise.
*/
public static function is_term_archive( $term_id, $taxonomy ) {
if ( ! $term_id ) {
return false;
}
return 'post_tag' === $taxonomy && is_tag( $term_id ) || is_tax( $taxonomy, $term_id );
}
/**
* Determines if AMP is enabled on the current request.
*
* @since 1.9.8
*
* @return bool True if AMP is enabled, false otherwise.
*/
public static function is_amp_enabled() {
return ( function_exists( 'amp_is_request' ) && amp_is_request() )
|| ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() );
}
/**
* Ensures a unique array.
*
* @since 1.9.10
*
* @param array $val Array to clean.
*
* @return array Cleaned array.
*/
public static function unique_array( $val ) {
if ( empty( $val ) ) {
return array();
}
$val = array_filter( $val );
return array_unique( $val );
}
/**
* A back-compatible parse_url helper.
*
* @since 2.3.0
* @deprecated 2.16.3 Use `wp_parse_url`.
*
* @param string $url URL to parse.
*
* @return array The URL parts.
*/
public static function parse_url( $url ) {
_deprecated_function( __METHOD__, '2.17.0', 'wp_parse_url' );
return wp_parse_url( $url );
}
/**
* Build Inline Data
*
* @since 2.3.0
*
* @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable.
* @param string $data String containing the javascript to be added.
*
* @return string The formatted script string.
*/
public static function build_inline_data( $object_name, $data ) {
return sprintf( 'var %s = %s;', $object_name, self::json_encode( $data ) );
}
/**
* Add Inline Script
*
* @since 2.3.0
*
* @see WP_Scripts::add_inline_script()
*
* @param string $handle Name of the script to add the inline script to.
* @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable.
* @param string $data String containing the javascript to be added.
* @param string $position Optional. Whether to add the inline script before the handle
* or after. Default 'after'.
*
* @return bool True on success, false on failure.
*/
public static function add_inline_script( $handle, $object_name, $data, $position = 'before' ) {
$data = apply_filters( 'om_add_inline_script', $data, $handle, $position, $object_name ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$output = self::build_inline_data( $object_name, $data );
$output = apply_filters( 'om_add_inline_script_output', $output, $data, $handle, $position, $object_name ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
return wp_add_inline_script( $handle, $output, $position );
}
/**
* Back-compatible wp_json_encode wrapper.
*
* @since 2.6.1
*
* @param mixed $data Data to encode.
*
* @return string JSON-encoded data.
*/
public static function json_encode( $data ) {
return function_exists( 'wp_json_encode' )
? wp_json_encode( $data )
: json_encode( $data ); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
}
/**
* Check if given date is before provided start date.
*
* @since 2.11.1
*
* @param DateTime $compare The date to compare against the start date.
* @param string $start The start date to compare against, in 'Y-m-d H:i:s' format.
*
* @return bool Whether the given date is before provided start date.
*/
public static function date_before( DateTime $compare, $start ) {
$start = DateTime::createFromFormat( 'Y-m-d H:i:s', $start, $compare->getTimezone() );
return $compare < $start;
}
/**
* Check if given date is between provided start/end date.
*
* @since 2.11.1
*
* @param DateTime $compare The date to compare against the start/end date.
* @param string $start The start date to compare against, in 'Y-m-d H:i:s' format.
* @param string $end The end date to compare against, in 'Y-m-d H:i:s' format.
*
* @return bool Whether the given date is between provided start/end date.
*/
public static function date_within( DateTime $compare, $start, $end ) {
return ! self::date_before( $compare, $start )
&& $compare < DateTime::createFromFormat( 'Y-m-d H:i:s', $end, $compare->getTimezone() );
}
/**
* Get the domains for each language when WPML is enabled.
*
* @since 2.16.19
*
* @return array $language_switcher The array of language code and domains.
*/
public static function get_wpml_language_domains() {
if ( ! self::is_wpml_active() ) {
return array();
}
global $sitepress;
// Get the language switcher settings.
$language_switcher = $sitepress->get_setting( 'language_domains', array() );
return $language_switcher;
}
/**
* Check if WPML is enabled.
*
* If "A different domain per language" is selected for "Language URL format",
* only then we are considering WPML is active. For language_negotiation_type setting:
* 1 = Different languages in directories;
* 2 = A different domain per language;
* 3 = Language name added as a parameter.
*
* @since 2.16.19
*
* @return bool True if WPML is active, false otherwise.
*/
public static function is_wpml_active() {
global $sitepress;
return defined( 'ICL_SITEPRESS_VERSION' ) && $sitepress && 2 === (int) $sitepress->get_setting( 'language_negotiation_type' );
}
}
Validate.php 0000644 00000022100 15153673736 0007021 0 ustar 00 <?php
/**
* Validate class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Validate class.
*
* @since 1.0.0
*/
class OMAPI_Validate {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Set our object.
$this->set();
// Possibly validate our API credentials.
$this->maybe_validate();
// Add validation messages.
add_action( 'admin_notices', array( $this, 'notices' ) );
// Add nonce check to dismiss-wp-pointer for the "please connect nag" dismissal.
add_action( 'wp_ajax_dismiss-wp-pointer', array( $this, 'validate_please_connect_notice_dismiss' ), 0 );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.0.0
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Maybe validate our API credentials if the transient has expired.
*
* @since 1.0.0
*/
public function maybe_validate() {
// Check to see if welcome options have been set. If not, let's delay this check for a day.
// Also set a transient so that we know the plugin has been activated.
$options = $this->base->get_option();
if ( empty( $options['welcome']['status'] ) || 'welcomed' !== $options['welcome']['status'] ) {
set_transient( '_omapi_validate', true, DAY_IN_SECONDS );
return;
}
// Check if the transient has expired.
if ( false !== get_transient( '_omapi_validate' ) ) {
return;
}
// Validate API.
$this->validate();
// Provide action to refresh optins.
do_action( 'optin_monster_api_validate_api' );
}
/**
* Validate API credentials.
*
* @since 1.0.0
*/
public function validate() {
$creds = $this->base->get_api_credentials();
if (
empty( $creds['apikey'] )
&& empty( $creds['user'] )
&& empty( $creds['key'] )
) {
return;
}
// Check for new apikey and only use the old user/key if we don't have it.
if ( empty( $creds['apikey'] ) ) {
$api = new OMAPI_Api(
'validate/',
array(
'user' => ! empty( $creds['user'] ) ? $creds['user'] : '',
'key' => ! empty( $creds['key'] ) ? $creds['key'] : '',
)
);
} else {
$api = new OMAPI_Api( 'verify/', array( 'apikey' => $creds['apikey'] ) );
}
$ret = $api->request();
if ( is_wp_error( $ret ) ) {
$option = $this->base->get_option();
$type = $ret->get_error_code();
switch ( $type ) {
case 'missing':
case 'auth':
// Set option values.
$option['is_invalid'] = true;
$option['is_expired'] = false;
$option['is_disabled'] = false;
break;
case 'disabled':
// Set option values.
$option['is_invalid'] = false;
$option['is_expired'] = false;
$option['is_disabled'] = true;
break;
case 'expired':
// Set option values.
$option['is_invalid'] = false;
$option['is_expired'] = true;
$option['is_disabled'] = false;
break;
default:
break;
}
// Update option.
update_option( 'optin_monster_api', $option );
// Set our transient to run again in an hour.
set_transient( '_omapi_validate', true, HOUR_IN_SECONDS );
} else {
set_transient( '_omapi_validate', true, DAY_IN_SECONDS );
}
}
/**
* Outputs any validation notices.
*
* @since 1.0.0
*/
public function notices() {
global $pagenow;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
$option = $this->base->get_option();
if ( isset( $option['is_invalid'] ) && $option['is_invalid'] ) {
if ( 'optin-monster-dashboard' !== $page ) {
if ( ! OMAPI_Partners::has_partner_url() ) {
echo '<div class="notice notice-error"><p>' . esc_html__( 'There was an error verifying your OptinMonster API credentials. They are either missing or they are no longer valid.', 'optin-monster-api' ) . '</p>';
echo '<p><a href="' . esc_url_raw( OMAPI_Urls::settings() ) . '" class="button button-primary button-large omapi-new-optin" title="' . esc_html__( 'View API Settings', 'optin-monster-api' ) . '">' . esc_html__( 'View API Settings', 'optin-monster-api' ) . '</a></p></div>';
}
}
} elseif ( isset( $option['is_disabled'] ) && $option['is_disabled'] ) {
echo '<div class="notice notice-error"><p>' . esc_html__( 'The subscription to this OptinMonster account has been disabled, likely due to a refund or other administrator action. Please contact OptinMonster support to resolve this issue.', 'optin-monster-api' ) . '</p>';
echo '<p><a href="' . esc_url( OPTINMONSTER_APP_URL ) . '/account/support/?utm_source=orgplugin&utm_medium=link&utm_campaign=wpdashboard" class="button button-primary button-large omapi-new-optin" title="' . esc_html__( 'Contact OptinMonster Support', 'optin-monster-api' ) . '" target="_blank">' . esc_html__( 'Contact Support', 'optin-monster-api' ) . '</a></p></div>';
} elseif ( isset( $option['is_expired'] ) && $option['is_expired'] ) {
echo '<div class="notice notice-error"><p>' . esc_html__( 'The subscription to this OptinMonster account has expired. Please renew your subscription to use the OptinMonster API.', 'optin-monster-api' ) . '</p>';
echo '<p><a href="' . esc_url( OPTINMONSTER_APP_URL ) . '/account/billing/?utm_source=orgplugin&utm_medium=link&utm_campaign=wpdashboard" class="button button-primary button-large omapi-new-optin" title="' . esc_html__( 'Renew Subscription', 'optin-monster-api' ) . '" target="_blank">' . esc_html__( 'Renew Subscription', 'optin-monster-api' ) . '</a></p></div>';
} else {
if ( $this->should_user_see_connect_nag() ) {
echo '
<div id="omapi-please-connect-notice" class="notice notice-success is-dismissible">
<h3 style="padding:2px;font-weight:normal;margin:.5em 0 0;">' . esc_html__( 'Get More Email Subscribers with OptinMonster', 'optin-monster-api' ) . '</h3>
<p>' . esc_html__( 'Please connect to or create an OptinMonster account to start using OptinMonster. This will enable you to start turning website visitors into subscribers & customers.', 'optin-monster-api' ) . '
</p>
<p>
<a href="' . esc_url_raw( OMAPI_Urls::onboarding() ) . '" class="button button-primary button-large omapi-new-optin" title="' . esc_html__( 'Get Started', 'optin-monster-api' ) . '">' . esc_html__( 'Get Started', 'optin-monster-api' ) . '</a>
<a style="margin-left:8px" href="' . esc_url( OMAPI_Urls::onboarding() ) . '" title="' . esc_attr__( 'Learn More', 'optin-monster-api' ) . '">' . esc_html__( 'Learn More →', 'optin-monster-api' ) . '</a>
</p>
</div>
';
}
}
}
/**
* Script to hide the please connect nag
*/
public function hide_connect_notice_script() {
?>
<script type="text/javascript">
jQuery(document).on( 'click', '#omapi-please-connect-notice .notice-dismiss', function( event ) {
event.preventDefault();
// Set the pointer to be closed for this user
jQuery.post( ajaxurl, {
pointer: 'omapi_please_connect_notice',
_wpnonce: '<?php echo esc_js( wp_create_nonce( 'dismiss_pointer' ) ); ?>',
action: 'dismiss-wp-pointer'
});
jQuery( '#omapi-please-connect-notice' ).fadeTo( 100, 0, function() {
jQuery( this ).slideUp(100, function() {
jQuery( this ).remove()
})
});
});
</script>
<?php
}
/**
* Check user meta and see if they have previously dismissed the please connect nag
*
* @return bool default false and true only if the 'omapi_please_connect_notice' is not in the wp dismissed pointers usermeta
*/
public function should_user_see_connect_nag() {
global $pagenow;
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
if (
$this->base->menu->is_om_page()
|| 'index.php' === $pagenow
|| $this->base->get_api_credentials()
) {
return false;
}
// Get array list of dismissed pointers for current user and convert it to array.
$dismissed_pointers = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
// Check if our pointer is not among dismissed ones and that the user should see this.
if (
! in_array( 'omapi_please_connect_notice', $dismissed_pointers, true )
&& current_user_can( 'activate_plugins' )
) {
// Add footer script to save when user dismisses.
add_action( 'admin_print_footer_scripts', array( $this, 'hide_connect_notice_script' ) );
return true;
}
return false;
}
/**
* Validates the please connect notice dismissal.
*
* @since 2.16.0
*
* @return void
*/
public function validate_please_connect_notice_dismiss() {
if ( isset( $_POST['pointer'] ) && 'omapi_please_connect_notice' !== $_POST['pointer'] ) {
return;
}
check_ajax_referer( 'dismiss_pointer' );
}
}
WPForms/RestApi.php 0000644 00000002711 15153673736 0010202 0 ustar 00 <?php
/**
* WPForms API routes for usage in WP's RestApi.
*
* @since 2.9.0
*
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rest Api class.
*
* @since 2.9.0
*/
class OMAPI_WPForms_RestApi extends OMAPI_BaseRestApi {
/**
* The OMAPI_WPForms instance.
*
* @since 2.8.0
*
* @var OMAPI_WPForms
*/
public $wpforms;
/**
* Constructor
*
* @since 2.13.0
*
* @param OMAPI_WPForms $wpforms
*/
public function __construct( OMAPI_WPForms $wpforms ) {
$this->wpforms = $wpforms;
parent::__construct();
}
/**
* Registers the Rest API routes for WPForms
*
* @since 2.9.0
*
* @return void
*/
public function register_rest_routes() {
register_rest_route(
$this->namespace,
'wpforms/forms',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_or_has_api_key' ),
'callback' => array( $this, 'forms' ),
)
);
}
/**
* Handles getting WPForms forms.
*
* Route: GET omapp/v1/woocommerce/forms
*
* @since 2.9.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function forms() {
try {
return new WP_REST_Response(
$this->wpforms->get_forms_array(),
200
);
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
}
WPForms/Save.php 0000644 00000004215 15153673736 0007532 0 ustar 00 <?php
/**
* WPForms Save class.
*
* @since 2.9.0
*
* @package OMAPI
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WPForms Save class.
*
* @since 2.9.0
*/
class OMAPI_WPForms_Save {
/**
* Holds save error.
*
* @since 2.9.0
*
* @var mixed
*/
public $error = null;
/**
* Holds the base class object.
*
* @since 2.9.0
*
* @var OMAPI
*/
public $base;
/**
* Primary class constructor.
*
* @since 2.9.0
*/
public function __construct() {
$this->base = OMAPI::get_instance();
}
/**
* Helper method to handle connecting WPForms to OptinMonster.
*
* @since 2.9.0
*
* @return void
*/
public function connect() {
$this->update_connection();
}
/**
* Helper method to handle disconnecting WPForms to OptinMonster.
*
* @since 2.9.0
*
* @return void
*/
public function disconnect() {
$this->update_connection( false );
}
/**
* Handles connecting or disconnecting WPForms to OptinMonster.
*
* @since 2.9.0
*
* @param bool $connect True to connect, false to disconnect.
*
* @return void
*/
public function update_connection( $connect = true ) {
$creds = $this->base->get_api_credentials();
if ( empty( $creds['apikey'] ) && empty( $creds['user'] ) && empty( $creds['key'] ) ) {
return;
}
// Make a connection request.
$action = $connect ? 'connect' : 'disconnect';
$api = new OMAPI_Api( 'wpforms/' . $action, $creds, 'POST', 'v2' );
$response = $api->request( OMAPI_Api::get_url_args() );
if ( is_wp_error( $response ) ) {
$message = $connect
/* translators: %s - Error message found while connecting to OptinMonster. */
? esc_html__( 'WPForms could not be connected to OptinMonster. The OptinMonster API returned with the following response: %s', 'optin-monster-api' )
/* translators: %s - Error message found while disconnecting to OptinMonster. */
: esc_html__( 'WPForms could not be disconnected from OptinMonster. The OptinMonster API returned with the following response: %s', 'optin-monster-api' );
$this->error = sprintf( $message, $response->get_error_message() );
}
}
}
WPForms.php 0000644 00000005152 15153673736 0006635 0 ustar 00 <?php
/**
* WPForms class.
*
* @since 2.9.0
*
* @package OMAPI
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The WPForms class.
*
* @since 2.9.0
*/
class OMAPI_WPForms extends OMAPI_Integrations_Base {
/**
* The OMAPI_WPForms_RestApi instance.
*
* @since 2.13.0
*
* @var null|OMAPI_WPForms_RestApi
*/
public $rest = null;
/**
* Holds the instance of OMAPI_WPForms_Save.
*
* @var null|OMAPI_WPForms_Save
*/
public $save = null;
/**
* Primary class constructor.
*
* @since 2.9.0
*/
public function __construct() {
parent::__construct();
$this->save = new OMAPI_WPForms_Save();
add_action( 'optin_monster_api_rest_register_routes', array( $this, 'maybe_init_rest_routes' ) );
// When WPForms is activated, connect it.
add_action( 'activate_wpforms-lite/wpforms.php', array( $this->save, 'connect' ) );
add_action( 'activate_wpforms/wpforms.php', array( $this->save, 'connect' ) );
// When WPForms is deactivated, disconnect.
add_action( 'deactivate_wpforms-lite/wpforms.php', array( $this->save, 'disconnect' ) );
add_action( 'deactivate_wpforms/wpforms.php', array( $this->save, 'disconnect' ) );
}
/**
* Check if the WPForms plugin is active.
*
* @since 2.9.0
*
* @return bool
*/
public static function is_active() {
return class_exists( 'WPForms', true );
}
/**
* Get WPForms forms array containing label and value.
*
* @since 2.9.0
*
* @return array
*/
public function get_forms_array() {
$forms = $this->get_forms();
$result = array();
if ( empty( $forms ) || ! is_array( $forms ) ) {
return $result;
}
foreach ( $forms as $form ) {
$result[] = array(
'value' => $form->ID,
'label' => $form->post_title,
);
}
return $result;
}
/**
* Get forms from WPForms plugin.
*
* @since 2.9.0
*
* @return array All the forms in WPForms plugin.
*/
public function get_forms() {
if ( ! function_exists( 'wpforms' ) ) {
return array();
}
return wpforms()->form->get( '', array( 'order' => 'DESC' ) );
}
/**
* Get the currently installed WPForms version.
*
* @since 2.9.0
*
* @return string The WPForms version.
*/
public static function version() {
if ( ! function_exists( 'wpforms' ) ) {
return '0.0.0';
}
$version = wpforms()->version;
return $version ? $version : '0.0.0';
}
/**
* Initiate our REST routes for EDD if EDD active.
*
* @since 2.13.0
*
* @return void
*/
public function maybe_init_rest_routes() {
if ( self::is_active() ) {
$this->rest = new OMAPI_WPForms_RestApi( $this );
}
}
}
Welcome.php 0000644 00000011502 15153673736 0006667 0 ustar 00 <?php
/**
* Welcome class.
*
* @since 1.1.4
*
* @package OMAPI
* @author Devin Vinson
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Welcome class.
*
* @since 1.1.4
*/
class OMAPI_Welcome {
/**
* Holds the class object.
*
* @since 1.1.4.2
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.1.4.2
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.1.4.2
*
* @var object
*/
public $base;
/**
* Primary class constructor.
*
* @since 1.1.4.2
*/
public function __construct() {
// If we are not in admin or admin ajax, return.
if ( ! is_admin() ) {
return;
}
// If user is in admin ajax or doing cron, return.
if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
return;
}
// If user is not logged in, return.
if ( ! is_user_logged_in() ) {
return;
}
// If user cannot manage_options, return.
if ( ! OMAPI::get_instance()->can_access( 'welcome' ) ) {
return;
}
// Set our object.
$this->set();
// Maybe load a dashboard widget.
add_action( 'admin_init', array( $this, 'redirect' ), 9999 );
add_action( 'wp_dashboard_setup', array( $this, 'dashboard_widget' ) );
}
/**
* Sets our object instance and base class instance.
*
* @since 1.1.4.2
*/
public function set() {
self::$instance = $this;
$this->base = OMAPI::get_instance();
}
/**
* Onboarding redirect.
*
* This function checks if a new install or update has just occurred. If so,
* then we redirect the user to the appropriate page.
*
* @since 2.6.0
*/
public function redirect() {
// Check if we should consider redirection.
if ( ! get_transient( 'optin_monster_api_activation_redirect' ) ) {
return;
}
// If we are redirecting, clear the transient so it only happens once.
delete_transient( 'optin_monster_api_activation_redirect' );
// Check option to disable welcome redirect.
if ( get_option( 'optin_monster_api_activation_redirect_disabled', false ) ) {
return;
}
// Only do this for single site installs.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['activate-multi'] ) || is_network_admin() ) {
return;
}
// Don't initiate onboarding if they are already connected.
if ( OMAPI_ApiKey::has_credentials() ) {
return;
}
$goto = OMAPI_Urls::dashboard();
// Check if they haven't yet been welcomed.
if ( 'welcomed' !== $this->base->get_option( 'welcome', 'status', 'none' ) ) {
$options = $this->base->get_option();
$options['welcome']['status'] = 'welcomed';
update_option( 'optin_monster_api', $options );
$goto = OMAPI_Urls::wizard();
}
wp_safe_redirect( $goto );
exit;
}
/**
* Loads a dashboard widget if the user has not entered and verified API credentials.
*
* @since 1.1.5.1
*/
public function dashboard_widget() {
if ( OMAPI_ApiKey::has_credentials() ) {
return;
}
wp_add_dashboard_widget(
'optin_monster_db_widget',
esc_html__( 'Please Connect OptinMonster', 'optin-monster-api' ),
array( $this, 'dashboard_widget_callback' )
);
global $wp_meta_boxes;
$normal_dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
$example_widget_backup = array( 'optin_monster_db_widget' => $normal_dashboard['optin_monster_db_widget'] );
unset( $normal_dashboard['optin_monster_db_widget'] );
$sorted_dashboard = array_merge( $example_widget_backup, $normal_dashboard );
$wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard;
}
/**
* Dashboard widget callback.
*
* @since 1.1.5.1
*/
public function dashboard_widget_callback() {
?>
<div class="optin-monster-db-widget" style="text-align:center;">
<p><img src="<?php echo plugins_url( '/assets/images/logo-om.png', OMAPI_FILE ); ?>" alt="<?php esc_attr_e( 'OptinMonster', 'optin-monster-api' ); ?>" width="300px" height="45px"></p>
<h3 style="font-weight:normal;font-size:1.3em;"><?php esc_html_e( 'Please Connect OptinMonster', 'optin-monster-api' ); ?></h3>
<p>
<?php
echo wp_kses_post( __( 'Instantly grow your email list, get more leads and increase sales with the <strong>#1 most powerful conversion optimization toolkit in the world.</strong>', 'optin-monster-api' ) );
?>
</p>
<p><a href="<?php echo esc_url( OMAPI_Urls::onboarding() ); ?>" class="button button-primary" title="<?php esc_attr_e( 'Get Started', 'optin-monster-api' ); ?>"><?php esc_html_e( 'Get Started', 'optin-monster-api' ); ?></a><a style="margin-left:8px" href="<?php echo esc_url( OMAPI_Urls::onboarding() ); ?>" title="<?php esc_attr_e( 'Learn More', 'optin-monster-api' ); ?>"><?php esc_html_e( 'Learn More →', 'optin-monster-api' ); ?></a></p>
</div>
<?php
}
}
Widget.php 0000644 00000014774 15153673737 0006536 0 ustar 00 <?php
/**
* Widget class.
*
* @since 1.0.0
*
* @package OMAPI
* @author Thomas Griffin
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Widget class.
*
* @since 1.0.0
*/
class OMAPI_Widget extends WP_Widget {
/**
* Holds the class object.
*
* @since 1.0.0
*
* @var object
*/
public static $instance;
/**
* Path to the file.
*
* @since 1.0.0
*
* @var string
*/
public $file = __FILE__;
/**
* Holds the base class object.
*
* @since 1.0.0
*
* @var object
*/
public $base;
/**
* Constructor. Sets up and creates the widget with appropriate settings.
*
* @since 1.0.0
*/
public function __construct() {
// Load the base class object.
$this->base = OMAPI::get_instance();
$widget_ops = apply_filters(
'optin_monster_api_widget_ops',
array(
'classname' => 'optin-monster-api',
'description' => esc_html__( 'Place an OptinMonster campaign into a widgetized area.', 'optin-monster-api' ),
)
);
$control_ops = apply_filters(
'optin_monster_api_widget_control_ops',
array(
'id_base' => 'optin-monster-api',
'height' => 350,
'width' => 225,
)
);
parent::__construct(
'optin-monster-api',
apply_filters( 'optin_monster_api_widget_name', esc_html__( 'OptinMonster', 'optin-monster-api' ) ),
$widget_ops,
$control_ops
);
}
/**
* Outputs the widget within the widgetized area.
*
* @since 1.0.0
*
* @param array $args The default widget arguments.
* @param array $instance The input settings for the current widget instance.
*
* @return void
*/
public function widget( $args, $instance ) {
$title = apply_filters( 'widget_title', isset( $instance['title'] ) ? $instance['title'] : '' );
$optin_id = isset( $instance['optin_monster_id'] ) ? $instance['optin_monster_id'] : 0;
do_action( 'optin_monster_api_widget_before_output', $args, $instance );
echo $args['before_widget'];
do_action( 'optin_monster_api_widget_before_title', $args, $instance );
// If a title exists, output it.
if ( $title ) {
echo $args['before_title'] . $title . $args['after_title'];
}
do_action( 'optin_monster_api_widget_before_optin', $args, $instance );
// If a optin has been selected, output it.
if ( $optin_id ) {
// Grab the optin object. If it does not exist, return early.
$optin = absint( $optin_id ) ? $this->base->get_optin( $optin_id ) : $this->base->get_optin_by_slug( $optin_id );
if ( ! $optin ) {
return;
}
// If in test mode but not logged in, skip over the optin.
$test = (bool) get_post_meta( $optin->ID, '_omapi_test', true );
if ( $test && ! is_user_logged_in() ) {
return;
}
// Load the optin.
optin_monster(
$optin->ID,
'id',
array(
'followrules' => ! empty( $instance['followrules'] ) ? 'true' : 'false',
)
);
}
do_action( 'optin_monster_api_widget_after_optin', $args, $instance );
echo $args['after_widget'];
do_action( 'optin_monster_api_widget_after_output', $args, $instance );
}
/**
* Sanitizes and updates the widget.
*
* @since 1.0.0
*
* @param array $new_instance The new input settings for the current widget instance.
* @param array $old_instance The old input settings for the current widget instance.
*
* @return array
*/
public function update( $new_instance, $old_instance ) {
// Set $instance to the old instance in case no new settings have been updated for a particular field.
$instance = $old_instance;
// Sanitize user inputs.
$instance['title'] = trim( $new_instance['title'] );
$instance['followrules'] = ! empty( $new_instance['followrules'] );
$instance['optin_monster_id'] = absint( $new_instance['optin_monster_id'] );
return apply_filters( 'optin_monster_api_widget_update_instance', $instance, $new_instance );
}
/**
* Outputs the widget form where the user can specify settings.
*
* @since 1.0.0
*
* @param array $instance The input settings for the current widget instance.
*
* @return void
*/
public function form( $instance ) {
// Get all available optins and widget properties.
$optins = $this->base->get_optins();
$title = isset( $instance['title'] ) ? $instance['title'] : '';
$followrules = ! empty( $instance['followrules'] );
$optin_id = isset( $instance['optin_monster_id'] ) ? $instance['optin_monster_id'] : false;
do_action( 'optin_monster_api_widget_before_form', $instance );
?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title', 'optin-monster-api' ); ?></label>
<input id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" style="width: 100%;" />
</p>
<?php do_action( 'optin_monster_api_widget_middle_form', $instance ); ?>
<p>
<label for="<?php echo esc_attr( $this->get_field_id( 'optin_monster_id' ) ); ?>"><?php esc_html_e( 'Campaign', 'optin-monster-api' ); ?></label>
<select id="<?php echo esc_attr( $this->get_field_id( 'optin_monster_id' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'optin_monster_id' ) ); ?>" style="width: 100%;">
<?php if ( ! empty( $optins ) ) {
foreach ( $optins as $optin ) {
$type = get_post_meta( $optin->ID, '_omapi_type', true );
$enabled = (bool) get_post_meta( $optin->ID, '_omapi_enabled', true );
// Only allow sidebar types.
if ( 'sidebar' !== $type && 'inline' !== $type ) {
continue;
}
// Display disabled or enabled selection.
if ( $enabled ) {
echo '<option value="' . esc_attr( $optin->ID ) . '"' . selected( $optin->ID, $optin_id, false ) . '>' . esc_html( $optin->post_title ) . '</option>';
} else {
echo '<option value="' . esc_attr( $optin->ID ) . '" disabled="disabled"' . selected( $optin->ID, $optin_id, false ) . '>' . esc_html( $optin->post_title ) . ' (' . esc_html__( 'Not Enabled', 'optin-monster-api' ) . ')</option>';
}
}
}
?>
</select>
</p>
<p>
<input id="<?php echo esc_attr( $this->get_field_id( 'followrules' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'followrules' ) ); ?>" type="checkbox" value="1" <?php checked( $followrules ); ?> />
<label for="<?php echo esc_attr( $this->get_field_id( 'followrules' ) ); ?>"><?php esc_html_e( 'Apply Advanced Output Settings?', 'optin-monster-api' ); ?></label>
</p>
<?php
do_action( 'optin_monster_api_widget_after_form', $instance );
}
}
WooCommerce/Order.php 0000644 00000007755 15153673737 0010606 0 ustar 00 <?php
/**
* WooCommerce Order class.
*
* @since 2.13.8
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WooCommerce Order class.
*
* @since 2.13.8
*/
class OMAPI_WooCommerce_Order {
/**
* Holds instances of this order object.
*
* @since 2.13.8
*
* @var OMAPI_WooCommerce_Order[]
*/
protected static $instances = array();
/**
* Holds the ID of the order.
*
* @since 2.13.8
*
* @var string
*/
protected $id;
/**
* Holds the order object.
*
* @since 2.13.8
*
* @var WC_Order
*/
protected $order;
/**
* Get instance of the OMAPI_WooCommerce_Order and cache it.
*
* @since 2.13.8
*
* @param string $id The order ID.
* @param boolean $cached Whether to use the cached instance or not.
*
* @return self
*/
public static function get( $id = '', $cached = true ) {
if ( empty( $id ) ) {
return new self( $id );
}
if ( $id instanceof WC_Order || $id instanceof WC_Abstract_Order ) {
$order = $id;
$id = (int) $order->get_id();
if ( ! isset( self::$instances[ $id ] ) ) {
new self( $id );
}
self::$instances[ $id ]->set_order( $order );
} elseif ( ! empty( $id->ID ) ) {
$id = (int) $id->ID;
} else {
$id = (int) $id;
}
if ( ! $cached || ! isset( self::$instances[ $id ] ) ) {
$me = new self( $id );
$me->fetch_and_set_order();
}
return self::$instances[ $id ];
}
/**
* Class constructor.
*
* @since 2.13.8
*
* @param string $id The order ID.
*/
protected function __construct( $id = '' ) {
// If no data has been passed, don't setup anything. Maybe we are in test or create mode?
if ( empty( $id ) ) {
return;
}
// Prepare properties.
$this->id = $id;
self::$instances[ $id ] = $this;
}
/**
* Fetches the order object and sets it.
*
* @since 2.13.8
*
* @return self
*/
protected function fetch_and_set_order() {
$this->set_order( wc_get_order( $this->id ) );
return $this;
}
/**
* Sets the order object.
*
* @since 2.13.8
*
* @param WC_Order $order The order object.
*
* @return self
*/
protected function set_order( $order ) {
$this->order = $order;
return $this;
}
/**
* Checks if the order exists/is valid.
*
* @since 2.13.8
*
* @return boolean
*/
public function is() {
if ( empty( $this->order ) ) {
return false;
}
$id = $this->order->get_id();
return ! empty( $id );
}
/**
* Gets order meta, use HPOS API when possible.
*
* @since 2.13.8
*
* @param string $key Meta Key.
* @param bool $single return first found meta with key, or all with $key.
* @param string $context What the value is for. Valid values are view and edit.
* @return mixed
*/
public function get_meta( $key = '', $single = true, $context = 'edit' ) {
return ! empty( $this->order ) && method_exists( $this->order, 'get_meta' )
? $this->order->get_meta( $key, $single, $context )
: get_post_meta( $this->id, $key, $single );
}
/**
* Updates order meta, use HPOS API when possible.
*
* If using HPOS, can pass $save = false to not save the order (for bulk updates).
*
* @since 2.13.8
*
* @param string $key The meta key.
* @param mixed $value The meta value.
* @param bool $save Whether to save the order after meta update (if using HPOS).
*
* @return boolean
*/
public function update_meta_data( $key, $value, $save = true ) {
if ( ! empty( $this->order ) && method_exists( $this->order, 'update_meta_data' ) ) {
$this->order->update_meta_data( $key, $value );
return $save ? $this->order->save() : false;
}
return update_post_meta( $this->id, $key, $value );
}
/**
* Proxy calls to the order object.
*
* @since 2.13.8
*
* @param string $method The method name.
* @param array $args The method arguments.
*
* @return mixed
*/
public function __call( $method, $args ) {
return $this->order
? call_user_func_array( array( $this->order, $method ), $args )
: null;
}
}
WooCommerce/RestApi.php 0000644 00000017776 15153673737 0011106 0 ustar 00 <?php
/**
* WooCommerce API routes for usage in WP's RestApi.
*
* @since 2.8.0
*
* @author Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Rest Api class.
*
* @since 2.8.0
*/
class OMAPI_WooCommerce_RestApi extends OMAPI_BaseRestApi {
/**
* The OMAPI_WooCommerce_Save instance.
*
* @since 2.13.0
*
* @var OMAPI_WooCommerce_Save
*/
public $save;
/**
* Constructor
*
* @since 2.13.0
*
* @param OMAPI_WooCommerce_Save $save
*/
public function __construct( OMAPI_WooCommerce_Save $save ) {
$this->save = $save;
parent::__construct();
}
/**
* Registers the Rest API routes for WooCommerce
*
* @since 2.8.0
*
* @return void
*/
public function register_rest_routes() {
register_rest_route(
$this->namespace,
'woocommerce/autogenerate',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_update_settings' ),
'callback' => array( $this, 'autogenerate' ),
)
);
register_rest_route(
$this->namespace,
'woocommerce/save',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_update_settings' ),
'callback' => array( $this, 'save' ),
)
);
register_rest_route(
$this->namespace,
'woocommerce/disconnect',
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'can_update_settings' ),
'callback' => array( $this, 'disconnect' ),
)
);
register_rest_route(
$this->namespace,
'woocommerce/key',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( $this, 'logged_in_and_can_access_route' ),
'callback' => array( $this, 'get_key' ),
)
);
register_rest_route(
$this->namespace,
'woocommerce/display-rules',
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => '__return_true',
'callback' => array( $this, 'get_display_rules_info' ),
)
);
}
/**
* Handles auto-generating the WooCommerce API key/secret.
*
* Route: POST omapp/v1/woocommerce/autogenerate
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi woocommerce_autogenerate
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function autogenerate( $request ) {
try {
$auto_generated_keys = $this->save->autogenerate();
if ( is_wp_error( $auto_generated_keys ) ) {
$e = new OMAPI_WpErrorException();
throw $e->setWpError( $auto_generated_keys );
}
if ( empty( $auto_generated_keys ) ) {
throw new Exception( esc_html__( 'WooCommerce REST API keys could not be auto-generated on your behalf. Please try again.', 'optin-monster-api' ), 400 );
}
$data = $this->base->get_option();
// Merge data array, with auto-generated keys array.
$data = array_merge( $data, $auto_generated_keys );
$this->save->connect( $data );
if ( ! empty( $this->save->error ) ) {
throw new Exception( $this->save->error, 400 );
}
return $this->get_key( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Handles saving the WooCommerce API key/secret.
*
* Route: POST omapp/v1/woocommerce/save
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi woocommerce_save
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function save( $request ) {
try {
$woo_key = $request->get_param( 'key' );
if ( empty( $woo_key ) ) {
throw new Exception( esc_html__( 'Consumer key missing!', 'optin-monster-api' ), 400 );
}
$woo_secret = $request->get_param( 'secret' );
if ( empty( $woo_secret ) ) {
throw new Exception( esc_html__( 'Consumer secret missing!', 'optin-monster-api' ), 400 );
}
$data = array(
'consumer_key' => $woo_key,
'consumer_secret' => $woo_secret,
);
$this->save->connect( $data );
if ( ! empty( $this->save->error ) ) {
throw new Exception( $this->save->error, 400 );
}
return $this->get_key( $request );
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Handles disconnecting the WooCommerce API key/secret.
*
* Route: POST omapp/v1/woocommerce/disconnect
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi woocommerce_disconnect
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function disconnect( $request ) {
try {
$this->save->disconnect( array() );
if ( ! empty( $this->save->error ) ) {
throw new Exception( $this->save->error, 400 );
}
return new WP_REST_Response(
array( 'message' => esc_html__( 'OK', 'optin-monster-api' ) ),
200
);
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Gets the associated WooCommerce API key data.
*
* Route: GET omapp/v1/woocommerce/key
*
* @since 2.0.0
* @since 2.8.0 Migrated from OMAPI_RestApi woocommerce_get_key
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
* @throws Exception If plugin action fails.
*/
public function get_key( $request ) {
try {
$keys_tab = OMAPI_WooCommerce::version_compare( '3.4.0' ) ? 'advanced' : 'api';
$keys_admin_url = admin_url( "admin.php?page=wc-settings&tab={$keys_tab}§ion=keys" );
if ( ! OMAPI_WooCommerce::is_minimum_version() && OMAPI_WooCommerce::is_connected() ) {
$error = '<p>' . esc_html( sprintf( __( 'OptinMonster requires WooCommerce %s or above.', 'optin-monster-api' ), OMAPI_WooCommerce::MINIMUM_VERSION ) ) . '</p>'
. '<p>' . esc_html_x( 'This site is currently running: ', 'the current version of WooCommerce: "WooCommerce x.y.z"', 'optin-monster-api' )
. '<code>WooCommerce ' . esc_html( OMAPI_WooCommerce::version() ) . '</code>.</p>'
. '<p>' . esc_html__( 'Please upgrade to the latest version of WooCommerce to enjoy deeper integration with OptinMonster.', 'optin-monster-api' ) . '</p>';
throw new Exception( $error, 404 );
}
if ( ! OMAPI_WooCommerce::is_connected() ) {
$error = '<p>' . sprintf( __( 'In order to integrate WooCommerce with the Display Rules in the campaign builder, OptinMonster needs <a href="%s" target="_blank">WooCommerce REST API credentials</a>. OptinMonster only needs Read access permissions to work.', 'optin-monster-api' ), esc_url( $keys_admin_url ) ) . '</p>';
throw new Exception( $error, 404 );
}
// Set some default key details.
$defaults = array(
'key_id' => '',
'description' => esc_html__( 'no description found', 'optin-monster-api' ),
'truncated_key' => esc_html__( 'no truncated key found', 'optin-monster-api' ),
);
// Get the key details.
$key_id = $this->base->get_option( 'woocommerce', 'key_id' );
$details = OMAPI_WooCommerce::get_key_details_by_id( $key_id );
$r = wp_parse_args( array_filter( $details ), $defaults );
return new WP_REST_Response(
array(
'id' => $key_id,
'description' => esc_html( $r['description'] ),
'truncated' => esc_html( $r['truncated_key'] ),
'editUrl' => esc_url_raw( add_query_arg( 'edit-key', $r['key_id'], $keys_admin_url ) ),
),
200
);
} catch ( Exception $e ) {
return $this->exception_to_response( $e );
}
}
/**
* Retrieves the WooCommerce cart data for display rules.
*
* Route: GET omapp/v1/woocommerce/display-rules
*
* @since 2.12.0
*
* @param WP_REST_Request $request The REST Request.
*
* @return WP_REST_Response The API Response
*/
public function get_display_rules_info( $request ) {
return new WP_REST_Response(
$this->base->woocommerce->get_cart(),
200
);
}
}
WooCommerce/Rules.php 0000644 00000007663 15153673737 0010623 0 ustar 00 <?php
/**
* WooCommerce Rules class.
*
* @since 2.8.0
*
* @package OMAPI
* @author Gabriel Oliveira
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WooCommerce_Rules class.
*
* @since 2.8.0
*/
class OMAPI_WooCommerce_Rules extends OMAPI_Rules_Base {
/**
* Holds the meta fields used for checking output statuses.
*
* @since 2.8.0
*
* @var array
*/
protected $fields = array(
'show_on_woocommerce',
'is_wc_shop',
'is_wc_product',
'is_wc_cart',
'is_wc_checkout',
'is_wc_account',
'is_wc_endpoint',
'is_wc_endpoint_order_pay',
'is_wc_endpoint_order_received',
'is_wc_endpoint_view_order',
'is_wc_endpoint_edit_account',
'is_wc_endpoint_edit_address',
'is_wc_endpoint_lost_password',
'is_wc_endpoint_customer_logout',
'is_wc_endpoint_add_payment_method',
'is_wc_product_category',
'is_wc_product_tag',
);
/**
* Check for woocommerce rules.
*
* @since 1.5.0
* @since 2.8.0 Migrated from OMAPI_Rules
*
* @throws OMAPI_Rules_False|OMAPI_Rules_True
* @return void
*/
public function run_checks() {
// If WooCommerce is not connected we can ignore the WooCommerce specific settings.
if ( ! OMAPI_WooCommerce::is_connected() ) {
return;
}
if (
! $this->rules->is_inline_check
// Separate never checks for WooCommerce pages that don't ID match
// No global check on purpose. Global is still true if only this setting is populated.
&& $this->rules->item_in_field( wc_get_page_id( 'shop' ), 'never' )
&& is_shop()
) {
throw new OMAPI_Rules_False( 'never on wc is_shop' );
}
try {
$this->check_fields();
} catch ( OMAPI_Rules_Exception $e ) {
if ( $e instanceof OMAPI_Rules_True ) {
throw new OMAPI_Rules_True( 'include woocommerce', 0, $e );
}
$this->rules->add_reason( $e );
}
}
/**
* Check for woocommerce rule fields.
*
* @since 1.5.0
* @since 2.8.0 Migrated from OMAPI_Rules
*
* @throws OMAPI_Rules_True
* @return void
*/
protected function check_fields() {
$wc_checks = array(
'show_on_woocommerce' => array( 'is_woocommerce' ), // is woocommerce anything
'is_wc_shop' => array( 'is_shop' ),
'is_wc_product' => array( 'is_product' ),
'is_wc_cart' => array( 'is_cart' ),
'is_wc_checkout' => array( 'is_checkout' ),
'is_wc_account' => array( 'is_account_page' ),
'is_wc_endpoint' => array( 'is_wc_endpoint_url' ),
'is_wc_endpoint_order_pay' => array( 'is_wc_endpoint_url', 'order-pay' ),
'is_wc_endpoint_order_received' => array( 'is_wc_endpoint_url', 'order-received' ),
'is_wc_endpoint_view_order' => array( 'is_wc_endpoint_url', 'view-order' ),
'is_wc_endpoint_edit_account' => array( 'is_wc_endpoint_url', 'edit-account' ),
'is_wc_endpoint_edit_address' => array( 'is_wc_endpoint_url', 'edit-address' ),
'is_wc_endpoint_lost_password' => array( 'is_wc_endpoint_url', 'lost-password' ),
'is_wc_endpoint_customer_logout' => array( 'is_wc_endpoint_url', 'customer-logout' ),
'is_wc_endpoint_add_payment_method' => array( 'is_wc_endpoint_url', 'add-payment-method' ),
);
foreach ( $wc_checks as $field => $callback ) {
$this->check_field( $field, $callback );
}
}
/**
* Check for woocommerce rule field.
*
* @since 2.10.0
*
* @param string $field The field to check.
* @param array $callback The callback to check.
*
* @return void
* @throws OMAPI_Rules_True
*/
protected function check_field( $field, $callback ) {
if ( $this->rules->field_empty( $field ) ) {
return;
}
$this->rules
->set_global_override( false )
->set_advanced_settings_field( $field, $this->rules->get_field_value( $field ) );
if ( call_user_func_array( array_shift( $callback ), $callback ) ) {
// If it passes, send it back.
throw new OMAPI_Rules_True( $field );
}
}
}
WooCommerce/Save.php 0000644 00000012200 15153673737 0010406 0 ustar 00 <?php
/**
* WooCommerce Save class.
*
* @since 2.8.0
*
* @package OMAPI
* @author Gabriel Oliveira and Eduardo Nakatsuka
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WooCommerce Save class.
*
* @since 2.8.0
*/
class OMAPI_WooCommerce_Save {
/**
* Holds the class object.
*
* @since 2.8.0
*
* @var OMAPI_WooCommerce_Save
*/
public static $instance;
/**
* Holds save error.
*
* @since 2.8.0
*
* @var mixed
*/
public $error = null;
/**
* Holds the base class object.
*
* @since 2.8.0
*
* @var OMAPI
*/
public $base;
/**
* The OMAPI_WooCommerce instance.
*
* @since 2.13.0
*
* @var OMAPI_WooCommerce
*/
public $woo;
/**
* Primary class constructor.
*
* @since 2.8.0
*
* @param OMAPI_WooCommerce $woo
*/
public function __construct( OMAPI_WooCommerce $woo ) {
$this->woo = $woo;
$this->base = OMAPI::get_instance();
self::$instance = $this;
}
/**
* Handles auto-generating WooCommerce API keys for use with OM.
*
* @since 1.7.0
* @since 2.8.0 Migrated from OMAPI_Save woocommerce_autogenerate
*
* @return array
*/
public function autogenerate() {
$cookies = array();
foreach ( $_COOKIE as $name => $val ) {
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
$cookies[] = "$name=" . rawurlencode( is_array( $val ) ? serialize( $val ) : $val );
}
$cookies = implode( '; ', $cookies );
$request_args = array(
'sslverify' => apply_filters( 'https_local_ssl_verify', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
'body' => array(
'action' => 'woocommerce_update_api_key',
'description' => esc_html__( 'OptinMonster API Read-Access (Auto-Generated)', 'optin-monster-api' ),
'permissions' => 'read',
'user' => get_current_user_id(),
'security' => wp_create_nonce( 'update-api-key' ),
),
'timeout' => 60,
'headers' => array(
'cookie' => $cookies,
),
);
$response = wp_remote_post( admin_url( 'admin-ajax.php' ), $request_args );
if ( is_wp_error( $response ) ) {
return $response;
}
$code = wp_remote_retrieve_response_code( $response );
$body = json_decode( wp_remote_retrieve_body( $response ) );
if (
200 === intval( $code )
&& ! empty( $body->success )
&& ! empty( $body->data->consumer_key )
&& ! empty( $body->data->consumer_secret )
) {
return (array) $body->data;
}
return array();
}
/**
* Handles connecting WooCommerce when the connect button is clicked.
*
* @since 1.7.0
* @since 2.8.0 Migrated from OMAPI_Save woocommerce_connect
*
* @param array $data The data passed in via POST request.
*
* @return void
*/
public function connect( $data ) {
$keys = $this->woo->validate_keys( $data );
if ( isset( $keys['error'] ) ) {
$this->error = $keys['error'];
} else {
// Get the version of the REST API we should use. The
// `v3` route wasn't added until WooCommerce 3.5.0.
$api_version = $this->woo->version_compare( '3.5.0' )
? 'v3'
: 'v2';
// Get current site url.
$url = esc_url_raw( site_url() );
// Make a connection request.
$response = $this->woo->connect(
array(
'consumerKey' => $keys['consumer_key'],
'consumerSecret' => $keys['consumer_secret'],
'apiVersion' => $api_version,
'shop' => $url,
'name' => esc_html( get_bloginfo( 'name' ) ),
)
);
// Output an error or register a successful connection.
if ( is_wp_error( $response ) ) {
$this->error = isset( $response->message )
? $response->message
: esc_html__( 'WooCommerce could not be connected to OptinMonster. The OptinMonster API returned with the following response: ', 'optin-monster-api' ) . $response->get_error_message();
} else {
// Get the shop hostname.
$site = wp_parse_url( $url );
$host = isset( $site['host'] ) ? $site['host'] : '';
// Set up the connected WooCommerce options.
$option = $this->base->get_option();
$option['woocommerce'] = array(
'api_version' => $api_version,
'key_id' => $keys['key_id'],
'shop' => $host,
);
// Save the option.
$this->base->save->update_option( $option, $data );
}
}
}
/**
* Handles disconnecting WooCommerce when the disconnect button is clicked.
*
* @since 1.7.0
* @since 2.8.0 Migrated from OMAPI_Save woocommerce_disconnect
*
* @param array $data The data passed in via POST request.
*
* @return void
*/
public function disconnect( $data ) {
$response = $this->woo->disconnect();
// Output an error or register a successful disconnection.
if ( is_wp_error( $response ) ) {
$this->error = isset( $response->message )
? $response->message
: esc_html__( 'WooCommerce could not be disconnected from OptinMonster. The OptinMonster API returned with the following response: ', 'optin-monster-api' ) . $response->get_error_message();
} else {
$option = $this->base->get_option();
unset( $option['woocommerce'] );
// Save the option.
$this->base->save->update_option( $option, $data );
}
}
}
WooCommerce.php 0000644 00000047116 15153673737 0007526 0 ustar 00 <?php
/**
* WooCommerce class.
*
* @since 1.7.0
*
* @package OMAPI
* @author Brandon Allen
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* The WooCommerce class.
*
* @since 1.7.0
*/
class OMAPI_WooCommerce extends OMAPI_Integrations_Base {
/**
* Path to the file.
*
* @since 1.7.0
*
* @var string
*/
public $file = __FILE__;
/**
* The minimum WooCommerce version required.
*
* @since 1.9.0
*
* @var string
*/
const MINIMUM_VERSION = '3.2.0';
/**
* Holds the cart class object.
*
* @since 2.8.0
*
* @var array
*/
protected $cart;
/**
* OMAPI_WooCommerce_Save object
*
* @since 2.8.0
*
* @var OMAPI_WooCommerce_Save
*/
public $save;
/**
* The OMAPI_EasyDigitalDownloads_RestApi instance.
*
* @since 2.13.0
*
* @var null|OMAPI_EasyDigitalDownloads_RestApi
*/
public $rest = null;
/**
* Primary class constructor.
*
* @since 1.7.0
*/
public function __construct() {
parent::__construct();
// Set our object.
$this->save = new OMAPI_WooCommerce_Save( $this );
add_action( 'optin_monster_api_rest_register_routes', array( $this, 'maybe_init_rest_routes' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'handle_enqueuing_assets' ) );
// Register WooCommerce Education Meta Boxes.
add_action( 'add_meta_boxes', array( $this, 'register_metaboxes' ) );
// Add custom OptinMonster note.
add_action( 'admin_init', array( $this, 'maybe_store_note' ) );
// Revenue attribution support.
add_action( 'woocommerce_thankyou', array( $this, 'maybe_store_revenue_attribution' ) );
add_action( 'woocommerce_order_status_changed', array( $this, 'maybe_store_revenue_attribution_on_order_status_change' ), 10, 3 );
}
/**
* Enqueue Metabox Assets
*
* @since 2.2.0
*
* @return void
*/
public function handle_enqueuing_assets() {
if ( ! function_exists( 'get_current_screen' ) ) {
return;
}
$screen = get_current_screen();
if ( empty( $screen->id ) ) {
return;
}
switch ( $screen->id ) {
case 'shop_coupon':
case 'product':
return $this->enqueue_metabox_assets();
case 'woocommerce_page_wc-admin':
return $this->enqueue_marketing_education_assets();
}
}
/**
* Enqueue Metabox Assets
*
* @since 2.2.0
*
* @return void
*/
public function enqueue_metabox_assets() {
wp_enqueue_style(
$this->base->plugin_slug . '-metabox',
$this->base->url . 'assets/dist/css/metabox.min.css',
array(),
$this->base->asset_version()
);
wp_enqueue_script(
$this->base->plugin_slug . '-metabox-js',
$this->base->url . 'assets/dist/js/metabox.min.js',
array(),
$this->base->asset_version(),
true
);
}
/**
* Enqueue marketing box script.
* Adds an OM product education box on the WooCommerce Marketing page.
*
* @since 2.2.0
*
* @return void
*/
public function enqueue_marketing_education_assets() {
wp_enqueue_script(
$this->base->plugin_slug . '-wc-marketing-box-js',
$this->base->url . 'assets/dist/js/wc-marketing.min.js',
array(),
$this->base->asset_version(),
true
);
add_action( 'admin_footer', array( $this, 'output_marketing_card_template' ) );
}
/**
* Handles outputting the marketing card html to the page.
*
* @since 2.2.0
*
* @return void
*/
public function output_marketing_card_template() {
$this->base->output_view( 'woocommerce-marketing-card.php' );
}
/**
* Connects WooCommerce to OptinMonster.
*
* @param array $data The array of consumer key and consumer secret.
*
* @since 1.7.0
*
* @returns WP_Error|array
*/
public function connect( $data ) {
if ( empty( $data['consumerKey'] ) || empty( $data['consumerSecret'] ) ) {
return new WP_Error(
'omapi-invalid-woocommerce-keys',
esc_html__( 'The consumer key or consumer secret appears to be invalid. Try again.', 'optin-monster-api' )
);
}
$data['woocommerce'] = self::version();
$data = array_merge( $data, OMAPI_Api::get_url_args() );
// Get the OptinMonster API credentials.
$creds = $this->get_request_api_credentials();
// Initialize the API class.
$api = new OMAPI_Api( 'woocommerce/shop', $creds, 'POST', 'v2' );
return $api->request( $data );
}
/**
* Disconnects WooCommerce from OptinMonster.
*
* @since 1.7.0
*/
public function disconnect() {
// Get the OptinMonster API credentials.
$creds = $this->get_request_api_credentials();
// Get the shop.
$shop = esc_attr( $this->base->get_option( 'woocommerce', 'shop' ) );
if ( empty( $shop ) ) {
return true;
}
// Initialize the API class.
$api = new OMAPI_Api( 'woocommerce/shop/' . rawurlencode( $shop ), $creds, 'DELETE', 'v2' );
return $api->request();
}
/**
* Returns the API credentials to be used in an API request.
*
* @since 1.7.0
*
* @return array
*/
public function get_request_api_credentials() {
$creds = $this->base->get_api_credentials();
// If set, return only the API key, not the legacy API credentials.
if ( $creds['apikey'] ) {
$_creds = array(
'apikey' => $creds['apikey'],
);
} else {
$_creds = array(
'user' => $creds['user'],
'key' => $creds['key'],
);
}
return $_creds;
}
/**
* Validates the passed consumer key and consumer secret.
*
* @since 1.7.0
*
* @param array $data The consumer key and consumer secret.
*
* @return array
*/
public function validate_keys( $data ) {
$key = isset( $data['consumer_key'] ) ? $data['consumer_key'] : '';
$secret = isset( $data['consumer_secret'] ) ? $data['consumer_secret'] : '';
if ( ! $key ) {
return array(
'error' => esc_html__( 'Consumer key is missing.', 'optin-monster-api' ),
);
}
if ( ! $secret ) {
return array(
'error' => esc_html__( 'Consumer secret is missing.', 'optin-monster-api' ),
);
}
// Attempt to find the passed consumer key in the database.
$keys = $this->get_keys_by_consumer_key( $data['consumer_key'] );
// If the consumer key is valid, then validate the consumer secret.
if (
empty( $keys['error'] )
&& $this->is_consumer_secret_valid( $keys['consumer_secret'], $secret )
) {
$keys['consumer_key'] = $key;
} else {
$keys['error'] = esc_html__( 'Consumer secret is invalid.', 'optin-monster-api' );
}
return $keys;
}
/**
* Return the keys for the given consumer key.
*
* This is a rough copy of the same method used by WooCommerce.
*
* @since 1.7.0
*
* @param string $consumer_key The consumer key passed by the user.
*
* @return array
*/
private function get_keys_by_consumer_key( $consumer_key ) {
global $wpdb;
$consumer_key = wc_api_hash( sanitize_text_field( $consumer_key ) );
$keys = $wpdb->get_row(
$wpdb->prepare(
"
SELECT key_id, consumer_secret
FROM {$wpdb->prefix}woocommerce_api_keys
WHERE consumer_key = %s
",
$consumer_key
),
ARRAY_A
);
if ( empty( $keys ) ) {
$keys = array(
'error' => esc_html__( 'Consumer key is invalid.', 'optin-monster-api' ),
);
}
return $keys;
}
/**
* Check if the consumer secret provided for the given user is valid
*
* This is a copy of the same method used by WooCommerce.
*
* @since 1.7.0
*
* @param string $keys_consumer_secret The consumer secret from the database.
* @param string $consumer_secret The consumer secret passed by the user.
*
* @return bool
*/
private function is_consumer_secret_valid( $keys_consumer_secret, $consumer_secret ) {
return hash_equals( $keys_consumer_secret, $consumer_secret );
}
/**
* Get WooCommerce API description and truncated key info by the key id.
*
* @since 1.7.0
*
* @param string $key_id The WooCommerce API key id.
*
* @return array
*/
public static function get_key_details_by_id( $key_id ) {
if ( empty( $key_id ) ) {
return array();
}
global $wpdb;
$data = $wpdb->get_row(
$wpdb->prepare(
"
SELECT key_id, description, truncated_key
FROM {$wpdb->prefix}woocommerce_api_keys
WHERE key_id = %d
",
absint( $key_id )
),
ARRAY_A
);
return $data;
}
/**
* Determines if the current site is has WooCommerce connected.
*
* Checks that the site stored in the OptinMonster option matches the
* current `siteurl` WP option, and that the saved key id still exists in
* the WooCommerce key table. If these two things aren't true, then the
* current site is not connected.
*
* @since 1.7.0
*
* @return boolean
*/
public static function is_connected() {
// If not active, then it is not connected as well.
if ( ! self::is_active() ) {
return false;
}
// Get current site details.
$site = wp_parse_url( site_url() );
$host = isset( $site['host'] ) ? $site['host'] : '';
// Get any options we have stored.
$option = OMAPI::get_instance()->get_option( 'woocommerce' );
$shop = isset( $option['shop'] ) ? $option['shop'] : '';
$key_id = isset( $option['key_id'] ) ? $option['key_id'] : '';
$key = $key_id ? self::get_key_details_by_id( $key_id ) : array();
$is_connected = ! empty( $key['key_id'] ) && $host === $shop;
return apply_filters( 'optinmonster_woocommerce_is_connected', $is_connected );
}
/**
* Add the category base to the category REST API response.
*
* @since 1.7.0
*
* @param WP_REST_Response $response The REST API response.
*
* @return WP_REST_Response
*/
public static function add_category_base_to_api_response( $response ) {
return self::add_base_to_api_response( $response, 'category_rewrite_slug' );
}
/**
* Add the tag base to the tag REST API response.
*
* @since 1.7.0
*
* @param WP_REST_Response $response The REST API response.
*
* @return WP_REST_Response
*/
public static function add_tag_base_to_api_response( $response ) {
return self::add_base_to_api_response( $response, 'tag_rewrite_slug' );
}
/**
* Add the category/tag base to the category/tag REST API response.
*
* @since 1.7.0
*
* @param WP_REST_Response $response The REST API response.
* @param string $base The base setting to retrieve.
*
* @return WP_REST_Response
*/
public static function add_base_to_api_response( $response, $base ) {
$permalink_options = wc_get_permalink_structure();
if ( isset( $permalink_options[ $base ] ) ) {
$response->data['base'] = $permalink_options[ $base ];
}
return $response;
}
/**
* Return the WooCommerce versions string.
*
* @since 1.9.0
*
* @return string
*/
public static function version() {
return defined( 'WC_VERSION' ) ? WC_VERSION : '0.0.0';
}
/**
* Add a OM product education metabox on the WooCommerce coupon and product pages.
*
* @since 2.2.0
*
* @return void
*/
public function register_metaboxes() {
add_meta_box(
'woocommerce_promote_coupon_metabox',
__( 'Promote this coupon', 'optin-monster-api' ),
array( $this, 'output_coupon_metabox' ),
'shop_coupon'
);
add_meta_box(
'woocommerce_popup_metabox',
__( 'Product Popups', 'optin-monster-api' ),
array( $this, 'output_product_metabox' ),
'product'
);
}
/**
* Output the markup for the coupon metabox.
*
* @since 2.2.0
*
* @return void
*/
public function output_coupon_metabox() {
$args = $this->metabox_args();
if ( ! $args['has_sites'] ) {
$args['not_connected_message'] = esc_html__( 'Please create a Free Account or Connect an Existing Account to promote coupons.', 'optin-monster-api' );
}
$this->base->output_view( 'coupon-metabox.php', $args );
}
/**
* Output the markup for the product metabox.
*
* @since 2.2.0
*
* @return void
*/
public function output_product_metabox() {
$args = $this->metabox_args();
if ( ! $args['has_sites'] ) {
$args['not_connected_message'] = esc_html__( 'Please create a Free Account or Connect an Existing Account to use Product Popups.', 'optin-monster-api' );
}
$this->base->output_view( 'product-metabox.php', $args );
}
/**
* Get the site-connected args for the metaboxes.
*
* @since 2.3.0
*
* @return array Array of site-connected args.
*/
protected function metabox_args() {
$args = array(
'has_sites' => $this->base->get_site_id(),
);
if ( ! $args['has_sites'] ) {
$args['not_connected_title'] = esc_html__( 'You Have Not Connected with OptinMonster', 'optin-monster-api' );
}
return $args;
}
/**
* Adds a note to the WooCommerce inbox.
*
* @since 2.2.0
*
* @return int
*/
public function maybe_store_note() {
// Check for Admin Note support.
if ( ! class_exists( 'Automattic\WooCommerce\Admin\Notes\Notes', false ) || ! class_exists( 'Automattic\WooCommerce\Admin\Notes\Note', false ) ) {
return;
}
// Make sure the WooCommerce Data Store is available.
if ( ! class_exists( 'WC_Data_Store', false ) ) {
return;
}
$note_name = 'om-wc-grow-revenue';
try {
// Load the Admin Notes from the WooCommerce Data Store.
$data_store = WC_Data_Store::load( 'admin-note' );
$note_ids = $data_store->get_notes_with_name( $note_name );
} catch ( Exception $e ) {
return;
}
// This ensures we don't create a duplicate note.
if ( ! empty( $note_ids ) ) {
return;
}
// If we're here, we can create a new note.
$note = new Automattic\WooCommerce\Admin\Notes\Note();
$note->set_title( __( 'Grow your store revenue with OptinMonster', 'optin-monster-api' ) );
$note->set_content( __( 'Create high-converting OptinMonster campaigns to promote product sales, reduce cart abandonment and incentivize purchases with time-sensitive coupon offers.', 'optin-monster-api' ) );
$note->set_type( Automattic\WooCommerce\Admin\Notes\Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
$note->set_layout( 'plain' );
$note->set_source( 'optinmonster' );
$note->set_name( $note_name );
$note->add_action(
'om-note-primary',
__( 'Create a campaign', 'optin-monster-api' ),
'admin.php?page=optin-monster-templates',
'unactioned',
true
);
$note->add_action(
'om-note-seconday',
__( 'Learn more', 'optin-monster-api' ),
'admin.php?page=optin-monster-about&selectedTab=getting-started',
'unactioned',
false
);
$note->save();
}
/**
* Maybe stores revenue attribution data when a purchase is successful.
*
* @since 2.6.13
*
* @param int $order_id The WooCommerce order ID.
* @param bool $force Flag to force storing the revenue attribution data.
*
* @return void
*/
public function maybe_store_revenue_attribution( $order_id = 0, $force = false ) {
// Grab the order. If we can't, return early.
$order = OMAPI_WooCommerce_Order::get( $order_id );
if ( ! $order->is() ) {
return;
}
// If we have already stored revenue attribution data before, return early.
$stored = $order->get_meta( '_om_revenue_attribution_complete' );
if ( $stored ) {
return;
}
// Grab some necessary data to send.
$data_on_order = $order->get_meta( '_om_revenue_attribution_data', true, 'edit' );
$data = wp_parse_args(
array(
'transaction_id' => absint( $order_id ),
'value' => esc_html( $order->get_total() ),
),
! empty( $data_on_order ) ? $data_on_order : $this->base->revenue->get_revenue_data()
);
// If the order is not complete, return early.
// This will happen for payments where further
// work is required (such as checks, etc.). In those
// instances, we need to store the data to be processed
// at a later time.
if ( ! $order->has_status( 'completed' ) && ! $force ) {
$order->update_meta_data( '_om_revenue_attribution_data', $data );
return;
}
// Attempt to make the revenue attribution request.
// It checks to determine if campaigns are set, etc.
$ret = $this->base->revenue->store( $data );
if ( ! $ret || is_wp_error( $ret ) ) {
return;
}
// Update the payment meta for storing revenue attribution data.
$order->update_meta_data( '_om_revenue_attribution_complete', time() );
}
/**
* Maybe stores revenue attribution data when a purchase is successful.
*
* @since 2.6.13
*
* @param int $order_id The WooCommerce order ID.
* @param string $old_status The old order status.
* @param string $new_status The new order status.
*
* @return void
*
* phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
*/
public function maybe_store_revenue_attribution_on_order_status_change( $order_id, $old_status, $new_status ) {
// phpcs:enable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
// If we don't have the proper new status, return early.
if ( 'completed' !== $new_status ) {
return;
}
// Maybe store the revenue attribution data.
return $this->maybe_store_revenue_attribution( $order_id, true );
}
/**
* Retrieve the cart from Woocommerce
*
* @since 2.8.0 Moved from OMAPI_Output->woocommerce_cart.
*
* @return array An array of WooCommerce cart data.
*/
public function get_cart() {
if ( ! empty( $this->cart ) ) {
return $this->cart;
}
// Bail if WooCommerce isn't currently active.
if ( ! self::is_active() ) {
return array();
}
// Check if WooCommerce is the minimum version.
if ( ! self::is_minimum_version() ) {
return array();
}
// Initialize the cart.
wc_load_cart();
// Bail if we don't have a cart object.
if ( ! isset( WC()->cart ) || '' === WC()->cart ) {
return array();
}
// Calculate the cart totals.
WC()->cart->calculate_totals();
// Get initial cart data.
$cart = WC()->cart->get_totals();
$cart['cart_items'] = WC()->cart->get_cart();
// Set the currency data.
$currencies = get_woocommerce_currencies();
$currency_code = get_woocommerce_currency();
$cart['currency'] = array(
'code' => $currency_code,
'symbol' => get_woocommerce_currency_symbol( $currency_code ),
'name' => isset( $currencies[ $currency_code ] ) ? $currencies[ $currency_code ] : '',
);
// Add in some extra data to the cart item.
foreach ( $cart['cart_items'] as $key => $item ) {
$item_details = array(
'type' => $item['data']->get_type(),
'sku' => $item['data']->get_sku(),
'categories' => $item['data']->get_category_ids(),
'tags' => $item['data']->get_tag_ids(),
'regular_price' => $item['data']->get_regular_price(),
'sale_price' => $item['data']->get_sale_price() ? $item['data']->get_sale_price() : $item['data']->get_regular_price(),
'virtual' => $item['data']->is_virtual(),
'downloadable' => $item['data']->is_downloadable(),
'sold_individually' => $item['data']->is_sold_individually(),
);
unset( $item['data'] );
$cart['cart_items'][ $key ] = array_merge( $item, $item_details );
}
// Save for later use if necessary.
$this->cart = $cart;
// Send back a response.
return $this->cart;
}
/**
* Check if the Woocommerce plugin is active.
*
* @since 2.8.0 Moved from OMAPI class
*
* @return bool
*/
public static function is_active() {
return class_exists( 'WooCommerce', true );
}
/**
* Initiate our REST routes for WooCommerce if WooCommerce active.
*
* @since 2.13.0
*
* @return void
*/
public function maybe_init_rest_routes() {
if ( self::is_active() ) {
$this->rest = new OMAPI_WooCommerce_RestApi( $this->save );
}
}
/**
* Declare compatibility with WooCommerce HPOS
*
* @see https://github.com/woocommerce/woocommerce/wiki/High-Performance-Order-Storage-Upgrade-Recipe-Book#declaring-extension-incompatibility
*
* @since 2.13.8
*
* @return void
*/
public static function declare_hpos_compat() {
if ( class_exists( '\\Automattic\\WooCommerce\\Utilities\\FeaturesUtil' ) ) {
$file = OMAPI::get_instance()->file;
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', $file, true );
}
}
}
WpErrorException.php 0000644 00000001312 15153673737 0010552 0 ustar 00 <?php
/**
* WP_Error Exception class.
*
* @since 2.0.0
*
* @package OMAPI
* @author Justin Sternberg
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* WP_Error Exception class.
*
* @since 2.0.0
*/
class OMAPI_WpErrorException extends Exception {
/**
* The WP_Error object to this exception.
*
* @since 2.0.0
*
* @var null|WP_Error
*/
protected $wp_error = null;
/**
* Sets the WP_Error object to this exception.
*
* @since 2.0.0
*
* @param WP_Error $error The WP_Error object.
*/
public function setWpError( WP_Error $error ) {
$this->wp_error = $error;
return $this;
}
public function getWpError() {
return $this->wp_error;
}
}