diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..8159ae00
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.php-cs-fixer.cache
+/vendor
+composer.phar
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
new file mode 100644
index 00000000..d94711a9
--- /dev/null
+++ b/.php-cs-fixer.dist.php
@@ -0,0 +1,15 @@
+in(__DIR__)
+;
+
+return (new Config())
+ ->setRules([
+ '@Symfony' => true,
+ ])
+ ->setFinder($finder)
+;
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..fdacc352
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,16 @@
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.3.0
+ hooks:
+ - id: check-json
+ - id: end-of-file-fixer
+ - id: pretty-format-json
+ args: ["--autofix"]
+ - id: trailing-whitespace
+- repo: local
+ hooks:
+ - id: php-cs-fixer
+ name: Run php-cs-fixer
+ entry: ./vendor/bin/php-cs-fixer fix .
+ language: system
+ pass_filenames: false
diff --git a/NOTICE b/NOTICE
index e108c9c3..10f1a94e 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,4 +1,4 @@
-Patreon Plugin for Wordpress
+Patreon Plugin for WordPress
Copyright 2015-2017 Patreon, Inc.
This product includes software developed at Patreon, Inc. (https://www.patreon.com/).
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..cceda7a6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+# Patreon WordPress Plugin
+## Development
+Before committing any changes, make sure that pre-commit can run.
+```
+pre-commit install
+```
+
+You'll also need PHP and composer to manage associated packages.
+```
+brew install php@8.3
+wget https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer -O - -q | php --
+```
+
+Finally install composer dependencies:
+```
+php composer.phar install
+```
+
+
+Adding new dev dependencies:
+```
+# Example, how php-cs-fixer was added
+php composer.phar require --dev friendsofphp/php-cs-fixer
+```
+
+## Releases
+Please make sure to follow [Wordpress plugin readme file standard](https://developer.wordpress.org/plugins/wordpress-org/how-your-readme-txt-works/) and update
+the readme.txt when necessary.
diff --git a/classes/patreon_admin_pointers.php b/classes/patreon_admin_pointers.php
index 008ecfba..e925f872 100644
--- a/classes/patreon_admin_pointers.php
+++ b/classes/patreon_admin_pointers.php
@@ -1,163 +1,161 @@
-id;
-
- // Get pointers for this screen
- $pointers = apply_filters( 'patreon-admin-pointers-' . $screen_id, array() );
-
- if ( ! $pointers || ! is_array( $pointers ) ) {
- return;
- }
-
- // Get dismissed pointers
- $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
- $valid_pointers =array();
-
- // Check pointers and remove dismissed ones.
- foreach ( $pointers as $pointer_id => $pointer ) {
-
- // Sanity check
- if ( in_array( $pointer_id, $dismissed ) || empty( $pointer ) || empty( $pointer_id ) || empty( $pointer['target'] ) || empty( $pointer['options'] ) ) {
- continue;
- }
-
- $pointer['pointer_id'] = $pointer_id;
-
- // Add the pointer to $valid_pointers array
- $valid_pointers['pointers'][] = $pointer;
-
- }
-
- // No valid pointers? Stop here.
- if ( empty( $valid_pointers ) ) {
- return;
- }
-
- // Add pointers style to queue.
- wp_enqueue_style( 'wp-pointer' );
-
- // Add pointers script to queue. Add custom script.
- wp_enqueue_script( 'patreon-wordpress-pointer', PATREON_PLUGIN_ASSETS . '/js/pointers.js', array( 'wp-pointer' ), PATREON_WORDPRESS_VERSION, true );
-
- // Add pointer options to script.
- wp_localize_script( 'patreon-wordpress-pointer', 'patreon_wordpress_pointer', $valid_pointers );
-
- }
-
- // Pointers start here
-
- public function cache_option_pointer( $pointers ) {
-
- // We want this pointer to appear only for existing installations at the date of publication of this version (1.3.9). 2 months after release of this version, this pointer can be removed.
-
- $plugin_activated = get_option( 'patreon-plugin-first-activated' );
-
- // If the plugin activation was not before release date of this version, bail out. Time nudged 5 hours ahead to make sure
-
- if ( $plugin_activated > 1573870261 ) {
- return;
- }
-
- $pointers['patreon_test_pointer'] = array(
- 'target' => '#toplevel_page_patreon-plugin',
- 'options' => array(
- 'content' => sprintf( '
%s
%s
',
- 'New Patreon setting',
- 'Your Patreon integration now tries to prevent caching of your gated content. This will help users to access the content they unlocked easily instead of still seeing the locked version that was cached. If you need to turn off this feature you can set "Prevent caching of gated content" option to "No".'
- ),
- 'position' => array( 'edge' => 'top', 'align' => 'middle' )
- )
- );
- if ( $plugin_activated > 1583330333 ) {
- return;
- }
-
- $pointers['pmp_compatibility'] = array(
- 'target' => '#toplevel_page_patreon-plugin',
- 'options' => array(
- 'content' => sprintf( '
%s
%s
',
- 'Patreon WordPress is now compatible with Paid Memberships Pro',
- 'You can now use Patreon WordPress to gate content alongside Paid Memberships Pro. You can gate content with PW, PMP, or both, using monthly subscriptions/pledges. Any qualifying Patreon patron or PMP member can unlock content gated with either plugin.'
- ),
- 'position' => array( 'edge' => 'top', 'align' => 'middle' )
- )
- );
- return $pointers;
- }
-
- public function pmp_compatibility_pointer( $pointers ) {
-
- // We want this pointer to appear only for existing installations
-
- $plugin_activated = get_option( 'patreon-plugin-first-activated' );
-
- // Set to appear to installations completed until a week after date of this commit
- if ( $plugin_activated > 1583884800 ) {
- return;
- }
-
- $pointers['pmp_compatibility'] = array(
- 'target' => '#menu-posts',
- 'options' => array(
- 'content' => sprintf( '
%s
%s
',
- 'Patreon WordPress is now compatible with Paid Memberships Pro',
- 'You can now use Patreon WordPress to gate posts alongside Paid Memberships Pro. You can gate content with PW, PMP, or both. Any qualifying Patreon patron or PMP monthly member can unlock content gated with either plugin. Read details here here.'
- ),
- 'position' => array( 'edge' => 'top', 'align' => 'middle' )
- )
- );
-
- return $pointers;
-
- }
- public function post_sync_pointer( $pointers ) {
-
- // We want this pointer to appear only for existing installations
-
- $plugin_activated = get_option( 'patreon-plugin-first-activated' );
-
- // Set to appear to installations completed until a week after date of this commit
- if ( $plugin_activated > 1590510554 ) {
- return;
- }
-
- $pointers['patreon_post_sync'] = array(
- 'target' => '#toplevel_page_patreon-plugin',
- 'options' => array(
- 'content' => sprintf( '
%s
%s
',
- 'You can now sync your Patreon posts!',
- 'Turn on post sync to have your Patreon posts automatically synced to your WP site! Works totally hands off!
Read details here here.'
- ),
- 'position' => array( 'edge' => 'top', 'align' => 'middle' )
- )
- );
-
- return $pointers;
-
- }
-
-
-}
\ No newline at end of file
+id;
+
+ // Get pointers for this screen
+ $pointers = apply_filters('patreon-admin-pointers-'.$screen_id, []);
+
+ if (!$pointers || !is_array($pointers)) {
+ return;
+ }
+
+ // Get dismissed pointers
+ $dismissed = explode(',', (string) get_user_meta(get_current_user_id(), 'dismissed_wp_pointers', true));
+ $valid_pointers = [];
+
+ // Check pointers and remove dismissed ones.
+ foreach ($pointers as $pointer_id => $pointer) {
+ // Sanity check
+ if (in_array($pointer_id, $dismissed) || empty($pointer) || empty($pointer_id) || empty($pointer['target']) || empty($pointer['options'])) {
+ continue;
+ }
+
+ $pointer['pointer_id'] = $pointer_id;
+
+ // Add the pointer to $valid_pointers array
+ $valid_pointers['pointers'][] = $pointer;
+ }
+
+ // No valid pointers? Stop here.
+ if (empty($valid_pointers)) {
+ return;
+ }
+
+ // Add pointers style to queue.
+ wp_enqueue_style('wp-pointer');
+
+ // Add pointers script to queue. Add custom script.
+ wp_enqueue_script('patreon-wordpress-pointer', PATREON_PLUGIN_ASSETS.'/js/pointers.js', ['wp-pointer'], PATREON_WORDPRESS_VERSION, true);
+
+ // Add pointer options to script.
+ wp_localize_script('patreon-wordpress-pointer', 'patreon_wordpress_pointer', $valid_pointers);
+ }
+
+ // Pointers start here
+
+ public function cache_option_pointer($pointers)
+ {
+ // We want this pointer to appear only for existing installations at the date of publication of this version (1.3.9). 2 months after release of this version, this pointer can be removed.
+
+ $plugin_activated = get_option('patreon-plugin-first-activated');
+
+ // If the plugin activation was not before release date of this version, bail out. Time nudged 5 hours ahead to make sure
+
+ if ($plugin_activated > 1573870261) {
+ return;
+ }
+
+ $pointers['patreon_test_pointer'] = [
+ 'target' => '#toplevel_page_patreon-plugin',
+ 'options' => [
+ 'content' => sprintf(
+ '
%s
%s
',
+ 'New Patreon setting',
+ 'Your Patreon integration now tries to prevent caching of your gated content. This will help users to access the content they unlocked easily instead of still seeing the locked version that was cached. If you need to turn off this feature you can set "Prevent caching of gated content" option to "No".'
+ ),
+ 'position' => ['edge' => 'top', 'align' => 'middle'],
+ ],
+ ];
+ if ($plugin_activated > 1583330333) {
+ return;
+ }
+
+ $pointers['pmp_compatibility'] = [
+ 'target' => '#toplevel_page_patreon-plugin',
+ 'options' => [
+ 'content' => sprintf(
+ '
%s
%s
',
+ 'Patreon WordPress is now compatible with Paid Memberships Pro',
+ 'You can now use Patreon WordPress to gate content alongside Paid Memberships Pro. You can gate content with PW, PMP, or both, using monthly subscriptions/pledges. Any qualifying Patreon patron or PMP member can unlock content gated with either plugin.'
+ ),
+ 'position' => ['edge' => 'top', 'align' => 'middle'],
+ ],
+ ];
+
+ return $pointers;
+ }
+
+ public function pmp_compatibility_pointer($pointers)
+ {
+ // We want this pointer to appear only for existing installations
+
+ $plugin_activated = get_option('patreon-plugin-first-activated');
+
+ // Set to appear to installations completed until a week after date of this commit
+ if ($plugin_activated > 1583884800) {
+ return;
+ }
+
+ $pointers['pmp_compatibility'] = [
+ 'target' => '#menu-posts',
+ 'options' => [
+ 'content' => sprintf(
+ '
%s
%s
',
+ 'Patreon WordPress is now compatible with Paid Memberships Pro',
+ 'You can now use Patreon WordPress to gate posts alongside Paid Memberships Pro. You can gate content with PW, PMP, or both. Any qualifying Patreon patron or PMP monthly member can unlock content gated with either plugin. Read details here here.'
+ ),
+ 'position' => ['edge' => 'top', 'align' => 'middle'],
+ ],
+ ];
+
+ return $pointers;
+ }
+
+ public function post_sync_pointer($pointers)
+ {
+ // We want this pointer to appear only for existing installations
+
+ $plugin_activated = get_option('patreon-plugin-first-activated');
+
+ // Set to appear to installations completed until a week after date of this commit
+ if ($plugin_activated > 1590510554) {
+ return;
+ }
+
+ $pointers['patreon_post_sync'] = [
+ 'target' => '#toplevel_page_patreon-plugin',
+ 'options' => [
+ 'content' => sprintf(
+ '
%s
%s
',
+ 'You can now sync your Patreon posts!',
+ 'Turn on post sync to have your Patreon posts automatically synced to your WP site! Works totally hands off!
Read details here here.'
+ ),
+ 'position' => ['edge' => 'top', 'align' => 'middle'],
+ ],
+ ];
+
+ return $pointers;
+ }
+}
diff --git a/classes/patreon_api.php b/classes/patreon_api.php
index 911db415..8eac0efb 100644
--- a/classes/patreon_api.php
+++ b/classes/patreon_api.php
@@ -1,214 +1,193 @@
Patreon Settings -> (re)Connect');
+
+ class Patreon_API
+ {
+ public $access_token;
+
+ public function __construct($access_token)
+ {
+ $this->access_token = $access_token;
+ }
+
+ public function fetch_user($v2 = false)
+ {
+ // Only uses v2 starting from this version!
+
+ // We construct the old return from the new returns by combining /me and pledge details
+
+ $api_return = $this->__get_json('identity?include=memberships.currently_entitled_tiers&fields[user]=email,first_name,full_name,image_url,last_name,thumb_url,url,vanity,is_email_verified&fields[member]=currently_entitled_amount_cents,lifetime_support_cents,last_charge_status,patron_status,last_charge_date,pledge_relationship_start', true);
+
+ $creator_id = get_option('patreon-creator-id', false);
+
+ if (isset($api_return['included'][0]) and is_array($api_return['included'][0])) {
+ $api_return['included'][0]['relationships']['creator']['data']['id'] = $creator_id;
+ $api_return['included'][0]['type'] = 'pledge';
+ $api_return['included'][0]['attributes']['amount_cents'] = $api_return['included'][0]['attributes']['currently_entitled_amount_cents'];
+ $api_return['included'][0]['attributes']['created_at'] = $api_return['included'][0]['attributes']['pledge_relationship_start'];
+
+ if ('Paid' != $api_return['included'][0]['attributes']['last_charge_status']) {
+ $api_return['included'][0]['attributes']['declined_since'] = $api_return['included'][0]['attributes']['last_charge_date'];
+ }
+ }
+
+ return $api_return;
+ }
+
+ public function fetch_campaign_and_patrons($v2 = false)
+ {
+ // Below conditional and different endpoint can be deprecated to only use v2 api after transition period. Currently we are using v1 for creator/campaign related calls
+
+ if ($v2) {
+ // New call to campaigns doesnt return pledges in v2 api - currently this function is not used anywhere in plugin. If 3rd party devs are using it, it will need to be looked into
+
+ // Requires having gotten permission for pledge scope during auth if used for a normal user instead of the creator
+
+ return $this->__get_json('campaigns');
+ }
+
+ return $this->__get_json('current_user/campaigns?include=rewards,creator,goals,pledges');
+ }
+
+ public function fetch_creator_info($v2 = false)
+ {
+ // Below conditional and different endpoint can be deprecated to only use v2 api after transition period. Currently we are using v1 for creator/campaign related calls
+
+ if ($v2) {
+ // New call to campaigns doesnt return pledges in v2 api - currently this function is not used anywhere in plugin. If 3rd party devs are using it, it will need to be looked into
+
+ $api_return = $this->__get_json('identity');
+ $api_return['included'][0]['id'] = $api_return['data'][0]['id'];
+
+ return $api_return;
+ }
+
+ return $this->__get_json('current_user/campaigns?include=creator');
+ }
+
+ public function fetch_campaign($v2 = false)
+ {
+ // Below conditional and different endpoint can be deprecated to only use v2 api after transition period
+
+ if ($v2) {
+ return $this->__get_json('campaigns?include=tiers,creator,goals', $v2);
+ }
+
+ return $this->__get_json('current_user/campaigns?include=rewards,creator,goals');
+ }
+
+ public function fetch_tiers($v2 = false)
+ {
+ // Below conditional and different endpoint can be deprecated to only use v2 api after transition period
+
+ if ($v2) {
+ return $this->__get_json('campaigns?include=tiers,creator,goals', $v2);
+ }
+
+ return $this->__get_json('current_user/campaigns?include=rewards');
+ }
+
+ public function __get_json($suffix, $v2 = false)
+ {
+ // v1 is deprecated. Return false
+ $result = ['error' => 'Either your site has an old connection version or it lost its connection info. Please reconnect your site at WP admin -> Patreon Settings -> (re)Connect'];
+
+ return $result;
+
+ $api_endpoint = 'https://'.PATREON_HOST.'/api/oauth2/api/'.$suffix;
+
+ if ($v2) {
+ $api_endpoint = 'https://'.PATREON_HOST.'/api/oauth2/v2/'.$suffix;
+ }
+
+ $headers = [
+ 'Authorization' => 'Bearer '.$this->access_token,
+ 'User-Agent' => 'Patreon-Wordpress, version '.PATREON_WORDPRESS_VERSION.PATREON_WORDPRESS_BETA_STRING.', platform '.php_uname('s').'-'.php_uname('r'),
+ ];
+
+ $api_request = [
+ 'headers' => $headers,
+ 'method' => 'GET',
+ ];
+
+ $response = wp_remote_request($api_endpoint, $api_request);
+ $result = $response;
+
+ if (is_wp_error($response)) {
+ $result = ['error' => $response->get_error_message()];
+ $GLOBALS['patreon_notice'] = $response->get_error_message();
+
+ $caller = 'none';
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+ if (isset($backtrace[1]['function'])) {
+ $caller = $backtrace[1]['function'];
+ }
+
+ Patreon_Wordpress::log_connection_error($caller.' - API v1 Class - '.$GLOBALS['patreon_notice']);
+
+ return $result;
+ }
+
+ // Log the connection as having error if the return is not 200
+
+ if (isset($response['response']['code']) and '200' != $response['response']['code'] and '201' != $response['response']['code']) {
+ $caller = 'none';
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+ if (isset($backtrace[1]['function'])) {
+ $caller = $backtrace[1]['function'];
+ }
+
+ $uuid = wp_remote_retrieve_header($response, 'x-patreon-uuid');
+
+ Patreon_Wordpress::log_connection_error($caller.' - API v1 Class - UUID '.$uuid.' - Response code: '.$response['response']['code'].' Response :'.$response['body']);
+ }
+
+ return json_decode($response['body'], true);
+ }
+ }
}
-else {
-
-
-
- Patreon_Wordpress::log_connection_error( 'Either your site has an old connection version or it lost its connection info. Please reconnect your site at WP admin -> Patreon Settings -> (re)Connect' );
-
- class Patreon_API {
-
- public $access_token;
-
- public function __construct( $access_token ) {
- $this->access_token = $access_token;
- }
-
- public function fetch_user( $v2 = false ) {
-
- // Only uses v2 starting from this version!
-
- // We construct the old return from the new returns by combining /me and pledge details
-
- $api_return = $this->__get_json( "identity?include=memberships.currently_entitled_tiers&fields[user]=email,first_name,full_name,image_url,last_name,thumb_url,url,vanity,is_email_verified&fields[member]=currently_entitled_amount_cents,lifetime_support_cents,last_charge_status,patron_status,last_charge_date,pledge_relationship_start", true );
-
- $creator_id = get_option( 'patreon-creator-id', false );
-
- if ( isset( $api_return['included'][0] ) AND is_array( $api_return['included'][0] ) ) {
-
- $api_return['included'][0]["relationships"]["creator"]["data"]["id"] = $creator_id;
- $api_return['included'][0]['type'] = 'pledge';
- $api_return['included'][0]['attributes']['amount_cents'] = $api_return['included'][0]['attributes']['currently_entitled_amount_cents'];
- $api_return['included'][0]['attributes']['created_at'] = $api_return['included'][0]['attributes']['pledge_relationship_start'];
-
- if ( $api_return['included'][0]['attributes']['last_charge_status'] != 'Paid' ) {
- $api_return['included'][0]['attributes']['declined_since'] = $api_return['included'][0]['attributes']['last_charge_date'];
- }
-
- }
-
- return $api_return;
- }
-
- public function fetch_campaign_and_patrons($v2 = false ) {
-
- // Below conditional and different endpoint can be deprecated to only use v2 api after transition period. Currently we are using v1 for creator/campaign related calls
-
- if ( $v2 ) {
- // New call to campaigns doesnt return pledges in v2 api - currently this function is not used anywhere in plugin. If 3rd party devs are using it, it will need to be looked into
-
- // Requires having gotten permission for pledge scope during auth if used for a normal user instead of the creator
-
- return $this->__get_json( "campaigns" );
- }
-
- return $this->__get_json( "current_user/campaigns?include=rewards,creator,goals,pledges" );
-
- }
-
- public function fetch_creator_info( $v2 = false ) {
-
- // Below conditional and different endpoint can be deprecated to only use v2 api after transition period. Currently we are using v1 for creator/campaign related calls
-
- if ( $v2 ) {
-
- // New call to campaigns doesnt return pledges in v2 api - currently this function is not used anywhere in plugin. If 3rd party devs are using it, it will need to be looked into
-
- $api_return = $this->__get_json( "identity" );
- $api_return['included'][0]['id'] = $api_return['data'][0]['id'];
-
- return $api_return;
- }
-
- return $this->__get_json( "current_user/campaigns?include=creator" );
-
- }
-
- public function fetch_campaign( $v2 = false ) {
-
- // Below conditional and different endpoint can be deprecated to only use v2 api after transition period
-
- if ( $v2 ) {
- return $this->__get_json( "campaigns?include=tiers,creator,goals", $v2 );
- }
-
- return $this->__get_json( "current_user/campaigns?include=rewards,creator,goals" );
-
- }
-
- public function fetch_tiers( $v2 = false ) {
-
- // Below conditional and different endpoint can be deprecated to only use v2 api after transition period
-
- if ( $v2 ) {
- return $this->__get_json( "campaigns?include=tiers,creator,goals", $v2 );
- }
-
- return $this->__get_json( "current_user/campaigns?include=rewards" );
-
- }
-
- public function __get_json( $suffix, $v2 = false ) {
-
- // v1 is deprecated. Return false
- $result = array( 'error' => 'Either your site has an old connection version or it lost its connection info. Please reconnect your site at WP admin -> Patreon Settings -> (re)Connect' );
-
- return $result;
-
- $api_endpoint = "https://api.patreon.com/oauth2/api/" . $suffix;
-
- if ( $v2 ) {
- $api_endpoint = "https://www.patreon.com/api/oauth2/v2/" . $suffix;
- }
-
- $headers = array(
- 'Authorization' => 'Bearer ' . $this->access_token,
- 'User-Agent' => 'Patreon-Wordpress, version ' . PATREON_WORDPRESS_VERSION . PATREON_WORDPRESS_BETA_STRING . ', platform ' . php_uname('s') . '-' . php_uname( 'r' ),
- );
-
- $api_request = array(
- 'headers' => $headers,
- 'method' => 'GET',
- );
-
- $response = wp_remote_request( $api_endpoint, $api_request );
- $result = $response;
-
- if ( is_wp_error( $response ) ) {
-
- $result = array( 'error' => $response->get_error_message() );
- $GLOBALS['patreon_notice'] = $response->get_error_message();
-
- $caller = 'none';
- $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
-
- if ( isset( $backtrace[1]['function'] ) ) {
- $caller = $backtrace[1]['function'];
- }
-
- Patreon_Wordpress::log_connection_error( $caller . ' - API v1 Class - ' . $GLOBALS['patreon_notice'] );
-
- return $result;
-
- }
-
- // Log the connection as having error if the return is not 200
-
- if ( isset( $response['response']['code'] ) AND $response['response']['code'] != '200' AND $response['response']['code'] != '201' ) {
-
- $caller = 'none';
- $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
-
- if ( isset( $backtrace[1]['function'] ) ) {
- $caller = $backtrace[1]['function'];
- }
-
- $uuid = wp_remote_retrieve_header( $response, 'x-patreon-uuid' );
-
- Patreon_Wordpress::log_connection_error( $caller . ' - API v1 Class - UUID ' .$uuid . ' - ' . 'Response code: ' . $response['response']['code'] . ' Response :' . $response['body'] );
- }
-
- return json_decode( $response['body'], true );
-
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/classes/patreon_api_v2.php b/classes/patreon_api_v2.php
index 8a203bbc..cab21ff2 100644
--- a/classes/patreon_api_v2.php
+++ b/classes/patreon_api_v2.php
@@ -1,473 +1,452 @@
-access_token = $access_token;
- }
-
- public function fetch_user() {
-
- // We construct the old return from the new returns by combining /me and pledge details
-
- $api_return = $this->__get_json( "identity?include=memberships.currently_entitled_tiers,memberships.campaign&fields[user]=email,first_name,full_name,image_url,last_name,thumb_url,url,vanity,is_email_verified&fields[member]=currently_entitled_amount_cents,lifetime_support_cents,campaign_lifetime_support_cents,last_charge_status,patron_status,last_charge_date,pledge_relationship_start,pledge_cadence" );
-
- $creator_id = get_option( 'patreon-creator-id', false );
- $campaign_id = get_option( 'patreon-campaign-id', false );
-
-
- if ( isset( $api_return['included'][0] ) AND is_array( $api_return['included'][0] ) ) {
-
- // Iterate through included memberships and find the one that matches the campaign.
-
- foreach ($api_return['included'] as $key => $value) {
-
- if ( $api_return['included'][$key]['type'] == 'member' AND ( isset( $api_return['included'][$key]['relationships']['campaign'] ) AND $campaign_id AND $api_return['included'][$key]['relationships']['campaign']['data']['id'] == $campaign_id ) ) {
-
- // The below procedure will take take the matching membership out of the array, put it to the top and reindex numberic keys. This will allow backwards compatibility to be kept
- $membership = $api_return['included'][$key];
- unset( $api_return['included'][$key] );
- array_unshift( $api_return['included'], $membership);
- array_values( $api_return['included']);
-
- $api_return['included'][0]["relationships"]["creator"]["data"]["id"] = $creator_id;
- $api_return['included'][0]['type'] = 'pledge';
- $api_return['included'][0]['attributes']['amount_cents'] = $api_return['included'][0]['attributes']['currently_entitled_amount_cents'];
- $api_return['included'][0]['attributes']['created_at'] = $api_return['included'][0]['attributes']['pledge_relationship_start'];
- $api_return['included'][0]['attributes']['lifetime_support_cents'] = $api_return['included'][0]['attributes']['campaign_lifetime_support_cents'];
-
- if ( $api_return['included'][0]['attributes']['last_charge_status'] != 'Paid' ) {
- $api_return['included'][0]['attributes']['declined_since'] = $api_return['included'][0]['attributes']['last_charge_date'];
- }
- }
- }
- }
-
- return $api_return;
- }
-
- public function fetch_campaign_and_patrons() {
- return $this->__get_json( "campaigns" );
- }
-
- public function fetch_creator_info() {
-
- $api_return = $this->__get_json( "campaigns?include=creator&fields[campaign]=created_at,creation_name,discord_server_id,image_small_url,image_url,is_charged_immediately,is_monthly,is_nsfw,main_video_embed,main_video_url,one_liner,one_liner,patron_count,pay_per_name,pledge_url,published_at,summary,thanks_embed,thanks_msg,thanks_video_url,has_rss,has_sent_rss_notify,rss_feed_title,rss_artwork_url,patron_count,discord_server_id,google_analytics_id&fields[user]=about,created,email,first_name,full_name,image_url,last_name,social_connections,thumb_url,url,vanity,is_email_verified" );
-
- return $api_return;
-
- }
-
- public function fetch_campaign() {
- return $this->__get_json( "campaigns?include=tiers,creator,goals" );
- }
-
- public function fetch_tiers() {
-
- $result = $this->__get_json( "campaigns?include=tiers&fields[tier]=amount_cents,created_at,description,discord_role_ids,edited_at,image_url,patron_count,post_count,published,published_at,remaining,requires_shipping,title,unpublished_at,url,user_limit" );
-
- // v2 doesnt seem to return the default tiers. We have to add them manually:
- if ( isset( $result['included'] ) ) {
-
- array_unshift(
- $result['included'],
- array(
- 'attributes' => array(
- 'amount' => 1,
- 'amount_cents' => 1,
- 'created_at' => '',
- 'description' => 'Patrons Only',
- 'remaining' => 0,
- 'requires_shipping' => null,
- 'url' => '',
- 'user_limit' => null,
- ),
- 'id' => 0,
- 'type' => 'reward',
- )
- );
-
- array_unshift(
- $result['included'],
- array(
- 'attributes' => array(
- 'amount' => 0,
- 'amount_cents' => 0,
- 'created_at' => '',
- 'description' => 'Everyone',
- 'remaining' => 0,
- 'requires_shipping' => null,
- 'url' => '',
- 'user_limit' => null,
- ),
- 'id' => -1,
- 'type' => 'reward',
- )
- );
-
- }
-
- return $result;
- }
-
- public function get_posts( $campaign_id = false, $page_size = 1, $cursor = null ) {
-
- // Gets posts of relevant campaign
-
- if ( !$campaign_id ) {
- $campaign_id = get_option( 'patreon-campaign-id', false );
- }
-
- $request = 'campaigns/'. $campaign_id .'/posts?page%5Bcount%5D=' . $page_size;
-
- if ( isset( $cursor ) ) {
- $cursor = urlencode($cursor);
- $request .= '&page%5Bcursor%5D='. $cursor;
- }
-
- if ( $campaign_id ) {
- return $this->__get_json( $request );
- }
-
- return false;
-
- }
-
- public function get_post( $post_id ) {
- return $this->__get_json( 'posts/' . $post_id . '?fields[post]=title,content,is_paid,is_public,published_at,url,embed_data,embed_url,app_id,app_status' );
- }
-
- public function add_post_webhook( $params = array() ) {
-
- // Contacts api to create or refresh client
- // Only uses v2
-
- if ( !isset( $params['campaign_id'] ) ) {
- $params['campaign_id'] = get_option( 'patreon-campaign-id', false );
- }
-
- // Site url with forced https
-
- $webhook_response_uri = site_url( '', 'https' ) . '/patreon-webhooks/';
-
- // Check if this url is legitimate with https:
-
- $check_url = wp_remote_get( $webhook_response_uri );
-
- if ( is_wp_error( $check_url ) ) {
- return;
- }
-
- $postfields = array(
- 'data' => array (
- 'type' => 'webhook',
- 'attributes' => array (
- 'triggers' => array (
- 'posts:publish',
- 'posts:update',
- 'posts:delete',
- ),
- 'uri' => $webhook_response_uri,
- ),
- 'relationships' => array (
- 'campaign' => array (
- 'data' => array (
- 'type' => 'campaign',
- 'id' => $params['campaign_id'],
- ),
- ),
- ),
- ),
- );
-
- $postfields= json_encode( $postfields );
-
- $args = array(
- 'method' => 'POST',
- 'params' => $postfields,
- );
-
- $response = $this->__get_json( "webhooks", $args );
-
- if ( isset( $response['errors'] ) AND isset( $response['errors'][0] ) AND isset( $response['errors'][0]['status'] ) AND $response['errors'][0]['status'] == '401' ) {
- update_option( 'patreon-creator-access-token-401', true );
- }
-
- return $response;
- }
- public function delete_post_webhook( $webhook_id ) {
-
- // Deletes a webhook
-
- $args = array(
- 'method' => 'DELETE',
- 'return_result_format' => 'full',
- );
-
- return $this->__get_json( "webhooks/" . $webhook_id, $args );
- }
-
- public function create_refresh_client( $params ) {
-
- // Contacts api to create or refresh client
- // Only uses v2
-
- $args = array(
- 'method' => 'POST',
- 'params' => $params,
- );
-
- return $this->__get_json( "clients?include=creator_token", $args );
- }
-
- public function delete_client( $params ) {
-
- // Contacts api to create or refresh client
- // Only uses v2
-
- $client_id = get_option( 'patreon-client-id', false );
-
- $args = array(
- 'method' => 'DELETE',
- 'params' => $params,
- 'return_result_format' => 'full',
- );
-
- return $this->__get_json( "clients/".$client_id, $args );
- }
-
- public function get_call_limits() {
-
- $campaign_id = get_option( 'patreon-campaign-id', '' );
-
- return array(
-
- 'campaigns' => array( 'limit' => 30, 'period' => 5 * 60 ),
- 'campaigns/'. $campaign_id . '/members' => array( 'limit' => 30, 'period' => 1 * 60 ),
- 'campaigns/'. $campaign_id => array( 'limit' => 30, 'period' => 1 * 60 ),
- 'campaigns/'. $campaign_id . '/posts' => array( 'limit' => 30, 'period' => 1 * 60 ),
- 'posts' => array( 'limit' => 30, 'period' => 1 * 60 ),
- 'webhooks' => array( 'limit' => 12, 'period' => 1 * 60 ),
- 'clients' => array( 'limit' => 12, 'period' => 1 * 60 ),
-
- );
- }
-
- public function throttle_call( $call ) {
-
- // Throttles the call
-
- $limits = $this->get_call_limits();
-
- $break_the_call_up = explode( '/', $call );
-
- // If the call is for webhooks/ or clients/, throttle it over the root endpoints:
-
- if ( $break_the_call_up[0] == 'webhooks' ) {
- $call = 'webhooks';
- }
-
- if ( $break_the_call_up[0] == 'clients' ) {
- $call = 'clients';
- }
-
- if ( !array_key_exists( $call, $limits ) ) {
- // Not in the least. Leave the throttling of this call to the api
- return false;
- }
-
- // Get the time of the last matching call
- $last_called = get_option( 'patreon_api_call_count_' . str_replace( '/', '_', $call ), false );
-
- if ( $last_called AND isset($last_called['counter_start']) AND $last_called['counter_start'] >= ( time() - $limits[$call]['period'] )) {
-
- // There is a counter that started in the last 5 minutes.
-
- if ( $last_called['count'] >= $limits[$call]['limit'] ) {
- // Throttle
- return true;
- }
-
- }
-
- // Either there is no counter, or the number of calls are within the limit. Don't throttle.
-
- return false;
-
- }
-
- public function increment_call_count( $call ) {
-
- $break_the_call_up = explode( '/', $call );
-
- // If the call is for webhooks/ or clients/, throttle it over the root endpoints:
-
- if ( $break_the_call_up[0] == 'webhooks' ) {
- $call = 'webhooks';
- }
-
- if ( $break_the_call_up[0] == 'clients' ) {
- $call = 'clients';
- }
-
- // Get the time of the last matching call
- $last_called = get_option( 'patreon_api_call_count_' . str_replace( '/', '_', $call ), false );
-
- $limits = $this->get_call_limits();
-
- if ( !array_key_exists( $call, $limits ) ) {
- // Not in the list. Leave the throttling of this call to the api
- return;
- }
-
- if ( !$last_called OR !isset($last_called['counter_start']) OR $last_called['counter_start'] < ( time() - $limits[$call]['period'] )) {
-
- // A call counter for this call does not exist or expired. Start a counter.
-
- $last_called = array( 'counter_start' => time(), 'count' => 1 );
-
- update_option( 'patreon_api_call_count_' . str_replace( '/', '_', $call ) , $last_called );
-
- return;
- }
-
- // A counter that started in the last 5 minutes exists. Increment the counter.
-
- $last_called['count']++;
-
- update_option( 'patreon_api_call_count_' . str_replace( '/', '_', $call ), $last_called );
-
- }
-
- public function __get_json( $suffix, $args = array() ) {
-
- // Defaults
-
- // Get the call endpoint
-
- $limits = $this->get_call_limits();
-
- $received_call = explode( '?', $suffix );
-
- if ( isset($received_call[0]) ) {
- $call = $received_call[0];
- }
-
- if ( $this->throttle_call( $call ) ) {
- return 'throttled_locally';
- }
-
- $method = 'GET';
- $params = false;
- $api_endpoint = "https://www.patreon.com/api/oauth2/v2/" . $suffix;
- $return_result_format = 'body';
-
- // Overrides
-
- if ( isset( $args['method'] ) AND $args['method'] != '' ) {
- $method = $args['method'];
- }
-
- if ( isset( $args['return_result_format'] ) AND $args['return_result_format'] != '' ) {
- $return_result_format = $args['return_result_format'];
- }
-
- if ( isset( $args['params'] ) ) {
- $params = $args['params'];
- }
-
- $headers = array(
- 'Authorization' => 'Bearer ' . $this->access_token,
- 'User-Agent' => 'Patreon-Wordpress, version ' . PATREON_WORDPRESS_VERSION . PATREON_WORDPRESS_BETA_STRING . ', platform ' . php_uname('s') . '-' . php_uname( 'r' ) . ' PW-Site: ' . get_site_url() . ' PW-Campaign-Id: ' . get_option( 'patreon-campaign-id', '' ) . ' PW-WP-Version: '. get_bloginfo( 'version' ) . ' PW-PHP-Version: '. phpversion(),
- );
-
-
- $api_request = array(
- 'headers' => $headers,
- 'method' => $method,
- );
-
- if ( $params ) {
- $api_request['body'] = $params;
- $api_request['data_format'] = 'body';
- $api_request['headers']['content-type'] = 'application/json';
- }
-
- if ( $method == 'GET' ) {
- $response = wp_remote_request( $api_endpoint, $api_request );
- }
-
- if ( $method == 'POST' ) {
- $response = wp_remote_post( $api_endpoint, $api_request );
- }
-
- if ( $method == 'DELETE' ) {
-
- if ( isset( $args['force_get'] ) AND $args['force_get'] ) {
- $response = wp_remote_request( $api_endpoint, $api_request );
- }
- else {
- $response = wp_remote_post( $api_endpoint, $api_request );
- }
-
- }
-
- $this->increment_call_count( $call );
-
- $result = $response;
-
- if ( is_wp_error( $response ) ) {
-
- $result = array( 'error' => $response->get_error_message() );
- $GLOBALS['patreon_notice'] = $response->get_error_message();
-
- $caller = 'none';
- $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
-
- if ( isset( $backtrace[1]['function'] ) ) {
- $caller = $backtrace[1]['function'];
- }
-
- Patreon_Wordpress::log_connection_error( $caller . ' - API v2 Class - WP error message ' . $GLOBALS['patreon_notice'] );
-
- return $result;
-
- }
-
- // Log the connection as having error if the return is not 200
-
- if ( isset( $response['response']['code'] ) AND $response['response']['code'] != '200' AND $response['response']['code'] != '201' ) {
-
- $caller = 'none';
- $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
-
- if ( isset( $backtrace[1]['function'] ) ) {
- $caller = $backtrace[1]['function'];
- }
-
- $uuid = wp_remote_retrieve_header( $response, 'x-patreon-uuid' );
-
- Patreon_Wordpress::log_connection_error( $caller . ' - API v2 Class - UUID ' .$uuid . ' - ' . 'Response code: ' . $response['response']['code'] . ' Response :' . $response['body'] );
-
- }
-
- // Return full result if full result was requested
- if ( $return_result_format == 'full' ) {
- return $response;
- }
-
- // Return json decoded response body by default
- return json_decode( $response['body'], true );
-
- }
-
-}
\ No newline at end of file
+access_token = $access_token;
+ }
+
+ public function fetch_user()
+ {
+ // We construct the old return from the new returns by combining /me and pledge details
+
+ $api_return = $this->__get_json('identity?include=memberships.currently_entitled_tiers,memberships.campaign&fields[user]=email,first_name,full_name,image_url,last_name,thumb_url,url,vanity,is_email_verified&fields[member]=currently_entitled_amount_cents,lifetime_support_cents,campaign_lifetime_support_cents,last_charge_status,patron_status,last_charge_date,pledge_relationship_start,pledge_cadence');
+
+ $creator_id = get_option('patreon-creator-id', false);
+ $campaign_id = get_option('patreon-campaign-id', false);
+
+ if (isset($api_return['included'][0]) and is_array($api_return['included'][0])) {
+ // Iterate through included memberships and find the one that matches the campaign.
+
+ foreach ($api_return['included'] as $key => $value) {
+ if ('member' == $api_return['included'][$key]['type'] and (isset($api_return['included'][$key]['relationships']['campaign']) and $campaign_id and $api_return['included'][$key]['relationships']['campaign']['data']['id'] == $campaign_id)) {
+ // The below procedure will take take the matching membership out of the array, put it to the top and reindex numberic keys. This will allow backwards compatibility to be kept
+ $membership = $api_return['included'][$key];
+ unset($api_return['included'][$key]);
+ array_unshift($api_return['included'], $membership);
+ array_values($api_return['included']);
+
+ $api_return['included'][0]['relationships']['creator']['data']['id'] = $creator_id;
+ $api_return['included'][0]['type'] = 'pledge';
+ $api_return['included'][0]['attributes']['amount_cents'] = $api_return['included'][0]['attributes']['currently_entitled_amount_cents'];
+ $api_return['included'][0]['attributes']['created_at'] = $api_return['included'][0]['attributes']['pledge_relationship_start'];
+ $api_return['included'][0]['attributes']['lifetime_support_cents'] = $api_return['included'][0]['attributes']['campaign_lifetime_support_cents'];
+
+ if ('Paid' != $api_return['included'][0]['attributes']['last_charge_status']) {
+ $api_return['included'][0]['attributes']['declined_since'] = $api_return['included'][0]['attributes']['last_charge_date'];
+ }
+ }
+ }
+ }
+
+ return $api_return;
+ }
+
+ public function fetch_campaign_and_patrons()
+ {
+ return $this->__get_json('campaigns');
+ }
+
+ public function fetch_creator_info()
+ {
+ $api_return = $this->__get_json('campaigns?include=creator&fields[campaign]=name,created_at,creation_name,discord_server_id,image_small_url,image_url,is_charged_immediately,is_monthly,is_nsfw,main_video_embed,main_video_url,one_liner,one_liner,patron_count,pay_per_name,pledge_url,published_at,summary,thanks_embed,thanks_msg,thanks_video_url,has_rss,has_sent_rss_notify,rss_feed_title,rss_artwork_url,patron_count,discord_server_id,google_analytics_id&fields[user]=about,created,email,first_name,full_name,image_url,last_name,social_connections,thumb_url,url,vanity,is_email_verified');
+
+ return $api_return;
+ }
+
+ public function fetch_campaign()
+ {
+ return $this->__get_json('campaigns?include=tiers,creator,goals');
+ }
+
+ public function fetch_tiers()
+ {
+ $result = $this->__get_json('campaigns?include=tiers&fields[tier]=amount_cents,created_at,description,discord_role_ids,edited_at,image_url,patron_count,post_count,published,published_at,remaining,requires_shipping,title,unpublished_at,url,user_limit');
+
+ // v2 doesnt seem to return the default tiers. We have to add them manually:
+ if (isset($result['included'])) {
+ array_unshift(
+ $result['included'],
+ [
+ 'attributes' => [
+ 'amount' => 1,
+ 'amount_cents' => 1,
+ 'created_at' => '',
+ 'description' => 'Patrons Only',
+ 'remaining' => 0,
+ 'requires_shipping' => null,
+ 'url' => '',
+ 'user_limit' => null,
+ ],
+ 'id' => 0,
+ 'type' => 'reward',
+ ]
+ );
+
+ array_unshift(
+ $result['included'],
+ [
+ 'attributes' => [
+ 'amount' => 0,
+ 'amount_cents' => 0,
+ 'created_at' => '',
+ 'description' => 'Everyone',
+ 'remaining' => 0,
+ 'requires_shipping' => null,
+ 'url' => '',
+ 'user_limit' => null,
+ ],
+ 'id' => -1,
+ 'type' => 'reward',
+ ]
+ );
+ }
+
+ return $result;
+ }
+
+ public function get_posts($campaign_id = false, $page_size = 1, $cursor = null)
+ {
+ // Gets posts of relevant campaign
+
+ if (!$campaign_id) {
+ $campaign_id = get_option('patreon-campaign-id', false);
+ }
+
+ $request = 'campaigns/'.$campaign_id.'/posts?page%5Bcount%5D='.$page_size;
+
+ if (isset($cursor)) {
+ $cursor = urlencode($cursor);
+ $request .= '&page%5Bcursor%5D='.$cursor;
+ }
+
+ if ($campaign_id) {
+ return $this->__get_json($request);
+ }
+
+ return false;
+ }
+
+ public function get_post($post_id)
+ {
+ return $this->__get_json('posts/'.$post_id.'?fields[post]=title,content,is_paid,is_public,published_at,url,embed_data,embed_url,app_id,app_status');
+ }
+
+ public function add_post_webhook($params = [])
+ {
+ // Contacts api to create or refresh client
+ // Only uses v2
+
+ if (!isset($params['campaign_id'])) {
+ $params['campaign_id'] = get_option('patreon-campaign-id', false);
+ }
+
+ // Site url with forced https
+
+ $webhook_response_uri = site_url('', 'https').'/patreon-webhooks/';
+
+ // Check if this url is legitimate with https:
+
+ $check_url = wp_remote_get($webhook_response_uri);
+
+ if (is_wp_error($check_url)) {
+ return;
+ }
+
+ $postfields = [
+ 'data' => [
+ 'type' => 'webhook',
+ 'attributes' => [
+ 'triggers' => [
+ 'posts:publish',
+ 'posts:update',
+ 'posts:delete',
+ ],
+ 'uri' => $webhook_response_uri,
+ ],
+ 'relationships' => [
+ 'campaign' => [
+ 'data' => [
+ 'type' => 'campaign',
+ 'id' => $params['campaign_id'],
+ ],
+ ],
+ ],
+ ],
+ ];
+
+ $postfields = json_encode($postfields);
+
+ $args = [
+ 'method' => 'POST',
+ 'params' => $postfields,
+ ];
+
+ $response = $this->__get_json('webhooks', $args);
+
+ if (isset($response['errors']) and isset($response['errors'][0]) and isset($response['errors'][0]['status']) and '401' == $response['errors'][0]['status']) {
+ update_option('patreon-creator-access-token-401', true);
+ }
+
+ return $response;
+ }
+
+ public function delete_post_webhook($webhook_id)
+ {
+ // Deletes a webhook
+
+ $args = [
+ 'method' => 'DELETE',
+ 'return_result_format' => 'full',
+ ];
+
+ return $this->__get_json('webhooks/'.$webhook_id, $args);
+ }
+
+ public function create_refresh_client($params)
+ {
+ // Contacts api to create or refresh client
+ // Only uses v2
+
+ $args = [
+ 'method' => 'POST',
+ 'params' => $params,
+ ];
+
+ return $this->__get_json('clients?include=creator_token', $args);
+ }
+
+ public function delete_client($params)
+ {
+ // Contacts api to create or refresh client
+ // Only uses v2
+
+ $client_id = get_option('patreon-client-id', false);
+
+ $args = [
+ 'method' => 'DELETE',
+ 'params' => $params,
+ 'return_result_format' => 'full',
+ ];
+
+ return $this->__get_json('clients/'.$client_id, $args);
+ }
+
+ public function get_call_limits()
+ {
+ $campaign_id = get_option('patreon-campaign-id', '');
+
+ return [
+ 'campaigns' => ['limit' => 30, 'period' => 5 * 60],
+ 'campaigns/'.$campaign_id.'/members' => ['limit' => 30, 'period' => 1 * 60],
+ 'campaigns/'.$campaign_id => ['limit' => 30, 'period' => 1 * 60],
+ 'campaigns/'.$campaign_id.'/posts' => ['limit' => 30, 'period' => 1 * 60],
+ 'posts' => ['limit' => 30, 'period' => 1 * 60],
+ 'webhooks' => ['limit' => 12, 'period' => 1 * 60],
+ 'clients' => ['limit' => 12, 'period' => 1 * 60],
+ ];
+ }
+
+ public function throttle_call($call)
+ {
+ // Throttles the call
+
+ $limits = $this->get_call_limits();
+
+ $break_the_call_up = explode('/', $call);
+
+ // If the call is for webhooks/ or clients/, throttle it over the root endpoints:
+
+ if ('webhooks' == $break_the_call_up[0]) {
+ $call = 'webhooks';
+ }
+
+ if ('clients' == $break_the_call_up[0]) {
+ $call = 'clients';
+ }
+
+ if (!array_key_exists($call, $limits)) {
+ // Not in the least. Leave the throttling of this call to the api
+ return false;
+ }
+
+ // Get the time of the last matching call
+ $last_called = get_option('patreon_api_call_count_'.str_replace('/', '_', $call), false);
+
+ if ($last_called and isset($last_called['counter_start']) and $last_called['counter_start'] >= (time() - $limits[$call]['period'])) {
+ // There is a counter that started in the last 5 minutes.
+
+ if ($last_called['count'] >= $limits[$call]['limit']) {
+ // Throttle
+ return true;
+ }
+ }
+
+ // Either there is no counter, or the number of calls are within the limit. Don't throttle.
+
+ return false;
+ }
+
+ public function increment_call_count($call)
+ {
+ $break_the_call_up = explode('/', $call);
+
+ // If the call is for webhooks/ or clients/, throttle it over the root endpoints:
+
+ if ('webhooks' == $break_the_call_up[0]) {
+ $call = 'webhooks';
+ }
+
+ if ('clients' == $break_the_call_up[0]) {
+ $call = 'clients';
+ }
+
+ // Get the time of the last matching call
+ $last_called = get_option('patreon_api_call_count_'.str_replace('/', '_', $call), false);
+
+ $limits = $this->get_call_limits();
+
+ if (!array_key_exists($call, $limits)) {
+ // Not in the list. Leave the throttling of this call to the api
+ return;
+ }
+
+ if (!$last_called or !isset($last_called['counter_start']) or $last_called['counter_start'] < (time() - $limits[$call]['period'])) {
+ // A call counter for this call does not exist or expired. Start a counter.
+
+ $last_called = ['counter_start' => time(), 'count' => 1];
+
+ update_option('patreon_api_call_count_'.str_replace('/', '_', $call), $last_called);
+
+ return;
+ }
+
+ // A counter that started in the last 5 minutes exists. Increment the counter.
+
+ ++$last_called['count'];
+
+ update_option('patreon_api_call_count_'.str_replace('/', '_', $call), $last_called);
+ }
+
+ public function __get_json($suffix, $args = [])
+ {
+ // Defaults
+
+ // Get the call endpoint
+
+ $limits = $this->get_call_limits();
+
+ $received_call = explode('?', $suffix);
+
+ if (isset($received_call[0])) {
+ $call = $received_call[0];
+ }
+
+ if ($this->throttle_call($call)) {
+ return 'throttled_locally';
+ }
+
+ $method = 'GET';
+ $params = false;
+ $api_endpoint = 'https://'.PATREON_HOST.'/api/oauth2/v2/'.$suffix;
+ $return_result_format = 'body';
+
+ // Overrides
+
+ if (isset($args['method']) and '' != $args['method']) {
+ $method = $args['method'];
+ }
+
+ if (isset($args['return_result_format']) and '' != $args['return_result_format']) {
+ $return_result_format = $args['return_result_format'];
+ }
+
+ if (isset($args['params'])) {
+ $params = $args['params'];
+ }
+
+ $headers = [
+ 'Authorization' => 'Bearer '.$this->access_token,
+ 'User-Agent' => 'Patreon-Wordpress, version '.PATREON_WORDPRESS_VERSION.PATREON_WORDPRESS_BETA_STRING.', platform '.php_uname('s').'-'.php_uname('r').' PW-Site: '.get_site_url().' PW-Campaign-Id: '.get_option('patreon-campaign-id', '').' PW-WP-Version: '.get_bloginfo('version').' PW-PHP-Version: '.phpversion(),
+ ];
+
+ $api_request = [
+ 'headers' => $headers,
+ 'method' => $method,
+ ];
+
+ if ($params) {
+ $api_request['body'] = $params;
+ $api_request['data_format'] = 'body';
+ $api_request['headers']['content-type'] = 'application/json';
+ }
+
+ if ('GET' == $method) {
+ $response = wp_remote_request($api_endpoint, $api_request);
+ }
+
+ if ('POST' == $method) {
+ $response = wp_remote_post($api_endpoint, $api_request);
+ }
+
+ if ('DELETE' == $method) {
+ if (isset($args['force_get']) and $args['force_get']) {
+ $response = wp_remote_request($api_endpoint, $api_request);
+ } else {
+ $response = wp_remote_post($api_endpoint, $api_request);
+ }
+ }
+
+ $this->increment_call_count($call);
+
+ $result = $response;
+
+ if (is_wp_error($response)) {
+ $result = ['error' => $response->get_error_message()];
+ $GLOBALS['patreon_notice'] = $response->get_error_message();
+
+ $caller = 'none';
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+ if (isset($backtrace[1]['function'])) {
+ $caller = $backtrace[1]['function'];
+ }
+
+ Patreon_Wordpress::log_connection_error($caller.' - API v2 Class - WP error message '.$GLOBALS['patreon_notice']);
+
+ return $result;
+ }
+
+ // Log the connection as having error if the return is not 200
+
+ if (isset($response['response']['code']) and '200' != $response['response']['code'] and '201' != $response['response']['code']) {
+ $caller = 'none';
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+
+ if (isset($backtrace[1]['function'])) {
+ $caller = $backtrace[1]['function'];
+ }
+
+ $uuid = wp_remote_retrieve_header($response, 'x-patreon-uuid');
+
+ Patreon_Wordpress::log_connection_error($caller.' - API v2 Class - UUID '.$uuid.' - Response code: '.$response['response']['code'].' Response :'.$response['body']);
+ }
+
+ // Return full result if full result was requested
+ if ('full' == $return_result_format) {
+ return $response;
+ }
+
+ // Return json decoded response body by default
+ return json_decode($response['body'], true);
+ }
+}
diff --git a/classes/patreon_compatibility.php b/classes/patreon_compatibility.php
index 09a9bbd3..d902d036 100644
--- a/classes/patreon_compatibility.php
+++ b/classes/patreon_compatibility.php
@@ -1,727 +1,634 @@
-check_if_plugin_active( 'jetpack/jetpack.php' ) ) {
- add_filter( 'jetpack_photon_skip_image', array( $this, 'jetpack_photon_skip_image' ), 99, 3 );
- }
-
- if ( $this->check_if_plugin_active( 'paid-memberships-pro/paid-memberships-pro.php' ) AND !is_admin() ) {
-
- add_filter('pmpro_has_membership_access_filter', array($this, 'override_pmp_gating_with_pw'), 10, 4);
- add_filter('ptrn/lock_or_not', array($this, 'override_pw_gating_with_pmp'), 10, 4);
-
- }
-
- }
-
- public function set_cache_exceptions() {
-
- // Sets exceptions for caching to prevent important pages from being cached
-
- // Check for flow or authorization pages which shouldnt be cached
- if ( strpos( $_SERVER['REQUEST_URI'],'/patreon-flow/' ) !== false
- OR strpos( $_SERVER['REQUEST_URI'], '/patreon-authorization/' ) !== false
- ) {
-
- // We are in either of these pages. Set do not cache page constant
- define( 'DONOTCACHEPAGE', true );
- // This constant is used in many plugins - wp super cache, w3 total cache, woocommerce etc and it should disable caching for this page
-
- }
- }
-
- public static function check_requirements() {
-
- // Checks if requirements for Patreon WordPress are being met
-
- // Check if permalinks are default (none). PW needs pretty permalinks of any sort
-
- $required = array();
-
- if ( get_option('permalink_structure') == '' ) {
-
- // Empty string - pretty permalinks are not enabled. This requirement fails
-
- $required[] = 'pretty_permalinks_are_required';
-
- }
-
- return $required;
-
- }
-
- public function check_permalinks() {
-
- // Checks if pretty permalinks are enabled. PW requires pretty permalinks (any). Default link format wont work.
-
- if ( !get_option( 'permalink_structure' ) ) {
-
- // The link structure is default. This will break flow redirections Queue warning.
-
- self::$toggle_warning = true;
-
- self::$site_health_info['pretty_permalinks_are_off'] = array(
- 'notice' => PATREON_PRETTY_PERMALINKS_ARE_OFF,
- // We can use this for ordering notices on health page
- 'heading' => PATREON_PRETTY_PERMALINKS_ARE_OFF_HEADING,
- 'order' => 1,
- 'level' => 'critical',
- );
-
- }
-
- }
-
- public function check_wp_super_cache_settings() {
-
- // Checks any important settings of WP super cache which may affect Patreon behavior if WP super cache is installed
-
- // Return if its not admin page and no one is going to see the notices
- if ( !is_admin() ) {
- return;
- }
-
- // Bail out if WP super cache is not installed
- if ( !Patreon_Wordpress::check_plugin_exists( 'wp-super-cache' ) ) {
- return;
- }
- // Bail out if WP super cache is not active
- if ( !Patreon_Wordpress::check_plugin_active( 'wp-super-cache/wp-cache.php' ) ) {
- return;
- }
-
- // Wp super cache loads its options into globals
- global $wp_cache_not_logged_in;
- global $wp_cache_make_known_anon;
- global $cache_enabled;
-
- $toggle_warning = false;
-
- if ( !is_plugin_active( 'wp-super-cache/wp-cache.php' ) OR !$cache_enabled ) {
- // WP Super Cache is not on. bail out
- return;
- }
-
- // Check for cache not logged in being not set - if its not set, logged in users are served cached files
-
- if ( !$wp_cache_not_logged_in ) {
-
- self::$toggle_warning = true;
-
- self::$site_health_info['wp_super_cache_caches_pages_for_known_users'] = array(
- 'notice' => PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED,
- 'heading' => PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED_HEADING,
- // We can use this for ordering notices on health page
- 'order' => 2,
- 'level' => 'important',
- );
-
- }
-
- // Check if Make all anon is set - if its set, logged in users are served cached files
-
- if ( $wp_cache_make_known_anon ) {
-
- self::$toggle_warning = true;
-
- self::$site_health_info['wp_super_cache_makes_logged_in_anonymous'] = array(
- 'notice' => PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED,
- 'heading' => PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED_HEADING,
- // We can use this for ordering notices on health page
- 'order' => 3,
- 'level' => 'important',
- );
-
- }
-
- }
- public function set_do_not_cache_flag_for_gated_content() {
-
- // This function checks if a singular content is being displayed and sets the do not cache flag if the content is gated. This is to help prevent caching plugins from caching this content.
-
- // Check if we are to try preventing caching of gated content
-
- if ( get_option( 'patreon-prevent-caching-gated-content', 'yes' ) != 'yes' ) {
- return;
- }
-
- global $post;
-
- // Bail out if no post object present
- if ( !$post ) {
- return;
- }
-
- // Bail out if not a singular page/post
- if ( !is_singular() ) {
- return;
- }
-
- // We are here, it means that this is singular content. Check if it is meant to be gated
-
- $gate_content = false;
-
- $lock_or_not = Patreon_Wordpress::lock_or_not( $post->ID );
-
- if ( isset( $lock_or_not['lock'] ) ) {
- $gate_content = $lock_or_not['lock'];
- }
-
- if ( $gate_content ) {
-
- // General caching plugin define - Prevents caching of non cached pages
- define( 'DONOTCACHEPAGE', true );
-
- // WP Super Cache - disables cache for already cached pages
- define( 'WPSC_SERVE_DISABLED', true );
-
- // W3 Total Cache - do not minify JS in gated page
- define( 'DONOTMINIFY', true );
-
- // W3 Total Caache - Do not serve gated page from CDN
- define( 'DONOTCDN', true );
-
- // W3 Total Cache - Do not use object cache for gated page
- define( 'DONOTCACHCEOBJECT', true );
-
- // Litespeed Cache - Equal to DONOTCACHEPAGE flag
- define('LSCACHE_NO_CACHE', true);
-
- // WP Fastest Cache compatibility - prevents page from being served from cache.
-
- if ( $this->check_if_plugin_active( 'wp-fastest-cache/wpFastestCache.php' ) AND function_exists( 'wpfc_exclude_current_page' ) ) {
- wpfc_exclude_current_page();
- }
-
- }
-
- }
- public function modify_headers() {
-
- // This function checks if a singular content is being displayed and sets cache control headers if the content is gated. This is to help prevent caching of this content.
-
- // Check if we are to try preventing caching of gated content
-
- if ( get_option( 'patreon-prevent-caching-gated-content', 'yes' ) != 'yes' ) {
- return;
- }
-
- global $post;
-
- // Bail out if no post object present
- if ( !$post ) {
- return;
- }
-
- // Bail out if not a singular page/post
- if ( !is_singular() ) {
- return;
- }
-
- // We are here, it means that this is singular content. Check if it is meant to be gated
-
- $gate_content = false;
-
- $lock_or_not = Patreon_Wordpress::lock_or_not( $post->ID );
-
- if ( isset( $lock_or_not['lock'] ) ) {
- $gate_content = $lock_or_not['lock'];
- }
-
- if ( $gate_content ) {
-
- // Set the content to be revalidated if 30 seconds passed since the request and to only be cached by browsers/devices
-
- header( "Cache-control: private, max-age=30, no-cache" );
-
- }
-
- }
- public function check_if_plugin_active( $plugin_slug ) {
-
- // This function is used for checking for plugins without using is_plugin_active() or get_plugins() functions and having to include plugin.php WP include at early hooks.
-
- // Including plugin.php at early hooks may cause issues with other plugins which include that WP file. functions due to the fact that if we include wp-admin/includes/plugin.php at an early hook like wp hook, it may cause issues with other plugins which may be including that file.
-
- // Check if file exists in plugins folder first.
-
- if ( !file_exists( WP_PLUGIN_DIR . '/' . $plugin_slug ) ) {
-
- // Not even installed. Bail out
- return false;
-
- }
-
- // At this point we know plugin is installed. Check if it is active
-
- $active_plugins = get_option( 'active_plugins' );
-
- // Iterate and check for matching slug
-
- foreach ( $active_plugins as $key => $value ) {
- if ( $active_plugins[$key] == $plugin_slug ) {
- // Matches, active. Return true
- return true;
- }
- }
-
- // Here and no matching slug. Plugin is not active.
-
- return false;
-
- }
-
- public function match_pmp_tiers( $patreon_level, $args = array() ) {
-
- // Takes a $ level, matches that to the nearest highest Paid Memberships Pro level and returns the id of that tier
-
- $matching_levels = array();
-
- // Get all membership levels
-
- $pmp_levels = pmpro_getAllLevels( true );
-
- // Iterate membership levels if any exists, and find matching level
-
-
- if ( is_array( $pmp_levels ) AND count( $pmp_levels ) > 0 ) {
-
- foreach( $pmp_levels as $key => $value ) {
-
- $pmp_level = $pmp_levels[$key];
-
- // If its not a reward element, continue, just to make sure
-
- if(
- !isset( $pmp_level->cycle_period )
- OR $pmp_level->cycle_period != 'Month'
- ) {
-
- // Not a monthly cycle. Continue
- continue;
- }
-
- if ( $patreon_level >= $pmp_level->billing_amount ) {
-
- // Matching level found return id of the tier
-
- $matching_levels[] = $pmp_level;
-
- }
-
- }
-
- }
-
- // Here and no result - no tier found.
-
- return $matching_levels;
-
- }
-
- public function pw_pmp_combined_gate( $user, $post_id ) {
-
- // This function checks whether user qualifies for content through PW or PMP
-
- // If the post is not gated by either PW or PMP, skip
-
- /*
-
- if ( !( $lock_or_not['lock'] OR !pmpro_has_membership_access( $post_id ) ) ) {
- return $content;
- }
-
- */
-
- // If user is not logged in, skip
-
- //if ( !is_user_logged_in() ) {
- //return $content;
- //}
-
- ///////////////////////////////////////////////////
- // Set easy to recognize flags for conditions
- // Covers various states of gating and access
- ///////////////////////////////////////////////////
-
- $pmp_gated = false;
-
- // Get pmp levels assigned to post.
- $post_membership_level_ids = $this->get_pmp_post_membership_level_ids( $post_id );
-
- // Set to true if content is gated with PMP
- if ( count( $post_membership_level_ids ) > 0 ) {
- $pmp_gated = true;
- }
-
- $pw_gated = false;
-
- // Set to true if content is gated by PW
- if ( $lock_or_not['lock'] ) {
- $pw_gated = true;
- }
-
- $user_qualifies = false;
-
-
- // Get PMP levels user has
- $user_pmp_levels = pmpro_getMembershipLevelsForUser( $user->ID );
-
- // Get user Patreon pledge if exists
- $user_patreon_level = Patreon_Wordpress::getUserPatronage( $user );
-
- $user_patreon_pledge_matching_pmp_levels = array();
-
- // If user has any Patreon pledge, get matching pmp levels
- if ( $user_patreon_level > 0 ) {
- $user_patreon_pledge_matching_pmp_levels = $this->match_pmp_tiers( $user_patreon_level );
- }
-
- // Get matching PMP levels for this post's Patreon level.
-
- $post_patreon_matching_pmp_levels = $this->match_pmp_tiers( $lock_or_not['patreon_level'] );
-
- // Check if content is gated with PMP and if the user has access:
-
- $user_has_pmp_access = pmpro_has_membership_access( $post_id );
-
- ///////////////////////////////////////////////////
- // EOF - Set easy to recognize flags for conditions
- // Covers various states of gating and access
- ///////////////////////////////////////////////////
-
-
-
- // Cases start here.
-
- // Gated only by Pmp
-
- if ( $pmp_gated AND !$pw_gated ) {
-
- // If user has access via PMP, allow the user
- if ( $user_has_pmp_access ) {
-
- // User qualifies via Pmp - allow user to see content
-
- $lock_or_not['lock'] = false;
- $lock_or_not['patreon_level'] = 0;
-
- return $content;
-
- }
-
- // Process to allow content over Patreon
-
- $user_qualifies_via_patreon = $this->user_qualifies_for_pmp_content_via_patreon( $user, $post_id );
-
- if ( $user_qualifies_via_patreon['user_qualifies'] ) {
-
- // User qualifies for PMP content via Patreon pledge
-
- $lock_or_not['lock'] = false;
- $lock_or_not['patreon_level'] = 0;
-
-
-
- return $content;
-
- }
-
-
- }
-
- // Gated only by PW
-
- if ( !$pmp_gated AND $pw_gated ) {
-
- // Process to allow content over PMP
-
-
-
- }
-
- // Gated by PW and PMP
-
- if ( $pmp_gated AND $pw_gated ) {
-
- // If user has access via PMP, allow the user
- if ( $user_has_pmp_access ) {
- // User qualifies via Pmp
-
- $lock_or_not['lock'] = false;
- $lock_or_not['patreon_level'] = 0;
-
- return $lock_or_not;
-
- }
-
-
-
- // Process to allow content over PMP
-
-
- }
-
-
-
-
- // Case 2 - User has pmp levels. Post doesnt have pmp levels.
-
- if( count( $user_pmp_levels ) > 0 AND count( $post_membership_level_ids ) == 0 ) {
-
- // Check if user's pmp level has any level that matches the levels that match Patreon
-
-
-
- }
-
- // Case 3 - User does not have pmp levels. Post has pmp levels. User has pmp levels matched from Patreon
-
- if( count( $user_patreon_pledge_matching_pmp_levels ) > 0 AND count( $user_pmp_levels ) == 0 AND count( $post_membership_level_ids ) > 0 ) {
-
- // Check if user has a matching pmp level over the Patreon pledge
-
- if( count( $user_patreon_pledge_matching_pmp_levels ) > 0 && count( array_intersect( $user_patreon_pledge_matching_pmp_levels, $post_membership_level_ids ) ) > 0 ) {
-
- $user_qualifies = true;
- $reason = 'user_has_pmp_level_matching_post_level';
-
- }
-
- }
-
-
-
- if ( $user_qualifies ) {
-
- $lock_or_not['lock'] = false;
- $lock_or_not['reason'] = 'user_qualifies_for_pmp_via_patreon';
-
-
- }
-
-
- return $lock_or_not;
- }
-
- public function get_pmp_post_membership_level_ids( $post_id = false ) {
-
- // Gets membership levels assigned to a post or post category
-
- global $wpdb;
-
- // Taken from PMP free version
-
- // No post id. Return false
- if ( !$post_id ) {
- return false;
- }
-
- $post = get_post( $post_id );
-
- if ( !$post OR !is_object( $post ) ) {
- return false;
- }
-
- if(isset($post->post_type) && $post->post_type == "post") {
- $post_categories = wp_get_post_categories($post->ID);
-
- if(!$post_categories) {
- //just check for entries in the memberships_pages table
- $sqlQuery = "SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '" . $post->ID . "'";
- }
- else {
- //are any of the post categories associated with membership levels? also check the memberships_pages table
- $sqlQuery = "(SELECT m.id, m.name FROM $wpdb->pmpro_memberships_categories mc LEFT JOIN $wpdb->pmpro_membership_levels m ON mc.membership_id = m.id WHERE mc.category_id IN(" . implode(",", $post_categories) . ") AND m.id IS NOT NULL) UNION (SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '" . $post->ID . "')";
- }
- }
- else {
- //are any membership levels associated with this page?
- $sqlQuery = "SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '" . $post->ID . "'";
- }
-
-
- $post_membership_levels = $wpdb->get_results($sqlQuery);
-
- $post_membership_level_ids = array();
-
- if ( $post_membership_levels ) {
-
- foreach($post_membership_levels as $level) {
- $post_membership_level_ids[] = $level->id;
- }
-
- }
-
- return $post_membership_level_ids;
-
- }
-
-
- public function override_pmp_gating_with_pw( $hasaccess, $post, $user, $post_membership_levels ) {
-
- // This function overrides PMP gating to allow users with qualifying Patreon pledges to access content
-
- // If the user has access or post is not gated with PMP, just return true
- if ( $hasaccess ) {
- return true;
- }
-
- // At this point, it means the post is gated with PMP and user does not have access.
-
- // Get user's Patreon pledge if s/he has any
-
- $user_patreon_level = Patreon_Wordpress::getUserPatronage( $user );
-
- $user_patreon_pledge_matching_pmp_levels = array();
-
- // If user has any Patreon pledge, get matching pmp levels
- if ( $user_patreon_level > 0 ) {
- $user_patreon_pledge_matching_pmp_levels = $this->match_pmp_tiers( $user_patreon_level / 100 );
- }
-
- // If PMP levels matched over user's Patreon pledge has any intersection with post PMP levels, allow access:
-
- foreach ( $post_membership_levels as $key => $value ) {
-
- foreach ( $user_patreon_pledge_matching_pmp_levels as $key_2 => $value_2 ) {
-
- // If there is any matching level, return true
-
- if ( $post_membership_levels[$key]->id == $user_patreon_pledge_matching_pmp_levels[$key_2]->id ) {
- // Match. Return true.
- return true;
- }
-
- }
- }
-
- // At this point user does not have access over matching PMP levels via Patreon pledge.
-
-
- // Check if post is gated with Patreon and user has access
-
- if ( Patreon_Wordpress::is_content_gated_with_pw( $post->ID ) ) {
-
- // It is. Check if user qualifies for this content for whatsoever reason.
-
- $lock_or_not = Patreon_Wordpress::lock_or_not( $post->id );
-
- if ( !$lock_or_not['lock'] ) {
-
- // Content is locked via Patreon, but is accessible to user. Allow PMP access
- return true;
-
- }
-
- }
-
- // At this point user has no qualifying status. Return false.
-
- return false;
-
- }
-
- public function override_pw_gating_with_pmp( $lock_or_not, $post_id, $declined, $user) {
-
- // This function overrides PW gating to allow users with qualifying PMP tiers to access content
-
- // If the post is not gated for the user, just return
-
- if ( !$lock_or_not['lock'] ) {
- return $lock_or_not;
- }
-
- // At this point, it means the post is gated with PW and user does not have access.
-
- // Check if post is gated with PMP and if user has access.
-
- $hasaccess = false;
-
- // Temporarily remove filter we attached to PMP so it wont cause infinite loop
- remove_filter( 'pmpro_has_membership_access_filter', array($this, 'override_pmp_gating_with_pw'), 10 );
-
- $hasaccess = pmpro_has_membership_access( $post_id, $user->ID );
-
- if ( $hasaccess AND count( $this->get_pmp_post_membership_level_ids( $post_id ) ) > 0 ) {
- // Post is gated with PMP and user has access. Unlock the post.
-
- $lock_or_not['lock'] = false;
- $lock_or_not['reason'] = 'valid_patron';
-
- // Allow content
- return $lock_or_not;
-
- }
-
- // The other option - the content is gated with PW and not PMP. If PMP user has a matching membership $ level that matches PW's tier level, allow access.
-
- // Get the matching PMP levels for the $ value of PW gated post if there is any
-
- $matching_levels = $this->match_pmp_tiers( $lock_or_not['patreon_level'] );
-
- if ( is_array( $matching_levels) AND count( $matching_levels ) > 0 ) {
-
- // User has membership levels which have matching or greater $ value than the $ level of Patreon gating. Allow content.
-
- $lock_or_not['lock'] = false;
- $lock_or_not['reason'] = 'valid_patron';
-
- return $lock_or_not;
-
- }
-
- // Re-add pmp filter:
-
- add_filter( 'pmpro_has_membership_access_filter', array($this, 'override_pmp_gating_with_pw'), 10, 4 );
-
- // Return unmodified result
-
- return $lock_or_not;
-
- }
-
- public function jetpack_photon_skip_image( $val, $src, $tag ) {
-
- // This function skips Jetpack's image functions for an image in case the image is a Patreon gated one
-
- // Skip if no source is given
- if ( !$src OR $src == '') {
- return $val;
- }
-
- $attachment_id = attachment_url_to_postid( $src );
-
- // Get Patreon level if there is:
-
- $patreon_level = get_post_meta( $attachment_id, 'patreon_level', true );
-
- if ( $patreon_level > 0 ) {
- return true;
- }
-
- return $val;
-
- }
-
-
-}
\ No newline at end of file
+check_if_plugin_active('jetpack/jetpack.php')) {
+ add_filter('jetpack_photon_skip_image', [$this, 'jetpack_photon_skip_image'], 99, 3);
+ }
+
+ if ($this->check_if_plugin_active('paid-memberships-pro/paid-memberships-pro.php') and !is_admin()) {
+ add_filter('pmpro_has_membership_access_filter', [$this, 'override_pmp_gating_with_pw'], 10, 4);
+ add_filter('ptrn/lock_or_not', [$this, 'override_pw_gating_with_pmp'], 10, 4);
+ }
+ }
+
+ public function set_cache_exceptions()
+ {
+ // Sets exceptions for caching to prevent important pages from being cached
+
+ // Check for flow or authorization pages which shouldnt be cached
+ if (false !== strpos($_SERVER['REQUEST_URI'], '/patreon-flow/')
+ or false !== strpos($_SERVER['REQUEST_URI'], '/patreon-authorization/')
+ ) {
+ // We are in either of these pages. Set do not cache page constant
+ define('DONOTCACHEPAGE', true);
+ // This constant is used in many plugins - wp super cache, w3 total cache, woocommerce etc and it should disable caching for this page
+ }
+ }
+
+ public static function check_requirements()
+ {
+ // Checks if requirements for Patreon WordPress are being met
+
+ // Check if permalinks are default (none). PW needs pretty permalinks of any sort
+
+ $required = [];
+
+ if ('' == get_option('permalink_structure')) {
+ // Empty string - pretty permalinks are not enabled. This requirement fails
+
+ $required[] = 'pretty_permalinks_are_required';
+ }
+
+ return $required;
+ }
+
+ public function check_permalinks()
+ {
+ // Checks if pretty permalinks are enabled. PW requires pretty permalinks (any). Default link format wont work.
+
+ if (!get_option('permalink_structure')) {
+ // The link structure is default. This will break flow redirections Queue warning.
+
+ self::$toggle_warning = true;
+
+ self::$site_health_info['pretty_permalinks_are_off'] = [
+ 'notice' => PATREON_PRETTY_PERMALINKS_ARE_OFF,
+ // We can use this for ordering notices on health page
+ 'heading' => PATREON_PRETTY_PERMALINKS_ARE_OFF_HEADING,
+ 'order' => 1,
+ 'level' => 'critical',
+ ];
+ }
+ }
+
+ public function check_wp_super_cache_settings()
+ {
+ // Checks any important settings of WP super cache which may affect Patreon behavior if WP super cache is installed
+
+ // Return if its not admin page and no one is going to see the notices
+ if (!is_admin()) {
+ return;
+ }
+
+ // Bail out if WP super cache is not installed
+ if (!Patreon_Wordpress::check_plugin_exists('wp-super-cache')) {
+ return;
+ }
+ // Bail out if WP super cache is not active
+ if (!Patreon_Wordpress::check_plugin_active('wp-super-cache/wp-cache.php')) {
+ return;
+ }
+
+ // Wp super cache loads its options into globals
+ global $wp_cache_not_logged_in;
+ global $wp_cache_make_known_anon;
+ global $cache_enabled;
+
+ $toggle_warning = false;
+
+ if (!is_plugin_active('wp-super-cache/wp-cache.php') or !$cache_enabled) {
+ // WP Super Cache is not on. bail out
+ return;
+ }
+
+ // Check for cache not logged in being not set - if its not set, logged in users are served cached files
+
+ if (!$wp_cache_not_logged_in) {
+ self::$toggle_warning = true;
+
+ self::$site_health_info['wp_super_cache_caches_pages_for_known_users'] = [
+ 'notice' => PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED,
+ 'heading' => PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED_HEADING,
+ // We can use this for ordering notices on health page
+ 'order' => 2,
+ 'level' => 'important',
+ ];
+ }
+
+ // Check if Make all anon is set - if its set, logged in users are served cached files
+
+ if ($wp_cache_make_known_anon) {
+ self::$toggle_warning = true;
+
+ self::$site_health_info['wp_super_cache_makes_logged_in_anonymous'] = [
+ 'notice' => PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED,
+ 'heading' => PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED_HEADING,
+ // We can use this for ordering notices on health page
+ 'order' => 3,
+ 'level' => 'important',
+ ];
+ }
+ }
+
+ public function set_do_not_cache_flag_for_gated_content()
+ {
+ // This function checks if a singular content is being displayed and sets the do not cache flag if the content is gated. This is to help prevent caching plugins from caching this content.
+
+ // Check if we are to try preventing caching of gated content
+
+ if ('yes' != get_option('patreon-prevent-caching-gated-content', 'yes')) {
+ return;
+ }
+
+ global $post;
+
+ // Bail out if no post object present
+ if (!$post) {
+ return;
+ }
+
+ // Bail out if not a singular page/post
+ if (!is_singular()) {
+ return;
+ }
+
+ // We are here, it means that this is singular content. Check if it is meant to be gated
+
+ $gate_content = false;
+
+ $lock_or_not = Patreon_Wordpress::lock_or_not($post->ID);
+
+ if (isset($lock_or_not['lock'])) {
+ $gate_content = $lock_or_not['lock'];
+ }
+
+ if ($gate_content) {
+ // General caching plugin define - Prevents caching of non cached pages
+ define('DONOTCACHEPAGE', true);
+
+ // WP Super Cache - disables cache for already cached pages
+ define('WPSC_SERVE_DISABLED', true);
+
+ // W3 Total Cache - do not minify JS in gated page
+ define('DONOTMINIFY', true);
+
+ // W3 Total Caache - Do not serve gated page from CDN
+ define('DONOTCDN', true);
+
+ // W3 Total Cache - Do not use object cache for gated page
+ define('DONOTCACHCEOBJECT', true);
+
+ // Litespeed Cache - Equal to DONOTCACHEPAGE flag
+ define('LSCACHE_NO_CACHE', true);
+
+ // WP Fastest Cache compatibility - prevents page from being served from cache.
+
+ if ($this->check_if_plugin_active('wp-fastest-cache/wpFastestCache.php') and function_exists('wpfc_exclude_current_page')) {
+ wpfc_exclude_current_page();
+ }
+ }
+ }
+
+ public function modify_headers()
+ {
+ // This function checks if a singular content is being displayed and sets cache control headers if the content is gated. This is to help prevent caching of this content.
+
+ // Check if we are to try preventing caching of gated content
+
+ if ('yes' != get_option('patreon-prevent-caching-gated-content', 'yes')) {
+ return;
+ }
+
+ global $post;
+
+ // Bail out if no post object present
+ if (!$post) {
+ return;
+ }
+
+ // Bail out if not a singular page/post
+ if (!is_singular()) {
+ return;
+ }
+
+ // We are here, it means that this is singular content. Check if it is meant to be gated
+
+ $gate_content = false;
+
+ $lock_or_not = Patreon_Wordpress::lock_or_not($post->ID);
+
+ if (isset($lock_or_not['lock'])) {
+ $gate_content = $lock_or_not['lock'];
+ }
+
+ if ($gate_content) {
+ // Set the content to be revalidated if 30 seconds passed since the request and to only be cached by browsers/devices
+
+ header('Cache-control: private, max-age=30, no-cache');
+ }
+ }
+
+ public function check_if_plugin_active($plugin_slug)
+ {
+ // This function is used for checking for plugins without using is_plugin_active() or get_plugins() functions and having to include plugin.php WP include at early hooks.
+
+ // Including plugin.php at early hooks may cause issues with other plugins which include that WP file. functions due to the fact that if we include wp-admin/includes/plugin.php at an early hook like wp hook, it may cause issues with other plugins which may be including that file.
+
+ // Check if file exists in plugins folder first.
+
+ if (!file_exists(WP_PLUGIN_DIR.'/'.$plugin_slug)) {
+ // Not even installed. Bail out
+ return false;
+ }
+
+ // At this point we know plugin is installed. Check if it is active
+
+ $active_plugins = get_option('active_plugins');
+
+ // Iterate and check for matching slug
+
+ foreach ($active_plugins as $key => $value) {
+ if ($active_plugins[$key] == $plugin_slug) {
+ // Matches, active. Return true
+ return true;
+ }
+ }
+
+ // Here and no matching slug. Plugin is not active.
+
+ return false;
+ }
+
+ public function match_pmp_tiers($patreon_level, $args = [])
+ {
+ // Takes a $ level, matches that to the nearest highest Paid Memberships Pro level and returns the id of that tier
+
+ $matching_levels = [];
+
+ // Get all membership levels
+
+ $pmp_levels = pmpro_getAllLevels(true);
+
+ // Iterate membership levels if any exists, and find matching level
+
+ if (is_array($pmp_levels) and count($pmp_levels) > 0) {
+ foreach ($pmp_levels as $key => $value) {
+ $pmp_level = $pmp_levels[$key];
+
+ // If its not a reward element, continue, just to make sure
+
+ if (
+ !isset($pmp_level->cycle_period)
+ or 'Month' != $pmp_level->cycle_period
+ ) {
+ // Not a monthly cycle. Continue
+ continue;
+ }
+
+ if ($patreon_level >= $pmp_level->billing_amount) {
+ // Matching level found return id of the tier
+
+ $matching_levels[] = $pmp_level;
+ }
+ }
+ }
+
+ // Here and no result - no tier found.
+
+ return $matching_levels;
+ }
+
+ public function pw_pmp_combined_gate($user, $post_id)
+ {
+ // This function checks whether user qualifies for content through PW or PMP
+
+ // If the post is not gated by either PW or PMP, skip
+
+ /*
+
+ if ( !( $lock_or_not['lock'] OR !pmpro_has_membership_access( $post_id ) ) ) {
+ return $content;
+ }
+
+ */
+
+ // If user is not logged in, skip
+
+ // if ( !is_user_logged_in() ) {
+ // return $content;
+ // }
+
+ // /////////////////////////////////////////////////
+ // Set easy to recognize flags for conditions
+ // Covers various states of gating and access
+ // /////////////////////////////////////////////////
+
+ $pmp_gated = false;
+
+ // Get pmp levels assigned to post.
+ $post_membership_level_ids = $this->get_pmp_post_membership_level_ids($post_id);
+
+ // Set to true if content is gated with PMP
+ if (count($post_membership_level_ids) > 0) {
+ $pmp_gated = true;
+ }
+
+ $pw_gated = false;
+
+ // Set to true if content is gated by PW
+ if ($lock_or_not['lock']) {
+ $pw_gated = true;
+ }
+
+ $user_qualifies = false;
+
+ // Get PMP levels user has
+ $user_pmp_levels = pmpro_getMembershipLevelsForUser($user->ID);
+
+ // Get user Patreon pledge if exists
+ $user_patreon_level = Patreon_Wordpress::getUserPatronage($user);
+
+ $user_patreon_pledge_matching_pmp_levels = [];
+
+ // If user has any Patreon pledge, get matching pmp levels
+ if ($user_patreon_level > 0) {
+ $user_patreon_pledge_matching_pmp_levels = $this->match_pmp_tiers($user_patreon_level);
+ }
+
+ // Get matching PMP levels for this post's Patreon level.
+
+ $post_patreon_matching_pmp_levels = $this->match_pmp_tiers($lock_or_not['patreon_level']);
+
+ // Check if content is gated with PMP and if the user has access:
+
+ $user_has_pmp_access = pmpro_has_membership_access($post_id);
+
+ // /////////////////////////////////////////////////
+ // EOF - Set easy to recognize flags for conditions
+ // Covers various states of gating and access
+ // /////////////////////////////////////////////////
+
+ // Cases start here.
+
+ // Gated only by Pmp
+
+ if ($pmp_gated and !$pw_gated) {
+ // If user has access via PMP, allow the user
+ if ($user_has_pmp_access) {
+ // User qualifies via Pmp - allow user to see content
+
+ $lock_or_not['lock'] = false;
+ $lock_or_not['patreon_level'] = 0;
+
+ return $content;
+ }
+
+ // Process to allow content over Patreon
+
+ $user_qualifies_via_patreon = $this->user_qualifies_for_pmp_content_via_patreon($user, $post_id);
+
+ if ($user_qualifies_via_patreon['user_qualifies']) {
+ // User qualifies for PMP content via Patreon pledge
+
+ $lock_or_not['lock'] = false;
+ $lock_or_not['patreon_level'] = 0;
+
+ return $content;
+ }
+ }
+
+ // Gated only by PW
+
+ if (!$pmp_gated and $pw_gated) {
+ // Process to allow content over PMP
+ }
+
+ // Gated by PW and PMP
+
+ if ($pmp_gated and $pw_gated) {
+ // If user has access via PMP, allow the user
+ if ($user_has_pmp_access) {
+ // User qualifies via Pmp
+
+ $lock_or_not['lock'] = false;
+ $lock_or_not['patreon_level'] = 0;
+
+ return $lock_or_not;
+ }
+
+ // Process to allow content over PMP
+ }
+
+ // Case 2 - User has pmp levels. Post doesnt have pmp levels.
+
+ if (count($user_pmp_levels) > 0 and 0 == count($post_membership_level_ids)) {
+ // Check if user's pmp level has any level that matches the levels that match Patreon
+ }
+
+ // Case 3 - User does not have pmp levels. Post has pmp levels. User has pmp levels matched from Patreon
+
+ if (count($user_patreon_pledge_matching_pmp_levels) > 0 and 0 == count($user_pmp_levels) and count($post_membership_level_ids) > 0) {
+ // Check if user has a matching pmp level over the Patreon pledge
+
+ if (count($user_patreon_pledge_matching_pmp_levels) > 0 && count(array_intersect($user_patreon_pledge_matching_pmp_levels, $post_membership_level_ids)) > 0) {
+ $user_qualifies = true;
+ $reason = 'user_has_pmp_level_matching_post_level';
+ }
+ }
+
+ if ($user_qualifies) {
+ $lock_or_not['lock'] = false;
+ $lock_or_not['reason'] = 'user_qualifies_for_pmp_via_patreon';
+ }
+
+ return $lock_or_not;
+ }
+
+ public function get_pmp_post_membership_level_ids($post_id = false)
+ {
+ // Gets membership levels assigned to a post or post category
+
+ global $wpdb;
+
+ // Taken from PMP free version
+
+ // No post id. Return false
+ if (!$post_id) {
+ return false;
+ }
+
+ $post = get_post($post_id);
+
+ if (!$post or !is_object($post)) {
+ return false;
+ }
+
+ if (isset($post->post_type) && 'post' == $post->post_type) {
+ $post_categories = wp_get_post_categories($post->ID);
+
+ if (!$post_categories) {
+ // just check for entries in the memberships_pages table
+ $sqlQuery = "SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '".$post->ID."'";
+ } else {
+ // are any of the post categories associated with membership levels? also check the memberships_pages table
+ $sqlQuery = "(SELECT m.id, m.name FROM $wpdb->pmpro_memberships_categories mc LEFT JOIN $wpdb->pmpro_membership_levels m ON mc.membership_id = m.id WHERE mc.category_id IN(".implode(',', $post_categories).") AND m.id IS NOT NULL) UNION (SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '".$post->ID."')";
+ }
+ } else {
+ // are any membership levels associated with this page?
+ $sqlQuery = "SELECT m.id, m.name FROM $wpdb->pmpro_memberships_pages mp LEFT JOIN $wpdb->pmpro_membership_levels m ON mp.membership_id = m.id WHERE mp.page_id = '".$post->ID."'";
+ }
+
+ $post_membership_levels = $wpdb->get_results($sqlQuery);
+
+ $post_membership_level_ids = [];
+
+ if ($post_membership_levels) {
+ foreach ($post_membership_levels as $level) {
+ $post_membership_level_ids[] = $level->id;
+ }
+ }
+
+ return $post_membership_level_ids;
+ }
+
+ public function override_pmp_gating_with_pw($hasaccess, $post, $user, $post_membership_levels)
+ {
+ // This function overrides PMP gating to allow users with qualifying Patreon pledges to access content
+
+ // If the user has access or post is not gated with PMP, just return true
+ if ($hasaccess) {
+ return true;
+ }
+
+ // At this point, it means the post is gated with PMP and user does not have access.
+
+ // Get user's Patreon pledge if s/he has any
+
+ $user_patreon_level = Patreon_Wordpress::getUserPatronage($user);
+
+ $user_patreon_pledge_matching_pmp_levels = [];
+
+ // If user has any Patreon pledge, get matching pmp levels
+ if ($user_patreon_level > 0) {
+ $user_patreon_pledge_matching_pmp_levels = $this->match_pmp_tiers($user_patreon_level / 100);
+ }
+
+ // If PMP levels matched over user's Patreon pledge has any intersection with post PMP levels, allow access:
+
+ foreach ($post_membership_levels as $key => $value) {
+ foreach ($user_patreon_pledge_matching_pmp_levels as $key_2 => $value_2) {
+ // If there is any matching level, return true
+
+ if ($post_membership_levels[$key]->id == $user_patreon_pledge_matching_pmp_levels[$key_2]->id) {
+ // Match. Return true.
+ return true;
+ }
+ }
+ }
+
+ // At this point user does not have access over matching PMP levels via Patreon pledge.
+
+ // Check if post is gated with Patreon and user has access
+
+ if (Patreon_Wordpress::is_content_gated_with_pw($post->ID)) {
+ // It is. Check if user qualifies for this content for whatsoever reason.
+
+ $lock_or_not = Patreon_Wordpress::lock_or_not($post->id);
+
+ if (!$lock_or_not['lock']) {
+ // Content is locked via Patreon, but is accessible to user. Allow PMP access
+ return true;
+ }
+ }
+
+ // At this point user has no qualifying status. Return false.
+
+ return false;
+ }
+
+ public function override_pw_gating_with_pmp($lock_or_not, $post_id, $declined, $user)
+ {
+ // This function overrides PW gating to allow users with qualifying PMP tiers to access content
+
+ // If the post is not gated for the user, just return
+
+ if (!$lock_or_not['lock']) {
+ return $lock_or_not;
+ }
+
+ // At this point, it means the post is gated with PW and user does not have access.
+
+ // Check if post is gated with PMP and if user has access.
+
+ $hasaccess = false;
+
+ // Temporarily remove filter we attached to PMP so it wont cause infinite loop
+ remove_filter('pmpro_has_membership_access_filter', [$this, 'override_pmp_gating_with_pw'], 10);
+
+ $hasaccess = pmpro_has_membership_access($post_id, $user->ID);
+
+ if ($hasaccess and count($this->get_pmp_post_membership_level_ids($post_id)) > 0) {
+ // Post is gated with PMP and user has access. Unlock the post.
+
+ $lock_or_not['lock'] = false;
+ $lock_or_not['reason'] = 'valid_patron';
+
+ // Allow content
+ return $lock_or_not;
+ }
+
+ // The other option - the content is gated with PW and not PMP. If PMP user has a matching membership $ level that matches PW's tier level, allow access.
+
+ // Get the matching PMP levels for the $ value of PW gated post if there is any
+
+ $matching_levels = $this->match_pmp_tiers($lock_or_not['patreon_level']);
+
+ if (is_array($matching_levels) and count($matching_levels) > 0) {
+ // User has membership levels which have matching or greater $ value than the $ level of Patreon gating. Allow content.
+
+ $lock_or_not['lock'] = false;
+ $lock_or_not['reason'] = 'valid_patron';
+
+ return $lock_or_not;
+ }
+
+ // Re-add pmp filter:
+
+ add_filter('pmpro_has_membership_access_filter', [$this, 'override_pmp_gating_with_pw'], 10, 4);
+
+ // Return unmodified result
+
+ return $lock_or_not;
+ }
+
+ public function jetpack_photon_skip_image($val, $src, $tag)
+ {
+ // This function skips Jetpack's image functions for an image in case the image is a Patreon gated one
+
+ // Skip if no source is given
+ if (!$src or '' == $src) {
+ return $val;
+ }
+
+ $attachment_id = attachment_url_to_postid($src);
+
+ // Get Patreon level if there is:
+
+ $patreon_level = get_post_meta($attachment_id, 'patreon_level', true);
+
+ if ($patreon_level > 0) {
+ return true;
+ }
+
+ return $val;
+ }
+}
diff --git a/classes/patreon_content_sync.php b/classes/patreon_content_sync.php
index 5b086b16..1d86cdb8 100644
--- a/classes/patreon_content_sync.php
+++ b/classes/patreon_content_sync.php
@@ -1,700 +1,607 @@
- time() AND !isset( $args['manual_import'] ) ) {
- return 'manual_import_exists_within_last_60_seconds';
- }
-
- // Set a flag to use in checking whether any post import happened at all.
- $at_least_one_post_imported = false;
-
- // Set the flag for detecting end of post import
- $end_of_post_import = false;
-
- // Check if an import is going on
-
- $post_import_in_progress = get_option( 'patreon-post-import-in-progress', false );
-
- if ( !$post_import_in_progress ) {
- // No ongoing import. Return
- return 'no_ongoing_post_import';
- }
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
- $client_id = get_option( 'patreon-client-id', false );
-
- if ( $creator_access_token AND $client_id ) {
-
- // Create new api object
-
- $api_client = new Patreon_API( $creator_access_token );
-
- // Get if there is a saved cursor
-
- $cursor = get_option( 'patreon-post-import-next-cursor', null );
-
- $posts = $api_client->get_posts( false, 5, $cursor );
-
-
- if ( isset( $posts['errors'][0]['code'] ) AND $posts['errors'][0]['code'] == 3 AND $posts['errors'][0]['source']['parameter'] == 'page[cursor]' ) {
- // Cursor expired. Delete the cursor for next run and return
- delete_option( 'patreon-post-import-next-cursor' );
- return 'expired_or_lost_cursor_deleted';
- }
-
- if ( !isset( $posts['data'] ) ) {
- // Couldnt get posts. Bail out
- return 'couldnt_get_posts';
- }
-
- if ( isset( $posts['meta']['pagination']['cursors']['next'] ) ) {
- update_option( 'patreon-post-import-next-cursor', $posts['meta']['pagination']['cursors']['next'] );
- }
-
- // If we have a saved cursor, the return is legitimate, but there is no more cursor then we are at the last page - iteration over. Mark it:
-
- if ( isset( $cursor ) AND !isset( $posts['meta']['pagination']['cursors']['next'] ) ) {
-
- delete_option( 'patreon-post-import-in-progress' );
- delete_option( 'patreon-post-import-next-cursor' );
- $end_of_post_import = true;
- }
-
- foreach ( $posts['data'] as $key => $value ) {
-
- $patreon_post = $api_client->get_post( $posts['data'][$key]['id'] );
-
- if ( !isset( $patreon_post['data']['id'] ) OR $patreon_post['data']['id'] == '' ) {
- // Couldn't get this post. Skip
- continue;
- }
-
- $this->add_update_patreon_post( $patreon_post );
- $at_least_one_post_imported = true;
- }
-
- }
-
- if ( $at_least_one_post_imported OR $end_of_post_import) {
-
- // Post import ended
- if ( $end_of_post_import ) {
- return 'post_import_ended';
- }
-
- // Imported at least one post
- return 'imported_posts';
- }
-
- return 'did_not_import_any_post';
-
- }
-
- public function add_update_patreon_post( $patreon_post ) {
-
- // Check if a matching WP post exists
-
- $matching_post_id = false;
-
- global $wpdb;
-
- $matching_posts = $wpdb->get_results( "SELECT post_id, meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = 'patreon-post-id' AND meta_value = '" . $patreon_post['data']['id'] . "' ", ARRAY_A );
-
- if ( count( $matching_posts ) > 0 ) {
-
- // Matching post found - just get the first one
- $matching_post_id = $matching_posts[0]['post_id'];
-
- }
-
- // If no matching posts were found from query, try to find from title
-
- if ( count( $matching_posts ) == 0 ) {
-
- // no matching posts. Try checking from the title.
- $matching_post = get_page_by_title( $patreon_post['data']['attributes']['title'], OBJECT, 'post' );
-
- if ( isset( $matching_post ) ) {
-
- // A post matching from title was found.
- $matching_post_id = $matching_post->ID;
-
- }
-
- }
-
- if ( !$matching_post_id ) {
- $result = $this->add_new_patreon_post( $patreon_post );
- }
- else {
-
- // Update if existing posts are set to be updated
- if ( get_option( 'patreon-update-posts', 'no' ) == 'yes' ) {
- $result = $this->update_patreon_post( $matching_post_id, $patreon_post );
- }
- else {
- // Updating posts disabled. Just return
- return false;
- }
-
- }
-
- if ( is_wp_error( $result ) OR $result == 0 ) {
- // Flopped - return false
- return false;
- }
-
- // Success
- return true;
-
- }
-
- public function add_new_patreon_post( $patreon_post ) {
-
- $post_type = get_option( 'patreon-sync-post-type', 'post' );
- $post_category = get_option( 'patreon-sync-post-category', 'category' );
- $post_term_id = get_option( 'patreon-sync-post-term', '1' );
- $post_author = get_option( 'patreon-post-author-for-synced-posts', 1 );
- $patron_only_post = false;
-
- if ( $patreon_post['data']['attributes']['is_paid'] ) {
- $patron_only_post = true;
- }
- else {
-
- // Not a pay per post - check tier level or patron only status
- // For now do this in else, when api returns tiers replace with proper logic
-
- if ( $patreon_post['data']['attributes']['is_public'] ) {
- $patron_only_post = false;
- }
- else {
- $patron_only_post = true;
- }
-
- }
-
- $post_date = date( 'Y-m-d H:i:s', time() );
-
- if ( get_option( 'patreon-override-synced-post-publish-date', 'no' ) == 'yes' ) {
-
- $utc_timezone = new DateTimeZone( "UTC" );
- $datetime = new DateTime( $patreon_post['data']['attributes']['published_at'], $utc_timezone );
- $post_date = $datetime->format( 'Y-m-d H:i:s' );
-
- }
-
- $post_status = 'publish';
-
- // Decide post status - publish or pending
-
- if ( $patron_only_post AND get_option( 'patreon-auto-publish-patron-only-posts', 'yes' ) == 'no' ) {
- $post_status = 'pending';
- }
-
- if ( !$patron_only_post AND get_option( 'patreon-auto-publish-public-posts', 'yes' ) == 'no' ) {
- $post_status = 'pending';
- }
-
- $post = array();
- $post['post_title'] = $patreon_post['data']['attributes']['title'];
- $post['post_content'] = $patreon_post['data']['attributes']['content'];
- $post['post_status'] = $post_status;
- $post['post_author'] = $post_author;
- $post['post_type'] = $post_type;
- $post['post_date'] = $post_date;
-
- // Parse and handle the images inside the post:
-
- global $Patreon_Wordpress;
- global $wpdb;
-
- $images = $Patreon_Wordpress->get_images_info_from_content( $post['post_content'] );
-
- if ( $images ) {
-
- $image_replacement = $this->check_replace_patreon_images_with_local_images( $post['post_content'], $images );
-
- $images = $image_replacement['images'];
- $post['post_content'] = $image_replacement['post_content'];
-
- }
-
- if ( isset( $patreon_post['data']['attributes']['embed_data']['url'] ) ) {
-
- // Process embeds:
-
- $post['post_content'] = $this->process_post_embeds( $post['post_content'], $patreon_post );
-
- // Temporarily remove wp post filters to allow iframes to be accepted into post
-
- remove_filter('content_save_pre', 'wp_filter_post_kses');
- remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
-
- }
-
- $inserted_post_id = wp_insert_post( $post );
-
- if ( isset( $patreon_post['data']['attributes']['embed_data']['url'] ) ) {
-
- // Re add post filters
-
- add_filter('content_save_pre', 'wp_filter_post_kses');
- add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
-
- }
-
- if ( is_wp_error( $inserted_post_id ) ) {
- // Handle error_get_last
- return;
- }
-
- // Now get the attachment ids we added to the post from earlier filenames
-
- if ( $images ) {
-
- foreach ( $images as $key => $value ) {
-
- $inserted_attachment = $wpdb->get_results( "SELECT ID FROM " . $wpdb->posts . " WHERE post_name = '" . $images[$key]['name'] ."'" );
-
- $inserted_attachment_id = $inserted_attachment[0]->ID;
-
- // Set Patreon post as parent of this attachment
-
- wp_update_post( array(
- 'ID' => $inserted_attachment_id,
- 'post_parent' => $inserted_post_id
- )
- );
-
- // Set image to patron only since its in a patron only post - for those who use image locking featured
-
- // If post is not public - set the contained images to be patron only
-
- if ( $patreon_post['data']['attributes']['is_paid'] ) {
- // Pay per post set to patron only
- // Add only if not exists
- add_post_meta( $inserted_attachment_id, 'patreon-level', 1, true );
- }
- else {
-
- // Not a pay per post - check tier level or patron only status
- // For now do this in else, when api returns tiers replace with proper logic
-
- if ( $patreon_post['data']['attributes']['is_public'] ) {
- add_post_meta( $inserted_attachment_id, 'patreon-level', 0, true );
- }
- else {
- add_post_meta( $inserted_attachment_id, 'patreon-level', 1, true );
- }
-
- }
-
- }
- }
-
- // Set featured image
- $this->set_featured_image_for_patreon_post( $inserted_post_id );
-
- // If post is not public - currently there is no $ value or tier returned by /posts endpoint, so just set it to $1 locally
-
- if ( $patron_only_post ) {
- // Pay per post set to patron only - update only if not exists
- add_post_meta( $inserted_post_id, 'patreon-level', 1, true );
- }
- else {
- add_post_meta( $inserted_post_id, 'patreon-level', 0, true );
- }
-
- // Set category/taxonomy
-
- $post_term = get_term( $post_term_id, $post_category );
- $post_term_slug = $post_term->slug;
-
- wp_set_object_terms( $inserted_post_id, $post_term_slug, $post_category );
-
- update_post_meta( $inserted_post_id, 'patreon-post-id', $patreon_post['data']['id'] );
- update_post_meta( $inserted_post_id, 'patreon-post-url', $patreon_post['data']['attributes']['url'] );
-
- }
-
- public function update_patreon_post( $post_id, $patreon_post ) {
-
- $post = array();
- $post['ID'] = $post_id;
- $post['post_title'] = $patreon_post['data']['attributes']['title'];
- $post['post_content'] = $patreon_post['data']['attributes']['content'];
-
- // Parse and handle the images inside the post:
-
- global $Patreon_Wordpress;
- global $wpdb;
-
- $images = $Patreon_Wordpress->get_images_info_from_content( $post['post_content'] );
-
- if ( $images ) {
-
- $image_replacement = $this->check_replace_patreon_images_with_local_images( $post['post_content'], $images );
-
- $images = $image_replacement['images'];
- $post['post_content'] = $image_replacement['post_content'];
-
- }
-
- if ( isset( $patreon_post['data']['attributes']['embed_data']['url'] ) ) {
-
- // Process embeds:
-
- $post['post_content'] = $this->process_post_embeds( $post['post_content'], $patreon_post );
-
- // Temporarily remove wp post filters to allow iframes to be accepted into post
-
- remove_filter('content_save_pre', 'wp_filter_post_kses');
- remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
-
- }
-
- $updated_post_id = wp_update_post( $post );
-
- if ( isset( $patreon_post['data']['attributes']['embed_data']['url'] ) ) {
-
- // Re add post filters
-
- add_filter('content_save_pre', 'wp_filter_post_kses');
- add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
-
- }
-
- if ( is_wp_error( $updated_post_id ) OR $updated_post_id == 0 ) {
- // Handle error_get_last
- return;
- }
-
- // Now get the attachment ids we added to the post from earlier filenames
-
- if ( $images ) {
-
- foreach ( $images as $key => $value ) {
-
- $inserted_attachment = $wpdb->get_results( "SELECT ID FROM " . $wpdb->posts . " WHERE post_name = '" . $images[$key]['name'] ."'" );
-
- $inserted_attachment_id = $inserted_attachment[0]->ID;
-
- // Set Patreon post as parent of this attachment
-
- wp_update_post( array(
- 'ID' => $inserted_attachment_id,
- 'post_parent' => $updated_post_id
- )
- );
-
- // Set image to patron only since its in a patron only post - for those who use image locking featured
-
- // If post is not public - set the contained images to be patron only
-
- if ( $patreon_post['data']['attributes']['is_paid'] ) {
- // Pay per post set to patron only
- update_post_meta( $inserted_attachment_id, 'patreon-level', 1 );
- }
- else {
-
- // Not a pay per post - check tier level or patron only status
- // For now do this in else, when api returns tiers replace with proper logic
-
- if ( $patreon_post['data']['attributes']['is_public'] ) {
- update_post_meta( $inserted_attachment_id, 'patreon-level', 0 );
- }
- else {
- update_post_meta( $inserted_attachment_id, 'patreon-level', 1 );
- }
-
- }
-
- }
- }
-
- // Set featured image for post
- $this->set_featured_image_for_patreon_post( $updated_post_id );
-
- // Repeating this as a bloc here since the logic for individual post vs logic for included images may change at any point
-
- // If post is not public - currently there is no $ value or tier returned by /posts endpoint, so just set it to $1 locally
-
- if ( $patreon_post['data']['attributes']['is_paid'] ) {
- // Pay per post set to patron only
- update_post_meta( $updated_post_id, 'patreon-level', 1 );
- }
- else {
-
- // Not a pay per post - check tier level or patron only status
- // For now do this in else, when api returns tiers replace with proper logic
-
- if ( $patreon_post['data']['attributes']['is_public'] ) {
- update_post_meta( $updated_post_id, 'patreon-level', 0 );
- }
- else {
- update_post_meta( $updated_post_id, 'patreon-level', 1 );
- }
-
- }
-
- update_post_meta( $updated_post_id, 'patreon-post-id', $patreon_post['data']['id'] );
- update_post_meta( $updated_post_id, 'patreon-post-url', $patreon_post['data']['attributes']['url'] );
-
- }
-
- public function delete_patreon_post( $post_id ) {
-
- // Deletes a Patreon linked post
-
- wp_delete_post( $post_id, true );
-
- }
-
- public function check_replace_patreon_images_with_local_images( $post_content, $images ) {
-
- global $Patreon_Wordpress;
-
- $featured_image_set = false;
-
- // There are inserted images in the post. Process.
- foreach ( $images as $key => $value ) {
-
- // Check if the image is from Patreon.
- $match_patreon_url = array();
-
- preg_match( '/https:\/\/.*\.patreonusercontent\.com/i', $images[$key]['url'], $match_patreon_url );
-
- if ( count( $match_patreon_url ) > 0 ) {
-
- // This image is at Patreon.
-
- // Check if image exists in media library:
-
- // Get the image hash
-
- $image_hash = $Patreon_Wordpress->get_remote_image_hash( $images[$key]['url'] );
-
- // Set the filename to local translated one with hash
- $images[$key]['filename'] = $image_hash . '.' . $images[$key]['extension'];
- $images[$key]['name'] = $image_hash;
-
- $attachment_id = $Patreon_Wordpress->get_file_id_from_media_library( $image_hash );
-
- if ( !$attachment_id ) {
-
- // Not in media library. Download, insert.
- $attachment_id = $Patreon_Wordpress->download_insert_media( $images[$key]['url'], $images[$key]['filename'] );
-
- }
-
- // If attachment was successfully inserted, put it into the post:
-
- if ( $attachment_id ) {
-
- // Was able to acquire an attachment id for this Patreon image. Replace its url instead of the original:
-
- $attachment_info = wp_get_attachment_image_src( $attachment_id, 'full' );
-
- if ( $attachment_info ) {
-
- // Got a url for local attachment. Replace into the src of Patreon image:
-
- $post_content = str_replace( $images[$key]['url'], $attachment_info[0], $post_content );
-
- }
-
- }
-
- }
-
- }
-
- return array(
- 'images' => $images,
- 'post_content' => $post_content,
- );
- }
-
- public function process_post_embeds( $post_content, $patreon_post ) {
-
- if ( isset( $patreon_post['data']['attributes']['embed_data']['provider'] ) ) {
-
- if ( $patreon_post['data']['attributes']['embed_data']['provider'] == 'Patreon' ) {
-
- // Check if it is a link embed with an image pulled for it:
-
- $headers = array_change_key_case ( get_headers ( $patreon_post['data']['attributes']['embed_data']['html'] , 1 ) );
-
- if (substr ($headers ['content-type'], 0, 5) == 'image') {
-
- // Check if there's an url
-
- if ( isset( $patreon_post['data']['attributes']['embed_data']['url'] ) AND $patreon_post['data']['attributes']['embed_data']['url'] != '' ) {
-
- // Get the image if not present in local library:
-
- $path = parse_url( $patreon_post['data']['attributes']['embed_data']['html'], PHP_URL_PATH);
-
- $image_hash = $Patreon_Wordpress->get_remote_image_hash( $patreon_post['data']['attributes']['embed_data']['html'] );
-
- $filename_remote = basename($path['path']);
- $filename_remote_path = pathinfo( $filename_remote );
-
- // Set the filename to local translated one with hash
- $filename = $image_hash . '.' . $filename_remote_path['extension'];
-
- // This image checking and acquisition code can be bundled into a wrapper function later
-
- global $Patreon_Wordpress;
-
- $attachment_id = $Patreon_Wordpress->get_file_id_from_media_library( $image_hash );
-
- if ( !$attachment_id ) {
-
- // Not in media library. Download, insert.
- $attachment_id = $Patreon_Wordpress->download_insert_media( $patreon_post['data']['attributes']['embed_data']['html'], $filename );
-
- }
-
- // If attachment was successfully inserted, put it into the post:
-
- if ( $attachment_id ) {
-
- // Was able to acquire an attachment id for this Patreon image. Replace its url instead of the original:
-
- $attachment_info = wp_get_attachment_image_src( $attachment_id, 'full' );
-
- if ( $attachment_info ) {
-
- $post_content = '' . $post_content;
- }
-
- }
-
- }
-
- }
-
-
- }
- if ( $patreon_post['data']['attributes']['embed_data']['provider'] == 'YouTube' ) {
-
- // Get Youtube embed info for WP
-
- $embed_info = wp_remote_get( 'http://www.youtube.com/oembed?url=' . urlencode( $patreon_post['data']['attributes']['embed_data']['url'] ) . '&format=json' );
-
-
- if ( !is_wp_error( $embed_info ) ) {
-
- if ( isset( $embed_info['body'] ) ) {
-
- $response = json_decode( $embed_info['body'], true );
-
- if ( isset( $response['html'] ) AND strlen( $response['html'] ) > 0 ) {
- $post_content = $response['html'] . $post_content;
- }
-
- }
-
- }
-
- }
- if ( $patreon_post['data']['attributes']['embed_data']['provider'] == 'Vimeo' ) {
-
- // Get Youtube embed info for WP
-
- $embed_info = wp_remote_get( 'https://vimeo.com/api/oembed.json?url=' . urlencode( $patreon_post['data']['attributes']['embed_data']['url'] ) );
-
- if ( !is_wp_error( $embed_info ) ) {
-
- if ( isset( $embed_info['body'] ) ) {
-
- $response = json_decode( $embed_info['body'], true );
-
- if ( isset( $response['html'] ) AND strlen( $response['html'] ) > 0 ) {
- $post_content = $response['html'] . $post_content;
- }
-
- }
-
- }
-
- }
-
- }
-
- return $post_content;
-
-
- }
- public function get_matching_post_by_patreon_post_id( $patreon_post_id ) {
-
- // Gets a WP post by a matching Patreon post id from its Patreon post id meta
- global $wpdb;
-
- $post = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE meta_key = 'patreon-post-id' AND meta_value = '" . $patreon_post_id . "' LIMIT 1", ARRAY_A);
-
- if ( isset( $post[0]['post_id'] ) ) {
- return $post[0]['post_id'];
- }
-
- return false;
-
- }
- public function set_featured_image_for_patreon_post( $post_id ) {
-
- // Gets gets an imported post, browses inserted images and sets the first as the featured image.
-
- if ( get_option( 'patreon-set-featured-image', 'no' ) == 'no' ) {
- return;
- }
-
- $attachments = get_attached_media( 'image', $post_id );
-
- if ( !is_array( $attachments ) OR count( $attachments ) == 0 ) {
- return;
- }
-
- // Get the first attachment:
-
- $reversed_attachments = array_reverse( $attachments );
-
- $first_attachment = array_pop( $reversed_attachments );
-
- // Set the first attachment as featured image
- set_post_thumbnail( $post_id, $first_attachment->ID );
-
- return;
- }
-
-}
\ No newline at end of file
+ time() and !isset($args['manual_import'])) {
+ return 'manual_import_exists_within_last_60_seconds';
+ }
+
+ // Set a flag to use in checking whether any post import happened at all.
+ $at_least_one_post_imported = false;
+
+ // Set the flag for detecting end of post import
+ $end_of_post_import = false;
+
+ // Check if an import is going on
+
+ $post_import_in_progress = get_option('patreon-post-import-in-progress', false);
+
+ if (!$post_import_in_progress) {
+ // No ongoing import. Return
+ return 'no_ongoing_post_import';
+ }
+
+ $creator_access_token = get_option('patreon-creators-access-token', false);
+ $client_id = get_option('patreon-client-id', false);
+
+ if ($creator_access_token and $client_id) {
+ // Create new api object
+
+ $api_client = new Patreon_API($creator_access_token);
+
+ // Get if there is a saved cursor
+
+ $cursor = get_option('patreon-post-import-next-cursor', null);
+
+ $posts = $api_client->get_posts(false, 5, $cursor);
+
+ if (isset($posts['errors'][0]['code']) and 3 == $posts['errors'][0]['code'] and 'page[cursor]' == $posts['errors'][0]['source']['parameter']) {
+ // Cursor expired. Delete the cursor for next run and return
+ delete_option('patreon-post-import-next-cursor');
+
+ return 'expired_or_lost_cursor_deleted';
+ }
+
+ if (!isset($posts['data'])) {
+ // Couldnt get posts. Bail out
+ return 'couldnt_get_posts';
+ }
+
+ if (isset($posts['meta']['pagination']['cursors']['next'])) {
+ update_option('patreon-post-import-next-cursor', $posts['meta']['pagination']['cursors']['next']);
+ }
+
+ // If we have a saved cursor, the return is legitimate, but there is no more cursor then we are at the last page - iteration over. Mark it:
+
+ if (isset($cursor) and !isset($posts['meta']['pagination']['cursors']['next'])) {
+ delete_option('patreon-post-import-in-progress');
+ delete_option('patreon-post-import-next-cursor');
+ $end_of_post_import = true;
+ }
+
+ foreach ($posts['data'] as $key => $value) {
+ $patreon_post = $api_client->get_post($posts['data'][$key]['id']);
+
+ if (!isset($patreon_post['data']['id']) or '' == $patreon_post['data']['id']) {
+ // Couldn't get this post. Skip
+ continue;
+ }
+
+ $this->add_update_patreon_post($patreon_post);
+ $at_least_one_post_imported = true;
+ }
+ }
+
+ if ($at_least_one_post_imported or $end_of_post_import) {
+ // Post import ended
+ if ($end_of_post_import) {
+ return 'post_import_ended';
+ }
+
+ // Imported at least one post
+ return 'imported_posts';
+ }
+
+ return 'did_not_import_any_post';
+ }
+
+ public function add_update_patreon_post($patreon_post)
+ {
+ // Check if a matching WP post exists
+
+ $matching_post_id = false;
+
+ global $wpdb;
+
+ $matching_posts = $wpdb->get_results('SELECT post_id, meta_value FROM '.$wpdb->postmeta." WHERE meta_key = 'patreon-post-id' AND meta_value = '".$patreon_post['data']['id']."' ", ARRAY_A);
+
+ if (count($matching_posts) > 0) {
+ // Matching post found - just get the first one
+ $matching_post_id = $matching_posts[0]['post_id'];
+ }
+
+ // If no matching posts were found from query, try to find from title
+
+ if (0 == count($matching_posts)) {
+ // no matching posts. Try checking from the title.
+ $matching_post = get_page_by_title($patreon_post['data']['attributes']['title'], OBJECT, 'post');
+
+ if (isset($matching_post)) {
+ // A post matching from title was found.
+ $matching_post_id = $matching_post->ID;
+ }
+ }
+
+ if (!$matching_post_id) {
+ $result = $this->add_new_patreon_post($patreon_post);
+ } else {
+ // Update if existing posts are set to be updated
+ if ('yes' == get_option('patreon-update-posts', 'no')) {
+ $result = $this->update_patreon_post($matching_post_id, $patreon_post);
+ } else {
+ // Updating posts disabled. Just return
+ return false;
+ }
+ }
+
+ if (is_wp_error($result) or 0 == $result) {
+ // Flopped - return false
+ return false;
+ }
+
+ // Success
+ return true;
+ }
+
+ public function add_new_patreon_post($patreon_post)
+ {
+ $post_type = get_option('patreon-sync-post-type', 'post');
+ $post_category = get_option('patreon-sync-post-category', 'category');
+ $post_term_id = get_option('patreon-sync-post-term', '1');
+ $post_author = get_option('patreon-post-author-for-synced-posts', 1);
+ $patron_only_post = false;
+
+ if ($patreon_post['data']['attributes']['is_paid']) {
+ $patron_only_post = true;
+ } else {
+ // Not a pay per post - check tier level or patron only status
+ // For now do this in else, when api returns tiers replace with proper logic
+
+ if ($patreon_post['data']['attributes']['is_public']) {
+ $patron_only_post = false;
+ } else {
+ $patron_only_post = true;
+ }
+ }
+
+ $post_date = date('Y-m-d H:i:s', time());
+
+ if ('yes' == get_option('patreon-override-synced-post-publish-date', 'no')) {
+ $utc_timezone = new DateTimeZone('UTC');
+ $datetime = new DateTime($patreon_post['data']['attributes']['published_at'], $utc_timezone);
+ $post_date = $datetime->format('Y-m-d H:i:s');
+ }
+
+ $post_status = 'publish';
+
+ // Decide post status - publish or pending
+
+ if ($patron_only_post and 'no' == get_option('patreon-auto-publish-patron-only-posts', 'yes')) {
+ $post_status = 'pending';
+ }
+
+ if (!$patron_only_post and 'no' == get_option('patreon-auto-publish-public-posts', 'yes')) {
+ $post_status = 'pending';
+ }
+
+ $post = [];
+ $post['post_title'] = $patreon_post['data']['attributes']['title'];
+ $post['post_content'] = $patreon_post['data']['attributes']['content'];
+ $post['post_status'] = $post_status;
+ $post['post_author'] = $post_author;
+ $post['post_type'] = $post_type;
+ $post['post_date'] = $post_date;
+
+ // Parse and handle the images inside the post:
+
+ global $Patreon_Wordpress;
+ global $wpdb;
+
+ $images = $Patreon_Wordpress->get_images_info_from_content($post['post_content']);
+
+ if ($images) {
+ $image_replacement = $this->check_replace_patreon_images_with_local_images($post['post_content'], $images);
+
+ $images = $image_replacement['images'];
+ $post['post_content'] = $image_replacement['post_content'];
+ }
+
+ if (isset($patreon_post['data']['attributes']['embed_data']['url'])) {
+ // Process embeds:
+
+ $post['post_content'] = $this->process_post_embeds($post['post_content'], $patreon_post);
+
+ // Temporarily remove wp post filters to allow iframes to be accepted into post
+
+ remove_filter('content_save_pre', 'wp_filter_post_kses');
+ remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
+ }
+
+ $inserted_post_id = wp_insert_post($post);
+
+ if (isset($patreon_post['data']['attributes']['embed_data']['url'])) {
+ // Re add post filters
+
+ add_filter('content_save_pre', 'wp_filter_post_kses');
+ add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
+ }
+
+ if (is_wp_error($inserted_post_id)) {
+ // Handle error_get_last
+ return;
+ }
+
+ // Now get the attachment ids we added to the post from earlier filenames
+
+ if ($images) {
+ foreach ($images as $key => $value) {
+ $inserted_attachment = $wpdb->get_results('SELECT ID FROM '.$wpdb->posts." WHERE post_name = '".$images[$key]['name']."'");
+
+ $inserted_attachment_id = $inserted_attachment[0]->ID;
+
+ // Set Patreon post as parent of this attachment
+
+ wp_update_post(
+ [
+ 'ID' => $inserted_attachment_id,
+ 'post_parent' => $inserted_post_id,
+ ]
+ );
+
+ // Set image to patron only since its in a patron only post - for those who use image locking featured
+
+ // If post is not public - set the contained images to be patron only
+
+ if ($patreon_post['data']['attributes']['is_paid']) {
+ // Pay per post set to patron only
+ // Add only if not exists
+ add_post_meta($inserted_attachment_id, 'patreon-level', 1, true);
+ } else {
+ // Not a pay per post - check tier level or patron only status
+ // For now do this in else, when api returns tiers replace with proper logic
+
+ if ($patreon_post['data']['attributes']['is_public']) {
+ add_post_meta($inserted_attachment_id, 'patreon-level', 0, true);
+ } else {
+ add_post_meta($inserted_attachment_id, 'patreon-level', 1, true);
+ }
+ }
+ }
+ }
+
+ // Set featured image
+ $this->set_featured_image_for_patreon_post($inserted_post_id);
+
+ // If post is not public - currently there is no $ value or tier returned by /posts endpoint, so just set it to $1 locally
+
+ if ($patron_only_post) {
+ // Pay per post set to patron only - update only if not exists
+ add_post_meta($inserted_post_id, 'patreon-level', 1, true);
+ } else {
+ add_post_meta($inserted_post_id, 'patreon-level', 0, true);
+ }
+
+ // Set category/taxonomy
+
+ $post_term = get_term($post_term_id, $post_category);
+ $post_term_slug = $post_term->slug;
+
+ wp_set_object_terms($inserted_post_id, $post_term_slug, $post_category);
+
+ update_post_meta($inserted_post_id, 'patreon-post-id', $patreon_post['data']['id']);
+ update_post_meta($inserted_post_id, 'patreon-post-url', $patreon_post['data']['attributes']['url']);
+ }
+
+ public function update_patreon_post($post_id, $patreon_post)
+ {
+ $post = [];
+ $post['ID'] = $post_id;
+ $post['post_title'] = $patreon_post['data']['attributes']['title'];
+ $post['post_content'] = $patreon_post['data']['attributes']['content'];
+
+ // Parse and handle the images inside the post:
+
+ global $Patreon_Wordpress;
+ global $wpdb;
+
+ $images = $Patreon_Wordpress->get_images_info_from_content($post['post_content']);
+
+ if ($images) {
+ $image_replacement = $this->check_replace_patreon_images_with_local_images($post['post_content'], $images);
+
+ $images = $image_replacement['images'];
+ $post['post_content'] = $image_replacement['post_content'];
+ }
+
+ if (isset($patreon_post['data']['attributes']['embed_data']['url'])) {
+ // Process embeds:
+
+ $post['post_content'] = $this->process_post_embeds($post['post_content'], $patreon_post);
+
+ // Temporarily remove wp post filters to allow iframes to be accepted into post
+
+ remove_filter('content_save_pre', 'wp_filter_post_kses');
+ remove_filter('content_filtered_save_pre', 'wp_filter_post_kses');
+ }
+
+ $updated_post_id = wp_update_post($post);
+
+ if (isset($patreon_post['data']['attributes']['embed_data']['url'])) {
+ // Re add post filters
+
+ add_filter('content_save_pre', 'wp_filter_post_kses');
+ add_filter('content_filtered_save_pre', 'wp_filter_post_kses');
+ }
+
+ if (is_wp_error($updated_post_id) or 0 == $updated_post_id) {
+ // Handle error_get_last
+ return;
+ }
+
+ // Now get the attachment ids we added to the post from earlier filenames
+
+ if ($images) {
+ foreach ($images as $key => $value) {
+ $inserted_attachment = $wpdb->get_results('SELECT ID FROM '.$wpdb->posts." WHERE post_name = '".$images[$key]['name']."'");
+
+ $inserted_attachment_id = $inserted_attachment[0]->ID;
+
+ // Set Patreon post as parent of this attachment
+
+ wp_update_post(
+ [
+ 'ID' => $inserted_attachment_id,
+ 'post_parent' => $updated_post_id,
+ ]
+ );
+
+ // Set image to patron only since its in a patron only post - for those who use image locking featured
+
+ // If post is not public - set the contained images to be patron only
+
+ if ($patreon_post['data']['attributes']['is_paid']) {
+ // Pay per post set to patron only
+ update_post_meta($inserted_attachment_id, 'patreon-level', 1);
+ } else {
+ // Not a pay per post - check tier level or patron only status
+ // For now do this in else, when api returns tiers replace with proper logic
+
+ if ($patreon_post['data']['attributes']['is_public']) {
+ update_post_meta($inserted_attachment_id, 'patreon-level', 0);
+ } else {
+ update_post_meta($inserted_attachment_id, 'patreon-level', 1);
+ }
+ }
+ }
+ }
+
+ // Set featured image for post
+ $this->set_featured_image_for_patreon_post($updated_post_id);
+
+ // Repeating this as a bloc here since the logic for individual post vs logic for included images may change at any point
+
+ // If post is not public - currently there is no $ value or tier returned by /posts endpoint, so just set it to $1 locally
+
+ if ($patreon_post['data']['attributes']['is_paid']) {
+ // Pay per post set to patron only
+ update_post_meta($updated_post_id, 'patreon-level', 1);
+ } else {
+ // Not a pay per post - check tier level or patron only status
+ // For now do this in else, when api returns tiers replace with proper logic
+
+ if ($patreon_post['data']['attributes']['is_public']) {
+ update_post_meta($updated_post_id, 'patreon-level', 0);
+ } else {
+ update_post_meta($updated_post_id, 'patreon-level', 1);
+ }
+ }
+
+ update_post_meta($updated_post_id, 'patreon-post-id', $patreon_post['data']['id']);
+ update_post_meta($updated_post_id, 'patreon-post-url', $patreon_post['data']['attributes']['url']);
+ }
+
+ public function delete_patreon_post($post_id)
+ {
+ // Deletes a Patreon linked post
+
+ wp_delete_post($post_id, true);
+ }
+
+ public function check_replace_patreon_images_with_local_images($post_content, $images)
+ {
+ global $Patreon_Wordpress;
+
+ $featured_image_set = false;
+
+ // There are inserted images in the post. Process.
+ foreach ($images as $key => $value) {
+ // Check if the image is from Patreon.
+ $match_patreon_url = [];
+
+ preg_match('/https:\/\/.*\.patreonusercontent\.com/i', $images[$key]['url'], $match_patreon_url);
+
+ if (count($match_patreon_url) > 0) {
+ // This image is at Patreon.
+
+ // Check if image exists in media library:
+
+ // Get the image hash
+
+ $image_hash = $Patreon_Wordpress->get_remote_image_hash($images[$key]['url']);
+
+ // Set the filename to local translated one with hash
+ $images[$key]['filename'] = $image_hash.'.'.$images[$key]['extension'];
+ $images[$key]['name'] = $image_hash;
+
+ $attachment_id = $Patreon_Wordpress->get_file_id_from_media_library($image_hash);
+
+ if (!$attachment_id) {
+ // Not in media library. Download, insert.
+ $attachment_id = $Patreon_Wordpress->download_insert_media($images[$key]['url'], $images[$key]['filename']);
+ }
+
+ // If attachment was successfully inserted, put it into the post:
+
+ if ($attachment_id) {
+ // Was able to acquire an attachment id for this Patreon image. Replace its url instead of the original:
+
+ $attachment_info = wp_get_attachment_image_src($attachment_id, 'full');
+
+ if ($attachment_info) {
+ // Got a url for local attachment. Replace into the src of Patreon image:
+
+ $post_content = str_replace($images[$key]['url'], $attachment_info[0], $post_content);
+ }
+ }
+ }
+ }
+
+ return [
+ 'images' => $images,
+ 'post_content' => $post_content,
+ ];
+ }
+
+ public function process_post_embeds($post_content, $patreon_post)
+ {
+ if (isset($patreon_post['data']['attributes']['embed_data']['provider'])) {
+ if ('Patreon' == $patreon_post['data']['attributes']['embed_data']['provider']) {
+ // Check if it is a link embed with an image pulled for it:
+
+ $headers = array_change_key_case(get_headers($patreon_post['data']['attributes']['embed_data']['html'], 1));
+
+ if ('image' == substr($headers['content-type'], 0, 5)) {
+ // Check if there's an url
+
+ if (isset($patreon_post['data']['attributes']['embed_data']['url']) and '' != $patreon_post['data']['attributes']['embed_data']['url']) {
+ // Get the image if not present in local library:
+
+ $path = parse_url($patreon_post['data']['attributes']['embed_data']['html'], PHP_URL_PATH);
+
+ $image_hash = $Patreon_Wordpress->get_remote_image_hash($patreon_post['data']['attributes']['embed_data']['html']);
+
+ $filename_remote = basename($path['path']);
+ $filename_remote_path = pathinfo($filename_remote);
+
+ // Set the filename to local translated one with hash
+ $filename = $image_hash.'.'.$filename_remote_path['extension'];
+
+ // This image checking and acquisition code can be bundled into a wrapper function later
+
+ global $Patreon_Wordpress;
+
+ $attachment_id = $Patreon_Wordpress->get_file_id_from_media_library($image_hash);
+
+ if (!$attachment_id) {
+ // Not in media library. Download, insert.
+ $attachment_id = $Patreon_Wordpress->download_insert_media($patreon_post['data']['attributes']['embed_data']['html'], $filename);
+ }
+
+ // If attachment was successfully inserted, put it into the post:
+
+ if ($attachment_id) {
+ // Was able to acquire an attachment id for this Patreon image. Replace its url instead of the original:
+
+ $attachment_info = wp_get_attachment_image_src($attachment_id, 'full');
+
+ if ($attachment_info) {
+ $post_content = ''.$post_content;
+ }
+ }
+ }
+ }
+ }
+ if ('YouTube' == $patreon_post['data']['attributes']['embed_data']['provider']) {
+ // Get Youtube embed info for WP
+
+ $embed_info = wp_remote_get('http://www.youtube.com/oembed?url='.urlencode($patreon_post['data']['attributes']['embed_data']['url']).'&format=json');
+
+ if (!is_wp_error($embed_info)) {
+ if (isset($embed_info['body'])) {
+ $response = json_decode($embed_info['body'], true);
+
+ if (isset($response['html']) and strlen($response['html']) > 0) {
+ $post_content = $response['html'].$post_content;
+ }
+ }
+ }
+ }
+ if ('Vimeo' == $patreon_post['data']['attributes']['embed_data']['provider']) {
+ // Get Youtube embed info for WP
+
+ $embed_info = wp_remote_get('https://vimeo.com/api/oembed.json?url='.urlencode($patreon_post['data']['attributes']['embed_data']['url']));
+
+ if (!is_wp_error($embed_info)) {
+ if (isset($embed_info['body'])) {
+ $response = json_decode($embed_info['body'], true);
+
+ if (isset($response['html']) and strlen($response['html']) > 0) {
+ $post_content = $response['html'].$post_content;
+ }
+ }
+ }
+ }
+ }
+
+ return $post_content;
+ }
+
+ public function get_matching_post_by_patreon_post_id($patreon_post_id)
+ {
+ // Gets a WP post by a matching Patreon post id from its Patreon post id meta
+ global $wpdb;
+
+ $post = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE meta_key = 'patreon-post-id' AND meta_value = '".$patreon_post_id."' LIMIT 1", ARRAY_A);
+
+ if (isset($post[0]['post_id'])) {
+ return $post[0]['post_id'];
+ }
+
+ return false;
+ }
+
+ public function set_featured_image_for_patreon_post($post_id)
+ {
+ // Gets gets an imported post, browses inserted images and sets the first as the featured image.
+
+ if ('no' == get_option('patreon-set-featured-image', 'no')) {
+ return;
+ }
+
+ $attachments = get_attached_media('image', $post_id);
+
+ if (!is_array($attachments) or 0 == count($attachments)) {
+ return;
+ }
+
+ // Get the first attachment:
+
+ $reversed_attachments = array_reverse($attachments);
+
+ $first_attachment = array_pop($reversed_attachments);
+
+ // Set the first attachment as featured image
+ set_post_thumbnail($post_id, $first_attachment->ID);
+
+ return;
+ }
+}
diff --git a/classes/patreon_frontend.php b/classes/patreon_frontend.php
index f0c9ddf0..ed7737bf 100644
--- a/classes/patreon_frontend.php
+++ b/classes/patreon_frontend.php
@@ -1,1604 +1,1522 @@
PATREON_CANT_LOGIN_STRICT_OAUTH,
- 'login_with_wordpress' => PATREON_LOGIN_WITH_WORDPRESS_NOW,
- 'patreon_cant_login_api_error' => PATREON_CANT_LOGIN_DUE_TO_API_ERROR,
- 'patreon_cant_login_api_error_credentials' => PATREON_CANT_LOGIN_DUE_TO_API_ERROR_CHECK_CREDENTIALS,
- 'patreon_no_locking_level_set_for_this_post' => PATREON_NO_LOCKING_LEVEL_SET_FOR_THIS_POST,
- 'patreon_no_post_id_to_unlock_post' => PATREON_NO_POST_ID_TO_UNLOCK_POST,
- 'patreon_weird_redirection_at_login' => PATREON_WEIRD_REDIRECTION_AT_LOGIN,
- 'patreon_could_not_create_wp_account' => PATREON_COULDNT_CREATE_WP_ACCOUNT,
- 'patreon_api_credentials_missing' => PATREON_API_CREDENTIALS_MISSING,
- 'admin_login_with_patreon_disabled' => PATREON_ADMIN_LOGIN_WITH_PATREON_DISABLED,
- 'email_exists_login_with_wp_first' => PATREON_EMAIL_EXISTS_LOGIN_WITH_WP_FIRST,
- 'login_with_patreon_disabled' => PATREON_LOGIN_WITH_PATREON_DISABLED,
- 'admin_bypass_filter_message' => PATREON_ADMIN_BYPASSES_FILTER_MESSAGE,
- 'no_code_receved_from_patreon' => PATREON_NO_CODE_RECEIVED_FROM_PATREON,
- 'no_patreon_action_provided_for_flow' => PATREON_NO_FLOW_ACTION_PROVIDED,
- 'patreon_direct_unlocks_not_turned_on' => PATREON_DIRECT_UNLOCKS_NOT_ON,
- 'patreon_couldnt_acquire_user_details' => PATREON_COULDNT_ACQUIRE_USER_DETAILS,
- 'pretty_permalinks_are_required' => PATREON_PRETTY_PERMALINKS_NEED_TO_BE_ENABLED,
- 'error_missing_credentials' => PATREON_ERROR_MISSING_CREDENTIALS,
- 'no_auth_for_client_creation' => PATREON_NO_AUTH_FOR_CLIENT_CREATION,
- 'failure_obtaining_credentials' => PATREON_NO_ACQUIRE_CLIENT_DETAILS,
- 'error_missing_credentials' => PATREON_NO_CREDENTIALS_RECEIVED,
- 'patreon_admin_message_default_title' => PATREON_ADMIN_MESSAGE_DEFAULT_TITLE,
- 'patreon_admin_message_default_content' => PATREON_ADMIN_MESSAGE_DEFAULT_CONTENT,
- 'client_delete_error_title' => PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_TITLE,
- 'client_delete_error_content' => PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_CONTENT,
- 'client_reconnect_delete_error_title' => PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_TITLE,
- 'client_reconnect_delete_error_content' => PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_CONTENT,
- 'all_post_category_fields_must_be_selected' => PATREON_ALL_POST_CATEGORY_FIELDS_MUST_BE_SELECTED,
- );
-
- self::$allowed_toggles = array(
- 'patreon-wordpress-advanced-options-toggle' => '',
- );
-
- }
-
- function patreonEnqueueJs() {
-
- wp_register_script( 'patreon-wordpress-js', PATREON_PLUGIN_ASSETS . '/js/app.js', array( 'jquery' ) );
- wp_enqueue_script( 'patreon-wordpress-js', PATREON_PLUGIN_ASSETS . '/js/app.js', array( 'jquery' ), '1.0', true );
-
- }
- function patreonEnqueueAdminCss() {
-
- wp_register_style( 'patreon-wordpress-admin-css', PATREON_PLUGIN_ASSETS . '/css/admin.css', false );
- wp_enqueue_style( 'patreon-wordpress-admin-css', PATREON_PLUGIN_ASSETS . '/css/admin.css', PATREON_WORDPRESS_VERSION );
-
- }
- function patreonEnqueueCss() {
-
- wp_register_style( 'patreon-wordpress-css', PATREON_PLUGIN_ASSETS.'/css/app.css', false );
- wp_enqueue_style('patreon-wordpress-css', PATREON_PLUGIN_ASSETS.'/css/app.css' );
-
- }
- function patreonPrintCss() {
-
- // Why we print out css direclty in header is that we want to account for any potential WP content directory location than default
- echo '';
-
- }
- public static function displayPatreonCampaignBanner( $patreon_level = false, $args = false ) {
-
- global $wp;
-
- // Allow 3rd party plugins to override interface - this will abort interface generation and replace it with the code that returns from this filter, and also allow 3rd party code to still apply rest of this function's filters without causing recursion
-
- $override_interface = array();
- $override_interface = apply_filters( 'ptrn/override_interface_template', $patreon_level, $args );
-
- if ( is_array( $override_interface ) AND isset( $override_interface['override'] ) ) {
- return $override_interface['interface'];
- }
-
- $post = false;
-
- // Get the post from post id if it is supplied
- if ( isset( $args['post_id'] ) ) {
- $post = get_post( $args['post_id'] );
- }
-
- if ( !$args OR !is_array( $args ) ) {
- global $post;
- $args = array();
- }
-
- $login_with_patreon = get_option( 'patreon-enable-login-with-patreon', false );
- $client_id = get_option( 'patreon-client-id', false );
-
- // Check existence of a custom patreon banners as saved in plugin options
- $custom_universal_banner = get_option( 'patreon-custom-universal-banner', false );
-
- // Default custom text banner
-
- $contribution_required = PATREON_TEXT_LOCKED_POST;
-
- if ( $custom_universal_banner AND $custom_universal_banner !='' ) {
- // Custom banner exists and it is not empty. Override the message
- $contribution_required = $custom_universal_banner;
- }
-
- if ( $patreon_level != false ) {
-
- $contribution_required = str_replace( '%%pledgelevel%%', $patreon_level, $contribution_required );
- $contribution_required = apply_filters( 'ptrn/contribution_required', $contribution_required, 'main_banner_message',$patreon_level, $post );
-
- }
-
- if ( $client_id ) {
-
- // Wrap message and buttons in divs for responsive interface mechanics
-
- $contribution_required = '
' . $contribution_required . '
';
-
- // But hide the custom banner if no custom banner was saved
-
- if ( !$custom_universal_banner OR $custom_universal_banner =='' ) {
- $contribution_required = '';
- }
-
- // Still apply the filters so it can be modified by 3rd party code
- $contribution_required = apply_filters( 'ptrn/final_state_main_banner_message', $contribution_required, $patreon_level, $post );
-
- $button_args = array();
-
- if ( isset( $args['direct_unlock'] ) ) {
-
- // This is a direct unlock intent that does not seek to particularly unlock a post. It can be anything. Set the relevant vars for universal button function:
- $button_args['direct_unlock'] = $args['direct_unlock'];
- $button_args['redirect'] = $args['redirect'];
-
- }
-
- if ( is_feed() ) {
- $button_args['is_feed'] = true;
- }
-
- $universal_button = self::patreonMakeUniversalButton( $patreon_level, false, false, false, $button_args );
- $universal_button = apply_filters( 'ptrn/final_state_universal_button', '
' . $universal_button . '
', $patreon_level, $post );
-
- $text_over_universal_button = apply_filters( 'ptrn/final_state_label_over_universal_button', self::getLabelOverUniversalButton( $patreon_level, $args ), $patreon_level, $post );
-
- $text_under_universal_button = apply_filters( 'ptrn/final_state_label_under_universal_button', self::getLabelUnderUniversalButton( $patreon_level, false, false, $args ), $patreon_level, $post );
-
- if ( is_feed() ) {
- $text_under_universal_button = PATREON_FEED_ACTION_TEXT;
- }
-
- // Wrap all of them in a responsive div
-
- $campaign_banner = '
'.
- $contribution_required.
- '
'.
- '
'.
- $text_over_universal_button.
- '
'.
- $universal_button.
- '
'.
- $text_under_universal_button.
- '
'.
- '
'.
- '
';
-
- // This extra button is solely here to test whether new wrappers cause any design issues in different themes. For easy comparison with existing unwrapped button. Remove when confirmed.
-
- $campaign_banner = apply_filters( 'ptrn/campaign_banner', $campaign_banner, $patreon_level, $post );
+class Patreon_Frontend
+{
+ public static $messages_map = [];
+ public static $allowed_toggles = [];
+ public static $current_user_logged_into_patreon = -1;
+
+ public function __construct()
+ {
+ add_action('login_enqueue_scripts', [$this, 'patreonEnqueueCss'], 10);
+ add_action('wp_enqueue_scripts', [$this, 'patreonEnqueueCss']);
+ add_action('wp_head', [$this, 'patreonPrintCss']);
+ add_action('wp_enqueue_scripts', [$this, 'patreonEnqueueJs']);
+ add_action('admin_enqueue_scripts', [$this, 'patreonEnqueueAdminCss']);
+ add_action('login_form', [$this, 'showPatreonMessages']);
+ add_action('login_form', [$this, 'displayPatreonLoginButtonInLoginForm']);
+ add_action('register_form', [$this, 'showPatreonMessages']);
+ add_action('register_form', [$this, 'displayPatreonLoginButtonInLoginForm']);
+ add_filter('the_content', [$this, 'protectContentFromUsers'], PHP_INT_MAX - 5);
+ // This filter will inject currency sign into label over button until proper internationalization is done
+ add_filter('ptrn/label_text_over_universal_button', [$this, 'replace_in_currency_sign'], PHP_INT_MAX - 5);
+ add_filter('ptrn/valid_patron_final_footer', [$this, 'replace_in_currency_sign'], PHP_INT_MAX - 5);
+ add_shortcode('patreon_login_button', [$this, 'LoginButtonShortcode']);
+ add_filter('get_avatar', [$this, 'show_patreon_avatar'], 10, 5);
+
+ self::$messages_map = [
+ 'patreon_cant_login_strict_oauth' => PATREON_CANT_LOGIN_STRICT_OAUTH,
+ 'login_with_wordpress' => PATREON_LOGIN_WITH_WORDPRESS_NOW,
+ 'patreon_cant_login_api_error' => PATREON_CANT_LOGIN_DUE_TO_API_ERROR,
+ 'patreon_cant_login_api_error_credentials' => PATREON_CANT_LOGIN_DUE_TO_API_ERROR_CHECK_CREDENTIALS,
+ 'patreon_no_locking_level_set_for_this_post' => PATREON_NO_LOCKING_LEVEL_SET_FOR_THIS_POST,
+ 'patreon_no_post_id_to_unlock_post' => PATREON_NO_POST_ID_TO_UNLOCK_POST,
+ 'patreon_weird_redirection_at_login' => PATREON_WEIRD_REDIRECTION_AT_LOGIN,
+ 'patreon_could_not_create_wp_account' => PATREON_COULDNT_CREATE_WP_ACCOUNT,
+ 'patreon_api_credentials_missing' => PATREON_API_CREDENTIALS_MISSING,
+ 'admin_login_with_patreon_disabled' => PATREON_ADMIN_LOGIN_WITH_PATREON_DISABLED,
+ 'email_exists_login_with_wp_first' => PATREON_EMAIL_EXISTS_LOGIN_WITH_WP_FIRST,
+ 'login_with_patreon_disabled' => PATREON_LOGIN_WITH_PATREON_DISABLED,
+ 'admin_bypass_filter_message' => PATREON_ADMIN_BYPASSES_FILTER_MESSAGE,
+ 'no_code_receved_from_patreon' => PATREON_NO_CODE_RECEIVED_FROM_PATREON,
+ 'no_patreon_action_provided_for_flow' => PATREON_NO_FLOW_ACTION_PROVIDED,
+ 'patreon_couldnt_acquire_user_details' => PATREON_COULDNT_ACQUIRE_USER_DETAILS,
+ 'pretty_permalinks_are_required' => PATREON_PRETTY_PERMALINKS_NEED_TO_BE_ENABLED,
+ 'error_missing_credentials' => PATREON_ERROR_MISSING_CREDENTIALS,
+ 'no_auth_for_client_creation' => PATREON_NO_AUTH_FOR_CLIENT_CREATION,
+ 'failure_obtaining_credentials' => PATREON_NO_ACQUIRE_CLIENT_DETAILS,
+ 'error_missing_credentials' => PATREON_NO_CREDENTIALS_RECEIVED,
+ 'patreon_admin_message_default_title' => PATREON_ADMIN_MESSAGE_DEFAULT_TITLE,
+ 'patreon_admin_message_default_content' => PATREON_ADMIN_MESSAGE_DEFAULT_CONTENT,
+ 'client_delete_error_title' => PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_TITLE,
+ 'client_delete_error_content' => PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_CONTENT,
+ 'client_reconnect_delete_error_title' => PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_TITLE,
+ 'client_reconnect_delete_error_content' => PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_CONTENT,
+ 'all_post_category_fields_must_be_selected' => PATREON_ALL_POST_CATEGORY_FIELDS_MUST_BE_SELECTED,
+ ];
+
+ self::$allowed_toggles = [
+ 'patreon-wordpress-advanced-options-toggle' => '',
+ ];
+ }
+
+ public function patreonEnqueueJs()
+ {
+ wp_register_script('patreon-wordpress-js', PATREON_PLUGIN_ASSETS.'/js/app.js', ['jquery']);
+ wp_enqueue_script('patreon-wordpress-js', PATREON_PLUGIN_ASSETS.'/js/app.js', ['jquery'], '1.0', true);
+ }
+
+ public function patreonEnqueueAdminCss()
+ {
+ wp_register_style('patreon-wordpress-admin-css', PATREON_PLUGIN_ASSETS.'/css/admin.css', false);
+ wp_enqueue_style('patreon-wordpress-admin-css', PATREON_PLUGIN_ASSETS.'/css/admin.css', PATREON_WORDPRESS_VERSION);
+ }
+
+ public function patreonEnqueueCss()
+ {
+ wp_register_style('patreon-wordpress-css', PATREON_PLUGIN_ASSETS.'/css/app.css', false);
+ wp_enqueue_style('patreon-wordpress-css', PATREON_PLUGIN_ASSETS.'/css/app.css');
+ }
+
+ public function patreonPrintCss()
+ {
+ // Why we print out css direclty in header is that we want to account for any potential WP content directory location than default
+ echo '';
+ }
+
+ public static function displayPatreonCampaignBanner($patreon_level = false, $args = false)
+ {
+ global $wp;
+
+ // Allow 3rd party plugins to override interface - this will abort interface generation and replace it with the code that returns from this filter, and also allow 3rd party code to still apply rest of this function's filters without causing recursion
+
+ $override_interface = [];
+ $override_interface = apply_filters('ptrn/override_interface_template', $patreon_level, $args);
+
+ if (is_array($override_interface) and isset($override_interface['override'])) {
+ return $override_interface['interface'];
+ }
+
+ $post = false;
+
+ // Get the post from post id if it is supplied
+ if (isset($args['post_id'])) {
+ $post = get_post($args['post_id']);
+ }
+
+ if (!$args or !is_array($args)) {
+ global $post;
+ $args = [];
+ }
+
+ $login_with_patreon = get_option('patreon-enable-login-with-patreon', false);
+ $client_id = get_option('patreon-client-id', false);
+
+ // Check existence of a custom patreon banners as saved in plugin options
+ $custom_universal_banner = get_option('patreon-custom-universal-banner', false);
+
+ // Default custom text banner
+
+ $contribution_required = PATREON_TEXT_LOCKED_POST;
+
+ if ($custom_universal_banner and '' != $custom_universal_banner) {
+ // Custom banner exists and it is not empty. Override the message
+ $contribution_required = $custom_universal_banner;
+ }
+
+ if (false != $patreon_level) {
+ $contribution_required = str_replace('%%pledgelevel%%', $patreon_level, $contribution_required);
+ $contribution_required = apply_filters('ptrn/contribution_required', $contribution_required, 'main_banner_message', $patreon_level, $post);
+ }
+
+ if ($client_id) {
+ // Wrap message and buttons in divs for responsive interface mechanics
+
+ $contribution_required = '
'.$contribution_required.'
';
+
+ // But hide the custom banner if no custom banner was saved
+
+ if (!$custom_universal_banner or '' == $custom_universal_banner) {
+ $contribution_required = '';
+ }
+
+ // Still apply the filters so it can be modified by 3rd party code
+ $contribution_required = apply_filters('ptrn/final_state_main_banner_message', $contribution_required, $patreon_level, $post);
+
+ $button_args = [];
+
+ if (isset($args['direct_unlock'])) {
+ // This is a direct unlock intent that does not seek to particularly unlock a post. It can be anything. Set the relevant vars for universal button function:
+ $button_args['direct_unlock'] = $args['direct_unlock'];
+ $button_args['redirect'] = $args['redirect'];
+ }
+
+ if (is_feed()) {
+ $button_args['is_feed'] = true;
+ }
+
+ $universal_button = self::patreonMakeUniversalButton($patreon_level, false, false, false, $button_args);
+ $universal_button = apply_filters('ptrn/final_state_universal_button', '
'.$universal_button.'
', $patreon_level, $post);
+
+ $text_over_universal_button = apply_filters('ptrn/final_state_label_over_universal_button', self::getLabelOverUniversalButton($patreon_level, $args), $patreon_level, $post);
+
+ $text_under_universal_button = apply_filters('ptrn/final_state_label_under_universal_button', self::getLabelUnderUniversalButton($patreon_level, false, false, $args), $patreon_level, $post);
+
+ if (is_feed()) {
+ $text_under_universal_button = PATREON_FEED_ACTION_TEXT;
+ }
+
+ // Wrap all of them in a responsive div
+
+ $campaign_banner = '
'.
+ $contribution_required.
+ '
'.
+ '
'.
+ $text_over_universal_button.
+ '
'.
+ $universal_button.
+ '
'.
+ $text_under_universal_button.
+ '
'.
+ '
'.
+ '
';
+
+ // This extra button is solely here to test whether new wrappers cause any design issues in different themes. For easy comparison with existing unwrapped button. Remove when confirmed.
+
+ $campaign_banner = apply_filters('ptrn/campaign_banner', $campaign_banner, $patreon_level, $post);
return $campaign_banner;
}
-
- }
- public static function getLabelOverUniversalButton( $patreon_level, $args = false ) {
-
- $label = PATREON_TEXT_OVER_BUTTON_1;
- $user_logged_into_patreon = self::isUserLoggedInPatreon();
- $is_patron = Patreon_Wordpress::isPatron( wp_get_current_user() );
- $messages = self::processPatreonMessages();
- $user = wp_get_current_user();
- $declined = Patreon_Wordpress::checkDeclinedPatronage( $user );
- $user_patronage = Patreon_Wordpress::getUserPatronage();
-
- // Get the creator or page name which is going to be used for interface text
-
- $creator_full_name = self::make_creator_name_for_interface();
-
- // Add 's to make it "creator's Patreon" when placed into label
-
- $creator_full_name .= "'s";
-
- // Override entire text if the creator set a custom site/creator name string:
-
- $patreon_custom_page_name = get_option( 'patreon-custom-page-name', false );
-
- if ( $patreon_custom_page_name AND $patreon_custom_page_name != '' ) {
- $creator_full_name = $patreon_custom_page_name;
- }
-
- // Get lock or not details if it is not given. If post id given, use it.
- if ( !isset( $args['lock'] ) ) {
-
- if ( isset( $args['post_id'] ) ) {
- $lock_or_not = Patreon_Wordpress::lock_or_not( $args['post_id'] );
- }
- else {
- $lock_or_not = Patreon_Wordpress::lock_or_not();
- }
- }
-
- // If lock or not details were not given, merge the incoming args with what we just got.
- if( $args AND is_array( $args ) AND !isset( $args['lock'] ) ) {
- $args = $lock_or_not + $args;
- }
-
- // If no args given, just feed lock or not:
-
- if ( !isset( $args ) ) {
- $args = $lock_or_not;
- }
-
- $post_id = false;
-
- if ( isset( $args['post_id'] ) ) {
- $post = get_post( $args['post_id'] );
- }
- else {
- global $post;
- }
-
- if ( isset( $args['reason'] ) AND $args['reason'] == 'user_not_logged_in' ) {
-
- $label = PATREON_TEXT_OVER_BUTTON_1;
-
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- $label = PATREON_TEXT_OVER_BUTTON_7;
- }
-
- if( isset( $args['post_total_patronage_level'] ) AND $args['post_total_patronage_level'] > 0 ) {
- $label = PATREON_TEXT_OVER_BUTTON_12;
-
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- // Double condition - has both active patron and total patronage conditions. Override text
- $label = PATREON_TEXT_OVER_BUTTON_13;
- }
- }
-
- }
-
- if ( isset( $args['reason'] ) AND $args['reason'] == 'not_a_patron' ) {
-
- $label = PATREON_TEXT_OVER_BUTTON_1;
-
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- $label = PATREON_TEXT_OVER_BUTTON_7;
- }
-
- if( isset( $args['post_total_patronage_level'] ) AND $args['post_total_patronage_level'] > 0 ) {
- $label = PATREON_TEXT_OVER_BUTTON_9;
-
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- // Double condition - has both active patron and total patronage conditions. Override text
- $label = PATREON_TEXT_OVER_BUTTON_10;
- }
- }
-
- }
-
- if ( isset( $args['reason'] ) AND $args['reason'] == 'payment_declined' ) {
- $label = PATREON_TEXT_OVER_BUTTON_3;
- }
-
- if ( isset( $args['reason'] ) AND $args['reason'] == 'active_pledge_not_enough' ) {
-
- $label = PATREON_TEXT_OVER_BUTTON_1A;
-
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- $label = PATREON_TEXT_OVER_BUTTON_8;
- }
-
- if( isset( $args['post_total_patronage_level'] ) AND $args['post_total_patronage_level'] > 0 ) {
- $label = PATREON_TEXT_OVER_BUTTON_9;
-
- // Double condition - override the text
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- $label = PATREON_TEXT_OVER_BUTTON_10;
- }
-
- }
-
- }
-
- if ( isset( $args['reason'] ) AND $args['reason'] == 'not_active_patron_at_post_date' ) {
-
- $label = PATREON_TEXT_OVER_BUTTON_8;
-
- if( isset( $args['post_total_patronage_level'] ) AND $args['post_total_patronage_level'] > 0 ) {
- $label = PATREON_TEXT_OVER_BUTTON_14;
- }
-
- }
-
- // We init vars so picky users will not see undefined var/index errors:
-
- $post_total_patronage_level = '';
- if( isset( $args['post_total_patronage_level'] ) ) {
- $post_total_patronage_level = $args['post_total_patronage_level'];
- }
-
- // Get creator url
-
- $creator_url = get_option( 'patreon-creator-url', false );
- $creator_url = apply_filters( 'ptrn/creator_profile_link_in_text_over_interface', $creator_url );
-
- $utm_content = 'creator_profile_link_in_text_over_interface';
-
- $filterable_utm_params = 'utm_term=&utm_content=' . $utm_content;
- $filterable_utm_params = apply_filters( 'ptrn/utm_params_for_creator_profile_link_in_text_over_interface', $filterable_utm_params );
-
- // Set default for content url
- $content_url = site_url();
-
- // Override if content url exists
- if ( $post ) {
- $content_url = get_permalink( $post );
- }
-
- $utm_params = 'utm_source=' . urlencode( $content_url ) . '&utm_medium=patreon_wordpress_plugin&utm_campaign=' . get_option( 'patreon-campaign-id' ) . '&' . $filterable_utm_params;
-
- // Simple check to see if creator url has ? (for non vanity urls)
- $append_with = '?';
- if ( strpos( $creator_url, '?' ) !== false ) {
- $append_with = '&';
- }
-
- $creator_url .= $append_with . $utm_params;
-
- if ( get_option( 'patreon-creator-has-tiers', 'yes' ) == 'yes' ) {
-
- // Get Patreon creator tiers
-
- $tiers = get_option( 'patreon-creator-tiers', false );
-
- foreach( $tiers['included'] as $key => $value ) {
-
- // If its not a reward element, continue, just to make sure
-
- if(
- !isset( $tiers['included'][$key]['type'] )
- OR ( $tiers['included'][$key]['type'] != 'reward'
- AND $tiers['included'][$key]['type'] != 'tier' )
- ) {
- continue;
- }
-
- $reward = $tiers['included'][$key];
-
- // Special conditions for label for element 0, which is 'everyone' and '1, which is 'patron only'
-
- if ( $reward['id'] == -1 ) {
- $tier_title = PATREON_TEXT_EVERYONE;
- }
- if ( $reward['id'] == 0 ) {
- $tier_title = PATREON_TEXT_ANY_PATRON;
- }
-
- if ( ( $reward['attributes']['amount_cents'] / 100 ) >= $patreon_level ) {
-
-
- // Matching level was present, but now found. Set selected and toggle flag.
- // selected = selected for XHTML compatibility
-
- // Use title if it exists, description if it does not.
-
- if ( isset( $reward['attributes']['title'] ) ) {
- $tier_title = $reward['attributes']['title'];
- }
-
- if ( $tier_title == '' ) {
-
- /// Detect if this is an old non bas64 desc
- if (base64_decode($reward['attributes']['description'], true) === false) {
- $tier_title = $reward['attributes']['description'];
- }
- else {
- $tier_title = base64_decode( $reward['attributes']['description'] );
- }
- }
-
- // If the title is too long, snip it
- if ( strlen( $tier_title ) > 23 ) {
- $tier_title = substr( $tier_title , 0 , 23 ) .'...';
- }
-
- $tier_title = '"' . $tier_title . '"';
-
- break;
- }
-
- }
-
- }
- else {
- // Creator has no tiers, possibly lite plan. Override text here.
-
- $tier_title = '$'.$patreon_level;
- }
-
- // Exception - when content is locked for 'any patron' and the user is not a patron, the interface text shows $0.01. For this special case, check and manipulate the chosen label to avoid this:
-
- if ( $label == PATREON_TEXT_OVER_BUTTON_1 AND $patreon_level == 0.01 AND !$is_patron ) {
- $label = PATREON_TEXT_OVER_BUTTON_15;
- }
-
- $label = apply_filters( 'ptrn/label_text_over_universal_button_raw', $label, $args['reason'], $user_logged_into_patreon, $is_patron, $args, $post, $creator_full_name, $patreon_level, $post_total_patronage_level, $creator_url, strip_tags( $tier_title ), self::patreonMakeCacheableFlowLink( $post ) );
-
- $label = str_replace( '%%creator_link%%', $creator_url, $label );
- $label = str_replace( '%%creator%%', $creator_full_name, $label );
- $label = str_replace( '%%pledgelevel%%', $patreon_level, $label );
- $label = str_replace( '%%tier_level%%', strip_tags( $tier_title ), $label );
- $label = str_replace( '%%flow_link%%', self::patreonMakeCacheableFlowLink( $post ), $label );
- $label = str_replace( '%%total_pledge%%', $post_total_patronage_level, $label );
-
- return $messages . apply_filters( 'ptrn/label_text_over_universal_button', str_replace( '%%pledgelevel%%',$patreon_level, $label ), $args['reason'], $user_logged_into_patreon, $is_patron, $patreon_level, $args );
-
- }
- public static function getLabelUnderUniversalButton( $patreon_level, $state = false, $post = false, $args = false ) {
-
- $label = '';
- $user_logged_into_patreon = self::isUserLoggedInPatreon();
- $is_patron = Patreon_Wordpress::isPatron( wp_get_current_user() );
- $messages = self::processPatreonMessages();
- $user = wp_get_current_user();
- $declined = Patreon_Wordpress::checkDeclinedPatronage( $user );
- $user_patronage = Patreon_Wordpress::getUserPatronage();
-
- // Get the creator or page name which is going to be used for interface text
-
- $creator_full_name = self::make_creator_name_for_interface();
-
- // Add 's to make it "creator's Patreon" when placed into label
-
- $creator_full_name .= "'s";
-
- // Override entire text if the creator set a custom site/creator name string:
-
- $patreon_custom_page_name = get_option( 'patreon-custom-page-name', false );
-
- if ( $patreon_custom_page_name AND $patreon_custom_page_name != '' ) {
- $creator_full_name = $patreon_custom_page_name;
- }
-
- // Get lock or not details if it is not given. If post id given, use it.
- if ( !isset( $args['lock'] ) ) {
-
- if ( isset( $args['post_id'] ) ) {
- $lock_or_not = Patreon_Wordpress::lock_or_not( $args['post_id'] );
- }
- else {
- $lock_or_not = Patreon_Wordpress::lock_or_not();
- }
- }
-
- // If lock or not details were not given, merge the incoming args with what we just got.
- if( $args AND is_array( $args ) AND !isset( $args['lock'] ) ) {
- $args = $lock_or_not + $args;
- }
-
- if ( $args['reason'] == 'user_not_logged_in' ) {
- $label = PATREON_TEXT_UNDER_BUTTON_2;
- }
-
- if ( $args['reason'] == 'not_a_patron' ) {
- $label = PATREON_TEXT_UNDER_BUTTON_2;
- }
-
- if ( $args['reason'] == 'payment_declined' ) {
- $label = PATREON_TEXT_UNDER_BUTTON_3;
- }
-
- if ( $args['reason'] == 'active_pledge_not_enough' ) {
- $label = PATREON_TEXT_UNDER_BUTTON_2;
- }
-
- if ( $args['reason'] == 'not_active_patron_at_post_date' ) {
- $label = PATREON_TEXT_UNDER_BUTTON_2;
- }
-
- $post_total_patronage_level = '';
- if( isset( $args['post_total_patronage_level'] ) ) {
- $post_total_patronage_level = $args['post_total_patronage_level'];
- }
-
- $flow_link = self::patreonMakeCacheableFlowLink();
-
- // Change with login link if user is not logged in - this gives non logged in patrons an easy way to login/refresh the content without having to go to pledge flow again
-
- if ( $args['reason'] == 'user_not_logged_in' ) {
- $flow_link = self::patreonMakeCacheableLoginLink();
- }
-
- $label = str_replace( '%%creator%%', $creator_full_name, $label );
- $label = str_replace( '%%pledgelevel%%', $patreon_level, $label );
- $label = str_replace( '%%flow_link%%', $flow_link, $label );
- $label = str_replace( '%%total_pledge%%', $post_total_patronage_level, $label );
-
- return apply_filters( 'ptrn/label_text_under_universal_button', $label, $args['reason'], $user_logged_into_patreon, $is_patron, $patreon_level, $state, $args);
-
- }
- public static function showPatreonMessages() {
-
- echo self::processPatreonMessages();
-
- }
- public static function processPatreonMessages() {
-
- $patreon_error = '';
- if ( isset( $_REQUEST['patreon_error'] ) ) {
-
- // If any specific error message is sent from Patreon, prepare it
- $patreon_error = ' - Patreon returned: ' . preg_replace("/[^A-Za-z0-9 ]/", '', $_REQUEST['patreon_error'] );
-
- }
-
- if ( isset( $_REQUEST['patreon_message'] ) ) {
-
- return '
', $href );
-
- }
- public static function lock_this_post( $post = false ) {
-
- if ( !$post ) {
- global $post;
- }
-
- // Just bail out if this is not the main query for content
- if ( !is_main_query() ) {
- return false;
- }
-
- $post_types = get_post_types( array( 'public'=>true ), 'names' );
-
- if ( in_array( get_post_type( $post ), $post_types ) ) {
-
- $exclude = array(
- );
-
- // Enables 3rd party plugins to modify the post types excluded from locking
- $exclude = apply_filters( 'ptrn/filter_excluded_posts', $exclude );
-
- if ( in_array( get_post_type( $post ), $exclude ) ) {
- return false;
- }
-
- // First check if entire site is locked, get the level for locking.
-
- $patreon_level = get_option( 'patreon-lock-entire-site', false );
-
- // Check if specific level is given for this post:
-
- $post_level = get_post_meta( $post->ID, 'patreon-level', true );
-
- // get post meta returns empty if no value is found. If so, set the value to 0.
-
- if ( $post_level == '' ) {
- $post_level = 0;
- }
-
- // Check if both post level and site lock level are set to 0 or nonexistent. If so return normal content.
-
- if( $post_level == 0
- && ( !$patreon_level
- || $patreon_level == 0 )
- ) {
- return false;
- }
-
- // If we are at this point, then this post is protected.
-
- // Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
- // It is independent of the plugin load order since it checks if it is defined.
- // It can be defined by any plugin until right before the_content filter is run.
-
- if ( apply_filters( 'ptrn/bypass_filtering', defined( 'PATREON_BYPASS_FILTERING' ) ) ) {
- return false;
+ }
+
+ public static function getLabelOverUniversalButton($patreon_level, $args = false)
+ {
+ $label = PATREON_TEXT_OVER_BUTTON_1;
+ $user_logged_into_patreon = self::isUserLoggedInPatreon();
+ $is_patron = Patreon_Wordpress::isPatron(wp_get_current_user());
+ $messages = self::processPatreonMessages();
+ $user = wp_get_current_user();
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+ $user_patronage = Patreon_Wordpress::getUserPatronage();
+
+ // Get the creator or page name which is going to be used for interface text
+
+ $creator_full_name = self::make_creator_name_for_interface();
+
+ // Add 's to make it "creator's Patreon" when placed into label
+
+ $creator_full_name .= "'s";
+
+ // Override entire text if the creator set a custom site/creator name string:
+
+ $patreon_custom_page_name = get_option('patreon-custom-page-name', false);
+
+ if ($patreon_custom_page_name and '' != $patreon_custom_page_name) {
+ $creator_full_name = $patreon_custom_page_name;
+ }
+
+ // Get lock or not details if it is not given. If post id given, use it.
+ if (!isset($args['lock'])) {
+ if (isset($args['post_id'])) {
+ $lock_or_not = Patreon_Wordpress::lock_or_not($args['post_id']);
+ } else {
+ $lock_or_not = Patreon_Wordpress::lock_or_not();
+ }
+ }
+
+ // If lock or not details were not given, merge the incoming args with what we just got.
+ if ($args and is_array($args) and !isset($args['lock'])) {
+ $args = $lock_or_not + $args;
+ }
+
+ // If no args given, just feed lock or not:
+
+ if (!isset($args)) {
+ $args = $lock_or_not;
+ }
+
+ $post_id = false;
+
+ if (isset($args['post_id'])) {
+ $post = get_post($args['post_id']);
+ } else {
+ global $post;
+ }
+
+ if (isset($args['reason']) and 'user_not_logged_in' == $args['reason']) {
+ $label = PATREON_TEXT_OVER_BUTTON_1;
+
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ $label = PATREON_TEXT_OVER_BUTTON_7;
+ }
+
+ if (isset($args['post_total_patronage_level']) and $args['post_total_patronage_level'] > 0) {
+ $label = PATREON_TEXT_OVER_BUTTON_12;
+
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ // Double condition - has both active patron and total patronage conditions. Override text
+ $label = PATREON_TEXT_OVER_BUTTON_13;
+ }
+ }
+ }
+
+ if (isset($args['reason']) and 'not_a_patron' == $args['reason']) {
+ $label = PATREON_TEXT_OVER_BUTTON_1;
+
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ $label = PATREON_TEXT_OVER_BUTTON_7;
+ }
+
+ if (isset($args['post_total_patronage_level']) and $args['post_total_patronage_level'] > 0) {
+ $label = PATREON_TEXT_OVER_BUTTON_9;
+
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ // Double condition - has both active patron and total patronage conditions. Override text
+ $label = PATREON_TEXT_OVER_BUTTON_10;
+ }
+ }
+ }
+
+ if (isset($args['reason']) and 'payment_declined' == $args['reason']) {
+ $label = PATREON_TEXT_OVER_BUTTON_3;
+ }
+
+ if (isset($args['reason']) and 'active_pledge_not_enough' == $args['reason']) {
+ $label = PATREON_TEXT_OVER_BUTTON_1A;
+
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ $label = PATREON_TEXT_OVER_BUTTON_8;
+ }
+
+ if (isset($args['post_total_patronage_level']) and $args['post_total_patronage_level'] > 0) {
+ $label = PATREON_TEXT_OVER_BUTTON_9;
+
+ // Double condition - override the text
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ $label = PATREON_TEXT_OVER_BUTTON_10;
+ }
+ }
+ }
+
+ if (isset($args['reason']) and 'not_active_patron_at_post_date' == $args['reason']) {
+ $label = PATREON_TEXT_OVER_BUTTON_8;
+
+ if (isset($args['post_total_patronage_level']) and $args['post_total_patronage_level'] > 0) {
+ $label = PATREON_TEXT_OVER_BUTTON_14;
+ }
+ }
+
+ // We init vars so picky users will not see undefined var/index errors:
+
+ $post_total_patronage_level = '';
+ if (isset($args['post_total_patronage_level'])) {
+ $post_total_patronage_level = $args['post_total_patronage_level'];
+ }
+
+ // Get creator url
+
+ $creator_url = get_option('patreon-creator-url', false);
+ $creator_url = apply_filters('ptrn/creator_profile_link_in_text_over_interface', $creator_url);
+
+ $utm_content = 'creator_profile_link_in_text_over_interface';
+
+ $filterable_utm_params = 'utm_term=&utm_content='.$utm_content;
+ $filterable_utm_params = apply_filters('ptrn/utm_params_for_creator_profile_link_in_text_over_interface', $filterable_utm_params);
+
+ // Set default for content url
+ $content_url = site_url();
+
+ // Override if content url exists
+ if ($post) {
+ $content_url = get_permalink($post);
+ }
+
+ $utm_params = 'utm_source='.urlencode($content_url).'&utm_medium=patreon_wordpress_plugin&utm_campaign='.get_option('patreon-campaign-id').'&'.$filterable_utm_params;
+
+ // Simple check to see if creator url has ? (for non vanity urls)
+ $append_with = '?';
+ if (false !== strpos($creator_url, '?')) {
+ $append_with = '&';
+ }
+
+ $creator_url .= $append_with.$utm_params;
+
+ if ('yes' == get_option('patreon-creator-has-tiers', 'yes')) {
+ // Get Patreon creator tiers
+
+ $tiers = get_option('patreon-creator-tiers', false);
+
+ foreach ($tiers['included'] as $key => $value) {
+ // If its not a reward element, continue, just to make sure
+
+ if (
+ !isset($tiers['included'][$key]['type'])
+ or ('reward' != $tiers['included'][$key]['type']
+ and 'tier' != $tiers['included'][$key]['type'])
+ ) {
+ continue;
+ }
+
+ $reward = $tiers['included'][$key];
+
+ // Special conditions for label for element 0, which is 'everyone' and '1, which is 'patron only'
+
+ if (-1 == $reward['id']) {
+ $tier_title = PATREON_TEXT_EVERYONE;
+ }
+ if (0 == $reward['id']) {
+ $tier_title = PATREON_TEXT_ANY_PATRON;
+ }
+
+ if (($reward['attributes']['amount_cents'] / 100) >= $patreon_level) {
+ // Matching level was present, but now found. Set selected and toggle flag.
+ // selected = selected for XHTML compatibility
+
+ // Use title if it exists, description if it does not.
+
+ if (isset($reward['attributes']['title'])) {
+ $tier_title = $reward['attributes']['title'];
+ }
+
+ if ('' == $tier_title) {
+ // / Detect if this is an old non bas64 desc
+ if (false === base64_decode($reward['attributes']['description'], true)) {
+ $tier_title = $reward['attributes']['description'];
+ } else {
+ $tier_title = base64_decode($reward['attributes']['description']);
+ }
+ }
+
+ // If the title is too long, snip it
+ if (strlen($tier_title) > 23) {
+ $tier_title = substr($tier_title, 0, 23).'...';
+ }
+
+ $tier_title = '"'.$tier_title.'"';
+
+ break;
+ }
+ }
+ } else {
+ // Creator has no tiers, possibly lite plan. Override text here.
+
+ $tier_title = '$'.$patreon_level;
+ }
+
+ // Exception - when content is locked for 'any patron' and the user is not a patron, the interface text shows $0.01. For this special case, check and manipulate the chosen label to avoid this:
+
+ if (PATREON_TEXT_OVER_BUTTON_1 == $label and 0.01 == $patreon_level and !$is_patron) {
+ $label = PATREON_TEXT_OVER_BUTTON_15;
+ }
+
+ $label = apply_filters('ptrn/label_text_over_universal_button_raw', $label, $args['reason'], $user_logged_into_patreon, $is_patron, $args, $post, $creator_full_name, $patreon_level, $post_total_patronage_level, $creator_url, strip_tags($tier_title), self::patreonMakeCacheableFlowLink($post));
+
+ $label = str_replace('%%creator_link%%', $creator_url, $label);
+ $label = str_replace('%%creator%%', $creator_full_name, $label);
+ $label = str_replace('%%pledgelevel%%', $patreon_level, $label);
+ $label = str_replace('%%tier_level%%', strip_tags($tier_title), $label);
+ $label = str_replace('%%flow_link%%', self::patreonMakeCacheableFlowLink($post), $label);
+ $label = str_replace('%%total_pledge%%', $post_total_patronage_level, $label);
+
+ return $messages.apply_filters('ptrn/label_text_over_universal_button', str_replace('%%pledgelevel%%', $patreon_level, $label), $args['reason'], $user_logged_into_patreon, $is_patron, $patreon_level, $args);
+ }
+
+ public static function getLabelUnderUniversalButton($patreon_level, $state = false, $post = false, $args = false)
+ {
+ $label = '';
+ $user_logged_into_patreon = self::isUserLoggedInPatreon();
+ $is_patron = Patreon_Wordpress::isPatron(wp_get_current_user());
+ $messages = self::processPatreonMessages();
+ $user = wp_get_current_user();
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+ $user_patronage = Patreon_Wordpress::getUserPatronage();
+
+ // Get the creator or page name which is going to be used for interface text
+
+ $creator_full_name = self::make_creator_name_for_interface();
+
+ // Add 's to make it "creator's Patreon" when placed into label
+
+ $creator_full_name .= "'s";
+
+ // Override entire text if the creator set a custom site/creator name string:
+
+ $patreon_custom_page_name = get_option('patreon-custom-page-name', false);
+
+ if ($patreon_custom_page_name and '' != $patreon_custom_page_name) {
+ $creator_full_name = $patreon_custom_page_name;
+ }
+
+ // Get lock or not details if it is not given. If post id given, use it.
+ if (!isset($args['lock'])) {
+ if (isset($args['post_id'])) {
+ $lock_or_not = Patreon_Wordpress::lock_or_not($args['post_id']);
+ } else {
+ $lock_or_not = Patreon_Wordpress::lock_or_not();
+ }
+ }
+
+ // If lock or not details were not given, merge the incoming args with what we just got.
+ if ($args and is_array($args) and !isset($args['lock'])) {
+ $args = $lock_or_not + $args;
+ }
+
+ if ('user_not_logged_in' == $args['reason']) {
+ $label = PATREON_TEXT_UNDER_BUTTON_2;
+ }
+
+ if ('not_a_patron' == $args['reason']) {
+ $label = PATREON_TEXT_UNDER_BUTTON_2;
+ }
+
+ if ('payment_declined' == $args['reason']) {
+ $label = PATREON_TEXT_UNDER_BUTTON_3;
+ }
+
+ if ('active_pledge_not_enough' == $args['reason']) {
+ $label = PATREON_TEXT_UNDER_BUTTON_2;
+ }
+
+ if ('not_active_patron_at_post_date' == $args['reason']) {
+ $label = PATREON_TEXT_UNDER_BUTTON_2;
+ }
+
+ $post_total_patronage_level = '';
+ if (isset($args['post_total_patronage_level'])) {
+ $post_total_patronage_level = $args['post_total_patronage_level'];
+ }
+
+ $flow_link = self::patreonMakeCacheableFlowLink();
+
+ // Change with login link if user is not logged in - this gives non logged in patrons an easy way to login/refresh the content without having to go to pledge flow again
+
+ if ('user_not_logged_in' == $args['reason']) {
+ $flow_link = self::patreonMakeCacheableLoginLink();
+ }
+
+ $label = str_replace('%%creator%%', $creator_full_name, $label);
+ $label = str_replace('%%pledgelevel%%', $patreon_level, $label);
+ $label = str_replace('%%flow_link%%', $flow_link, $label);
+ $label = str_replace('%%total_pledge%%', $post_total_patronage_level, $label);
+
+ return apply_filters('ptrn/label_text_under_universal_button', $label, $args['reason'], $user_logged_into_patreon, $is_patron, $patreon_level, $state, $args);
+ }
+
+ public static function showPatreonMessages()
+ {
+ echo self::processPatreonMessages();
+ }
+
+ public static function processPatreonMessages()
+ {
+ $patreon_error = '';
+ if (isset($_REQUEST['patreon_error'])) {
+ // If any specific error message is sent from Patreon, prepare it
+ $patreon_error = ' - Patreon returned: '.preg_replace('/[^A-Za-z0-9 ]/', '', $_REQUEST['patreon_error']);
+ }
+
+ if (isset($_REQUEST['patreon_message'])) {
+ return '
';
+ }
+
+ return '';
+ }
+
+ public static function patreonMakeUniversalButton($min_cents = false, $state = false, $post = false, $client_id = false, $args = false)
+ {
+ // This very customizable function takes numerous parameters to customize universal flow links and creates the desired link
+
+ // If no post is given, get the active post:
+
+ if (!$post) {
+ global $post;
+ }
+
+ // If it is a direct unlock request, unset the post
+
+ if (isset($args['direct_unlock'])) {
+ $post = false;
+
+ // Set the post to the id if it is given:
+ if (isset($args['post_id']) and '' != $args['post_id']) {
+ $post = get_post($args['post_id']);
+ }
+ }
+
+ $send_pledge_level = 1;
+
+ if ($min_cents) {
+ $send_pledge_level = $min_cents * 100;
+ }
+
+ if (!$client_id) {
+ $client_id = get_option('patreon-client-id', false);
+ }
+
+ // If we werent given any state vars to send, initialize the array
+ if (!$state) {
+ $state = [];
+ }
+
+ // Get the address of the current page, and save it as final redirect uri.
+ // Start with home url for redirect. If post is valid, get permalink.
+
+ $final_redirect = home_url();
+
+ if ($post) {
+ $final_redirect = get_permalink($post->ID);
+ }
+
+ $state['final_redirect_uri'] = $final_redirect;
+
+ // $href = self::MakeUniversalFlowLink($send_pledge_level,$state,$client_id);
+
+ // We changed the above universal flow link maker to a function which will create cache-able links
+ // Some of the vars in current function which the earlier function used may not be needed now - clean up later #REVISIT
+
+ $flow_link_args = [];
+
+ if (isset($args['direct_unlock'])) {
+ // If direct unlock request is given, set cacheable flow link vars.
+ $flow_link_args['direct_unlock'] = $args['direct_unlock'];
+ $flow_link_args['redirect'] = $args['redirect'];
+
+ if (isset($args['post_id'])) {
+ $flow_link_args['post_id'] = $args['post_id'];
+ }
+ }
+
+ $href = self::patreonMakeCacheableFlowLink($post, $flow_link_args);
+ $label_text = self::patreonMakeUniversalButtonLabel();
+ $button = self::patreonMakeUniversalButtonImage($label_text);
+
+ if (isset($args['is_feed'])) {
+ return '';
+ }
+
+ return apply_filters('ptrn/patron_button', ''.$button.'', $min_cents);
+ }
+
+ public static function patreonMakeCacheableLoginLink()
+ {
+ global $wp;
+
+ $current_url = home_url($wp->request);
+ $flow_link = site_url().'/patreon-flow/?patreon-login=yes&patreon-final-redirect='.urlencode($current_url);
+
+ return $flow_link;
+ }
+
+ public static function patreonMakeCacheableFlowLink($post = false, $args = false)
+ {
+ if (!$post) {
+ global $post;
+ }
+
+ $unlock_post_id = '';
+
+ if (isset($post) and isset($post->ID)) {
+ $unlock_post_id = $post->ID;
+ }
+
+ $flow_link = site_url().'/patreon-flow/?patreon-unlock-post='.$unlock_post_id;
+
+ if (isset($args['direct_unlock'])) {
+ $append_post_id = '';
+ // If direct unlock request is given, override all :
+
+ if (isset($args['post_id'])) {
+ $append_post_id = '&patreon-post-id='.$args['post_id'];
+ }
+
+ $flow_link = site_url().'/patreon-flow/?patreon-direct-unlock='.$args['direct_unlock'].$append_post_id.'&patreon-redirect='.urlencode(base64_encode($args['redirect']));
+ }
+
+ return $flow_link;
+ }
+
+ public static function patreonMakeCacheableImageFlowLink($attachment_id, $post_id = false)
+ {
+ if (!$post_id) {
+ global $post;
+ }
+
+ $unlock_post_id = $post_id;
+
+ if (!$unlock_post_id and (isset($post) and isset($post->ID))) {
+ $unlock_post_id = $post->ID;
+ }
+
+ $flow_link = site_url().'/patreon-flow/?patreon-unlock-post='.$unlock_post_id.'&patreon-unlock-image='.$attachment_id;
+
+ return $flow_link;
+ }
+
+ public static function patreonMakeUniversalButtonImage($label)
+ {
+ return '
'.$label.'
';
+ }
+
+ public static function MakeUniversalFlowLink($pledge_level, $state = false, $client_id = false, $post = false, $args = false)
+ {
+ if (!$post and !isset($args['direct_unlock'])) {
+ global $post;
+ }
+
+ if (!$client_id) {
+ $client_id = get_option('patreon-client-id', false);
+ }
+
+ // If we werent given any state vars to send, initialize the array
+ if (!$state) {
+ $state = [];
+
+ // Get the address of the current page, and save it as final redirect uri.
+ // Start with home url for redirect. If post is valid, get permalink.
+
+ $final_redirect = home_url();
+
+ if ($post) {
+ $final_redirect = get_permalink($post->ID);
+ }
+
+ // We dont want to redirect people to login page. So check if we are there.
+ if ('wp-login.php' === $GLOBALS['pagenow']) {
+ $final_redirect = site_url();
+ }
+
+ $state['final_redirect_uri'] = $final_redirect;
+ }
+
+ $redirect_uri = site_url().'/patreon-authorization/';
+ $v2_params = '&scope=identity%20identity[email]';
+
+ $send_post_id = false;
+
+ if ($post) {
+ $send_post_id = $post->ID;
+ }
+
+ $pledge_level = apply_filters('ptrn/patron_link_pledge_level', $pledge_level, $send_post_id, $args);
+
+ $href = 'https://'.PATREON_HOST.'/oauth2/become-patron?response_type=code&min_cents='.$pledge_level.'&client_id='.$client_id.$v2_params.'&redirect_uri='.$redirect_uri.'&state='.urlencode(base64_encode(json_encode($state)));
+
+ // 3rd party dev goodie! Apply custom filters so they can manipulate the url:
+
+ $href = apply_filters('ptrn/patron_link', $href);
+ $utm_content = 'post_unlock_button';
+
+ if (isset($args) and isset($args['link_interface_item']) and 'image_unlock_button' == $args['link_interface_item']) {
+ $utm_content = 'image_unlock_button';
+ }
+
+ if (isset($args) and isset($args['link_interface_item']) and 'direct_unlock_button' == $args['link_interface_item']) {
+ $utm_content = 'direct_unlock_button';
+ }
+
+ $filterable_utm_params = 'utm_term=&utm_content='.$utm_content;
+ $filterable_utm_params = apply_filters('ptrn/utm_params_for_patron_link', $filterable_utm_params);
+
+ // Set default for content url
+ $content_url = site_url();
+
+ // Override if content url exists
+ if ($post) {
+ $content_url = get_permalink($post);
+ }
+
+ $utm_params = 'utm_source='.urlencode($content_url).'&utm_medium=patreon_wordpress_plugin&utm_campaign='.get_option('patreon-campaign-id').'&'.$filterable_utm_params;
+
+ return $href.'&'.$utm_params;
+ }
+
+ public static function patreonMakeUniversalButtonLabel()
+ {
+ // Default label:
+
+ $label = apply_filters('ptrn/universal_button_label', PATREON_TEXT_UNLOCK_WITH_PATREON);
+ $user_logged_into_patreon = self::isUserLoggedInPatreon();
+ $is_patron = Patreon_Wordpress::isPatron();
+
+ // Change this after getting info about which value confirms user's payment is declined. The only different button label is for that condition.
+
+ return $label;
+ }
+
+ public static function isUserLoggedInPatreon()
+ {
+ if (-1 != self::$current_user_logged_into_patreon) {
+ return self::$current_user_logged_into_patreon;
+ }
+
+ $user_logged_into_patreon = false;
+
+ if (is_user_logged_in()) {
+ // User is logged into WP. Check if user has valid patreon data :
+
+ $user = wp_get_current_user();
+
+ if ($user) {
+ // REVISIT - whats below may be a concern - it connects to API to check for valid user for every generation of button. If we could cache it it would be better
+ $user_response = Patreon_Wordpress::getPatreonUser($user);
+
+ if ($user_response) {
+ // This is a user logged into Patreon.
+ $user_logged_into_patreon = true;
+ }
}
-
- if ( current_user_can( 'manage_options' ) ) {
- // Here we need to put a notification to admins so they will know they can see the content because they are admin_login_with_patreon_disabled
- return false;
- }
-
- // Passed checks. If post level is not 0, override patreon level and hence site locking value with post's. This will allow Creators to lock entire site and then set a different value for individual posts for access. Ie, site locking is $5, but one particular post can be $10, and it will require $10 to see.
-
- if ( $post_level != 0 ) {
- $patreon_level = $post_level;
- }
-
- $user = wp_get_current_user();
- $user_pledge_relationship_start = Patreon_Wordpress::get_user_pledge_relationship_start();
- $user_patronage = Patreon_Wordpress::getUserPatronage();
- $user_lifetime_patronage = Patreon_Wordpress::get_user_lifetime_patronage();
- $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
-
- // Check if post was set for active patrons only
- $patreon_active_patrons_only = get_post_meta( $post->ID, 'patreon-active-patrons-only', true );
-
- // Check if specific total patronage is given for this post:
- $post_total_patronage_level = get_post_meta( $post->ID, 'patreon-total-patronage-level', true );
-
- $hide_content = true;
-
- if ( !( $user_patronage == false
- || $user_patronage < ( $patreon_level * 100 )
- || $declined ) ) {
-
- $hide_content = false;
-
- // Seems valid patron. Lets see if active patron option was set and the user fulfills it
-
- if ( $patreon_active_patrons_only == '1'
- AND $user_pledge_relationship_start >= strtotime( get_the_date( '', $post->ID ) ) ) {
-
- $hide_content = true;
-
- }
- }
-
-
- if ( $post_total_patronage_level !='' AND $post_total_patronage_level > 0 ) {
-
- // Total patronage set if user has lifetime patronage over this level, we let him see the content
- if ( $user_lifetime_patronage >= $post_total_patronage_level * 100 ) {
- $hide_content = false;
- }
-
- }
-
-
- if ( $hide_content ) {
-
- // protect content from user
-
- // Get client id
-
- $client_id = get_option( 'patreon-client-id', false );
-
- // // If client id exists. Do the banner. If not, no point in protecting since we wont be able to send people to patronage. If so dont modify normal content.
-
- if ( $client_id ) {
- return $patreon_level;
- }
-
- }
-
- // If we are here, it means post is protected, user is patron, patronage is valid. Slap the post footer:
-
- return false;
- }
-
- // Return content in all other cases
- return false;
-
- }
- public static function protectContentFromUsers( $content, $post_id = false ) {
-
- // This function receives content and optionally post id.
-
- // If content is received but no post id, the function acts to lock the existing post. In this case it can be hooked to the_content filter
-
- // If post id is given, then the function acts as a stand alone function to generate a lock interface and return the interface to whatever called it (another function or routine)
-
- // This way it can be used to lock posts if hooked to the_content and post_id is not passed and to decide lock and if so, generate lock interface and return it if post_id is passed.
-
- // If post id is not given, try to get it from post global
- if ( !$post_id ) {
-
- global $post;
-
- if ( isset( $post ) AND is_object( $post ) ) {
- $post_id = $post->ID;
- }
-
- }
-
- // Allow addons to override this function - this will bypass this function, but also will allow addons to apply this function's filters in their own gating function to keep compatibility with other addons
-
- $override_content_filtering = array();
-
- $override_content_filtering = apply_filters( 'ptrn/override_content_filtering', $content, $post_id );
-
- if ( is_array($override_content_filtering) AND isset( $override_content_filtering['override'] ) ) {
- return $override_content_filtering['content'];
- }
-
- // Just bail out if this is not the main query for content and there still isnt a post id
- if ( !is_main_query() AND !$post_id ) {
- return $content;
- }
-
- // Now send the post id to locking decision function
-
- $lock_or_not = Patreon_Wordpress::lock_or_not($post_id);
-
- // An array with args should be returned
- $hide_content = false;
- $patreon_level = 0;
- $user_patronage = 0;
-
- if ( isset( $lock_or_not['lock'] ) ) {
- $hide_content = $lock_or_not['lock'];
- }
- if ( isset( $lock_or_not['patreon_level'] ) ) {
- $patreon_level = $lock_or_not['patreon_level'];
- }
- if ( isset( $lock_or_not['user_active_pledge'] ) ) {
- $user_patronage = $lock_or_not['user_active_pledge'];
- }
-
- $lock_or_not['post_id'] = $post_id;
-
- if ( $hide_content ) {
-
- // protect content from user
-
- // Get client id
-
- $client_id = get_option( 'patreon-client-id', false );
-
- // // If client id exists. Do the banner. If not, no point in protecting since we wont be able to send people to patronage. If so dont modify normal content.
-
- if ( $client_id ) {
-
- $content = self::displayPatreonCampaignBanner( $patreon_level, $lock_or_not );
- $content = apply_filters( 'ptrn/post_content', $content, $patreon_level, $user_patronage, $lock_or_not );
-
- return $content;
-
- }
- }
-
- if ( !$hide_content AND $lock_or_not['reason'] == 'post_is_public' ) {
- // This is not a locked post. Return content without any footer
- return $content;
- }
-
- // If is an admin, just return the content with an admin-related notice
-
- if( current_user_can( 'manage_options' ) ) {
- return $content . self::MakeAdminPostFooter( $patreon_level );
- }
-
- // If we are here, it means post is protected, user is patron, patronage is valid. Slap the post footer:
- return $content . self::MakeValidPatronFooter( $patreon_level, $user_patronage, $lock_or_not );
-
- }
- public static function MakeAdminPostFooter( $patreon_level ) {
-
- return '
';
-
- }
- public static function MakeValidPatronFooter( $patreon_level, $user_patronage, $args = false ) {
-
- // Creates conditional text for footer shown to valid patrons
-
- $label = PATREON_VALID_PATRON_POST_FOOTER_TEXT;
- $user_logged_into_patreon = self::isUserLoggedInPatreon();
- $is_patron = Patreon_Wordpress::isPatron( wp_get_current_user() );
- $messages = self::processPatreonMessages();
- $user = wp_get_current_user();
- $declined = Patreon_Wordpress::checkDeclinedPatronage( $user );
- $user_patronage = Patreon_Wordpress::getUserPatronage();
-
- // Get the creator or page name which is going to be used for interface text
-
- $creator_full_name = self::make_creator_name_for_interface();
-
- // Add 's to make it "creator's Patreon" when placed into label
-
- $creator_full_name .= "'s";
-
- // Override entire text if the creator set a custom site/creator name string:
-
- $patreon_custom_page_name = get_option( 'patreon-custom-page-name', false );
-
- if ( $patreon_custom_page_name AND $patreon_custom_page_name != '' ) {
- $creator_full_name = $patreon_custom_page_name;
- }
-
- // Get lock or not details if it is not given. If post id given, use it.
- if ( !isset( $args['lock'] ) ) {
-
- if ( isset( $args['post_id'] ) ) {
- $lock_or_not = Patreon_Wordpress::lock_or_not( $args['post_id'] );
- }
- else {
- $lock_or_not = Patreon_Wordpress::lock_or_not();
- }
- }
-
- // If lock or not details were not given, merge the incoming args with what we just got.
- if( $args AND is_array( $args ) AND !isset( $args['lock'] ) ) {
- $args = $lock_or_not + $args;
- }
-
- // If no args given, just feed lock or not:
-
- if ( !isset( $args ) ) {
- $args = $lock_or_not;
- }
-
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- $label = PATREON_TEXT_OVER_BUTTON_11;
- }
-
- if( isset( $args['post_total_patronage_level'] ) AND $args['post_total_patronage_level'] > 0 ) {
-
- $label = PATREON_TEXT_OVER_BUTTON_12;
-
- // Double condition - override the text
- if ( isset( $args['patreon_active_patrons_only'] ) AND $args['patreon_active_patrons_only'] == 1 ) {
- $label = PATREON_TEXT_OVER_BUTTON_10;
- }
-
- }
-
-
- if ( get_option( 'patreon-creator-has-tiers', 'yes' ) == 'yes' ) {
-
- // Get Patreon creator tiers
-
- $tiers = get_option( 'patreon-creator-tiers', false );
-
- foreach( $tiers['included'] as $key => $value ) {
-
- // If its not a reward element, continue, just to make sure
-
- if(
- !isset( $tiers['included'][$key]['type'] )
- OR ( $tiers['included'][$key]['type'] != 'reward'
- AND $tiers['included'][$key]['type'] != 'tier' )
- ) {
- continue;
- }
-
- $reward = $tiers['included'][$key];
-
- // Special conditions for label for element 0, which is 'everyone' and '1, which is 'patron only'
-
- if ( $reward['id'] == -1 ) {
- $tier_title = PATREON_TEXT_EVERYONE;
- }
- if ( $reward['id'] == 0 ) {
- $tier_title = PATREON_TEXT_ANY_PATRON;
- }
-
- if ( ( $reward['attributes']['amount_cents'] / 100 ) >= $patreon_level ) {
-
- // Matching level was present, but now found. Set selected and toggle flag.
- // selected = selected for XHTML compatibility
-
- // Use title if it exists, description if it does not.
-
- if ( isset( $reward['attributes']['title'] ) ) {
- $tier_title = $reward['attributes']['title'];
- }
-
- if ( $tier_title == '' ) {
-
- /// Detect if this is an old non bas64 desc
- if (base64_decode($reward['attributes']['description'], true) === false) {
- $tier_title = $reward['attributes']['description'];
- }
- else {
- $tier_title = base64_decode( $reward['attributes']['description'] );
- }
- }
-
- // If the title is too long, snip it
- if ( strlen( $tier_title ) > 23 ) {
- $tier_title = substr( $tier_title , 0 , 23 ) .'...';
- }
-
- $tier_title = '"' . $tier_title . '"';
-
- break;
- }
-
- }
- }
- else {
- $tier_title = '';
- }
-
- // Get creator url
-
- $creator_url = get_option( 'patreon-creator-url', false );
- $creator_url = apply_filters( 'ptrn/creator_profile_link_in_valid_patron_footer', $creator_url );
-
- $utm_content = 'creator_profile_link_in_valid_patron_footer';
-
- $filterable_utm_params = 'utm_term=&utm_content=' . $utm_content;
- $filterable_utm_params = apply_filters( 'ptrn/utm_params_for_creator_profile_link_in_valid_patron_footer', $filterable_utm_params );
-
- global $post;
-
- if ( isset( $args['post_id'] ) ) {
- $post = get_post( $args['post_id'] );
- }
-
- // Set default for content url
- $content_url = site_url();
-
- // Override if content url exists
- if ( $post ) {
- $content_url = get_permalink( $post );
- }
-
- $utm_params = 'utm_source=' . urlencode( $content_url ) . '&utm_medium=patreon_wordpress_plugin&utm_campaign=' . get_option( 'patreon-campaign-id' ) . '&' . $filterable_utm_params;
-
- // Simple check to see if creator url has ? (for non vanity urls)
- $append_with = '?';
- if ( strpos( $creator_url, '?' ) !== false ) {
- $append_with = '&';
- }
-
- $creator_url .= $append_with . $utm_params;
-
- $label = str_replace( '%%creator_link%%', $creator_url, $label );
- $label = str_replace( '%%tier_level%%', strip_tags( $tier_title ), $label );
- $label = str_replace( '%%creator%%', $creator_full_name, $label );
- $label = str_replace( '%%pledgelevel%%', $patreon_level, $label );
- $label = str_replace( '%%flow_link%%', self::patreonMakeCacheableFlowLink(), $label );
- if ( isset( $args['post_total_patronage_level'] ) ) {
- $label = str_replace( '%%total_pledge%%', $args['post_total_patronage_level'], $label );
- }
-
- $post_footer = str_replace( '%%pledgelevel%%', $patreon_level, apply_filters( 'ptrn/valid_patron_footer_text', $label , $patreon_level, $user_patronage ) );
- $post_footer = apply_filters( 'ptrn/valid_patron_processed_message', $label, $patreon_level, $user_patronage );
-
- $post_footer =
- '
'.
- $post_footer .
- '
';
-
- return apply_filters( 'ptrn/valid_patron_final_footer', $post_footer, $args['reason'], $patreon_level, $user_patronage, $args );
-
- }
- public static function displayPatreonLoginButtonInLoginForm() {
-
- // For displaying login button in the form - wrapper
-
- // Check if the login button hide option is on
- if ( !get_option( 'patreon-hide-login-button', false ) ) {
- echo '
' . self::showPatreonLoginButton() . '
';
- }
-
- }
- public static function showPatreonLoginButton( $args = array() ) {
-
- // Set default login image
- $log_in_img = PATREON_PLUGIN_ASSETS . '/img/patreon login@1x.png';
-
- $user = wp_get_current_user();
- $user_patreon_id = '';
-
- if ( $user AND isset( $user->ID ) ) {
- $user_patreon_id = get_user_meta( $user->ID, 'patreon_user_id', true );
- }
-
- if ( is_user_logged_in() AND $user_patreon_id == '' ) {
- // Logged in user without a connected Patreon account. Show connect button
- $log_in_img = PATREON_PLUGIN_ASSETS . '/img/patreon connect@1x.png';
- }
-
- // Override image if argument given
- if ( isset( $args['img'] ) ) {
- $log_in_img = $args['img'];
- }
-
- $client_id = get_option( 'patreon-client-id', false );
-
- if ( $client_id == false ) {
- return '';
- }
- $button = '';
- /* inline styles - prevent themes from overriding */
- $button .= '
- ';
-
- if ( isset( $_REQUEST['patreon-msg'] ) && $_REQUEST['patreon-msg'] == 'login_with_patreon' ) {
- $button .= '
You can now login with your WordPress username/password.
';
- } else {
- $button .= apply_filters( 'ptrn/login_button', '' );
- }
- return $button;
-
- }
- public static function LoginButtonShortcode( $args ) {
-
- // Check if this user connected his/her account to Patreon
-
- $user = wp_get_current_user();
- $user_patreon_id = '';
-
- if ( $user AND isset( $user->ID ) ) {
- $user_patreon_id = get_user_meta( $user->ID, 'patreon_user_id', true );
- }
-
- if ( !is_user_logged_in() OR ( is_user_logged_in() AND $user_patreon_id == '' ) ) {
- return Patreon_Frontend::showPatreonLoginButton();
- }
-
- }
- public static function show_patreon_avatar( $avatar, $id_or_email, $size, $default, $alt ) {
-
- // Checks if the user has a Patreon avatar saved, and returns that avatar in place of WP/site default
- $user = false;
-
- if ( is_numeric( $id_or_email ) ) {
-
- $id = (int) $id_or_email;
- $user = get_user_by( 'id' , $id );
-
- } elseif ( is_object( $id_or_email ) ) {
-
- if ( ! empty( $id_or_email->user_id ) ) {
- $id = (int) $id_or_email->user_id;
- $user = get_user_by( 'id' , $id );
- }
-
- } else {
- $user = get_user_by( 'email', $id_or_email );
- }
-
- if ( $user && is_object( $user ) ) {
-
- // Get user's Patreon avatar meta:
-
- $user_patreon_avatar = get_user_meta( $user->ID, 'patreon-avatar-url', true );
-
- if ( !$size OR !is_numeric($size) OR $size == 0 ) {
- // Set size to WP default 48 if no size was provided by WP installation for whatsoever reason
- $size = 48;
- }
-
- // Override avatar if there is a saved Patreon avatar
- if ( $user_patreon_avatar != '' ) {
- $avatar = "";
- }
- }
-
- return $avatar;
-
- }
- public static function make_creator_name_for_interface() {
-
- // This function decides which identifier (page name, creator full, first, last name or custom name) should be used for locked post interface text
-
- // Go ahead with a cascading series of fallbacks to make sure we have a creator name to use in the end
-
- $creator_interface_name = Patreon_Wordpress::get_page_name();
-
- if ( !$creator_interface_name OR $creator_interface_name == '' ) {
- $creator_interface_name = get_option( 'patreon-creator-first-name', false );
- }
-
- if ( !$creator_interface_name OR $creator_interface_name == '' ) {
- $creator_interface_name = 'this creator';
- }
-
- // We skipped using full name or surname because some creators may not want their full name exposed. If they want it, they can set it up in the plugin options
-
- // Return the value
-
- return $creator_interface_name;
- }
- public static function gate_custom_content( $patreon_level, $args = array() ) {
-
- // This function gates any part of a WP website using $ level. It is a wrapper function that is to be used as an easy version of custom locking code
-
- // Custom lock
-
- $user = wp_get_current_user();
-
- if ( isset( $args['user'] ) AND $args['user'] AND is_object( $args['user'] ) ) {
- $user = $args['user'];
- }
-
- // Check how much patronage the user has
- $user_patronage = Patreon_Wordpress::getUserPatronage( $user );
-
- // Check if user is a patron whose payment is declined
- $declined = Patreon_Wordpress::checkDeclinedPatronage( $user );
-
- // Set where we want to have the user land at - this can be anything. The below code sets it to this exact page. This page may be a category page, search page, a custom page url with custom parameters - anything
-
- global $wp;
-
- $redir = home_url( add_query_arg( $_GET, $wp->request ) );
-
- // If the user has less patronage than $patreon_level, or declined, and is not an admin-level user, lock the post.
-
- if ( ( $user_patronage < ( $patreon_level * 100) OR $declined ) AND !current_user_can( 'manage_options' ) ) {
-
- // Generate the unlock interface wherever it is
- return Patreon_Frontend::displayPatreonCampaignBanner(
- $patreon_level,
- array( 'direct_unlock' => $patreon_level,
- 'redirect' => $redir
- )
- );
- // the first param and direct_unlock param set the unlock value for this interface. Both need to be provided and be the same
- }
-
- // Here. Then patron is valid. Return false.
- return false;
-
- }
-
- public static function hide_ad_for_patrons( $patreon_level, $ad = '', $args = array() ) {
-
- // This function allows show/hiding any part of a WP website using $ level. It is a wrapper function that is to be used as an easy version of custom locking code
-
- // Custom lock
-
- $user = wp_get_current_user();
-
- if ( isset( $args['user'] ) AND $args['user'] AND is_object( $args['user'] ) ) {
- $user = $args['user'];
- }
-
- // Check how much patronage the user has
- $user_patronage = Patreon_Wordpress::getUserPatronage( $user );
-
- // Check if user is a patron whose payment is declined
- $declined = Patreon_Wordpress::checkDeclinedPatronage( $user );
-
- // Set where we want to have the user land at - this can be anything. The below code sets it to this exact page. This page may be a category page, search page, a custom page url with custom parameters - anything
-
- global $wp;
-
- $redir = home_url( add_query_arg( $_GET, $wp->request ) );
-
- // If the user has less patronage than $patreon_level, or declined, and is not an admin-level user, lock the post.
-
- if ( ( $user_patronage < ( $patreon_level * 100) OR $declined ) AND !current_user_can( 'manage_options' ) ) {
- return $ad;
- }
-
- // Here. Then patron is valid. Return false.
- return '';
-
- }
-
- public static function login_widget() {
-
- // Displays a conditional Patreon login widget
-
- $user = wp_get_current_user();
- $user_patreon_id = '';
-
- if ( $user AND isset( $user->ID ) ) {
- $user_patreon_id = get_user_meta( $user->ID, 'patreon_user_id', true );
- }
-
- if ( !is_user_logged_in() ) {
- return Patreon_Frontend::showPatreonLoginButton();
- }
-
- if ( is_user_logged_in() AND $user_patreon_id == '' ) {
- // WP logged in user with potentially unconnected Patreon account. Show connect button and any other specific info (if needed in future) - login button function will take care of showing the connect version of the image
- return Patreon_Frontend::showPatreonLoginButton();
- }
-
- // User logged in and has Patreon connected. Display logout link.
- return str_replace( '%%click_here%%', ''. PATREON_CLICK_HERE . '', PATREON_LOGIN_WIDGET_LOGOUT );
-
- }
-
- public static function replace_in_currency_sign( $label ) {
-
- $currency_sign_front = '$';
- $currency_sign_behind = '';
-
- $saved_currency_sign_front = get_option( 'patreon-currency-sign', false );
- $currency_sign_behind = get_option( 'patreon-currency-sign-behind', false );
-
- if ( $saved_currency_sign_front ) {
- $currency_sign_front = $saved_currency_sign_front;
- }
- if ( $currency_sign_behind ) {
- $currency_sign_behind = $currency_sign_behind;
- }
- $formatted_text = str_replace( '%%currency_sign_front%%', $currency_sign_front, $label );
- $formatted_text = str_replace( '%%currency_sign_behind%%', ' ' .$currency_sign_behind, $formatted_text );
-
- return $formatted_text;
-
- }
-
+ }
+
+ return self::$current_user_logged_into_patreon = $user_logged_into_patreon;
+ }
+
+ public static function patreonMakeLoginLink($client_id = false, $state = false, $post = false, $args = false)
+ {
+ if (!$post) {
+ global $post;
+ }
+
+ if (!$client_id) {
+ $client_id = get_option('patreon-client-id', false);
+ }
+
+ $redirect_uri = site_url().'/patreon-authorization/';
+
+ // If we werent given any state vars to send, initialize the array
+
+ if (!$state) {
+ $state = [];
+
+ // Get the address of the current page, and save it as final redirect uri.
+ // Start with home url for redirect. If post is valid, get permalink.
+
+ $final_redirect = home_url();
+
+ if ($post) {
+ $final_redirect = get_permalink($post->ID);
+ }
+
+ // We dont want to redirect people to login page. So check if we are there.
+ if ('wp-login.php' === $GLOBALS['pagenow']) {
+ $final_redirect = site_url();
+ }
+
+ $state['final_redirect_uri'] = $final_redirect;
+ }
+
+ $redirect_uri = site_url().'/patreon-authorization/';
+ $v2_params = '&scope=identity+'.urlencode('identity[email]');
+
+ $href = 'https://'.PATREON_HOST.'/oauth2/authorize?response_type=code&client_id='
+ .$client_id.$v2_params
+ .'&redirect_uri='.urlencode($redirect_uri)
+ .'&state='.urlencode(base64_encode(json_encode($state)));
+
+ $href = apply_filters('ptrn/login_link', $href);
+ $filterable_utm_params = 'utm_term=&utm_content=login_button';
+ $filterable_utm_params = apply_filters('ptrn/utm_params_for_login_link', $filterable_utm_params);
+
+ // Set default for content url
+ $content_url = site_url();
+
+ // Override if content url exists
+ if ($post) {
+ $content_url = get_permalink($post);
+ }
+
+ $utm_params = 'utm_source='.urlencode($content_url).'&utm_medium=patreon_wordpress_plugin&utm_campaign='.get_option('patreon-campaign-id').'&'.$filterable_utm_params;
+
+ return $href.'&'.$utm_params;
+ }
+
+ public static function patreonMakeLoginButton($client_id = false)
+ {
+ if (!$client_id) {
+ $client_id = get_option('patreon-client-id', false);
+ }
+
+ // Check if user is logged in to WP, for determination of label text
+
+ // Set login label to default
+ $login_label = apply_filters('ptrn/login_button_label', PATREON_TEXT_CONNECT);
+
+ if (is_user_logged_in()) {
+ // User is logged into WP. Check if user has valid patreon data :
+
+ $user = wp_get_current_user();
+
+ if ($user) {
+ $user_response = Patreon_Wordpress::getPatreonUser($user);
+ // ^ REVISIT - whats above may be a concern - it connects to API to check for valid user for every generation of button. If we could cache it it would be better
+
+ if ($user_response) {
+ // This is a user logged into Patreon. use refresh text
+ $login_label = PATREON_TEXT_REFRESH;
+ }
+ }
+ }
+
+ $href = self::patreonMakeCacheableLoginLink();
+
+ return apply_filters('ptrn/login_button', '
', $href);
+ }
+
+ public static function lock_this_post($post = false)
+ {
+ if (!$post) {
+ global $post;
+ }
+
+ // Just bail out if this is not the main query for content
+ if (!is_main_query()) {
+ return false;
+ }
+
+ $post_types = get_post_types(['public' => true], 'names');
+
+ if (in_array(get_post_type($post), $post_types)) {
+ $exclude = [
+ ];
+
+ // Enables 3rd party plugins to modify the post types excluded from locking
+ $exclude = apply_filters('ptrn/filter_excluded_posts', $exclude);
+
+ if (in_array(get_post_type($post), $exclude)) {
+ return false;
+ }
+
+ // First check if entire site is locked, get the level for locking.
+
+ $patreon_level = get_option('patreon-lock-entire-site', false);
+
+ // Check if specific level is given for this post:
+
+ $post_level = get_post_meta($post->ID, 'patreon-level', true);
+
+ // get post meta returns empty if no value is found. If so, set the value to 0.
+
+ if ('' == $post_level) {
+ $post_level = 0;
+ }
+
+ // Check if both post level and site lock level are set to 0 or nonexistent. If so return normal content.
+
+ if (0 == $post_level
+ && (!$patreon_level
+ || 0 == $patreon_level)
+ ) {
+ return false;
+ }
+
+ // If we are at this point, then this post is protected.
+
+ // Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
+ // It is independent of the plugin load order since it checks if it is defined.
+ // It can be defined by any plugin until right before the_content filter is run.
+
+ if (apply_filters('ptrn/bypass_filtering', defined('PATREON_BYPASS_FILTERING'))) {
+ return false;
+ }
+
+ if (current_user_can('manage_options')) {
+ // Here we need to put a notification to admins so they will know they can see the content because they are admin_login_with_patreon_disabled
+ return false;
+ }
+
+ // Passed checks. If post level is not 0, override patreon level and hence site locking value with post's. This will allow Creators to lock entire site and then set a different value for individual posts for access. Ie, site locking is $5, but one particular post can be $10, and it will require $10 to see.
+
+ if (0 != $post_level) {
+ $patreon_level = $post_level;
+ }
+
+ $user = wp_get_current_user();
+ $user_pledge_relationship_start = Patreon_Wordpress::get_user_pledge_relationship_start();
+ $user_patronage = Patreon_Wordpress::getUserPatronage();
+ $user_lifetime_patronage = Patreon_Wordpress::get_user_lifetime_patronage();
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+
+ // Check if post was set for active patrons only
+ $patreon_active_patrons_only = get_post_meta($post->ID, 'patreon-active-patrons-only', true);
+
+ // Check if specific total patronage is given for this post:
+ $post_total_patronage_level = get_post_meta($post->ID, 'patreon-total-patronage-level', true);
+
+ $hide_content = true;
+
+ if (!(false == $user_patronage
+ || $user_patronage < ($patreon_level * 100)
+ || $declined)) {
+ $hide_content = false;
+
+ // Seems valid patron. Lets see if active patron option was set and the user fulfills it
+
+ if ('1' == $patreon_active_patrons_only
+ and $user_pledge_relationship_start >= strtotime(get_the_date('', $post->ID))) {
+ $hide_content = true;
+ }
+ }
+
+ if ('' != $post_total_patronage_level and $post_total_patronage_level > 0) {
+ // Total patronage set if user has lifetime patronage over this level, we let him see the content
+ if ($user_lifetime_patronage >= $post_total_patronage_level * 100) {
+ $hide_content = false;
+ }
+ }
+
+ if ($hide_content) {
+ // protect content from user
+
+ // Get client id
+
+ $client_id = get_option('patreon-client-id', false);
+
+ // // If client id exists. Do the banner. If not, no point in protecting since we wont be able to send people to patronage. If so dont modify normal content.
+
+ if ($client_id) {
+ return $patreon_level;
+ }
+ }
+
+ // If we are here, it means post is protected, user is patron, patronage is valid. Slap the post footer:
+
+ return false;
+ }
+
+ // Return content in all other cases
+ return false;
+ }
+
+ public static function protectContentFromUsers($content, $post_id = false)
+ {
+ // This function receives content and optionally post id.
+
+ // If content is received but no post id, the function acts to lock the existing post. In this case it can be hooked to the_content filter
+
+ // If post id is given, then the function acts as a stand alone function to generate a lock interface and return the interface to whatever called it (another function or routine)
+
+ // This way it can be used to lock posts if hooked to the_content and post_id is not passed and to decide lock and if so, generate lock interface and return it if post_id is passed.
+
+ // If post id is not given, try to get it from post global
+ if (!$post_id) {
+ global $post;
+
+ if (isset($post) and is_object($post)) {
+ $post_id = $post->ID;
+ }
+ }
+
+ // Allow addons to override this function - this will bypass this function, but also will allow addons to apply this function's filters in their own gating function to keep compatibility with other addons
+
+ $override_content_filtering = [];
+
+ $override_content_filtering = apply_filters('ptrn/override_content_filtering', $content, $post_id);
+
+ if (is_array($override_content_filtering) and isset($override_content_filtering['override'])) {
+ return $override_content_filtering['content'];
+ }
+
+ // Just bail out if this is not the main query for content and there still isnt a post id
+ if (!is_main_query() and !$post_id) {
+ return $content;
+ }
+
+ // Now send the post id to locking decision function
+
+ $lock_or_not = Patreon_Wordpress::lock_or_not($post_id);
+
+ // An array with args should be returned
+ $hide_content = false;
+ $patreon_level = 0;
+ $user_patronage = 0;
+
+ if (isset($lock_or_not['lock'])) {
+ $hide_content = $lock_or_not['lock'];
+ }
+ if (isset($lock_or_not['patreon_level'])) {
+ $patreon_level = $lock_or_not['patreon_level'];
+ }
+ if (isset($lock_or_not['user_active_pledge'])) {
+ $user_patronage = $lock_or_not['user_active_pledge'];
+ }
+
+ $lock_or_not['post_id'] = $post_id;
+
+ if ($hide_content) {
+ // protect content from user
+
+ // Get client id
+
+ $client_id = get_option('patreon-client-id', false);
+
+ // // If client id exists. Do the banner. If not, no point in protecting since we wont be able to send people to patronage. If so dont modify normal content.
+
+ if ($client_id) {
+ $content = self::displayPatreonCampaignBanner($patreon_level, $lock_or_not);
+ $content = apply_filters('ptrn/post_content', $content, $patreon_level, $user_patronage, $lock_or_not);
+
+ return $content;
+ }
+ }
+
+ if (!$hide_content and 'post_is_public' == $lock_or_not['reason']) {
+ // This is not a locked post. Return content without any footer
+ return $content;
+ }
+
+ // If is an admin, just return the content with an admin-related notice
+
+ if (current_user_can('manage_options')) {
+ return $content.self::MakeAdminPostFooter($patreon_level);
+ }
+
+ // If we are here, it means post is protected, user is patron, patronage is valid. Slap the post footer:
+ return $content.self::MakeValidPatronFooter($patreon_level, $user_patronage, $lock_or_not);
+ }
+
+ public static function MakeAdminPostFooter($patreon_level)
+ {
+ return '
';
+ }
+
+ public static function MakeValidPatronFooter($patreon_level, $user_patronage, $args = false)
+ {
+ // Creates conditional text for footer shown to valid patrons
+
+ $label = PATREON_VALID_PATRON_POST_FOOTER_TEXT;
+ $user_logged_into_patreon = self::isUserLoggedInPatreon();
+ $is_patron = Patreon_Wordpress::isPatron(wp_get_current_user());
+ $messages = self::processPatreonMessages();
+ $user = wp_get_current_user();
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+ $user_patronage = Patreon_Wordpress::getUserPatronage();
+
+ // Get the creator or page name which is going to be used for interface text
+
+ $creator_full_name = self::make_creator_name_for_interface();
+
+ // Add 's to make it "creator's Patreon" when placed into label
+
+ $creator_full_name .= "'s";
+
+ // Override entire text if the creator set a custom site/creator name string:
+
+ $patreon_custom_page_name = get_option('patreon-custom-page-name', false);
+
+ if ($patreon_custom_page_name and '' != $patreon_custom_page_name) {
+ $creator_full_name = $patreon_custom_page_name;
+ }
+
+ // Get lock or not details if it is not given. If post id given, use it.
+ if (!isset($args['lock'])) {
+ if (isset($args['post_id'])) {
+ $lock_or_not = Patreon_Wordpress::lock_or_not($args['post_id']);
+ } else {
+ $lock_or_not = Patreon_Wordpress::lock_or_not();
+ }
+ }
+
+ // If lock or not details were not given, merge the incoming args with what we just got.
+ if ($args and is_array($args) and !isset($args['lock'])) {
+ $args = $lock_or_not + $args;
+ }
+
+ // If no args given, just feed lock or not:
+
+ if (!isset($args)) {
+ $args = $lock_or_not;
+ }
+
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ $label = PATREON_TEXT_OVER_BUTTON_11;
+ }
+
+ if (isset($args['post_total_patronage_level']) and $args['post_total_patronage_level'] > 0) {
+ $label = PATREON_TEXT_OVER_BUTTON_12;
+
+ // Double condition - override the text
+ if (isset($args['patreon_active_patrons_only']) and 1 == $args['patreon_active_patrons_only']) {
+ $label = PATREON_TEXT_OVER_BUTTON_10;
+ }
+ }
+
+ if ('yes' == get_option('patreon-creator-has-tiers', 'yes')) {
+ // Get Patreon creator tiers
+
+ $tiers = get_option('patreon-creator-tiers', false);
+
+ foreach ($tiers['included'] as $key => $value) {
+ // If its not a reward element, continue, just to make sure
+
+ if (
+ !isset($tiers['included'][$key]['type'])
+ or ('reward' != $tiers['included'][$key]['type']
+ and 'tier' != $tiers['included'][$key]['type'])
+ ) {
+ continue;
+ }
+
+ $reward = $tiers['included'][$key];
+
+ // Special conditions for label for element 0, which is 'everyone' and '1, which is 'patron only'
+
+ if (-1 == $reward['id']) {
+ $tier_title = PATREON_TEXT_EVERYONE;
+ }
+ if (0 == $reward['id']) {
+ $tier_title = PATREON_TEXT_ANY_PATRON;
+ }
+
+ if (($reward['attributes']['amount_cents'] / 100) >= $patreon_level) {
+ // Matching level was present, but now found. Set selected and toggle flag.
+ // selected = selected for XHTML compatibility
+
+ // Use title if it exists, description if it does not.
+
+ if (isset($reward['attributes']['title'])) {
+ $tier_title = $reward['attributes']['title'];
+ }
+
+ if ('' == $tier_title) {
+ // / Detect if this is an old non bas64 desc
+ if (false === base64_decode($reward['attributes']['description'], true)) {
+ $tier_title = $reward['attributes']['description'];
+ } else {
+ $tier_title = base64_decode($reward['attributes']['description']);
+ }
+ }
+
+ // If the title is too long, snip it
+ if (strlen($tier_title) > 23) {
+ $tier_title = substr($tier_title, 0, 23).'...';
+ }
+
+ $tier_title = '"'.$tier_title.'"';
+
+ break;
+ }
+ }
+ } else {
+ $tier_title = '';
+ }
+
+ // Get creator url
+
+ $creator_url = get_option('patreon-creator-url', false);
+ $creator_url = apply_filters('ptrn/creator_profile_link_in_valid_patron_footer', $creator_url);
+
+ $utm_content = 'creator_profile_link_in_valid_patron_footer';
+
+ $filterable_utm_params = 'utm_term=&utm_content='.$utm_content;
+ $filterable_utm_params = apply_filters('ptrn/utm_params_for_creator_profile_link_in_valid_patron_footer', $filterable_utm_params);
+
+ global $post;
+
+ if (isset($args['post_id'])) {
+ $post = get_post($args['post_id']);
+ }
+
+ // Set default for content url
+ $content_url = site_url();
+
+ // Override if content url exists
+ if ($post) {
+ $content_url = get_permalink($post);
+ }
+
+ $utm_params = 'utm_source='.urlencode($content_url).'&utm_medium=patreon_wordpress_plugin&utm_campaign='.get_option('patreon-campaign-id').'&'.$filterable_utm_params;
+
+ // Simple check to see if creator url has ? (for non vanity urls)
+ $append_with = '?';
+ if (false !== strpos($creator_url, '?')) {
+ $append_with = '&';
+ }
+
+ $creator_url .= $append_with.$utm_params;
+
+ $label = str_replace('%%creator_link%%', $creator_url, $label);
+ $label = str_replace('%%tier_level%%', strip_tags($tier_title), $label);
+ $label = str_replace('%%creator%%', $creator_full_name, $label);
+ $label = str_replace('%%pledgelevel%%', $patreon_level, $label);
+ $label = str_replace('%%flow_link%%', self::patreonMakeCacheableFlowLink(), $label);
+ if (isset($args['post_total_patronage_level'])) {
+ $label = str_replace('%%total_pledge%%', $args['post_total_patronage_level'], $label);
+ }
+
+ $post_footer = str_replace('%%pledgelevel%%', $patreon_level, apply_filters('ptrn/valid_patron_footer_text', $label, $patreon_level, $user_patronage));
+ $post_footer = apply_filters('ptrn/valid_patron_processed_message', $label, $patreon_level, $user_patronage);
+
+ $post_footer =
+ '
'.
+ $post_footer.
+ '
';
+
+ return apply_filters('ptrn/valid_patron_final_footer', $post_footer, $args['reason'], $patreon_level, $user_patronage, $args);
+ }
+
+ public static function displayPatreonLoginButtonInLoginForm()
+ {
+ // For displaying login button in the form - wrapper
+
+ // Check if the login button hide option is on
+ if (!get_option('patreon-hide-login-button', false)) {
+ echo '
'.self::showPatreonLoginButton().'
';
+ }
+ }
+
+ public static function showPatreonLoginButton($args = [])
+ {
+ // Set default login image
+ $log_in_img = PATREON_PLUGIN_ASSETS.'/img/patreon login@1x.png';
+
+ $user = wp_get_current_user();
+ $user_patreon_id = '';
+
+ if ($user and isset($user->ID)) {
+ $user_patreon_id = get_user_meta($user->ID, 'patreon_user_id', true);
+ }
+
+ if (is_user_logged_in() and '' == $user_patreon_id) {
+ // Logged in user without a connected Patreon account. Show connect button
+ $log_in_img = PATREON_PLUGIN_ASSETS.'/img/patreon connect@1x.png';
+ }
+
+ // Override image if argument given
+ if (isset($args['img'])) {
+ $log_in_img = $args['img'];
+ }
+
+ $client_id = get_option('patreon-client-id', false);
+
+ if (false == $client_id) {
+ return '';
+ }
+ $button = '';
+ /* inline styles - prevent themes from overriding */
+ $button .= '
+ ';
+
+ if (isset($_REQUEST['patreon-msg']) && 'login_with_patreon' == $_REQUEST['patreon-msg']) {
+ $button .= '
You can now login with your WordPress username/password.
';
+ } else {
+ $button .= apply_filters('ptrn/login_button', '');
+ }
+
+ return $button;
+ }
+
+ public static function LoginButtonShortcode($args)
+ {
+ // Check if this user connected his/her account to Patreon
+
+ $user = wp_get_current_user();
+ $user_patreon_id = '';
+
+ if ($user and isset($user->ID)) {
+ $user_patreon_id = get_user_meta($user->ID, 'patreon_user_id', true);
+ }
+
+ if (!is_user_logged_in() or (is_user_logged_in() and '' == $user_patreon_id)) {
+ return Patreon_Frontend::showPatreonLoginButton();
+ }
+ }
+
+ public static function show_patreon_avatar($avatar, $id_or_email, $size, $default, $alt)
+ {
+ // Checks if the user has a Patreon avatar saved, and returns that avatar in place of WP/site default
+ $user = false;
+
+ if (is_numeric($id_or_email)) {
+ $id = (int) $id_or_email;
+ $user = get_user_by('id', $id);
+ } elseif (is_object($id_or_email)) {
+ if (!empty($id_or_email->user_id)) {
+ $id = (int) $id_or_email->user_id;
+ $user = get_user_by('id', $id);
+ }
+ } else {
+ $user = get_user_by('email', $id_or_email);
+ }
+
+ if ($user && is_object($user)) {
+ // Get user's Patreon avatar meta:
+
+ $user_patreon_avatar = get_user_meta($user->ID, 'patreon-avatar-url', true);
+
+ if (!$size or !is_numeric($size) or 0 == $size) {
+ // Set size to WP default 48 if no size was provided by WP installation for whatsoever reason
+ $size = 48;
+ }
+
+ // Override avatar if there is a saved Patreon avatar
+ if ('' != $user_patreon_avatar) {
+ $avatar = "";
+ }
+ }
+
+ return $avatar;
+ }
+
+ public static function make_creator_name_for_interface()
+ {
+ // This function decides which identifier (page name, creator full, first, last name or custom name) should be used for locked post interface text
+
+ // Go ahead with a cascading series of fallbacks to make sure we have a creator name to use in the end
+
+ $creator_interface_name = Patreon_Wordpress::get_page_name();
+
+ if (!$creator_interface_name or '' == $creator_interface_name) {
+ $creator_interface_name = get_option('patreon-campaign-name', false);
+ }
+
+ if (!$creator_interface_name or '' == $creator_interface_name) {
+ $creator_interface_name = 'this creator';
+ }
+
+ // We skipped using full name or surname because some creators may not want their full name exposed. If they want it, they can set it up in the plugin options
+
+ // Return the value
+
+ return $creator_interface_name;
+ }
+
+ public static function gate_custom_content($patreon_level, $args = [])
+ {
+ // This function gates any part of a WP website using $ level. It is a wrapper function that is to be used as an easy version of custom locking code
+
+ // Custom lock
+
+ $user = wp_get_current_user();
+
+ if (isset($args['user']) and $args['user'] and is_object($args['user'])) {
+ $user = $args['user'];
+ }
+
+ // Check how much patronage the user has
+ $user_patronage = Patreon_Wordpress::getUserPatronage($user);
+
+ // Check if user is a patron whose payment is declined
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+
+ // Set where we want to have the user land at - this can be anything. The below code sets it to this exact page. This page may be a category page, search page, a custom page url with custom parameters - anything
+
+ global $wp;
+
+ $redir = home_url(add_query_arg($_GET, $wp->request));
+
+ // If the user has less patronage than $patreon_level, or declined, and is not an admin-level user, lock the post.
+
+ if (($user_patronage < ($patreon_level * 100) or $declined) and !current_user_can('manage_options')) {
+ // Generate the unlock interface wherever it is
+ return Patreon_Frontend::displayPatreonCampaignBanner(
+ $patreon_level,
+ ['direct_unlock' => $patreon_level,
+ 'redirect' => $redir,
+ ]
+ );
+ // the first param and direct_unlock param set the unlock value for this interface. Both need to be provided and be the same
+ }
+
+ // Here. Then patron is valid. Return false.
+ return false;
+ }
+
+ public static function hide_ad_for_patrons($patreon_level, $ad = '', $args = [])
+ {
+ // This function allows show/hiding any part of a WP website using $ level. It is a wrapper function that is to be used as an easy version of custom locking code
+
+ // Custom lock
+
+ $user = wp_get_current_user();
+
+ if (isset($args['user']) and $args['user'] and is_object($args['user'])) {
+ $user = $args['user'];
+ }
+
+ // Check how much patronage the user has
+ $user_patronage = Patreon_Wordpress::getUserPatronage($user);
+
+ // Check if user is a patron whose payment is declined
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+
+ // Set where we want to have the user land at - this can be anything. The below code sets it to this exact page. This page may be a category page, search page, a custom page url with custom parameters - anything
+
+ global $wp;
+
+ $redir = home_url(add_query_arg($_GET, $wp->request));
+
+ // If the user has less patronage than $patreon_level, or declined, and is not an admin-level user, lock the post.
+
+ if (($user_patronage < ($patreon_level * 100) or $declined) and !current_user_can('manage_options')) {
+ return $ad;
+ }
+
+ // Here. Then patron is valid. Return false.
+ return '';
+ }
+
+ public static function login_widget()
+ {
+ // Displays a conditional Patreon login widget
+
+ $user = wp_get_current_user();
+ $user_patreon_id = '';
+
+ if ($user and isset($user->ID)) {
+ $user_patreon_id = get_user_meta($user->ID, 'patreon_user_id', true);
+ }
+
+ if (!is_user_logged_in()) {
+ return Patreon_Frontend::showPatreonLoginButton();
+ }
+
+ if (is_user_logged_in() and '' == $user_patreon_id) {
+ // WP logged in user with potentially unconnected Patreon account. Show connect button and any other specific info (if needed in future) - login button function will take care of showing the connect version of the image
+ return Patreon_Frontend::showPatreonLoginButton();
+ }
+
+ // User logged in and has Patreon connected. Display logout link.
+ return str_replace('%%click_here%%', ''.PATREON_CLICK_HERE.'', PATREON_LOGIN_WIDGET_LOGOUT);
+ }
+
+ public static function replace_in_currency_sign($label)
+ {
+ $currency_sign_front = '$';
+ $currency_sign_behind = '';
+
+ $saved_currency_sign_front = get_option('patreon-currency-sign', false);
+ $currency_sign_behind = get_option('patreon-currency-sign-behind', false);
+
+ if ($saved_currency_sign_front) {
+ $currency_sign_front = $saved_currency_sign_front;
+ }
+ if ($currency_sign_behind) {
+ $currency_sign_behind = $currency_sign_behind;
+ }
+ $formatted_text = str_replace('%%currency_sign_front%%', $currency_sign_front, $label);
+ $formatted_text = str_replace('%%currency_sign_behind%%', ' '.$currency_sign_behind, $formatted_text);
+
+ return $formatted_text;
+ }
}
diff --git a/classes/patreon_login.php b/classes/patreon_login.php
index 3cbdbb8d..e64ae007 100644
--- a/classes/patreon_login.php
+++ b/classes/patreon_login.php
@@ -1,585 +1,527 @@
$user,
- 'user_response' => $user_response,
- 'tokens' => $tokens,
- );
-
- do_action( 'patreon_do_action_after_local_user_is_updated_with_patreon_info', $filter_args );
-
- }
-
- public static function updateLoggedInUserForStrictoAuth( $user_response, $tokens, $redirect = false ) {
-
- $user = wp_get_current_user();
-
- if ( $user->ID == 0 ) {
-
- $redirect = add_query_arg( 'patreon_message', 'patreon_cant_login_strict_oauth', $redirect );
- wp_redirect( $redirect );
- exit;
-
- } else {
-
- /* update user meta data with patreon data */
- self::updateExistingUser( $user->ID, $user_response, $tokens );
- wp_redirect( $redirect );
- exit;
-
- }
-
- }
-
- public static function checkTokenExpiration( $user_id = false ) {
-
- if ( $user_id ) {
- $user = get_user_by( 'ID', $user_id );
- } else {
- $user = wp_get_current_user();
- }
-
- if ( $user AND $user->ID != 0 ) {
-
- // Valid user is logged in. Check the token:
-
- $expiration = get_user_meta( $user->ID, 'patreon_token_expires_in', true );
- $minted = get_user_meta( $user->ID, 'patreon_token_minted', true );
-
- if ( $minted != '' ) {
- // We have value. get secs to use them in comparison.
-
- $minted = explode(' ',$minted);
- // Cast to integer
- $minted = (int) $minted[1];
-
- if ( (int) microtime( true ) >= ( $minted + $expiration ) ) {
-
- // This token is expired. Nuke it.
- delete_user_meta( $user->ID, 'patreon_access_token' );
- }
-
- } else {
-
- // No minted value. Even if there may be no access token created and saved, still nuke it.
- delete_user_meta( $user->ID, 'patreon_access_token' );
-
- }
-
- }
-
- }
-
- public static function createOrLogInUserFromPatreon($user_response, $tokens, $redirect = false) {
-
- global $wpdb;
-
- $login_with_patreon = get_option( 'patreon-enable-login-with-patreon', true );
- $admins_editors_login_with_patreon = get_option( 'patreon-enable-allow-admins-login-with-patreon', false );
- $danger_user_list = Patreon_Login::getDangerUserList();
-
- // Check if user is logged in to wp:
-
- // Logged in user. We just link the user up and be done.
- if ( is_user_logged_in() ) {
-
- $user = wp_get_current_user();
-
- self::updateExistingUser( $user->ID, $user_response, $tokens );
-
- // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
-
- $filter_args = array(
- 'user' => $user,
- 'redirect' => $redirect,
- 'user_response' => $user_response,
- 'tokens' => $tokens,
- );
-
- do_action( 'patreon_action_after_wp_logged_user_is_updated', $filter_args );
-
- // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
- update_user_meta( $user->ID, 'patreon_user_last_returned_from_any_flow', time() );
-
- wp_redirect( $redirect );
- exit;
-
- }
-
- ////////////////////////////////////////////////
- // If we are here, User is not logged in. Go through login or creation procedure:
- ////////////////////////////////////////////////
-
- $patreon_user_id = $user_response['data']['id'];
-
- // First see if any user was linked to this patreon user:
-
- global $wpdb;
-
- $prepared_sql = $wpdb->prepare(
- "SELECT * FROM " . $wpdb->usermeta . " WHERE meta_key = 'patreon_user_id' AND meta_value = %s",
- array( $patreon_user_id )
- );
-
- // Now get the result :
-
- $patreon_linked_accounts = $wpdb->get_results( $prepared_sql, ARRAY_A );
-
- if ( count( $patreon_linked_accounts ) > 0 ) {
-
- ////////////////////////////////////////////////
- // We have linked users. Get the last login dates for each. The reason we did it by taking ids and iterating them is to avoid querying both patreon ids and login dates at the same time and having to use a self join query on wp_usermeta
- ////////////////////////////////////////////////
-
- $sort_logins = array();
-
- foreach ( $patreon_linked_accounts as $key => $value ) {
-
- $last_logged_in = get_user_meta( $patreon_linked_accounts[$key]['user_id'], 'patreon_last_logged_in', true );
-
- $sort_logins[ $patreon_linked_accounts[ $key ]['user_id'] ] = $last_logged_in;
-
- }
-
- // Sort by time, descending
- arsort( $sort_logins );
-
- // We got the last login dates of the accounts, sorted. The first one is the last account used.
-
- $user_id_to_log_in = key( $sort_logins );
-
- // Attempt logging in that user.
-
- $user = get_user_by( 'id', $user_id_to_log_in );
-
- if ( $login_with_patreon ) {
-
- if ( $admins_editors_login_with_patreon == false && array_key_exists( $user->user_login, $danger_user_list ) ) {
-
- /* dont log admin / editor in */
- wp_redirect( wp_login_url() . '?patreon_message=admin_login_with_patreon_disabled', '301' );
- exit;
-
- } else {
-
- /* log user into existing wordpress account with matching username */
- wp_set_current_user( $user->ID, $user->user_login );
- wp_set_auth_cookie( $user->ID );
- do_action( 'wp_login', $user->user_login, $user );
-
- // Import Patreon avatar for this user since it is a new user
-
- self::get_update_user_patreon_avatar( $user_response['data']['attributes']['thumb_url'], $user );
-
- /* update user meta data with patreon data */
- self::updateExistingUser( $user->ID, $user_response, $tokens );
-
- // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
-
- $filter_args = array(
- 'user' => $user,
- 'redirect' => $redirect,
- 'user_response' => $user_response,
- 'tokens' => $tokens,
- );
-
- do_action( 'patreon_do_action_after_user_logged_in_via_patreon', $filter_args );
-
-
- // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
- update_user_meta( $user->ID, 'patreon_user_last_returned_from_any_flow', time() );
-
- wp_redirect( $redirect );
- exit;
-
- }
-
- }
- else {
-
- wp_redirect( wp_login_url() . '?patreon_message=login_with_patreon_disabled', '301' );
- exit;
-
- }
-
- }
-
- // At this point lets do a check for existing email if the email is going to be imported:
-
- // Default email to random complex string so get_user_by search will fail if email doesnt come from Patreon. SHA256
- $check_user_email = 'F01F2C59D413DB4B63882B0F25EB5FDD621946A99F6A2D6C2F8D26C1D600584F';
-
- if ( isset( $user_response['data']['attributes']['is_email_verified'] ) AND $user_response['data']['attributes']['is_email_verified'] ) {
- $check_user_email = $user_response['data']['attributes']['email'];
- }
-
- $user = get_user_by( 'email', $check_user_email );
-
- if ( $user != false ) {
-
- // A user with same Patreon email exists. This means that we cannot create this user with this email, but also we cannot link to this account since there may be WP installs which dont do email verification - could lead to identity spoofing
-
- // Give a message to the user to log in with the WP account and then log in with Patreon
-
- wp_redirect( wp_login_url() . '?patreon_message=email_exists_login_with_wp_first', '301' );
- exit;
-
- }
-
- // We are here, meaning that user was not logged in, and there were no linked accounts, no matching email. This means we will create a new user.
-
-
- $username = 'patreon_' . $patreon_user_id;
- $user = get_user_by( 'login', $username );
-
- if ( $user == false ) {
-
- /* create wordpress user with provided username */
-
- $random_password = wp_generate_password( 64, false );
- $user_email = '';
-
- // Import user email only if the email was verified
-
- if ( isset($user_response['data']['attributes']['is_email_verified']) AND $user_response['data']['attributes']['is_email_verified'] ) {
- $user_email = $user_response['data']['attributes']['email'];
- }
-
- $user_id = wp_create_user( $username, $random_password, $user_email );
-
- if ( $user_id ) {
-
- $user = get_user_by( 'id', $user_id );
-
- // Check and set user names:
-
- $display_name = $username;
- $first_name = '';
- $last_name = '';
-
-
- if ( isset( $user_response['data']['attributes']['full_name'] ) ) {
- $display_name = $user_response['data']['attributes']['full_name'];
- }
-
- if ( isset( $user_response['data']['attributes']['first_name'] ) ) {
-
- update_user_meta( $user_id, 'first_name', $user_response['data']['attributes']['first_name'] );
-
- $first_name = $user_response['data']['attributes']['first_name'];
- // Override display name with first name if its set
- $display_name = $user_response['data']['attributes']['first_name'];
-
- }
-
- if ( isset( $user_response['data']['attributes']['last_name'] ) ) {
- $last_name = $user_response['data']['attributes']['last_name'];
- }
-
- $args = array(
- 'ID' => $user_id,
- 'display_name' => $display_name,
- 'first_name' => $first_name,
- 'last_name' => $last_name,
- );
-
- wp_update_user( $args );
-
- // Import Patreon avatar for this user since it is a new user
-
- self::get_update_user_patreon_avatar( $user_response['data']['attributes']['thumb_url'], $user );
-
-
- wp_set_current_user( $user->ID, $user->data->user_login );
- wp_set_auth_cookie( $user->ID );
- do_action( 'wp_login', $user->data->user_login, $user );
-
- /* update user meta data with patreon data */
- self::updateExistingUser( $user->ID, $user_response, $tokens );
-
- // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
-
- $filter_args = array(
- 'user' => $user,
- 'redirect' => $redirect,
- 'user_response' => $user_response,
- 'tokens' => $tokens,
- );
-
- do_action( 'patreon_do_action_after_new_user_created_from_patreon_logged_in', $filter_args );
-
- // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
- update_user_meta( $user->ID, 'patreon_user_last_returned_from_any_flow', time() );
-
- wp_redirect( $redirect );
- exit;
-
- } else {
-
- /* wordpress account creation failed */
-
- $redirect = add_query_arg( 'patreon_message', 'patreon_could_not_create_wp_account', $redirect );
- wp_redirect( $redirect );
- exit;
-
- }
- } else {
-
- /* We created this patreon user before. Update and log in.
-
- /* update user meta data with patreon data */
- wp_set_current_user( $user->ID, $user->data->user_login );
- wp_set_auth_cookie( $user->ID );
- do_action( 'wp_login', $user->data->user_login, $user );
-
- /* update user meta data with patreon data */
- self::updateExistingUser( $user->ID, $user_response, $tokens );
-
- // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
-
- $filter_args = array(
- 'user' => $user,
- 'redirect' => $redirect,
- 'user_response' => $user_response,
- 'tokens' => $tokens,
- );
-
- do_action( 'patreon_do_action_after_existing_user_from_patreon_logged_in', $filter_args );
-
- // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
- update_user_meta( $user->ID, 'patreon_user_last_returned_from_any_flow', time() );
-
-
- wp_redirect( $redirect );
- exit;
-
- }
-
- }
-
- public static function getDangerUserList() {
-
- $args = array(
- 'role__in' => array( 'administrator','editor' ),
- 'orderby' => 'login',
- 'order' => 'ASC',
+class Patreon_Login
+{
+ public static function updateExistingUser($user_id, $user_response, $tokens)
+ {
+ /* update user meta data with patreon data */
+ update_user_meta($user_id, 'patreon_refresh_token', $tokens['refresh_token']);
+ update_user_meta($user_id, 'patreon_access_token', $tokens['access_token']);
+ update_user_meta($user_id, 'patreon_user', $user_response['data']['attributes']['vanity']);
+ update_user_meta($user_id, 'patreon_user_id', $user_response['data']['id']);
+ update_user_meta($user_id, 'patreon_last_logged_in', time());
+
+ $patreon_created = '';
+ if (isset($user_response['data']['attributes']['created'])) {
+ $patreon_created = $user_response['data']['attributes']['created'];
+ }
+
+ update_user_meta($user_id, 'patreon_created', $patreon_created);
+ update_user_meta($user_id, 'patreon_token_minted', microtime());
+ update_user_meta($user_id, 'patreon_token_expires_in', $tokens['expires_in']);
+
+ $user = get_user_by('ID', $user_id);
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
+
+ $filter_args = [
+ 'user' => $user,
+ 'user_response' => $user_response,
+ 'tokens' => $tokens,
+ ];
+
+ do_action('patreon_do_action_after_local_user_is_updated_with_patreon_info', $filter_args);
+ }
+
+ public static function updateLoggedInUserForStrictoAuth($user_response, $tokens, $redirect = false)
+ {
+ $user = wp_get_current_user();
+
+ if (0 == $user->ID) {
+ $redirect = add_query_arg('patreon_message', 'patreon_cant_login_strict_oauth', $redirect);
+ wp_redirect($redirect);
+ exit;
+ } else {
+ /* update user meta data with patreon data */
+ self::updateExistingUser($user->ID, $user_response, $tokens);
+ wp_redirect($redirect);
+ exit;
+ }
+ }
+
+ public static function checkTokenExpiration($user_id = false)
+ {
+ if ($user_id) {
+ $user = get_user_by('ID', $user_id);
+ } else {
+ $user = wp_get_current_user();
+ }
+
+ if ($user and 0 != $user->ID) {
+ // Valid user is logged in. Check the token:
+
+ $expiration = get_user_meta($user->ID, 'patreon_token_expires_in', true);
+ $minted = get_user_meta($user->ID, 'patreon_token_minted', true);
+
+ if ('' != $minted) {
+ // We have value. get secs to use them in comparison.
+
+ $minted = explode(' ', $minted);
+ // Cast to integer
+ $minted = (int) $minted[1];
+
+ if ((int) microtime(true) >= ($minted + $expiration)) {
+ // This token is expired. Nuke it.
+ delete_user_meta($user->ID, 'patreon_access_token');
+ }
+ } else {
+ // No minted value. Even if there may be no access token created and saved, still nuke it.
+ delete_user_meta($user->ID, 'patreon_access_token');
+ }
+ }
+ }
+
+ public static function createOrLogInUserFromPatreon($user_response, $tokens, $redirect = false)
+ {
+ global $wpdb;
+
+ $login_with_patreon = get_option('patreon-enable-login-with-patreon', true);
+ $admins_editors_login_with_patreon = get_option('patreon-enable-allow-admins-login-with-patreon', false);
+ $danger_user_list = Patreon_Login::getDangerUserList();
+
+ // Check if user is logged in to wp:
+
+ // Logged in user. We just link the user up and be done.
+ if (is_user_logged_in()) {
+ $user = wp_get_current_user();
+
+ self::updateExistingUser($user->ID, $user_response, $tokens);
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
+
+ $filter_args = [
+ 'user' => $user,
+ 'redirect' => $redirect,
+ 'user_response' => $user_response,
+ 'tokens' => $tokens,
+ ];
+
+ do_action('patreon_action_after_wp_logged_user_is_updated', $filter_args);
+
+ // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
+ update_user_meta($user->ID, 'patreon_user_last_returned_from_any_flow', time());
+
+ wp_redirect($redirect);
+ exit;
+ }
+
+ // //////////////////////////////////////////////
+ // If we are here, User is not logged in. Go through login or creation procedure:
+ // //////////////////////////////////////////////
+
+ $patreon_user_id = $user_response['data']['id'];
+
+ // First see if any user was linked to this patreon user:
+
+ global $wpdb;
+
+ $prepared_sql = $wpdb->prepare(
+ 'SELECT * FROM '.$wpdb->usermeta." WHERE meta_key = 'patreon_user_id' AND meta_value = %s",
+ [$patreon_user_id]
);
-
- $danger_users = get_users( $args );
-
- $danger_user_list = array();
-
- if ( !empty( $danger_users ) ) {
-
- foreach( $danger_users as $danger_user ) {
- $danger_user_list[$danger_user->data->user_login] = $danger_user;
- }
- }
-
- return $danger_user_list;
-
- }
-
- public static function get_update_user_patreon_avatar( $patreon_image_url, $user = false ) {
-
- // Gets and saves a user's Patreon profile image from Patreon into WP media folder
-
- // Get user from current user if user object was not provided
- if ( $user == false ) {
- $user = wp_get_current_user();
- }
-
- // If still no user object, return false
- if ( $user->ID == 0 ) {
- return false;
- }
-
- // Abort if GD is not installed
-
- if ( !( extension_loaded( 'gd' ) AND function_exists( 'gd_info' ) ) ) {
- return false;
- }
-
- $patreon_image_data = wp_remote_get( $patreon_image_url );
-
- if ( is_wp_error( $patreon_image_data ) ) {
- return false;
- }
-
- $headers = $patreon_image_data['headers'];
-
- // If mime type is not set, abort
-
- if ( !isset( $headers ) OR !isset( $headers['content-type'] ) ) {
- return false;
- }
-
- $mime_type = $headers['content-type'];
-
- $patreon_image = $patreon_image_data['body'];
-
- if ( $mime_type == 'image/png' ) {
- $extension = 'png';
- }
- if ( $mime_type == 'image/gif' ) {
- $extension = 'gif';
- }
- if ($mime_type =='image/jpeg') {
- $extension = 'jpg';
- }
- if ( $mime_type == 'image/bmp' ) {
- $extension = 'bmp';
- }
-
- // Get the existing avatar from user meta if it was saved:
-
- $user_patreon_avatar_path = get_user_meta( $user->ID, 'patreon-avatar-file', true );
-
- // Delete existing Patreon avatar
-
- if ( file_exists( $user_patreon_avatar_path ) ) {
- unlink( $user_patreon_avatar_path );
- }
-
- // Upload new one
- $uploaded = wp_upload_bits( 'patreon_avatar_' . $user->ID . '.' . $extension, null, $patreon_image );
-
- if ( is_wp_error( $uploaded ) ) {
- return false;
- }
-
- // All went through.
-
- // Remove the existing avatar metas
-
- delete_user_meta( $user->ID, 'patreon-avatar-url' );
- delete_user_meta( $user->ID, 'patreon-avatar-file' );
-
- // Add the Patreon avatar as meta to the user - this will false if adding meta fails, primary key if it succeeds
-
- // At this point, if earlier file deletion succeeded, the avatar should be named properly with patreon_avatar_ + user id + extension. If not, then it would get -1, -2 etc suffix after patreon_avatar + user id since WP would upload it as a new file to not override existing file
-
- if ( add_user_meta( $user->ID, 'patreon-avatar-url', $uploaded['url'] ) AND
- add_user_meta( $user->ID, 'patreon-avatar-file', $uploaded['file'] )
- ) {
- return true;
- }
-
- return false;
-
- }
-
- public static function disconnect_account_from_patreon() {
-
- // Disconnects an account from Patreon.
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_disconnect_user_account_from_patreon']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_disconnect_user_account_from_patreon'] ), 'patreon_wordpress_nonce_disconnect_user_account_from_patreon' ) ) {
- return;
- }
-
- $user = wp_get_current_user();
-
- if ( current_user_can( 'manage_options' ) OR $user->ID == $_REQUEST['patreon_disconnect_user_id'] ) {
-
- // Delete all Patreon user meta for this WP user id here
- // User id to delete:
-
- $user_to_disconnect = get_user_by( 'ID', $_REQUEST['patreon_disconnect_user_id'] );
-
- if ( isset( $user_to_disconnect->ID ) ) {
-
- delete_user_meta( $user_to_disconnect->ID, 'patreon_refresh_token' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_access_token' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_user' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_user_id' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_last_logged_in' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_created' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_token_minted' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_token_expires_in' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon-avatar-url' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon-avatar-file' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_user' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_latest_patron_info' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_email' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_latest_patron_info_timestamp' );
- delete_user_meta( $user_to_disconnect->ID, 'patreon_user_details_last_updated' );
-
- }
- else {
- // Problem! No valid user id to disconnect or no valid user
-
- ?>
-
- Ooops! Disconnect failed - could not find matching user account!
-
- get_edit_profile_url( $user->ID ) ) );
-
- if ( !current_user_can( 'manage_options' ) ) {
-
- ?>
- Disconnected! Now you can connect your site account to another Patreon account.
-
-
-
-
-
-
-
-
-
-
- Disconnected! Only the owner of this user account can reconnect it to his/her Patreon account.
-
- get_results($prepared_sql, ARRAY_A);
+
+ if (count($patreon_linked_accounts) > 0) {
+ // //////////////////////////////////////////////
+ // We have linked users. Get the last login dates for each. The reason we did it by taking ids and iterating them is to avoid querying both patreon ids and login dates at the same time and having to use a self join query on wp_usermeta
+ // //////////////////////////////////////////////
+
+ $sort_logins = [];
+
+ foreach ($patreon_linked_accounts as $key => $value) {
+ $last_logged_in = get_user_meta($patreon_linked_accounts[$key]['user_id'], 'patreon_last_logged_in', true);
+
+ $sort_logins[$patreon_linked_accounts[$key]['user_id']] = $last_logged_in;
+ }
+
+ // Sort by time, descending
+ arsort($sort_logins);
+
+ // We got the last login dates of the accounts, sorted. The first one is the last account used.
+
+ $user_id_to_log_in = key($sort_logins);
+
+ // Attempt logging in that user.
+
+ $user = get_user_by('id', $user_id_to_log_in);
+
+ if ($login_with_patreon) {
+ if (false == $admins_editors_login_with_patreon && array_key_exists($user->user_login, $danger_user_list)) {
+ /* dont log admin / editor in */
+ wp_redirect(wp_login_url().'?patreon_message=admin_login_with_patreon_disabled', '301');
+ exit;
+ } else {
+ /* log user into existing wordpress account with matching username */
+ wp_set_current_user($user->ID, $user->user_login);
+ wp_set_auth_cookie($user->ID);
+ do_action('wp_login', $user->user_login, $user);
+
+ // Import Patreon avatar for this user since it is a new user
+
+ self::get_update_user_patreon_avatar($user_response['data']['attributes']['thumb_url'], $user);
+
+ /* update user meta data with patreon data */
+ self::updateExistingUser($user->ID, $user_response, $tokens);
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
+
+ $filter_args = [
+ 'user' => $user,
+ 'redirect' => $redirect,
+ 'user_response' => $user_response,
+ 'tokens' => $tokens,
+ ];
+
+ do_action('patreon_do_action_after_user_logged_in_via_patreon', $filter_args);
+
+ // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
+ update_user_meta($user->ID, 'patreon_user_last_returned_from_any_flow', time());
+
+ wp_redirect($redirect);
+ exit;
+ }
+ } else {
+ wp_redirect(wp_login_url().'?patreon_message=login_with_patreon_disabled', '301');
+ exit;
+ }
+ }
+
+ // At this point lets do a check for existing email if the email is going to be imported:
+
+ // Default email to random complex string so get_user_by search will fail if email doesnt come from Patreon. SHA256
+ $check_user_email = 'F01F2C59D413DB4B63882B0F25EB5FDD621946A99F6A2D6C2F8D26C1D600584F';
+
+ if (isset($user_response['data']['attributes']['is_email_verified']) and $user_response['data']['attributes']['is_email_verified']) {
+ $check_user_email = $user_response['data']['attributes']['email'];
+ }
+
+ $user = get_user_by('email', $check_user_email);
+
+ if (false != $user) {
+ // A user with same Patreon email exists. This means that we cannot create this user with this email, but also we cannot link to this account since there may be WP installs which dont do email verification - could lead to identity spoofing
+
+ // Give a message to the user to log in with the WP account and then log in with Patreon
+
+ wp_redirect(wp_login_url().'?patreon_message=email_exists_login_with_wp_first', '301');
+ exit;
+ }
+
+ // We are here, meaning that user was not logged in, and there were no linked accounts, no matching email. This means we will create a new user.
+
+ $username = 'patreon_'.$patreon_user_id;
+ $user = get_user_by('login', $username);
+
+ if (false == $user) {
+ /* create wordpress user with provided username */
+
+ $random_password = wp_generate_password(64, false);
+ $user_email = '';
+
+ // Import user email only if the email was verified
+
+ if (isset($user_response['data']['attributes']['is_email_verified']) and $user_response['data']['attributes']['is_email_verified']) {
+ $user_email = $user_response['data']['attributes']['email'];
+ }
+
+ $user_id = wp_create_user($username, $random_password, $user_email);
+
+ if ($user_id) {
+ $user = get_user_by('id', $user_id);
+
+ // Check and set user names:
+
+ $display_name = $username;
+ $first_name = '';
+ $last_name = '';
+
+ if (isset($user_response['data']['attributes']['full_name'])) {
+ $display_name = $user_response['data']['attributes']['full_name'];
+ }
+
+ if (isset($user_response['data']['attributes']['first_name'])) {
+ update_user_meta($user_id, 'first_name', $user_response['data']['attributes']['first_name']);
+
+ $first_name = $user_response['data']['attributes']['first_name'];
+ // Override display name with first name if its set
+ $display_name = $user_response['data']['attributes']['first_name'];
+ }
+
+ if (isset($user_response['data']['attributes']['last_name'])) {
+ $last_name = $user_response['data']['attributes']['last_name'];
+ }
+
+ $args = [
+ 'ID' => $user_id,
+ 'display_name' => $display_name,
+ 'first_name' => $first_name,
+ 'last_name' => $last_name,
+ ];
+
+ wp_update_user($args);
+
+ // Import Patreon avatar for this user since it is a new user
+
+ self::get_update_user_patreon_avatar($user_response['data']['attributes']['thumb_url'], $user);
+
+ wp_set_current_user($user->ID, $user->data->user_login);
+ wp_set_auth_cookie($user->ID);
+ do_action('wp_login', $user->data->user_login, $user);
+
+ /* update user meta data with patreon data */
+ self::updateExistingUser($user->ID, $user_response, $tokens);
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
+
+ $filter_args = [
+ 'user' => $user,
+ 'redirect' => $redirect,
+ 'user_response' => $user_response,
+ 'tokens' => $tokens,
+ ];
+
+ do_action('patreon_do_action_after_new_user_created_from_patreon_logged_in', $filter_args);
+
+ // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
+ update_user_meta($user->ID, 'patreon_user_last_returned_from_any_flow', time());
+
+ wp_redirect($redirect);
+ exit;
+ } else {
+ /* wordpress account creation failed */
+
+ $redirect = add_query_arg('patreon_message', 'patreon_could_not_create_wp_account', $redirect);
+ wp_redirect($redirect);
+ exit;
+ }
+ } else {
+ /* We created this patreon user before. Update and log in.
+
+ /* update user meta data with patreon data */
+ wp_set_current_user($user->ID, $user->data->user_login);
+ wp_set_auth_cookie($user->ID);
+ do_action('wp_login', $user->data->user_login, $user);
+
+ /* update user meta data with patreon data */
+ self::updateExistingUser($user->ID, $user_response, $tokens);
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user after the user returns from Patreon
+
+ $filter_args = [
+ 'user' => $user,
+ 'redirect' => $redirect,
+ 'user_response' => $user_response,
+ 'tokens' => $tokens,
+ ];
+
+ do_action('patreon_do_action_after_existing_user_from_patreon_logged_in', $filter_args);
+
+ // Save this time when patron returned from a Patreon flow to use for deciding when to call the api in unlock actions
+ update_user_meta($user->ID, 'patreon_user_last_returned_from_any_flow', time());
+
+ wp_redirect($redirect);
+ exit;
+ }
+ }
+
+ public static function getDangerUserList()
+ {
+ $args = [
+ 'role__in' => ['administrator', 'editor'],
+ 'orderby' => 'login',
+ 'order' => 'ASC',
+ ];
+
+ $danger_users = get_users($args);
+
+ $danger_user_list = [];
+
+ if (!empty($danger_users)) {
+ foreach ($danger_users as $danger_user) {
+ $danger_user_list[$danger_user->data->user_login] = $danger_user;
+ }
+ }
+
+ return $danger_user_list;
+ }
+
+ public static function get_update_user_patreon_avatar($patreon_image_url, $user = false)
+ {
+ // Gets and saves a user's Patreon profile image from Patreon into WP media folder
+
+ // Get user from current user if user object was not provided
+ if (false == $user) {
+ $user = wp_get_current_user();
+ }
+
+ // If still no user object, return false
+ if (0 == $user->ID) {
+ return false;
+ }
+
+ // Abort if GD is not installed
+
+ if (!(extension_loaded('gd') and function_exists('gd_info'))) {
+ return false;
+ }
+
+ $patreon_image_data = wp_remote_get($patreon_image_url);
+
+ if (is_wp_error($patreon_image_data)) {
+ return false;
+ }
+
+ $headers = $patreon_image_data['headers'];
+
+ // If mime type is not set, abort
+
+ if (!isset($headers) or !isset($headers['content-type'])) {
+ return false;
+ }
+
+ $mime_type = $headers['content-type'];
+
+ $patreon_image = $patreon_image_data['body'];
+
+ if ('image/png' == $mime_type) {
+ $extension = 'png';
+ }
+ if ('image/gif' == $mime_type) {
+ $extension = 'gif';
+ }
+ if ('image/jpeg' == $mime_type) {
+ $extension = 'jpg';
+ }
+ if ('image/bmp' == $mime_type) {
+ $extension = 'bmp';
+ }
+
+ // Get the existing avatar from user meta if it was saved:
+
+ $user_patreon_avatar_path = get_user_meta($user->ID, 'patreon-avatar-file', true);
+
+ // Delete existing Patreon avatar
+
+ if (file_exists($user_patreon_avatar_path)) {
+ unlink($user_patreon_avatar_path);
+ }
+
+ // Upload new one
+ $uploaded = wp_upload_bits('patreon_avatar_'.$user->ID.'.'.$extension, null, $patreon_image);
+
+ if (is_wp_error($uploaded)) {
+ return false;
+ }
+
+ // All went through.
+
+ // Remove the existing avatar metas
+
+ delete_user_meta($user->ID, 'patreon-avatar-url');
+ delete_user_meta($user->ID, 'patreon-avatar-file');
+
+ // Add the Patreon avatar as meta to the user - this will false if adding meta fails, primary key if it succeeds
+
+ // At this point, if earlier file deletion succeeded, the avatar should be named properly with patreon_avatar_ + user id + extension. If not, then it would get -1, -2 etc suffix after patreon_avatar + user id since WP would upload it as a new file to not override existing file
+
+ if (add_user_meta($user->ID, 'patreon-avatar-url', $uploaded['url'])
+ and add_user_meta($user->ID, 'patreon-avatar-file', $uploaded['file'])
+ ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function disconnect_account_from_patreon()
+ {
+ // Disconnects an account from Patreon.
+
+ if (!isset($_REQUEST['patreon_wordpress_nonce_disconnect_user_account_from_patreon']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_disconnect_user_account_from_patreon']), 'patreon_wordpress_nonce_disconnect_user_account_from_patreon')) {
+ return;
+ }
+
+ $user = wp_get_current_user();
+
+ if (current_user_can('manage_options') or $user->ID == $_REQUEST['patreon_disconnect_user_id']) {
+ // Delete all Patreon user meta for this WP user id here
+ // User id to delete:
+
+ $user_to_disconnect = get_user_by('ID', $_REQUEST['patreon_disconnect_user_id']);
+
+ if (isset($user_to_disconnect->ID)) {
+ delete_user_meta($user_to_disconnect->ID, 'patreon_refresh_token');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_access_token');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_user');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_user_id');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_last_logged_in');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_created');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_token_minted');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_token_expires_in');
+ delete_user_meta($user_to_disconnect->ID, 'patreon-avatar-url');
+ delete_user_meta($user_to_disconnect->ID, 'patreon-avatar-file');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_user');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_latest_patron_info');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_email');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_latest_patron_info_timestamp');
+ delete_user_meta($user_to_disconnect->ID, 'patreon_user_details_last_updated');
+ } else {
+ // Problem! No valid user id to disconnect or no valid user
+
+ ?>
+
+ Ooops! Disconnect failed - could not find matching user account!
+
+ get_edit_profile_url($user->ID)]);
+
+ if (!current_user_can('manage_options')) {
+ ?>
+ Disconnected! Now you can connect your site account to another Patreon account.
+
+
+
+
+
+
+
+
+
+
+ Disconnected! Only the owner of this user account can reconnect it to his/her Patreon account.
+
+ true ), 'names' );
-
- $exclude = array(
- );
-
- // Enables 3rd party plugins to modify the post types excluded from locking
- $exclude = apply_filters( 'ptrn/filter_excluded_posts_metabox', $exclude );
-
- if ( in_array( $post_type, $exclude ) == false && in_array( $post_type, $post_types ) ) {
-
- add_meta_box(
- 'patreon-level', // Unique ID
- esc_html__( 'Patreon Level', 'Patreon Contribution Requirement' ),
- array( $this, 'patreon_plugin_meta_box' ),
- $post_type,
- 'side',
- 'default'
- );
-
- }
-
- }
-
- function patreon_plugin_meta_box( $object, $box ) {
-
- $current_user = wp_get_current_user();
-
- global $post;
-
- $label = 'Require the below membership tier or higher to view this post. (Makes entire post patron only) (?)';
-
- if ( get_option( 'patreon-creator-access-token-401', false ) ) {
- $label = '
Sorry, it looks like your site\'s connection to Patreon is broken. Please click here to reconnect by using the "(re)Connect site" button.
' . $label;
- }
-
- // Override messaging for lite plans
-
- $creator_tiers = get_option( 'patreon-creator-tiers', false );
-
- $advanced_post_options_toggle_status = get_user_meta( $current_user->ID, 'patreon-wordpress-advanced-options-toggle', true );
-
- $no_tiers = false;
-
- if ( !$creator_tiers OR $creator_tiers == '' OR !is_array( $creator_tiers['included'][1] ) ) {
- $no_tiers = true;
- }
-
- if ( $no_tiers ) {
-
- $label = 'To use tiers, your Patreon account must be Pro Plan or above and your site must be connected to Patreon. Upgrade your plan here or force-reconnect your site here.';
-
- $advanced_post_options_toggle_status = 'on';
-
- }
-
- $readonly = '';
- $disabled = '';
-
- if ( !get_option( 'patreon-creator-id', false ) ) {
-
- $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
- $disabled = " disabled";
-
- }
-
- wp_nonce_field( basename( __FILE__ ), 'patreon_metabox_nonce' );
-
- ?>
-
-
-
-
-
-
-
-
-
id="patreon-wordpress-advanced-options-toggle">
- Require the below precise $ monthly membership or over to view this post. (optional - overrides the above select box when used) (?)';
-
- $readonly = '';
-
- if ( $no_tiers ) {
-
- $label = 'Gate this content for the below amount. (?) Patreon will default it to $5, but your patrons can enter a custom amount while pledging at Patreon';
-
- }
-
- if ( !get_option( 'patreon-creator-id', false ) ) {
-
- $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
- $readonly = " readonly";
-
- }
- ?>
-
-
-
-
- $ >
-
-
- (?)';
- $readonly = '';
-
- if ( !get_option( 'patreon-creator-id', false ) ) {
-
- $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
- $readonly = " readonly";
-
- }
-
- ?>
-
-
- (?)';
- $readonly = '';
-
- if ( !get_option( 'patreon-creator-id', false ) ) {
-
- $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
- $readonly = " readonly";
-
- }
-
- ?>
-
-
-
- $ >
-
-
-
-
-
-
-
-
-
-
- post_type );
-
- if ( !current_user_can( $post_type->cap->edit_post, $post_id ) ) {
- return $post_id;
- }
-
- if( isset( $_POST['patreon-level'] ) && is_numeric( $_POST['patreon-level'] ) ) {
- $new_patreon_level = $_POST['patreon-level'];
- } else {
- $new_patreon_level = 0;
- }
-
- $patreon_level = get_post_meta( $post_id, 'patreon-level', true );
-
- // Now, an exception for the old metabox which was moved to patreon-level-exact - if it is different from the value already saved or from 0, override the select box with its value since it would mean a specific override initiated by user.
-
- if( isset( $_POST['patreon-level-exact'] ) && is_numeric( $_POST['patreon-level-exact'] ) ) {
-
- if ( $_POST['patreon-level-exact'] != $patreon_level ) {
- $new_patreon_level = $_POST['patreon-level-exact'];
- }
-
- }
-
- update_post_meta( $post_id, 'patreon-level', $new_patreon_level );
-
- // Handles active patrons only toggle
- if ( isset( $_POST['patreon-active-patrons-only']) && $_POST['patreon-active-patrons-only'] != '') {
- update_post_meta( $post_id, 'patreon-active-patrons-only', 1 );
- } else {
- delete_post_meta( $post_id, 'patreon-active-patrons-only' );
- }
-
- // Handles lifetime patronage value
- if ( isset( $_POST['patreon-total-patronage-level'] ) && is_numeric( $_POST['patreon-total-patronage-level'] ) ) {
- $new_patreon_lifetime_patronage_level = $_POST['patreon-total-patronage-level'];
- } else {
- $new_patreon_lifetime_patronage_level = 0;
- }
-
- update_post_meta( $post_id, 'patreon-total-patronage-level', $new_patreon_lifetime_patronage_level );
-
- }
-}
\ No newline at end of file
+class Patron_Metabox
+{
+ public function __construct()
+ {
+ add_action('add_meta_boxes', [$this, 'patreon_plugin_meta_boxes']);
+ add_action('save_post', [$this, 'patreon_plugin_save_post_class_meta'], 10, 2);
+ }
+
+ public function patreon_plugin_meta_boxes($post_type)
+ {
+ $post_types = get_post_types(['public' => true], 'names');
+
+ $exclude = [
+ ];
+
+ // Enables 3rd party plugins to modify the post types excluded from locking
+ $exclude = apply_filters('ptrn/filter_excluded_posts_metabox', $exclude);
+
+ if (false == in_array($post_type, $exclude) && in_array($post_type, $post_types)) {
+ add_meta_box(
+ 'patreon-level', // Unique ID
+ esc_html__('Patreon Level', 'Patreon Contribution Requirement'),
+ [$this, 'patreon_plugin_meta_box'],
+ $post_type,
+ 'side',
+ 'default'
+ );
+ }
+ }
+
+ public function patreon_plugin_meta_box($object, $box)
+ {
+ $current_user = wp_get_current_user();
+
+ global $post;
+
+ $label = 'Require the below membership tier or higher to view this post. (Makes entire post patron only) (?)';
+
+ if (get_option('patreon-creator-access-token-401', false)) {
+ $label = '
Sorry, it looks like your site\'s connection to Patreon is broken. Please click here to reconnect by using the "(re)Connect site" button.
'.$label;
+ }
+
+ // Override messaging for lite plans
+
+ $creator_tiers = get_option('patreon-creator-tiers', false);
+
+ $advanced_post_options_toggle_status = get_user_meta($current_user->ID, 'patreon-wordpress-advanced-options-toggle', true);
+
+ $no_tiers = false;
+
+ if (!$creator_tiers or '' == $creator_tiers or !is_array($creator_tiers['included'][1])) {
+ $no_tiers = true;
+ }
+
+ if ($no_tiers) {
+ $label = 'To use tiers, your Patreon account must be Pro Plan or above and your site must be connected to Patreon. Upgrade your plan here or force-reconnect your site here.';
+
+ $advanced_post_options_toggle_status = 'on';
+ }
+
+ $readonly = '';
+ $disabled = '';
+
+ if (!get_option('patreon-creator-id', false)) {
+ $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
+ $disabled = ' disabled';
+ }
+
+ wp_nonce_field(basename(__FILE__), 'patreon_metabox_nonce');
+
+ ?>
+
+
+
+
+
+
+
+
+
id="patreon-wordpress-advanced-options-toggle">
+ Require the below precise $ monthly membership or over to view this post. (optional - overrides the above select box when used) (?)';
+
+ $readonly = '';
+
+ if ($no_tiers) {
+ $label = 'Gate this content for the below amount. (?) Patreon will default it to $5, but your patrons can enter a custom amount while pledging at Patreon';
+ }
+
+ if (!get_option('patreon-creator-id', false)) {
+ $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
+ $readonly = ' readonly';
+ }
+ ?>
+
+
+
+
+ $ >
+
+
+ (?)';
+ $readonly = '';
+
+ if (!get_option('patreon-creator-id', false)) {
+ $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
+ $readonly = ' readonly';
+ }
+
+ ?>
+
+
+ (?)';
+ $readonly = '';
+
+ if (!get_option('patreon-creator-id', false)) {
+ $label = 'Post locking won\'t work without Creator ID. Please confirm you have it here';
+ $readonly = ' readonly';
+ }
+
+ ?>
+
+
+
+ $ >
+
+
+
+
+
+
+
+
+
+
+ post_type);
+
+ if (!current_user_can($post_type->cap->edit_post, $post_id)) {
+ return $post_id;
+ }
+
+ if (isset($_POST['patreon-level']) && is_numeric($_POST['patreon-level'])) {
+ $new_patreon_level = $_POST['patreon-level'];
+ } else {
+ $new_patreon_level = 0;
+ }
+
+ $patreon_level = get_post_meta($post_id, 'patreon-level', true);
+
+ // Now, an exception for the old metabox which was moved to patreon-level-exact - if it is different from the value already saved or from 0, override the select box with its value since it would mean a specific override initiated by user.
+
+ if (isset($_POST['patreon-level-exact']) && is_numeric($_POST['patreon-level-exact'])) {
+ if ($_POST['patreon-level-exact'] != $patreon_level) {
+ $new_patreon_level = $_POST['patreon-level-exact'];
+ }
+ }
+
+ update_post_meta($post_id, 'patreon-level', $new_patreon_level);
+
+ // Handles active patrons only toggle
+ if (isset($_POST['patreon-active-patrons-only']) && '' != $_POST['patreon-active-patrons-only']) {
+ update_post_meta($post_id, 'patreon-active-patrons-only', 1);
+ } else {
+ delete_post_meta($post_id, 'patreon-active-patrons-only');
+ }
+
+ // Handles lifetime patronage value
+ if (isset($_POST['patreon-total-patronage-level']) && is_numeric($_POST['patreon-total-patronage-level'])) {
+ $new_patreon_lifetime_patronage_level = $_POST['patreon-total-patronage-level'];
+ } else {
+ $new_patreon_lifetime_patronage_level = 0;
+ }
+
+ update_post_meta($post_id, 'patreon-total-patronage-level', $new_patreon_lifetime_patronage_level);
+ }
+}
diff --git a/classes/patreon_oauth.php b/classes/patreon_oauth.php
index 8995573d..87999e25 100644
--- a/classes/patreon_oauth.php
+++ b/classes/patreon_oauth.php
@@ -1,92 +1,85 @@
client_id = get_option( 'patreon-client-id', false );
- $this->client_secret = get_option( 'patreon-client-secret', false );
-
- }
-
- public function get_tokens( $code, $redirect_uri, $params = array() ) {
-
- return $this->__update_token(
- array_merge(
- array(
-
- "grant_type" => "authorization_code",
- "code" => $code,
- "client_id" => $this->client_id,
- "client_secret" => $this->client_secret,
- "redirect_uri" => $redirect_uri
- ),
- $params
- )
- );
-
- }
-
- public function refresh_token( $refresh_token, $redirect_uri ) {
-
- return $this->__update_token( array(
- "grant_type" => "refresh_token",
- "refresh_token" => $refresh_token,
- "client_id" => $this->client_id,
- "client_secret" => $this->client_secret
- ) );
-
- }
-
- private function __update_token( $params ) {
-
- $api_endpoint = "https://api.patreon.com/oauth2/token";
-
- $headers = array(
- 'User-Agent' => 'Patreon-Wordpress, version ' . PATREON_WORDPRESS_VERSION . PATREON_WORDPRESS_BETA_STRING . ', platform ' . php_uname('s') . '-' . php_uname( 'r' ),
- );
-
- $api_request = array(
- 'method' => 'POST',
- 'body' => $params,
- 'headers' => $headers,
- );
-
- $response = wp_remote_post( $api_endpoint, $api_request );
-
- if ( is_wp_error( $response ) ) {
-
- $result = array( 'error' => $response->get_error_message() );
- $GLOBALS['patreon_notice'] = $response->get_error_message();
-
- Patreon_Wordpress::log_connection_error( $GLOBALS['patreon_notice'] );
-
- return $result;
-
- }
-
- $response_decoded = json_decode( $response['body'], true );
-
- // Log the connection as having error if the return is not 200
-
- if ( isset( $response['response']['code'] ) AND $response['response']['code'] != '200' ) {
-
- Patreon_Wordpress::log_connection_error( 'Response code: ' . $response['response']['code'] . ' Response :' . $response['body'] );
-
- }
-
- if ( is_array( $response_decoded ) ) {
- return $response_decoded;
- }
-
- // Commented out to address issues caused by Patreon's maintenance in between 01 - 02 Feb 2019 - the plugin was showing Patreon's maintenance page at WP sites yin certain cases
- // echo $response['body'];
- // wp_die();
- }
-
-}
\ No newline at end of file
+if (!defined('ABSPATH')) {
+ exit;
+} // Exit if accessed directly
+
+class Patreon_OAuth
+{
+ public $client_id;
+ public $client_secret;
+
+ public function __construct()
+ {
+ $this->client_id = get_option('patreon-client-id', false);
+ $this->client_secret = get_option('patreon-client-secret', false);
+ }
+
+ public function get_tokens($code, $redirect_uri, $params = [])
+ {
+ return $this->__update_token(
+ array_merge(
+ [
+ 'grant_type' => 'authorization_code',
+ 'code' => $code,
+ 'client_id' => $this->client_id,
+ 'client_secret' => $this->client_secret,
+ 'redirect_uri' => $redirect_uri,
+ ],
+ $params
+ )
+ );
+ }
+
+ public function refresh_token($refresh_token, $redirect_uri)
+ {
+ return $this->__update_token([
+ 'grant_type' => 'refresh_token',
+ 'refresh_token' => $refresh_token,
+ 'client_id' => $this->client_id,
+ 'client_secret' => $this->client_secret,
+ ]);
+ }
+
+ private function __update_token($params)
+ {
+ $api_endpoint = 'https://'.PATREON_HOST.'/api/oauth2/token';
+
+ $headers = [
+ 'User-Agent' => 'Patreon-Wordpress, version '.PATREON_WORDPRESS_VERSION.PATREON_WORDPRESS_BETA_STRING.', platform '.php_uname('s').'-'.php_uname('r'),
+ ];
+
+ $api_request = [
+ 'method' => 'POST',
+ 'body' => $params,
+ 'headers' => $headers,
+ ];
+
+ $response = wp_remote_post($api_endpoint, $api_request);
+
+ if (is_wp_error($response)) {
+ $result = ['error' => $response->get_error_message()];
+ $GLOBALS['patreon_notice'] = $response->get_error_message();
+
+ Patreon_Wordpress::log_connection_error($GLOBALS['patreon_notice']);
+
+ return $result;
+ }
+
+ $response_decoded = json_decode($response['body'], true);
+
+ // Log the connection as having error if the return is not 200
+
+ if (isset($response['response']['code']) and '200' != $response['response']['code']) {
+ Patreon_Wordpress::log_connection_error('Response code: '.$response['response']['code'].' Response :'.$response['body']);
+ }
+
+ if (is_array($response_decoded)) {
+ return $response_decoded;
+ }
+
+ // Commented out to address issues caused by Patreon's maintenance in between 01 - 02 Feb 2019 - the plugin was showing Patreon's maintenance page at WP sites yin certain cases
+ // echo $response['body'];
+ // wp_die();
+ }
+}
diff --git a/classes/patreon_options.php b/classes/patreon_options.php
index aa478c68..6a58b5f6 100644
--- a/classes/patreon_options.php
+++ b/classes/patreon_options.php
@@ -1,85 +1,80 @@
'page',
- 'post_status' => 'publish',
- 'posts_per_page' => -1,
- 'sort_order' => 'asc',
- 'orderby' => 'title'
- );
- $all_pages = get_pages( $args );
+
+ public function patreon_plugin_setup_page()
+ {
+ $args = [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'sort_order' => 'asc',
+ 'orderby' => 'title',
+ ];
+ $all_pages = get_pages($args);
$danger_user_list = Patreon_Login::getDangerUserList();
@@ -87,13 +82,13 @@ function patreon_plugin_setup_page(){
- ';
-
- echo '
';
-
- // Put some defaults so sites with warnings on will be fine
- $heading = 'patreon_admin_message_default_title';
- $content = 'patreon_admin_message_default_content';
-
- if ( isset( $_REQUEST['patreon_admin_message_title'] ) ) {
- $heading = $_REQUEST['patreon_admin_message_title'];
- }
- if ( isset( $_REQUEST['patreon_admin_message_content'] ) ) {
- $content = $_REQUEST['patreon_admin_message_content'];
- }
-
- $heading = Patreon_Frontend::$messages_map[ $heading ];
- $content = Patreon_Frontend::$messages_map[ $content ];
-
- echo '
' . $heading . '
' . $content . '
';
-
- echo '';
-
- }
-
- function patreon_plugin_post_sync_page(){
- // For now, dud to prevent any PHP notices when going to post sync wizard from admi menu. Can be expanded later.
- return;
- }
- function patreon_plugin_health_check_page(){
-
- ?>
-
-
Health check of your Patreon integration
- Below are settings or issues which may affect your Patreon integration. Please check the recommendations and implement them to have your integration function better. You can get help for any of these items by visiting our support forum and posting a thread.
-
- Your site is using:
WP with PHP
- Patreon WordPress with API v
-
- Patron Plugin Pro internal['version'] ?> Patreon Button, Widgets and Plugin internal['version'] ?>
-
-
- You can click here to copy the output of this page to share with support team or post it in the forum.
-
-
-
-
-
-
Your Patreon integration health is great!
-
-
- 0 ) {
-
- $health_info = Patreon_Compatibility::$site_health_info;
-
- // Sort according to priority
-
- usort( $health_info, function($a, $b ) {
- return $a['order'] - $b['order'];
- } );
-
- // Add last 50 connection errors at the end.
-
-
- foreach ( $health_info as $key => $value ) {
- ?>
-
-
';
+
+ // Put some defaults so sites with warnings on will be fine
+ $heading = 'patreon_admin_message_default_title';
+ $content = 'patreon_admin_message_default_content';
+
+ if (isset($_REQUEST['patreon_admin_message_title'])) {
+ $heading = $_REQUEST['patreon_admin_message_title'];
+ }
+ if (isset($_REQUEST['patreon_admin_message_content'])) {
+ $content = $_REQUEST['patreon_admin_message_content'];
+ }
+
+ $heading = Patreon_Frontend::$messages_map[$heading];
+ $content = Patreon_Frontend::$messages_map[$content];
+
+ echo '
'.$heading.'
'.$content.'
';
+
+ echo '';
+ }
+
+ public function patreon_plugin_post_sync_page()
+ {
+ // For now, dud to prevent any PHP notices when going to post sync wizard from admi menu. Can be expanded later.
+ return;
}
-
- public function sanitize_page_name( $input ) {
- // Allow only permitted chars - Escape any potential special char among the allowed just in case
- $input = preg_replace("/[^A-Za-z0-9_\-\ \!\?\$\,\.\#\(\)\^\&\=\[\]\%\@\+]/", '', $input);
- // Further sanitization here if needed in future
- return $input;
- }
-
- public function site_locking_value( $input ) {
- $input = preg_replace("/[^0-9,.]/", '', $input);
- // Further sanitization here if needed in future
- return $input;
- }
-
-}
\ No newline at end of file
+
+ public function patreon_plugin_health_check_page()
+ {
+ ?>
+
+
Health check of your Patreon integration
+ Below are settings or issues which may affect your Patreon integration. Please check the recommendations and implement them to have your integration function better. You can get help for any of these items by visiting our support forum and posting a thread.
+
+ Your site is using:
WP with PHP
+ Patreon WordPress with API v
+
+ Patron Plugin Pro internal['version']; ?> Patreon Button, Widgets and Plugin internal['version']; ?>
+
+
+ You can click here to copy the output of this page to share with support team or post it in the forum.
+
+
+
+
+
+
Your Patreon integration health is great!
+
+
+ 0) {
+ $health_info = Patreon_Compatibility::$site_health_info;
+
+ // Sort according to priority
+
+ usort($health_info, function ($a, $b) {
+ return $a['order'] - $b['order'];
+ });
+
+ // Add last 50 connection errors at the end.
+
+ foreach ($health_info as $key => $value) {
+ ?>
+
+
';
-
- return $interface;
-
- }
- public function ParseContentForProtectedImages( $content ) {
-
- global $post;
-
- if ( !is_singular() OR is_admin() ) {
- // Currently we only support single post/page/custom posts
- return $content;
- }
-
- // This function parses the content to check for protected images and tag them with a css class for click-catching to send the user to Patreon flow
-
- // Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
- // It is independent of the plugin load order since it checks if it is defined.
- // It can be defined by any plugin until right before the_content filter is run.
-
- if ( apply_filters( 'ptrn/bypass_image_filtering', defined( 'PATREON_BYPASS_IMAGE_FILTERING' ) ) ) {
- return $content;
- }
-
- // Check if user is an admin, if so dont tag images
-
- if ( current_user_can( 'manage_options' ) ) {
- return $content;
- }
-
- // Here we are either in an unlocked post, or we are in a post that was locked but now displayed due to Patron having a sufficient pledge level. Now we have to parse and tag images.
-
- // Use regex to get image src's and process them to save memory and cpu. This tolerates more any content which has broken html. A comparison of regex vs dom for this process is referenced below in Autoptimizer plugin's blog
- // https://blog.futtta.be/2014/05/01/php-html-parsing-performance-shootout-regex-vs-dom/
-
- // Get image srcs - we have to check pledge level and tag every image
- // We want entire image tag:
-
- $start = microtime( true );
-
- preg_match_all( '/.+?)[\'"].*>/i', $content, $matches );
- // preg_match_all("/(]*>)/",$content,$matches);
- $images = $matches[1];
-
- $time_elapsed_secs = microtime( true ) - $start;
-
- foreach ( $images as $key => $value ) {
-
- $attachment_id = Patreon_Protect::get_attachment_id_from_url( $images[$key] );
-
- // The above returns 0 if it cant find the attachment post id
-
- if ( $attachment_id == 0 ) {
-
- // Couldnt determine attachment post id. Try to get id from thumbnail
- $attachment_id = Patreon_Protect::getAttachmentIDfromThumbnailURL( $images[$key] );
-
- if( $attachment_id == 0 ) {
- //No go. skip processing this image
- continue;
- }
-
- }
-
- $lock_the_image = self::checkPatronPledgeForImage( $attachment_id );
-
- if( $lock_the_image > 0 ) {
-
- // Valid pledge not found, not admin, image is locked. Add the class:
- $replace = str_replace( 'class="','class="patreon-locked-image ', $matches[0][$key] );
-
- // Make universal flow link with pledge level:
- $flow_link = Patreon_Frontend::patreonMakeCacheableImageFlowLink( $attachment_id, $post->ID );
-
- // Encode the link enough to make sure no url sensitive chars will remain
- $flow_link = base64_encode( $flow_link );
-
- // Place the link in an attribute to image tag
- $replace = str_replace( 'class="','data-patreon-flow-url="' . $flow_link . '" class="', $replace );
-
- // Put back to content:
- $content = str_replace( $matches[0][$key], $replace, $content );
-
- }
-
- }
-
- return $content;
-
- }
- public static function addCustomCSSinAdmin() {
-
- echo "";
-
- }
- public static function addImageToolbar() {
-
- // Adds the hidden floating image toolbar
-
- $screen = get_current_screen();
- if ( $screen->parent_base != 'edit' ) {
- return;
- }
-
- ?>
-
-
-
-
-
- 'attachment',
- 'fields' => 'ids',
- 'meta_query' => array(
- array(
- 'key' => '_wp_attached_file',
- 'value' => $file,
- 'compare' => 'LIKE',
- ),
- )
- );
-
- // query attachments
- $ids = get_posts( $query );
-
- if ( ! empty( $ids ) ) {
-
- foreach ( $ids as $id ) {
-
- // first entry of returned array is the URL
- if ( $url === array_shift( wp_get_attachment_image_src( $id, 'full' ) ) )
- return $id;
- }
- }
-
- $query['meta_query'][0]['key'] = '_wp_attachment_metadata';
-
- // query attachments again
- $ids = get_posts( $query );
-
- if ( empty( $ids) )
- return 0;
-
- foreach ( $ids as $id ) {
-
- $meta = wp_get_attachment_metadata( $id );
-
- foreach ( $meta['sizes'] as $size => $values ) {
-
- if ( $values['file'] === $file && $url === array_shift( wp_get_attachment_image_src( $id, $size ) ) )
- return $id;
- }
- }
-
- return 0;
- }
-
-}
+RewriteRule ^".$upload_dir.'/(.*)$ index.php?patreon_action=serve_patron_only_image&patron_only_image=$1 [QSA,L]
+# END Patreon WordPress'.PHP_EOL;
+
+ file_put_contents(ABSPATH.'.htaccess', $htaccess.$append);
+ }
+
+ public static function removePatreonRewriteRules()
+ {
+ // Check if htaccess exists, bail out if not
+
+ if (!file_exists(ABSPATH.'.htaccess')) {
+ return;
+ }
+
+ $htaccess = file_get_contents(ABSPATH.'.htaccess');
+
+ $start_marker = '# BEGIN Patreon WordPress Image Protection';
+ $end_marker = '# END Patreon WordPress';
+
+ // Check if rules are in htaccess
+ if (false === strpos($htaccess, $start_marker)) {
+ return;
+ }
+
+ $start = strpos($htaccess, $start_marker);
+ $end = strpos($htaccess, $end_marker);
+
+ $snipped = preg_replace('/'.PHP_EOL.$start_marker.'.+?'.$end_marker.PHP_EOL.'/is', '', $htaccess);
+
+ file_put_contents(ABSPATH.'.htaccess', $snipped);
+ }
+
+ public function saveAttachmentLevel($attachment_id = false)
+ {
+ if (!(is_admin() and current_user_can('manage_options'))) {
+ return;
+ }
+
+ $_REQUEST['patreon_attachment_patreon_level'] = preg_replace('/[^0-9.]/', '', $_REQUEST['patreon_attachment_patreon_level']);
+
+ if (update_post_meta($_REQUEST['patreon_attachment_id'], 'patreon_level', $_REQUEST['patreon_attachment_patreon_level'])) {
+ $update_status = 'updated';
+ }
+
+ if ('updated' == $update_status) {
+ $message = 'Pledge level for the image was updated!';
+ } else {
+ $message = 'Pledge level for the image was updated! The value you posted may be same with the value already set!';
+ }
+
+ $args = [
+ 'attachment_id' => $attachment_id,
+ 'patreon_level' => $_REQUEST['patreon_attachment_patreon_level'],
+ 'message' => $message,
+ ];
+
+ echo self::make_image_lock_interface($args);
+
+ // Delete all cached images for this attachment
+ self::deleteCachedAttachmentPlaceholders($_REQUEST['patreon_attachment_id']);
+
+ wp_die();
+ }
+
+ public function makeAttachmentPledgeEditor($attachment_id = false)
+ {
+ if (!(is_admin() and current_user_can('manage_options'))) {
+ echo 'Not in admin or without admin capabilities!';
+ wp_die();
+ }
+
+ if (isset($_REQUEST['pw_image_source']) and '' != $_REQUEST['pw_image_source']) {
+ $attachment_url = $_REQUEST['pw_image_source'];
+ }
+
+ $message = '';
+
+ if (!$attachment_url or '' == $attachment_url) {
+ // This is not a rewritten image request.
+ $message = 'No attachment url provided. Cannot lock.';
+ }
+ // Get attachment from attachment url.
+
+ $attachment_id = Patreon_Wordpress::get_attachment_id_from_url($attachment_url);
+
+ // The above returns 0 if it cant find the attachment post id
+
+ if (0 == $attachment_id) {
+ // Couldnt determine attachment post id. Try to get id from thumbnail
+ $attachment_id = Patreon_Protect::getAttachmentIDfromThumbnailURL($attachment_url);
+
+ if (0 == $attachment_id) {
+ $message = 'Can not find attachment id. Cannot lock.';
+ }
+ }
+
+ // Check if image locking is enabled
+
+ if (!get_option('patreon-enable-file-locking', false)) {
+ // Give a message if the image locking feature is not enabled
+ $message = 'Image locking is not enabled in settings. Locking will not work';
+ }
+
+ $patreon_level = get_post_meta($attachment_id, 'patreon_level', true);
+
+ $args = [
+ 'attachment_id' => $attachment_id,
+ 'patreon_level' => $patreon_level,
+ 'message' => $message,
+ ];
+
+ echo self::make_image_lock_interface($args);
+
+ wp_die();
+ }
+
+ public function make_image_lock_interface($args = [])
+ {
+ $interface = '';
+
+ $interface .= '
';
+
+ return $interface;
+ }
+
+ public function ParseContentForProtectedImages($content)
+ {
+ global $post;
+
+ if (!is_singular() or is_admin()) {
+ // Currently we only support single post/page/custom posts
+ return $content;
+ }
+
+ // This function parses the content to check for protected images and tag them with a css class for click-catching to send the user to Patreon flow
+
+ // Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
+ // It is independent of the plugin load order since it checks if it is defined.
+ // It can be defined by any plugin until right before the_content filter is run.
+
+ if (apply_filters('ptrn/bypass_image_filtering', defined('PATREON_BYPASS_IMAGE_FILTERING'))) {
+ return $content;
+ }
+
+ // Check if user is an admin, if so dont tag images
+
+ if (current_user_can('manage_options')) {
+ return $content;
+ }
+
+ // Here we are either in an unlocked post, or we are in a post that was locked but now displayed due to Patron having a sufficient pledge level. Now we have to parse and tag images.
+
+ // Use regex to get image src's and process them to save memory and cpu. This tolerates more any content which has broken html. A comparison of regex vs dom for this process is referenced below in Autoptimizer plugin's blog
+ // https://blog.futtta.be/2014/05/01/php-html-parsing-performance-shootout-regex-vs-dom/
+
+ // Get image srcs - we have to check pledge level and tag every image
+ // We want entire image tag:
+
+ $start = microtime(true);
+
+ preg_match_all('/.+?)[\'"].*>/i', $content, $matches);
+ // preg_match_all("/(]*>)/",$content,$matches);
+ $images = $matches[1];
+
+ $time_elapsed_secs = microtime(true) - $start;
+
+ foreach ($images as $key => $value) {
+ $attachment_id = Patreon_Protect::get_attachment_id_from_url($images[$key]);
+
+ // The above returns 0 if it cant find the attachment post id
+
+ if (0 == $attachment_id) {
+ // Couldnt determine attachment post id. Try to get id from thumbnail
+ $attachment_id = Patreon_Protect::getAttachmentIDfromThumbnailURL($images[$key]);
+
+ if (0 == $attachment_id) {
+ // No go. skip processing this image
+ continue;
+ }
+ }
+
+ $lock_the_image = self::checkPatronPledgeForImage($attachment_id);
+
+ if ($lock_the_image > 0) {
+ // Valid pledge not found, not admin, image is locked. Add the class:
+ $replace = str_replace('class="', 'class="patreon-locked-image ', $matches[0][$key]);
+
+ // Make universal flow link with pledge level:
+ $flow_link = Patreon_Frontend::patreonMakeCacheableImageFlowLink($attachment_id, $post->ID);
+
+ // Encode the link enough to make sure no url sensitive chars will remain
+ $flow_link = base64_encode($flow_link);
+
+ // Place the link in an attribute to image tag
+ $replace = str_replace('class="', 'data-patreon-flow-url="'.$flow_link.'" class="', $replace);
+
+ // Put back to content:
+ $content = str_replace($matches[0][$key], $replace, $content);
+ }
+ }
+
+ return $content;
+ }
+
+ public static function addCustomCSSinAdmin()
+ {
+ echo "";
+ }
+
+ public static function addImageToolbar()
+ {
+ // Adds the hidden floating image toolbar
+
+ $screen = get_current_screen();
+ if ('edit' != $screen->parent_base) {
+ return;
+ }
+
+ ?>
+
+
+
+
+
+ 'attachment',
+ 'fields' => 'ids',
+ 'meta_query' => [
+ [
+ 'key' => '_wp_attached_file',
+ 'value' => $file,
+ 'compare' => 'LIKE',
+ ],
+ ],
+ ];
+
+ // query attachments
+ $ids = get_posts($query);
+
+ if (!empty($ids)) {
+ foreach ($ids as $id) {
+ // first entry of returned array is the URL
+ if ($url === array_shift(wp_get_attachment_image_src($id, 'full'))) {
+ return $id;
+ }
+ }
+ }
+
+ $query['meta_query'][0]['key'] = '_wp_attachment_metadata';
+
+ // query attachments again
+ $ids = get_posts($query);
+
+ if (empty($ids)) {
+ return 0;
+ }
+
+ foreach ($ids as $id) {
+ $meta = wp_get_attachment_metadata($id);
+
+ foreach ($meta['sizes'] as $size => $values) {
+ if ($values['file'] === $file && $url === array_shift(wp_get_attachment_image_src($id, $size))) {
+ return $id;
+ }
+ }
+ }
+
+ return 0;
+ }
+}
diff --git a/classes/patreon_routing.php b/classes/patreon_routing.php
index 00b220f4..8c0df618 100644
--- a/classes/patreon_routing.php
+++ b/classes/patreon_routing.php
@@ -1,802 +1,702 @@
flush_rules();
- // Refresh/add htaccess rules:
- Patreon_Protect::removePatreonRewriteRules();
- Patreon_Protect::addPatreonRewriteRules();
- update_option( 'patreon-rewrite-rules-flushed', true );
-
- }
-
- }
-
- function add_rewrite_rules( $wp_rewrite ) {
-
- $rules = array(
- 'patreon-authorization\/?$' => 'index.php?patreon-oauth=true',
- 'patreon-flow\/?$' => 'index.php?patreon-flow=true',
- 'patreon-setup\/?$' => 'index.php?patreon-setup=true',
- 'patreon-webhooks\/?$' => 'index.php?patreon-webhooks=true',
- );
-
- $wp_rewrite->rules = $rules + (array) $wp_rewrite->rules;
-
- }
-
- function query_vars( $public_query_vars ) {
-
- array_push( $public_query_vars, 'patreon-oauth' );
- array_push( $public_query_vars, 'patreon-flow' );
- array_push( $public_query_vars, 'patreon-unlock-post' );
- array_push( $public_query_vars, 'patreon-unlock-image' );
- array_push( $public_query_vars, 'patreon-direct-unlock' );
- array_push( $public_query_vars, 'patreon-post-id' );
- array_push( $public_query_vars, 'patreon-login' );
- array_push( $public_query_vars, 'patreon-final-redirect' );
- array_push( $public_query_vars, 'code' );
- array_push( $public_query_vars, 'state' );
- array_push( $public_query_vars, 'patreon-redirect' );
- array_push( $public_query_vars, 'patreon-webhooks' );
- return $public_query_vars;
-
- }
-
- function parse_request( &$wp ) {
-
- if ( strpos( $_SERVER['REQUEST_URI'],'/patreon-flow/' ) !== false ) {
-
- // First slap the noindex header so search engines wont index this page:
- header( 'X-Robots-Tag: noindex, nofollow' );
-
- // Make sure browsers dont cache this
- header( 'cache-control: no-cache, must-revalidate, max-age=0' );
-
- if( array_key_exists( 'patreon-login', $wp->query_vars ) ) {
-
- // Login intent.
-
- $final_redirect = wp_login_url();
-
- if( isset( $wp->query_vars['patreon-final-redirect'] ) ) {
-
- $final_redirect = $wp->query_vars['patreon-final-redirect'];
- }
-
- $state = array(
- 'final_redirect_uri' => $final_redirect,
- );
-
- // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user + content before going to Patreon flow
-
- $filter_args = array(
- 'state' => $state,
- 'user' => wp_get_current_user(),
- );
-
- do_action( 'patreon_do_action_before_patreon_login', $filter_args );
-
- $login_url = Patreon_Frontend::patreonMakeLoginLink( false, $state );
-
- wp_redirect( $login_url );
- exit;
-
- }
-
- if( array_key_exists( 'patreon-direct-unlock', $wp->query_vars ) ) {
-
- $final_redirect = wp_login_url();
-
- if( isset( $wp->query_vars['patreon-direct-unlock'] ) ) {
-
- $patreon_level = $wp->query_vars['patreon-direct-unlock'];
- $redirect = base64_decode( urldecode( $wp->query_vars['patreon-redirect'] ) );
-
- if( !$patreon_level OR $patreon_level == '' OR $patreon_level == 0 ) {
- $patreon_level = 1;
- }
-
- $client_id = get_option( 'patreon-client-id', false );
-
- if( !$client_id ) {
-
- // No client id, no point in being here. Make it go with an error.
-
- $final_redirect = add_query_arg( 'patreon_message', 'patreon_cant_login_api_error_credentials', $final_redirect );
-
- wp_redirect( $final_redirect );
- exit;
-
- }
-
- $post = false;
-
- // If post id set, get the post
- if( isset( $wp->query_vars['patreon-post-id'] ) ) {
- $post = get_post( $wp->query_vars['patreon-post-id'] );
- }
-
- $link_interface_item = 'direct_unlock_button';
- $state['final_redirect_uri'] = $redirect;
- $send_pledge_level = $patreon_level * 100;
-
- $flow_link = Patreon_Frontend::MakeUniversalFlowLink( $send_pledge_level, $state, $client_id, $post, array('link_interface_item' => $link_interface_item ) );
-
- wp_redirect( $flow_link );
- exit;
-
- }
-
- }
-
- if( array_key_exists( 'patreon-unlock-post', $wp->query_vars ) ) {
-
- // We have a login/flow request, Get the post id
-
- if( isset( $wp->query_vars['patreon-unlock-post'] ) ) {
-
- // First check if entire site is locked, get the level for locking.
-
- $patreon_level = get_option( 'patreon-lock-entire-site', false );
-
- // Account for any value the creator can put into this option, and also the default false
- if( !$patreon_level OR $patreon_level == '' ) {
- $patreon_level = 0;
- }
-
- if( $wp->query_vars['patreon-unlock-post'] != '' ) {
-
- // Got post id. Get the post, and prepare necessary vars. Get the post first
-
- $post = get_post( $wp->query_vars['patreon-unlock-post'] );
-
- // If there is no post var, and entire site is not locked, no point in being here
- if( !$post AND $patreon_level == 0 ) {
- // No post, no point in being here.
-
- $final_redirect = add_query_arg( 'patreon_message', 'patreon_no_post_id_to_unlock_post', $final_redirect );
- wp_redirect( home_url() );
- exit;
-
- }
-
- }
-
- // Start with home url for redirect. If post is valid, get permalink.
-
- $final_redirect = home_url();
-
- if( $post ) {
- $final_redirect = get_permalink( $post->ID );
- }
-
- // Check if specific level is given for this post:
-
- $post_level = get_post_meta( $post->ID, 'patreon-level', true );
-
- // get post meta returns empty if no value is found. If so, set the value to 0.
-
- if( $post_level == '' ) {
- $post_level = 0;
- }
-
- if( $post_level > 0 ) {
- $patreon_level = $post_level;
- }
-
- $link_interface_item = 'post_unlock_button';
-
- // If this is an image unlock request, override patreon level with image's:
-
- if( isset( $wp->query_vars['patreon-unlock-image'] ) AND $wp->query_vars['patreon-unlock-image'] != '' ) {
-
- $patreon_level = get_post_meta( $wp->query_vars['patreon-unlock-image'], 'patreon_level', true );
-
- if( !$patreon_level OR $patreon_level == 0) {
- $patreon_level = 0;
- }
-
- $link_interface_item = 'image_unlock_button';
-
- }
-
- $client_id = get_option( 'patreon-client-id', false );
-
- if( !$client_id ) {
-
- // No client id, no point in being here. Make it go with an error.
-
- $final_redirect = add_query_arg( 'patreon_message', 'patreon_cant_login_api_error_credentials', $final_redirect );
-
- wp_redirect( $final_redirect );
- exit;
-
- }
-
- $state['final_redirect_uri'] = $final_redirect;
-
- $send_pledge_level = $patreon_level * 100;
-
- // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user + content before going to Patreon flow
-
- $filter_args = array(
- 'link_interface_item' => $link_interface_item,
- 'post' => $post,
- 'post_level' => $post_level,
- 'patreon_level' => $patreon_level,
- 'state' => $state,
- 'user' => wp_get_current_user(),
- );
-
- do_action( 'patreon_do_action_before_universal_flow', $filter_args );
-
- $flow_link = Patreon_Frontend::MakeUniversalFlowLink( $send_pledge_level, $state, $client_id, $post, array('link_interface_item' => $link_interface_item ) );
-
- wp_redirect( $flow_link );
- exit;
-
- }
-
- }
-
- // Catch all
- $redirect = add_query_arg( 'patreon_message', 'no_patreon_action_provided_for_flow', wp_login_url() );
- wp_redirect( $redirect );
- exit;
-
- }
-
-
- if ( strpos( $_SERVER['REQUEST_URI'], '/patreon-authorization/' ) !== false ) {
-
- // First slap the noindex header so search engines wont index this page:
- header( 'X-Robots-Tag: noindex, nofollow' );
-
- // Make sure browsers dont cache this
- header( 'cache-control: no-cache, must-revalidate, max-age=0' );
-
- if( array_key_exists( 'code', $wp->query_vars ) ) {
-
- // Get state vars if they exist
-
- if( $wp->query_vars['state'] !='' ) {
- $state = json_decode( base64_decode( urldecode( $wp->query_vars['state'] ) ), true );
- }
-
- $redirect = false;
-
- // Check if final_redirect exists in state vars - if so, override redirect:
-
- if( isset( $state['final_redirect_uri'] ) AND $state['final_redirect_uri'] != '' ) {
- $redirect = $state['final_redirect_uri'];
- }
-
- // Check if this code was sent for a site connect request
-
- if ( isset( $state['patreon_action'] ) AND $state['patreon_action'] == 'connect_site' ) {
-
- // This code was given for setup process to allow request of credentials. Go ahead:
-
- if ( !current_user_can( 'manage_options' ) ) {
- // If user is not an admin, abort
- echo 'Sorry - to connect your site to Patreon you need to be an admin user.';
- exit;
-
- }
-
- $oauth_client = new Patreon_Oauth;
-
- // Set the client id to plugin wide client id one for setup process
-
- $oauth_client->client_id = PATREON_PLUGIN_CLIENT_ID;
-
- $tokens = $oauth_client->get_tokens( $wp->query_vars['code'], site_url() . '/patreon-authorization/', array( 'scopes' => 'w:identity.clients' ) );
-
- if ( isset( $tokens['access_token'] ) ) {
-
- // Exception - If we are here with a legit access token, re-mark this installation as v2 - can be removed when all installations are using v2
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-can-use-api-v2', true );
-
- // We got auth. Proceed with creating the client
-
- // Create new api object
-
- $api_client = new Patreon_API( $tokens['access_token'] );
-
- $params = array(
- 'data' => array(
- 'type' => 'oauth-client',
- 'attributes' => Patreon_Wordpress::collect_app_info(),
- )
- );
-
- $client_result = $api_client->create_refresh_client( json_encode( $params ) );
-
- if ( isset( $client_result['data']['type'] ) AND $client_result['data']['type'] == 'oauth-client' ) {
-
- $client_id = $client_result['data']['id'];
- $client_secret = $client_result['data']['attributes']['client_secret'];
- $creator_access_token = $client_result['included'][0]['attributes']['access_token'];
- $creator_refresh_token = $client_result['included'][0]['attributes']['refresh_token'];
-
- // Some error handling here - later to be updated
-
- if ( !isset( $client_id ) OR $client_id == '' OR
- !isset( $client_secret ) OR $client_secret == '' OR
- !isset( $creator_access_token ) OR $creator_access_token == '' OR
- !isset( $creator_refresh_token ) OR $creator_refresh_token == ''
- )
- {
- // One or more of the app details is kaput. Redirect with an error message.
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0&patreon_message=error_missing_credentials') );
- exit;
-
- }
-
- // All good. Update the client details locally
-
- $existing_client_id = get_option( 'patreon-client-id', false );
-
- if ( $existing_client_id != $client_id ) {
- $client_id_updated = update_option('patreon-client-id', sanitize_text_field( $client_id ) );
- }
- else {
- $client_id_updated = true;
- }
-
-
- if ( $client_id_updated AND
- update_option('patreon-client-secret', sanitize_text_field( $client_secret ) ) AND
- update_option('patreon-creators-access-token', sanitize_text_field( $creator_access_token ) ) AND
- update_option('patreon-creators-refresh-token', sanitize_text_field( $creator_refresh_token ) )
- ) {
- // All succeeded.
-
- // Save entire return to options
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-setup-done', true );
- update_option( 'patreon-redirect_to_setup_wizard', false );
- update_option( 'patreon-setup-wizard-last-call-result', $client_result );
-
- delete_option( 'patreon-creator-access-token-401' );
-
- // Redirect to success screen
-
- // First apply a filter so that 3rd party addons can redirect to a custom final screen
-
- // Check if post syncing is set up, if not, redirect to post sync page.
-
- $setup_final_redirect = apply_filters( 'ptrn/setup_wizard_final_redirect', admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=final') );
-
- if ( !get_option( 'patreon-post-sync-set-up', false ) ) {
-
- // Post sync not set up. Redirect it to relevant page
-
- $setup_final_redirect = apply_filters( 'ptrn/setup_wizard_post_sync_redirect', admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=post_sync_0') );
-
- }
-
-
- wp_redirect( $setup_final_redirect );
- exit;
-
- }
-
- }
-
- // If we are here, something else is wrong. Come out with an error
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0&patreon_message=failure_obtaining_credentials') );
- exit;
-
- }
- else {
-
- // No auth. Error handling here.
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0&patreon_message=no_auth_for_client_creation') );
- exit;
-
- }
-
- }
-
- // Check if this code was sent for a site reconnect request
- // This block is separate from the site connect block to allow for potential differentiation in connect and reconnect flow
-
- if ( isset( $state['patreon_action'] ) AND $state['patreon_action'] == 'reconnect_site' ) {
-
- // This code was given for setup process to allow request of credentials. Go ahead:
-
- if ( !current_user_can( 'manage_options' ) ) {
- // If user is not an admin, abort
- echo 'Sorry - to reconnect your site to Patreon you need to be an admin user.';
- exit;
-
- }
-
- $oauth_client = new Patreon_Oauth;
-
- // Set the client id to plugin wide client id one for setup process
-
- $oauth_client->client_id = PATREON_PLUGIN_CLIENT_ID;
-
- $tokens = $oauth_client->get_tokens( $wp->query_vars['code'], site_url() . '/patreon-authorization/', array( 'scopes' => 'w:identity.clients' ) );
-
- if ( isset( $tokens['access_token'] ) ) {
-
- // We got auth. Proceed with creating the client
-
- // Exception - If we are here with a legit access token, re-mark this installation as v2 - can be removed when all installations are using v2
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-can-use-api-v2', true );
-
- // Create new api object
-
- $api_client = new Patreon_API( $tokens['access_token'] );
-
- $params = array(
- 'data' => array(
- 'type' => 'oauth-client',
- 'attributes' => Patreon_Wordpress::collect_app_info(),
- )
- );
-
- $client_result = $api_client->create_refresh_client( json_encode( $params ) );
-
- if ( isset( $client_result['data']['type'] ) AND $client_result['data']['type'] == 'oauth-client' ) {
-
- $client_id = $client_result['data']['id'];
- $client_secret = $client_result['data']['attributes']['client_secret'];
- $creator_access_token = $client_result['included'][0]['attributes']['access_token'];
- $creator_refresh_token = $client_result['included'][0]['attributes']['refresh_token'];
-
- // Some error handling here - later to be updated
-
- if ( !isset( $client_id ) OR $client_id == '' OR
- !isset( $client_secret ) OR $client_secret == '' OR
- !isset( $creator_access_token ) OR $creator_access_token == '' OR
- !isset( $creator_refresh_token ) OR $creator_refresh_token == ''
- )
- {
- // One or more of the app details is kaput. Redirect with an error message.
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0&patreon_message=error_missing_credentials') );
- exit;
-
- }
-
- // All good. Update the client details locally
-
- $existing_client_id = get_option( 'patreon-client-id', false );
-
- if ( $existing_client_id == $client_id AND
- update_option('patreon-client-secret', sanitize_text_field( $client_secret ) ) AND
- update_option('patreon-creators-access-token', sanitize_text_field( $creator_access_token ) ) AND
- update_option('patreon-creators-refresh-token', sanitize_text_field( $creator_refresh_token ) )
- ) {
- // All succeeded.
-
- // Save entire return to options
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-setup-done', true );
- update_option( 'patreon-redirect_to_setup_wizard', false );
- update_option( 'patreon-setup-wizard-last-call-result', $client_result );
-
- delete_option( 'patreon-creator-access-token-401' );
-
- // Redirect to success screen
-
- // First apply a filter so that 3rd party addons can redirect to a custom final screen
-
- $setup_final_redirect = apply_filters( 'ptrn/setup_wizard_final_redirect', admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_final') );
-
- wp_redirect( $setup_final_redirect );
- exit;
-
- }
-
- }
-
- // If we are here, something else is wrong. Come out with an error
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0&patreon_message=failure_obtaining_credentials') );
- exit;
-
-
- }
- else {
-
- // No auth. Error handling here.
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0&patreon_message=no_auth_for_client_creation') );
- exit;
-
-
- }
-
- }
-
-
- $redirect = apply_filters( 'ptrn/redirect', $redirect );
-
- if( get_option( 'patreon-client-id', false ) == false || get_option( 'patreon-client-secret', false ) == false ) {
-
- /* redirect to homepage because of oauth client_id or secure_key error */
- $redirect = add_query_arg( 'patreon_message', 'patreon_api_credentials_missing', $redirect );
- wp_redirect( $redirect );
- exit;
-
- } else {
- $oauth_client = new Patreon_Oauth;
- }
-
- $tokens = $oauth_client->get_tokens( $wp->query_vars['code'], site_url() . '/patreon-authorization/' );
-
- if( array_key_exists( 'error', $tokens ) ) {
-
- if( $tokens['error']=='invalid_client' ) {
-
- // Credentials are wrong. Redirect with an informative message
- $redirect = add_query_arg( 'patreon_message', 'patreon_cant_login_api_error_credentials', $redirect );
-
- }
- else {
-
- // Some other error from api. Append the message from Patreon too.
- $redirect = add_query_arg( 'patreon_message', 'patreon_cant_login_api_error', $redirect );
- $redirect = add_query_arg( 'patreon_error', $tokens['error'], $redirect );
-
- }
-
- wp_redirect( $redirect );
- exit;
-
- } else {
-
- $api_client = new Patreon_API( $tokens['access_token'] );
-
- $user_response = $api_client->fetch_user();
-
- // Check out if there is a proper user return.
-
- if( !is_array( $user_response ) OR !isset( $user_response['data']['id'] ) ) {
-
- // We didnt get user info back from the API. Cancel with a message
-
- $redirect = add_query_arg( 'patreon_message', 'patreon_couldnt_acquire_user_details', $redirect );
-
- wp_redirect( $redirect );
- exit;
-
- }
-
- if( apply_filters( 'ptrn/force_strict_oauth', get_option( 'patreon-enable-strict-oauth', false ) ) ) {
- $user = Patreon_Login::updateLoggedInUserForStrictoAuth( $user_response, $tokens, $redirect );
- } else {
- $user = Patreon_Login::createOrLogInUserFromPatreon( $user_response, $tokens, $redirect );
- }
-
- //shouldn't get here
- $redirect = add_query_arg( 'patreon_message', 'patreon_weird_redirection_at_login', $redirect );
-
- wp_redirect( $redirect );
- exit;
-
- }
-
- } else {
-
- $redirect = add_query_arg( 'patreon_message', 'no_code_receved_from_patreon', wp_login_url() );
- wp_redirect( $redirect );
- exit;
-
- }
-
- }
-
- if ( strpos( $_SERVER['REQUEST_URI'], '/patreon-webhooks/' ) !== false ) {
-
- // First slap the noindex header so search engines wont index this page:
- header( 'X-Robots-Tag: noindex, nofollow' );
-
- // Make sure browsers dont cache this
- header( 'cache-control: no-cache, must-revalidate, max-age=0' );
-
- // Abort if apiv ersion used is not v2
- $api_version = get_option( 'patreon-installation-api-version', '1' );
-
- if ( $api_version != '2' ) {
- return;
- }
-
- if( array_key_exists( 'patreon-webhooks', $wp->query_vars ) ) {
-
- $webhook_info = get_option( 'patreon-post-sync-webhook', false );
-
- if ( !$webhook_info ) {
- return;
- }
-
- global $Patreon_Wordpress;
-
- // Parts taken from FB's webhook example
- $secret = $webhook_info['data']['attributes']['secret'];
- $raw_post_data = file_get_contents('php://input');
- //$header_signature = $_SERVER['X-Patreon-Signature'];
-
- $header_signature = '';
- $event = '';
- $headers = $Patreon_Wordpress->get_all_headers();
-
- // If this is not an event from Patreon bail out
-
- if ( !isset( $headers['X-Patreon-Signature'] ) OR !isset( $headers['X-Patreon-Event'] ) ) {
- return;
- }
-
- $header_signature = $headers['X-Patreon-Signature'];
- $event = $headers['X-Patreon-Event'];
-
- // Signature matching
- $expected_signature = hash_hmac( 'md5', $raw_post_data, $secret );
-
- $verified = false;
-
- if ( is_string( $header_signature ) AND hash_equals( $header_signature, $expected_signature ) ) {
- $verified = true;
- }
-
- if ( !$verified ) {
- return;
- }
-
- // This is a verified post from Patreon - process
-
- // Check if raw post data exists - if not bail out
- if ( strlen( $raw_post_data ) == 0 ) {
- return;
- }
-
- $event_info = json_decode( $raw_post_data, true );
-
- // Check if event is a legitimate array - if not bail out
- if ( !( is_array( $event_info ) AND count( $event_info ) > 0 ) ) {
- return;
- }
-
- // This is a legitimate Patreon event - process
-
- if( $event == 'posts:publish' ) {
-
- // Add post.
-
- $patreon_post_id = $event_info['data']['id'];
-
- // Get Patreon post
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
- $client_id = get_option( 'patreon-client-id', false );
-
- $patreon_post = false;
-
- if ( $creator_access_token AND $client_id ) {
-
- // Create new api object
- $api_client = new Patreon_API( $creator_access_token );
-
- $patreon_post = $api_client->get_post( $patreon_post_id );
-
- }
-
- if ( !$patreon_post OR !isset( $patreon_post['data']['id'] ) OR $patreon_post['data']['id'] == '' ) {
- // Couldn't get this post. Skip
- return;
- }
-
- $result = $Patreon_Wordpress::$patreon_content_sync->add_update_patreon_post( $patreon_post );
-
- if ( !$result ) {
- // Failure. Error handling if necessary
-
- }
-
- }
-
- if( $event == 'posts:update' ) {
-
- // Update relevant post.
-
- $patreon_post_id = $event_info['data']['id'];
-
- // Get Patreon post
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
- $client_id = get_option( 'patreon-client-id', false );
-
- $patreon_post = false;
-
- if ( $creator_access_token AND $client_id ) {
-
- // Create new api object
- $api_client = new Patreon_API( $creator_access_token );
-
- $patreon_post = $api_client->get_post( $patreon_post_id );
-
- }
-
- if ( !$patreon_post OR !isset( $patreon_post['data']['id'] ) OR $patreon_post['data']['id'] == '' ) {
- // Couldn't get this post. Skip
- return;
- }
-
- if ( get_option( 'patreon-update-posts', 'no' ) == 'yes' ) {
-
- $result = $Patreon_Wordpress::$patreon_content_sync->add_update_patreon_post( $patreon_post );
-
- if ( !$result ) {
- // Failure. Error handling if necessary - not needed for now
- }
- }
-
- }
-
- if( $event == 'posts:delete' ) {
-
- // Delete relevant post.
-
- // Get matching WP post from post meta:
-
- $patreon_post_id = $event_info['data']['id'];
-
- if ( get_option( 'patreon-remove-deleted-posts', 'no' ) == 'yes' ) {
-
- $wp_post_id = $Patreon_Wordpress::$patreon_content_sync->get_matching_post_by_patreon_post_id( $patreon_post_id );
-
- $result = $Patreon_Wordpress::$patreon_content_sync->delete_patreon_post( $wp_post_id );
-
- if ( !$result OR is_null( $result ) ) {
- // Delete failed - this may be a local issue. Can be used to give error to Patreon via header in future
- }
- }
-
- }
-
- status_header( 200 );
- nocache_headers();
- exit;
-
- }
-
- }
-
- }
-
-}
\ No newline at end of file
+class Patreon_Routing
+{
+ public function __construct()
+ {
+ add_action('generate_rewrite_rules', [$this, 'add_rewrite_rules']);
+ add_filter('query_vars', [$this, 'query_vars']);
+ add_action('parse_request', [$this, 'parse_request']);
+ add_action('init', [$this, 'force_rewrite_rules']);
+ }
+
+ public function force_rewrite_rules()
+ {
+ global $wp_rewrite;
+
+ if (false == get_option('patreon-rewrite-rules-flushed', false)) {
+ $wp_rewrite->flush_rules();
+ // Refresh/add htaccess rules:
+ Patreon_Protect::removePatreonRewriteRules();
+ Patreon_Protect::addPatreonRewriteRules();
+ update_option('patreon-rewrite-rules-flushed', true);
+ }
+ }
+
+ public function add_rewrite_rules($wp_rewrite)
+ {
+ $rules = [
+ 'patreon-authorization\/?$' => 'index.php?patreon-oauth=true',
+ 'patreon-flow\/?$' => 'index.php?patreon-flow=true',
+ 'patreon-setup\/?$' => 'index.php?patreon-setup=true',
+ 'patreon-webhooks\/?$' => 'index.php?patreon-webhooks=true',
+ ];
+
+ $wp_rewrite->rules = $rules + (array) $wp_rewrite->rules;
+ }
+
+ public function query_vars($public_query_vars)
+ {
+ array_push($public_query_vars, 'patreon-oauth');
+ array_push($public_query_vars, 'patreon-flow');
+ array_push($public_query_vars, 'patreon-unlock-post');
+ array_push($public_query_vars, 'patreon-unlock-image');
+ array_push($public_query_vars, 'patreon-direct-unlock');
+ array_push($public_query_vars, 'patreon-post-id');
+ array_push($public_query_vars, 'patreon-login');
+ array_push($public_query_vars, 'patreon-final-redirect');
+ array_push($public_query_vars, 'code');
+ array_push($public_query_vars, 'state');
+ array_push($public_query_vars, 'patreon-redirect');
+ array_push($public_query_vars, 'patreon-webhooks');
+
+ return $public_query_vars;
+ }
+
+ public function parse_request(&$wp)
+ {
+ if (false !== strpos($_SERVER['REQUEST_URI'], '/patreon-flow/')) {
+ // First slap the noindex header so search engines wont index this page:
+ header('X-Robots-Tag: noindex, nofollow');
+
+ // Make sure browsers dont cache this
+ header('cache-control: no-cache, must-revalidate, max-age=0');
+
+ if (array_key_exists('patreon-login', $wp->query_vars)) {
+ // Login intent.
+
+ $final_redirect = wp_login_url();
+
+ if (isset($wp->query_vars['patreon-final-redirect'])) {
+ $final_redirect = $wp->query_vars['patreon-final-redirect'];
+ }
+
+ $state = [
+ 'final_redirect_uri' => $final_redirect,
+ ];
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user + content before going to Patreon flow
+
+ $filter_args = [
+ 'state' => $state,
+ 'user' => wp_get_current_user(),
+ ];
+
+ do_action('patreon_do_action_before_patreon_login', $filter_args);
+
+ $login_url = Patreon_Frontend::patreonMakeLoginLink(false, $state);
+
+ wp_redirect($login_url);
+ exit;
+ }
+
+ if (array_key_exists('patreon-direct-unlock', $wp->query_vars)) {
+ $final_redirect = wp_login_url();
+
+ if (isset($wp->query_vars['patreon-direct-unlock'])) {
+ $patreon_level = $wp->query_vars['patreon-direct-unlock'];
+ $redirect = base64_decode(urldecode($wp->query_vars['patreon-redirect']));
+
+ if (!$patreon_level or '' == $patreon_level or 0 == $patreon_level) {
+ $patreon_level = 1;
+ }
+
+ $client_id = get_option('patreon-client-id', false);
+
+ if (!$client_id) {
+ // No client id, no point in being here. Make it go with an error.
+
+ $final_redirect = add_query_arg('patreon_message', 'patreon_cant_login_api_error_credentials', $final_redirect);
+
+ wp_redirect($final_redirect);
+ exit;
+ }
+
+ $post = false;
+
+ // If post id set, get the post
+ if (isset($wp->query_vars['patreon-post-id'])) {
+ $post = get_post($wp->query_vars['patreon-post-id']);
+ }
+
+ $link_interface_item = 'direct_unlock_button';
+ $state['final_redirect_uri'] = $redirect;
+ $send_pledge_level = $patreon_level * 100;
+
+ $flow_link = Patreon_Frontend::MakeUniversalFlowLink($send_pledge_level, $state, $client_id, $post, ['link_interface_item' => $link_interface_item]);
+
+ wp_redirect($flow_link);
+ exit;
+ }
+ }
+
+ if (array_key_exists('patreon-unlock-post', $wp->query_vars)) {
+ // We have a login/flow request, Get the post id
+
+ if (isset($wp->query_vars['patreon-unlock-post'])) {
+ // First check if entire site is locked, get the level for locking.
+
+ $patreon_level = get_option('patreon-lock-entire-site', false);
+
+ // Account for any value the creator can put into this option, and also the default false
+ if (!$patreon_level or '' == $patreon_level) {
+ $patreon_level = 0;
+ }
+
+ if ('' != $wp->query_vars['patreon-unlock-post']) {
+ // Got post id. Get the post, and prepare necessary vars. Get the post first
+
+ $post = get_post($wp->query_vars['patreon-unlock-post']);
+
+ // If there is no post var, and entire site is not locked, no point in being here
+ if (!$post and 0 == $patreon_level) {
+ // No post, no point in being here.
+
+ $final_redirect = add_query_arg('patreon_message', 'patreon_no_post_id_to_unlock_post', $final_redirect);
+ wp_redirect(home_url());
+ exit;
+ }
+ }
+
+ // Start with home url for redirect. If post is valid, get permalink.
+
+ $final_redirect = home_url();
+
+ if ($post) {
+ $final_redirect = get_permalink($post->ID);
+ }
+
+ // Check if specific level is given for this post:
+
+ $post_level = get_post_meta($post->ID, 'patreon-level', true);
+
+ // get post meta returns empty if no value is found. If so, set the value to 0.
+
+ if ('' == $post_level) {
+ $post_level = 0;
+ }
+
+ if ($post_level > 0) {
+ $patreon_level = $post_level;
+ }
+
+ $link_interface_item = 'post_unlock_button';
+
+ // If this is an image unlock request, override patreon level with image's:
+
+ if (isset($wp->query_vars['patreon-unlock-image']) and '' != $wp->query_vars['patreon-unlock-image']) {
+ $patreon_level = get_post_meta($wp->query_vars['patreon-unlock-image'], 'patreon_level', true);
+
+ if (!$patreon_level or 0 == $patreon_level) {
+ $patreon_level = 0;
+ }
+
+ $link_interface_item = 'image_unlock_button';
+ }
+
+ $client_id = get_option('patreon-client-id', false);
+
+ if (!$client_id) {
+ // No client id, no point in being here. Make it go with an error.
+
+ $final_redirect = add_query_arg('patreon_message', 'patreon_cant_login_api_error_credentials', $final_redirect);
+
+ wp_redirect($final_redirect);
+ exit;
+ }
+
+ $state['final_redirect_uri'] = $final_redirect;
+
+ $send_pledge_level = $patreon_level * 100;
+
+ // Below filter vars and the following filter allows plugin devs to acquire/filter info about Patron/user + content before going to Patreon flow
+
+ $filter_args = [
+ 'link_interface_item' => $link_interface_item,
+ 'post' => $post,
+ 'post_level' => $post_level,
+ 'patreon_level' => $patreon_level,
+ 'state' => $state,
+ 'user' => wp_get_current_user(),
+ ];
+
+ do_action('patreon_do_action_before_universal_flow', $filter_args);
+
+ $flow_link = Patreon_Frontend::MakeUniversalFlowLink($send_pledge_level, $state, $client_id, $post, ['link_interface_item' => $link_interface_item]);
+
+ wp_redirect($flow_link);
+ exit;
+ }
+ }
+
+ // Catch all
+ $redirect = add_query_arg('patreon_message', 'no_patreon_action_provided_for_flow', wp_login_url());
+ wp_redirect($redirect);
+ exit;
+ }
+
+ if (false !== strpos($_SERVER['REQUEST_URI'], '/patreon-authorization/')) {
+ // First slap the noindex header so search engines wont index this page:
+ header('X-Robots-Tag: noindex, nofollow');
+
+ // Make sure browsers dont cache this
+ header('cache-control: no-cache, must-revalidate, max-age=0');
+
+ if (array_key_exists('code', $wp->query_vars)) {
+ // Get state vars if they exist
+
+ if ('' != $wp->query_vars['state']) {
+ $state = json_decode(base64_decode(urldecode($wp->query_vars['state'])), true);
+ }
+
+ $redirect = false;
+
+ // Check if final_redirect exists in state vars - if so, override redirect:
+
+ if (isset($state['final_redirect_uri']) and '' != $state['final_redirect_uri']) {
+ $redirect = $state['final_redirect_uri'];
+ }
+
+ // Check if this code was sent for a site connect request
+
+ if (isset($state['patreon_action']) and 'connect_site' == $state['patreon_action']) {
+ // This code was given for setup process to allow request of credentials. Go ahead:
+
+ if (!current_user_can('manage_options')) {
+ // If user is not an admin, abort
+ echo 'Sorry - to connect your site to Patreon you need to be an admin user.';
+ exit;
+ }
+
+ $oauth_client = new Patreon_Oauth();
+
+ // Set the client id to plugin wide client id one for setup process
+
+ $oauth_client->client_id = PATREON_PLUGIN_CLIENT_ID;
+
+ $tokens = $oauth_client->get_tokens($wp->query_vars['code'], site_url().'/patreon-authorization/', ['scopes' => 'w:identity.clients']);
+
+ if (isset($tokens['access_token'])) {
+ // Exception - If we are here with a legit access token, re-mark this installation as v2 - can be removed when all installations are using v2
+
+ update_option('patreon-installation-api-version', '2');
+ update_option('patreon-can-use-api-v2', true);
+
+ // We got auth. Proceed with creating the client
+
+ // Create new api object
+
+ $api_client = new Patreon_API($tokens['access_token']);
+
+ $params = [
+ 'data' => [
+ 'type' => 'oauth-client',
+ 'attributes' => Patreon_Wordpress::collect_app_info(),
+ ],
+ ];
+
+ $client_result = $api_client->create_refresh_client(json_encode($params));
+
+ if (isset($client_result['data']['type']) and 'oauth-client' == $client_result['data']['type']) {
+ $client_id = $client_result['data']['id'];
+ $client_secret = $client_result['data']['attributes']['client_secret'];
+ $creator_access_token = $client_result['included'][0]['attributes']['access_token'];
+ $creator_refresh_token = $client_result['included'][0]['attributes']['refresh_token'];
+
+ // Some error handling here - later to be updated
+
+ if (!isset($client_id) or '' == $client_id
+ or !isset($client_secret) or '' == $client_secret
+ or !isset($creator_access_token) or '' == $creator_access_token
+ or !isset($creator_refresh_token) or '' == $creator_refresh_token
+ ) {
+ // One or more of the app details is kaput. Redirect with an error message.
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0&patreon_message=error_missing_credentials'));
+ exit;
+ }
+
+ // All good. Update the client details locally
+
+ $existing_client_id = get_option('patreon-client-id', false);
+
+ if ($existing_client_id != $client_id) {
+ $client_id_updated = update_option('patreon-client-id', sanitize_text_field($client_id));
+ } else {
+ $client_id_updated = true;
+ }
+
+ if ($client_id_updated
+ and update_option('patreon-client-secret', sanitize_text_field($client_secret))
+ and update_option('patreon-creators-access-token', sanitize_text_field($creator_access_token))
+ and update_option('patreon-creators-refresh-token', sanitize_text_field($creator_refresh_token))
+ ) {
+ // All succeeded.
+
+ // Save entire return to options
+
+ update_option('patreon-installation-api-version', '2');
+ update_option('patreon-setup-done', true);
+ update_option('patreon-redirect_to_setup_wizard', false);
+ update_option('patreon-setup-wizard-last-call-result', $client_result);
+
+ delete_option('patreon-creator-access-token-401');
+
+ // Redirect to success screen
+
+ // First apply a filter so that 3rd party addons can redirect to a custom final screen
+
+ // Check if post syncing is set up, if not, redirect to post sync page.
+
+ $setup_final_redirect = apply_filters('ptrn/setup_wizard_final_redirect', admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=final'));
+
+ if (!get_option('patreon-post-sync-set-up', false)) {
+ // Post sync not set up. Redirect it to relevant page
+
+ $setup_final_redirect = apply_filters('ptrn/setup_wizard_post_sync_redirect', admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=post_sync_0'));
+ }
+
+ wp_redirect($setup_final_redirect);
+ exit;
+ }
+ }
+
+ // If we are here, something else is wrong. Come out with an error
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0&patreon_message=failure_obtaining_credentials'));
+ exit;
+ } else {
+ // No auth. Error handling here.
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0&patreon_message=no_auth_for_client_creation'));
+ exit;
+ }
+ }
+
+ // Check if this code was sent for a site reconnect request
+ // This block is separate from the site connect block to allow for potential differentiation in connect and reconnect flow
+
+ if (isset($state['patreon_action']) and 'reconnect_site' == $state['patreon_action']) {
+ // This code was given for setup process to allow request of credentials. Go ahead:
+
+ if (!current_user_can('manage_options')) {
+ // If user is not an admin, abort
+ echo 'Sorry - to reconnect your site to Patreon you need to be an admin user.';
+ exit;
+ }
+
+ $oauth_client = new Patreon_Oauth();
+
+ // Set the client id to plugin wide client id one for setup process
+
+ $oauth_client->client_id = PATREON_PLUGIN_CLIENT_ID;
+
+ $tokens = $oauth_client->get_tokens($wp->query_vars['code'], site_url().'/patreon-authorization/', ['scopes' => 'w:identity.clients']);
+
+ if (isset($tokens['access_token'])) {
+ // We got auth. Proceed with creating the client
+
+ // Exception - If we are here with a legit access token, re-mark this installation as v2 - can be removed when all installations are using v2
+
+ update_option('patreon-installation-api-version', '2');
+ update_option('patreon-can-use-api-v2', true);
+
+ // Create new api object
+
+ $api_client = new Patreon_API($tokens['access_token']);
+
+ $params = [
+ 'data' => [
+ 'type' => 'oauth-client',
+ 'attributes' => Patreon_Wordpress::collect_app_info(),
+ ],
+ ];
+
+ $client_result = $api_client->create_refresh_client(json_encode($params));
+
+ if (isset($client_result['data']['type']) and 'oauth-client' == $client_result['data']['type']) {
+ $client_id = $client_result['data']['id'];
+ $client_secret = $client_result['data']['attributes']['client_secret'];
+ $creator_access_token = $client_result['included'][0]['attributes']['access_token'];
+ $creator_refresh_token = $client_result['included'][0]['attributes']['refresh_token'];
+
+ // Some error handling here - later to be updated
+
+ if (!isset($client_id) or '' == $client_id
+ or !isset($client_secret) or '' == $client_secret
+ or !isset($creator_access_token) or '' == $creator_access_token
+ or !isset($creator_refresh_token) or '' == $creator_refresh_token
+ ) {
+ // One or more of the app details is kaput. Redirect with an error message.
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0&patreon_message=error_missing_credentials'));
+ exit;
+ }
+
+ // All good. Update the client details locally
+
+ $existing_client_id = get_option('patreon-client-id', false);
+
+ if ($existing_client_id == $client_id
+ and update_option('patreon-client-secret', sanitize_text_field($client_secret))
+ and update_option('patreon-creators-access-token', sanitize_text_field($creator_access_token))
+ and update_option('patreon-creators-refresh-token', sanitize_text_field($creator_refresh_token))
+ ) {
+ // All succeeded.
+
+ // Save entire return to options
+
+ update_option('patreon-installation-api-version', '2');
+ update_option('patreon-setup-done', true);
+ update_option('patreon-redirect_to_setup_wizard', false);
+ update_option('patreon-setup-wizard-last-call-result', $client_result);
+
+ delete_option('patreon-creator-access-token-401');
+
+ // Redirect to success screen
+
+ // First apply a filter so that 3rd party addons can redirect to a custom final screen
+
+ $setup_final_redirect = apply_filters('ptrn/setup_wizard_final_redirect', admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_final'));
+
+ wp_redirect($setup_final_redirect);
+ exit;
+ }
+ }
+
+ // If we are here, something else is wrong. Come out with an error
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0&patreon_message=failure_obtaining_credentials'));
+ exit;
+ } else {
+ // No auth. Error handling here.
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0&patreon_message=no_auth_for_client_creation'));
+ exit;
+ }
+ }
+
+ $redirect = apply_filters('ptrn/redirect', $redirect);
+
+ if (false == get_option('patreon-client-id', false) || false == get_option('patreon-client-secret', false)) {
+ /* redirect to homepage because of oauth client_id or secure_key error */
+ $redirect = add_query_arg('patreon_message', 'patreon_api_credentials_missing', $redirect);
+ wp_redirect($redirect);
+ exit;
+ } else {
+ $oauth_client = new Patreon_Oauth();
+ }
+
+ $tokens = $oauth_client->get_tokens($wp->query_vars['code'], site_url().'/patreon-authorization/');
+
+ if (array_key_exists('error', $tokens)) {
+ if ('invalid_client' == $tokens['error']) {
+ // Credentials are wrong. Redirect with an informative message
+ $redirect = add_query_arg('patreon_message', 'patreon_cant_login_api_error_credentials', $redirect);
+ } else {
+ // Some other error from api. Append the message from Patreon too.
+ $redirect = add_query_arg('patreon_message', 'patreon_cant_login_api_error', $redirect);
+ $redirect = add_query_arg('patreon_error', $tokens['error'], $redirect);
+ }
+
+ wp_redirect($redirect);
+ exit;
+ } else {
+ $api_client = new Patreon_API($tokens['access_token']);
+
+ $user_response = $api_client->fetch_user();
+
+ // Check out if there is a proper user return.
+
+ if (!is_array($user_response) or !isset($user_response['data']['id'])) {
+ // We didnt get user info back from the API. Cancel with a message
+
+ $redirect = add_query_arg('patreon_message', 'patreon_couldnt_acquire_user_details', $redirect);
+
+ wp_redirect($redirect);
+ exit;
+ }
+
+ if (apply_filters('ptrn/force_strict_oauth', get_option('patreon-enable-strict-oauth', false))) {
+ $user = Patreon_Login::updateLoggedInUserForStrictoAuth($user_response, $tokens, $redirect);
+ } else {
+ $user = Patreon_Login::createOrLogInUserFromPatreon($user_response, $tokens, $redirect);
+ }
+
+ // shouldn't get here
+ $redirect = add_query_arg('patreon_message', 'patreon_weird_redirection_at_login', $redirect);
+
+ wp_redirect($redirect);
+ exit;
+ }
+ } else {
+ $redirect = add_query_arg('patreon_message', 'no_code_receved_from_patreon', wp_login_url());
+ wp_redirect($redirect);
+ exit;
+ }
+ }
+
+ if (false !== strpos($_SERVER['REQUEST_URI'], '/patreon-webhooks/')) {
+ // First slap the noindex header so search engines wont index this page:
+ header('X-Robots-Tag: noindex, nofollow');
+
+ // Make sure browsers dont cache this
+ header('cache-control: no-cache, must-revalidate, max-age=0');
+
+ // Abort if apiv ersion used is not v2
+ $api_version = get_option('patreon-installation-api-version', '1');
+
+ if ('2' != $api_version) {
+ return;
+ }
+
+ if (array_key_exists('patreon-webhooks', $wp->query_vars)) {
+ $webhook_info = get_option('patreon-post-sync-webhook', false);
+
+ if (!$webhook_info) {
+ return;
+ }
+
+ global $Patreon_Wordpress;
+
+ // Parts taken from FB's webhook example
+ $secret = $webhook_info['data']['attributes']['secret'];
+ $raw_post_data = file_get_contents('php://input');
+ // $header_signature = $_SERVER['X-Patreon-Signature'];
+
+ $header_signature = '';
+ $event = '';
+ $headers = $Patreon_Wordpress->get_all_headers();
+
+ // If this is not an event from Patreon bail out
+
+ if (!isset($headers['X-Patreon-Signature']) or !isset($headers['X-Patreon-Event'])) {
+ return;
+ }
+
+ $header_signature = $headers['X-Patreon-Signature'];
+ $event = $headers['X-Patreon-Event'];
+
+ // Signature matching
+ $expected_signature = hash_hmac('md5', $raw_post_data, $secret);
+
+ $verified = false;
+
+ if (is_string($header_signature) and hash_equals($header_signature, $expected_signature)) {
+ $verified = true;
+ }
+
+ if (!$verified) {
+ return;
+ }
+
+ // This is a verified post from Patreon - process
+
+ // Check if raw post data exists - if not bail out
+ if (0 == strlen($raw_post_data)) {
+ return;
+ }
+
+ $event_info = json_decode($raw_post_data, true);
+
+ // Check if event is a legitimate array - if not bail out
+ if (!(is_array($event_info) and count($event_info) > 0)) {
+ return;
+ }
+
+ // This is a legitimate Patreon event - process
+
+ if ('posts:publish' == $event) {
+ // Add post.
+
+ $patreon_post_id = $event_info['data']['id'];
+
+ // Get Patreon post
+
+ $creator_access_token = get_option('patreon-creators-access-token', false);
+ $client_id = get_option('patreon-client-id', false);
+
+ $patreon_post = false;
+
+ if ($creator_access_token and $client_id) {
+ // Create new api object
+ $api_client = new Patreon_API($creator_access_token);
+
+ $patreon_post = $api_client->get_post($patreon_post_id);
+ }
+
+ if (!$patreon_post or !isset($patreon_post['data']['id']) or '' == $patreon_post['data']['id']) {
+ // Couldn't get this post. Skip
+ return;
+ }
+
+ $result = $Patreon_Wordpress::$patreon_content_sync->add_update_patreon_post($patreon_post);
+
+ if (!$result) {
+ // Failure. Error handling if necessary
+ }
+ }
+
+ if ('posts:update' == $event) {
+ // Update relevant post.
+
+ $patreon_post_id = $event_info['data']['id'];
+
+ // Get Patreon post
+
+ $creator_access_token = get_option('patreon-creators-access-token', false);
+ $client_id = get_option('patreon-client-id', false);
+
+ $patreon_post = false;
+
+ if ($creator_access_token and $client_id) {
+ // Create new api object
+ $api_client = new Patreon_API($creator_access_token);
+
+ $patreon_post = $api_client->get_post($patreon_post_id);
+ }
+
+ if (!$patreon_post or !isset($patreon_post['data']['id']) or '' == $patreon_post['data']['id']) {
+ // Couldn't get this post. Skip
+ return;
+ }
+
+ if ('yes' == get_option('patreon-update-posts', 'no')) {
+ $result = $Patreon_Wordpress::$patreon_content_sync->add_update_patreon_post($patreon_post);
+
+ if (!$result) {
+ // Failure. Error handling if necessary - not needed for now
+ }
+ }
+ }
+
+ if ('posts:delete' == $event) {
+ // Delete relevant post.
+
+ // Get matching WP post from post meta:
+
+ $patreon_post_id = $event_info['data']['id'];
+
+ if ('yes' == get_option('patreon-remove-deleted-posts', 'no')) {
+ $wp_post_id = $Patreon_Wordpress::$patreon_content_sync->get_matching_post_by_patreon_post_id($patreon_post_id);
+
+ $result = $Patreon_Wordpress::$patreon_content_sync->delete_patreon_post($wp_post_id);
+
+ if (!$result or is_null($result)) {
+ // Delete failed - this may be a local issue. Can be used to give error to Patreon via header in future
+ }
+ }
+ }
+
+ status_header(200);
+ nocache_headers();
+ exit;
+ }
+ }
+ }
+}
diff --git a/classes/patreon_user_profiles.php b/classes/patreon_user_profiles.php
index b91fe4b7..f073cb4f 100644
--- a/classes/patreon_user_profiles.php
+++ b/classes/patreon_user_profiles.php
@@ -1,173 +1,159 @@
-
-
-
-
-
-
+ ID)) {
+ $old = get_user_by('id', $user->ID);
+
+ if ($user->user_email != $old->user_email && (!current_user_can('create_users'))) {
+ $user->user_email = $old->user_email;
+ }
+ }
+ }
+}
diff --git a/classes/patreon_wordpress.php b/classes/patreon_wordpress.php
index bfd9a23e..e0dc6792 100644
--- a/classes/patreon_wordpress.php
+++ b/classes/patreon_wordpress.php
@@ -1,3236 +1,3003 @@
ID ) OR $user->ID == 0 ) {
- return false;
- }
-
- if ( isset( self::$patreon_user_info_cache[$user->ID] ) ) {
- return self::$patreon_user_info_cache[$user->ID];
- }
-
- // Check if the user just returned from any Patreon oAuth flow (pledge, login, register etc)
-
- $user_just_returned_from_patreon_flow = false;
-
- $user_last_returned_from_flow = get_user_meta( $user->ID, 'patreon_user_last_returned_from_any_flow', true );
-
- if ( ( $user_last_returned_from_flow AND $user_last_returned_from_flow != '' ) AND $user_last_returned_from_flow >= ( time() - 2 ) ) {
- // User returned from a Patreon flow within the last 2 seconds
- $user_just_returned_from_patreon_flow = true;
- }
-
- // Use the cached patron info if it exists, if its newer than 2 seconds, and the user has not returned from any Patreon flow (login or pledge)
- // 2 secs should cover the case in which the patrons make a new pledge at patreon.com and visit/refresh a remote app or a site page.
-
- // Returns empty string if it does not exist
- $user_response_timestamp = get_user_meta( $user->ID, 'patreon_latest_patron_info_timestamp', true );
-
- if ( !$user_just_returned_from_patreon_flow AND ( $user_response_timestamp AND $user_response_timestamp != '' ) AND $user_response_timestamp >= ( time() - 2 ) ) {
- // Cached patron info is fresh. Use it.
-
- $user_response = get_user_meta( $user->ID, 'patreon_latest_patron_info', true );
-
- // Add the info to the page-run cache and return it
- return Patreon_Wordpress::add_to_patreon_user_info_cache( $user->ID, $user_response );
-
- }
-
- /* get user meta data and query patreon api */
- $patreon_access_token = get_user_meta( $user->ID, 'patreon_access_token', true );
-
- if ( $patreon_access_token != '' ) {
-
- $api_client = new Patreon_API( $patreon_access_token );
-
- // Get the user from the API
- $user_response = $api_client->fetch_user();
-
- // Here we check the returned result if its valid
-
- if ( isset( $user_response['included'][0] ) AND is_array( $user_response['included'][0] ) ) {
- // Valid return. Save it with timestamp
-
- update_user_meta( $user->ID, 'patreon_latest_patron_info', $user_response );
- update_user_meta( $user->ID, 'patreon_latest_patron_info_timestamp', time() );
-
- return Patreon_Wordpress::add_to_patreon_user_info_cache( $user->ID, $user_response );
-
- }
-
- // Couldnt get user from Patreon. Try to refresh tokens if it was a token error
- $token_refreshed = false;
-
- if ( isset( $user_response['errors'] ) && is_array( $user_response['errors'] ) ) {
-
- foreach ( $user_response['errors'] as $error ) {
-
- if( $error['code'] == 1 ) {
-
- $token_refreshed = self::refresh_user_access_token( $user );
-
- }
- }
- }
-
- // Reload token
- $patreon_access_token = get_user_meta( $user->ID, 'patreon_access_token', true );
-
- if ( $token_refreshed AND $patreon_access_token != '' ) {
-
- $api_client = new Patreon_API( $patreon_access_token );
-
- $user_response = $api_client->fetch_user();
-
- if ( isset( $user_response['included'][0] ) AND is_array( $user_response['included'][0] ) ) {
-
- // Valid return. Save it with timestamp
-
- update_user_meta( $user->ID, 'patreon_latest_patron_info', $user_response );
- update_user_meta( $user->ID, 'patreon_latest_patron_info_timestamp', time() );
-
- return Patreon_Wordpress::add_to_patreon_user_info_cache( $user->ID, $user_response );
-
- }
-
- }
-
- // For whatsoever reason the returns are not valid and we cant refresh the user
- // Check if a saved return exists for this user
-
- $user_response = get_user_meta( $user->ID, 'patreon_latest_patron_info', true );
- $user_response_timestamp = get_user_meta( $user->ID, 'patreon_latest_patron_info_timestamp', true );
-
- // Check if there is a valid saved user return and whether it has a timestamp within desired range
- if ( isset( $user_response['included'][0] ) AND is_array( $user_response['included'][0] ) AND $user_response_timestamp >= ( time() - ( 3600 * 24 * 3 ) ) ) {
-
- return Patreon_Wordpress::add_to_patreon_user_info_cache( $user->ID, $user_response );
- }
-
- }
-
- // All failed - return false
-
- return Patreon_Wordpress::add_to_patreon_user_info_cache( $user->ID, false );
-
- }
-
- static function refresh_user_access_token( $user ) {
-
- $refresh_token = get_user_meta( $user->ID, 'patreon_refresh_token', true );
-
- $oauth_client = new Patreon_Oauth;
- $tokens = $oauth_client->refresh_token( $refresh_token, site_url().'/patreon-authorization/' );
-
- if ( isset( $tokens['access_token'] ) ) {
-
- update_user_meta( $user->ID, 'patreon_refresh_token', $tokens['refresh_token'] );
- update_user_meta( $user->ID, 'patreon_access_token', $tokens['access_token'] );
-
- return $tokens['access_token'];
- }
-
- return false;
-
- }
-
- static function updatePatreonUser() {
-
- /* check if current user is loggedin, get ID */
-
- if ( is_user_logged_in() == false ) {
- return false;
- }
-
- $user = wp_get_current_user();
-
- if ( $user == false ) {
- return false;
- }
-
- $last_update = get_user_meta( $user->ID, 'patreon_user_details_last_updated', true );
-
- // If last update time is not empty and it is closer to time() than one day, dont update
- if ( !( $last_update == '' OR ( ( time() - $last_update ) > 86400 ) ) ) {
- return false;
- }
-
- /* query Patreon API to get users patreon details */
- $user_response = self::getPatreonUser( $user );
-
- if ( $user_response == false ) {
- return false;
- }
-
- if ( isset( $user_response['data'] ) ) {
-
- // Set the update time
- update_user_meta( $user->ID, 'patreon_user_details_last_updated', time() );
-
- /* all the details you want to update on wordpress user account */
- update_user_meta( $user->ID, 'patreon_user', $user_response['data']['attributes']['vanity'] );
-
- $patreon_created = '';
- if ( isset( $user_response['data']['attributes']['created'] ) ) {
- $patreon_created = $user_response['data']['attributes']['created'];
- }
-
- update_user_meta( $user->ID, 'patreon_created', $patreon_created );
- update_user_meta( $user->ID, 'user_firstname', $user_response['data']['attributes']['first_name'] );
- update_user_meta( $user->ID, 'user_lastname', $user_response['data']['attributes']['last_name'] );
-
- }
-
- }
- public static function checkPatreonCreatorID() {
-
- // Check if creator id doesnt exist. Account for the case in which creator id was saved as empty by the Creator
-
- if ( !get_option( 'patreon-creator-id', false ) OR get_option( 'patreon-creator-id', false )== '' ) {
-
- // Making sure access credentials are there to avoid fruitlessly contacting the api:
-
- if ( get_option( 'patreon-client-id', false )
- && get_option( 'patreon-client-secret', false )
- && get_option( 'patreon-creators-access-token' , false )
- ) {
-
- // Credentials are in. Go.
-
- $creator_id = self::getPatreonCreatorID();
-
- }
- if ( isset( $creator_id ) ) {
- // Creator id acquired. Update.
- update_option( 'patreon-creator-id', $creator_id );
- }
-
- }
-
- }
- public static function check_creator_tiers() {
-
- // Check if creator tier info doesnt exist. This will make sure the new version is compatible with existing installs and will show the tiers in locked interface text from the get go
-
- // When we move to webhooks, this code can be changed to read from the already present creator details
-
- $creator_tiers = get_option( 'patreon-creator-tiers', false );
-
- if ( !$creator_tiers OR $creator_tiers == '' OR !is_array( $creator_tiers['included'][1] ) ) {
-
- // Refresh tiers if this is not a lite plan. We dont want this on every page load.
-
- if ( get_option( 'patreon-creator-has-tiers', 'yes' ) ) {
- // Trigger an update of creator tiers
- self::update_creator_tiers_from_api();
- }
-
- }
- }
- public static function checkPatreonCreatorURL() {
-
- // Check if creator url doesnt exist.
-
- if ( !get_option( 'patreon-creator-url', false ) OR get_option( 'patreon-creator-url', false ) == '' ) {
-
- // Making sure access credentials are there to avoid fruitlessly contacting the api:
-
- if ( get_option( 'patreon-client-id', false )
- && get_option( 'patreon-client-secret', false )
- && get_option( 'patreon-creators-access-token', false )
- ) {
-
- // Credentials are in. Go.
- $creator_url = self::getPatreonCreatorURL();
-
- }
- if ( isset( $creator_url ) ) {
-
- // Creator id acquired. Update.
- update_option( 'patreon-creator-url', $creator_url );
-
- }
-
- }
-
- }
- public static function check_plugin_activation_date_for_existing_installs() {
-
- // Checks if plugin first activation date is saved for existing installs. Its here for backwards compatibility for existing installs before this version (1.2.5), and in case this meta info is lost in the db for any reason
-
- $plugin_first_activated = get_option( 'patreon-plugin-first-activated', 0 );
-
- if ( $plugin_first_activated == 0 ) {
- // If no date was set, set it to now
- update_option( 'patreon-plugin-first-activated', time() );
- update_option( 'patreon-existing-installation', true );
- }
-
- }
- public static function checkPatreonCampaignID() {
-
- // Check if campaign id doesnt exist.
-
- if ( !get_option( 'patreon-campaign-id', false ) OR get_option( 'patreon-campaign-id', false ) == '' ) {
-
- // Making sure access credentials are there to avoid fruitlessly contacting the api:
-
- if ( get_option( 'patreon-client-id', false )
- && get_option( 'patreon-client-secret', false )
- && get_option( 'patreon-creators-access-token', false )
- ) {
-
- // Credentials are in. Go.
- $campaign_id = self::getPatreonCampaignID();
-
- }
- if ( isset( $campaign_id ) ) {
-
- // Creator id acquired. Update.
- update_option( 'patreon-campaign-id', $campaign_id );
-
- }
-
- }
-
- }
- public static function checkPatreonCreatorName() {
-
- // This function checks and saves creator's full name, name and surname. These are used in post locking interface
-
- if ( !get_option( 'patreon-creator-full-name', false ) OR get_option( 'patreon-creator-full-name', false ) == '' ) {
-
- // Making sure access credentials are there to avoid fruitlessly contacting the api:
-
- if ( get_option('patreon-client-id', false ) && get_option( 'patreon-client-secret', false ) && get_option('patreon-creators-access-token', false ) ) {
-
- // Credentials are in. Go.
- $creator_info = self::getPatreonCreatorInfo();
-
- }
- if ( isset( $creator_info['included'][0]['attributes']['full_name'] ) ) {
-
- // Creator id acquired. Update.
- update_option( 'patreon-creator-full-name', $creator_info['included'][0]['attributes']['full_name'] );
-
- }
- if ( isset( $creator_info['included'][0]['attributes']['first_name'] ) ) {
-
- // Creator id acquired. Update.
- update_option( 'patreon-creator-first-name', $creator_info['included'][0]['attributes']['first_name'] );
-
- }
- if ( isset( $creator_info['included'][0]['attributes']['last_name'] ) ) {
-
- // Creator id acquired. Update.
- update_option( 'patreon-creator-last-name', $creator_info['included'][0]['attributes']['last_name'] );
-
- }
- }
- }
- public static function getPatreonCreatorInfo() {
-
- $api_client = new Patreon_API( get_option( 'patreon-creators-access-token' , false ) );
+class Patreon_Wordpress
+{
+ public static $patreon_routing;
+ public static $patreon_frontend;
+ public static $patreon_posts;
+ public static $patreon_protect;
+ public static $patreon_options;
+ public static $patron_metabox;
+ public static $patreon_compatibility;
+ public static $patreon_login;
+ public static $patreon_user_profiles;
+ public static $patreon_admin_pointers;
+ public static $patreon_content_sync;
+ public static $current_user_pledge_amount = -1;
+ public static $current_user_patronage_declined = -1;
+ public static $current_user_is_patron = -1;
+ public static $patreon_user_info_cache = [];
+ public static $patreon_pledge_info_cache = [];
+ public static $current_member_details = -1;
+ public static $current_user_patronage_duration = -1;
+ public static $current_user_lifetime_patronage = -1;
+ public static $current_user_pledge_relationship_start = -1;
+ public static $lock_or_not = [];
+
+ public function __construct()
+ {
+ include_once 'patreon_login.php';
+ include_once 'patreon_routing.php';
+ include_once 'patreon_frontend.php';
+ include_once 'patreon_api.php';
+ include_once 'patreon_oauth.php';
+ include_once 'patreon_options.php';
+ include_once 'patreon_metabox.php';
+ include_once 'patreon_user_profiles.php';
+ include_once 'patreon_protect.php';
+ include_once 'patreon_compatibility.php';
+ include_once 'patreon_admin_pointers.php';
+ include_once 'patreon_content_sync.php';
+
+ self::$patreon_routing = new Patreon_Routing();
+ self::$patreon_frontend = new Patreon_Frontend();
+
+ if (is_admin()) {
+ self::$patreon_options = new Patreon_Options();
+ self::$patron_metabox = new Patron_Metabox();
+ }
+
+ self::$patreon_user_profiles = new Patreon_User_Profiles();
+ self::$patreon_protect = new Patreon_Protect();
+ self::$patreon_compatibility = new Patreon_Compatibility();
+ self::$patreon_login = new Patreon_Login();
+
+ if (is_admin()) {
+ self::$patreon_admin_pointers = new Patreon_Admin_Pointers();
+ }
+
+ self::$patreon_content_sync = new Patreon_Content_Sync();
+
+ add_action('wp_head', [$this, 'updatePatreonUser'], 10);
+ add_action('init', [$this, 'checkPatreonCreatorID']);
+ add_action('init', [$this, 'check_creator_tiers']);
+ add_action('init', [$this, 'check_post_sync_webhook']);
+ add_action('init', [&$this, 'order_independent_actions_to_run_on_init_start'], 0);
+ add_action('init', [$this, 'check_plugin_activation_date_for_existing_installs']);
+ add_action('admin_init', [$this, 'post_credential_update_api_connectivity_check']);
+ add_action('update_option_patreon-client-id', [$this, 'toggle_check_api_credentials_on_setting_save'], 10, 2);
+ add_action('update_option_patreon-client-secret', [$this, 'toggle_check_api_credentials_on_setting_save'], 10, 2);
+ add_action('update_option_patreon-creators-access-token', [$this, 'toggle_check_api_credentials_on_setting_save'], 10, 2);
+ add_action('update_option_patreon-creators-refresh-token', [$this, 'toggle_check_api_credentials_on_setting_save'], 10, 2);
+ add_action('init', [$this, 'check_creator_token_expiration']);
+ add_action('init', [$this, 'checkPatreonCampaignID']);
+ add_action('init', [$this, 'checkPatreonCreatorURL']);
+ add_action('init', [$this, 'checkPatreonCreatorName']);
+ add_action('init', 'Patreon_Login::checkTokenExpiration');
+ add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']);
+ add_action('upgrader_process_complete', 'Patreon_Wordpress::AfterUpdateActions', 10, 2);
+ add_action('admin_notices', [$this, 'AdminMessages']);
+ add_action('admin_init', [$this, 'add_privacy_policy_section'], 20);
+ add_action('admin_init', [$this, 'check_setup'], 5);
+ add_filter('pre_set_site_transient_update_plugins', [$this, 'check_for_update']);
+ add_action('wp_ajax_patreon_wordpress_dismiss_admin_notice', [$this, 'dismiss_admin_notice'], 10, 1);
+ add_action('wp_ajax_patreon_wordpress_toggle_option', [$this, 'toggle_option'], 10, 1);
+ add_action('wp_ajax_patreon_wordpress_populate_patreon_level_select', [$this, 'populate_patreon_level_select_from_ajax'], 10, 1);
+ add_action('plugin_action_links_'.PATREON_WORDPRESS_PLUGIN_SLUG, [$this, 'add_plugin_action_links'], 10, 1);
+ add_action('wp_ajax_patreon_make_attachment_pledge_editor', [self::$patreon_protect, 'makeAttachmentPledgeEditor']);
+ add_action('wp_ajax_nopriv_patreon_make_attachment_pledge_editor', [self::$patreon_protect, 'makeAttachmentPledgeEditor']);
+ add_action('wp_ajax_patreon_save_attachment_patreon_level', [self::$patreon_protect, 'saveAttachmentLevel']);
+ add_action('wp_ajax_nopriv_patreon_save_attachment_patreon_level', [self::$patreon_protect, 'saveAttachmentLevel']);
+ add_action('wp_ajax_patreon_wordpress_start_post_import', [$this, 'start_post_import']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_start_post_import', [$this, 'start_post_import']);
+ add_action('wp_ajax_patreon_wordpress_import_next_batch_of_posts', [$this, 'import_next_batch_of_posts']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_import_next_batch_of_posts', [$this, 'import_next_batch_of_posts']);
+ add_action('wp_ajax_patreon_wordpress_cancel_manual_post_import', [$this, 'cancel_manual_post_import']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_cancel_manual_post_import', [$this, 'cancel_manual_post_import']);
+ add_action('wp_ajax_patreon_wordpress_set_update_posts_option', [$this, 'set_update_posts_option']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_set_update_posts_option', [$this, 'set_update_posts_option']);
+ add_action('wp_ajax_patreon_wordpress_set_delete_posts_option', [$this, 'set_delete_posts_option']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_set_delete_posts_option', [$this, 'set_delete_posts_option']);
+ add_action('wp_ajax_patreon_wordpress_get_taxonomies_for_post_type', [$this, 'make_taxonomy_select']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_get_taxonomies_for_post_type', [$this, 'make_taxonomy_select']);
+ add_action('wp_ajax_patreon_wordpress_get_terms_for_taxonomy', [$this, 'make_term_select']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_get_terms_for_taxonomy', [$this, 'make_term_select']);
+ add_action('wp_ajax_patreon_wordpress_save_post_sync_category', [$this, 'save_post_sync_category']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_save_post_sync_category', [$this, 'save_post_sync_category']);
+ add_action('wp_ajax_patreon_wordpress_set_post_author_for_post_sync', [$this, 'set_post_author_for_post_sync']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_set_post_author_for_post_sync', [$this, 'set_post_author_for_post_sync']);
+ add_filter('cron_schedules', [&$this, 'add_patreon_cron_schedules']);
+ add_action('wp_ajax_patreon_wordpress_disconnect_patreon_account', [self::$patreon_login, 'disconnect_account_from_patreon']);
+ add_action('wp_ajax_nopriv_patreon_wordpress_disconnect_patreon_account', [self::$patreon_login, 'disconnect_account_from_patreon']);
+
+ // Schedule an action if it's not already scheduled
+ if (!wp_next_scheduled('patreon_five_minute_action')) {
+ wp_schedule_event(time(), 'patreon_five_minute_cron_schedule', 'patreon_five_minute_action');
+ }
+
+ add_action('patreon_five_minute_action', [&$this, 'patreon_five_minute_cron_job']);
+ }
+
+ public static function getPatreonUser($user = false)
+ {
+ if (!$user) {
+ $user = wp_get_current_user();
+ }
+
+ // Bail out if there's no user object or user id or user is anon
+ if (!$user or !is_object($user) or !isset($user->ID) or 0 == $user->ID) {
+ return false;
+ }
+
+ if (isset(self::$patreon_user_info_cache[$user->ID])) {
+ return self::$patreon_user_info_cache[$user->ID];
+ }
+
+ // Check if the user just returned from any Patreon oAuth flow (pledge, login, register etc)
+
+ $user_just_returned_from_patreon_flow = false;
+
+ $user_last_returned_from_flow = get_user_meta($user->ID, 'patreon_user_last_returned_from_any_flow', true);
+
+ if (($user_last_returned_from_flow and '' != $user_last_returned_from_flow) and $user_last_returned_from_flow >= (time() - 2)) {
+ // User returned from a Patreon flow within the last 2 seconds
+ $user_just_returned_from_patreon_flow = true;
+ }
+
+ // Use the cached patron info if it exists, if its newer than 2 seconds, and the user has not returned from any Patreon flow (login or pledge)
+ // 2 secs should cover the case in which the patrons make a new pledge at patreon.com and visit/refresh a remote app or a site page.
+
+ // Returns empty string if it does not exist
+ $user_response_timestamp = get_user_meta($user->ID, 'patreon_latest_patron_info_timestamp', true);
+
+ if (!$user_just_returned_from_patreon_flow and ($user_response_timestamp and '' != $user_response_timestamp) and $user_response_timestamp >= (time() - 2)) {
+ // Cached patron info is fresh. Use it.
+
+ $user_response = get_user_meta($user->ID, 'patreon_latest_patron_info', true);
+
+ // Add the info to the page-run cache and return it
+ return Patreon_Wordpress::add_to_patreon_user_info_cache($user->ID, $user_response);
+ }
+
+ /* get user meta data and query patreon api */
+ $patreon_access_token = get_user_meta($user->ID, 'patreon_access_token', true);
+
+ if ('' != $patreon_access_token) {
+ $api_client = new Patreon_API($patreon_access_token);
+
+ // Get the user from the API
+ $user_response = $api_client->fetch_user();
+
+ // Here we check the returned result if its valid
+
+ if (isset($user_response['included'][0]) and is_array($user_response['included'][0])) {
+ // Valid return. Save it with timestamp
+
+ update_user_meta($user->ID, 'patreon_latest_patron_info', $user_response);
+ update_user_meta($user->ID, 'patreon_latest_patron_info_timestamp', time());
+
+ return Patreon_Wordpress::add_to_patreon_user_info_cache($user->ID, $user_response);
+ }
+
+ // Couldnt get user from Patreon. Try to refresh tokens if it was a token error
+ $token_refreshed = false;
+
+ if (isset($user_response['errors']) && is_array($user_response['errors'])) {
+ foreach ($user_response['errors'] as $error) {
+ if (1 == $error['code']) {
+ $token_refreshed = self::refresh_user_access_token($user);
+ }
+ }
+ }
+
+ // Reload token
+ $patreon_access_token = get_user_meta($user->ID, 'patreon_access_token', true);
+
+ if ($token_refreshed and '' != $patreon_access_token) {
+ $api_client = new Patreon_API($patreon_access_token);
+
+ $user_response = $api_client->fetch_user();
+
+ if (isset($user_response['included'][0]) and is_array($user_response['included'][0])) {
+ // Valid return. Save it with timestamp
+
+ update_user_meta($user->ID, 'patreon_latest_patron_info', $user_response);
+ update_user_meta($user->ID, 'patreon_latest_patron_info_timestamp', time());
+
+ return Patreon_Wordpress::add_to_patreon_user_info_cache($user->ID, $user_response);
+ }
+ }
+
+ // For whatsoever reason the returns are not valid and we cant refresh the user
+ // Check if a saved return exists for this user
+
+ $user_response = get_user_meta($user->ID, 'patreon_latest_patron_info', true);
+ $user_response_timestamp = get_user_meta($user->ID, 'patreon_latest_patron_info_timestamp', true);
+
+ // Check if there is a valid saved user return and whether it has a timestamp within desired range
+ if (isset($user_response['included'][0]) and is_array($user_response['included'][0]) and $user_response_timestamp >= (time() - (3600 * 24 * 3))) {
+ return Patreon_Wordpress::add_to_patreon_user_info_cache($user->ID, $user_response);
+ }
+ }
+
+ // All failed - return false
+
+ return Patreon_Wordpress::add_to_patreon_user_info_cache($user->ID, false);
+ }
+
+ public static function refresh_user_access_token($user)
+ {
+ $refresh_token = get_user_meta($user->ID, 'patreon_refresh_token', true);
+
+ $oauth_client = new Patreon_Oauth();
+ $tokens = $oauth_client->refresh_token($refresh_token, site_url().'/patreon-authorization/');
+
+ if (isset($tokens['access_token'])) {
+ update_user_meta($user->ID, 'patreon_refresh_token', $tokens['refresh_token']);
+ update_user_meta($user->ID, 'patreon_access_token', $tokens['access_token']);
+
+ return $tokens['access_token'];
+ }
+
+ return false;
+ }
+
+ public static function updatePatreonUser()
+ {
+ /* check if current user is loggedin, get ID */
+
+ if (false == is_user_logged_in()) {
+ return false;
+ }
+
+ $user = wp_get_current_user();
+
+ if (false == $user) {
+ return false;
+ }
+
+ $last_update = get_user_meta($user->ID, 'patreon_user_details_last_updated', true);
+
+ // If last update time is not empty and it is closer to time() than one day, dont update
+ if (!('' == $last_update or ((time() - $last_update) > 86400))) {
+ return false;
+ }
+
+ /* query Patreon API to get users patreon details */
+ $user_response = self::getPatreonUser($user);
+
+ if (false == $user_response) {
+ return false;
+ }
+
+ if (isset($user_response['data'])) {
+ // Set the update time
+ update_user_meta($user->ID, 'patreon_user_details_last_updated', time());
+
+ /* all the details you want to update on wordpress user account */
+ update_user_meta($user->ID, 'patreon_user', $user_response['data']['attributes']['vanity']);
+
+ $patreon_created = '';
+ if (isset($user_response['data']['attributes']['created'])) {
+ $patreon_created = $user_response['data']['attributes']['created'];
+ }
+
+ update_user_meta($user->ID, 'patreon_created', $patreon_created);
+ update_user_meta($user->ID, 'user_firstname', $user_response['data']['attributes']['first_name']);
+ update_user_meta($user->ID, 'user_lastname', $user_response['data']['attributes']['last_name']);
+ }
+ }
+
+ public static function checkPatreonCreatorID()
+ {
+ // Check if creator id doesnt exist. Account for the case in which creator id was saved as empty by the Creator
+
+ if (!get_option('patreon-creator-id', false) or '' == get_option('patreon-creator-id', false)) {
+ // Making sure access credentials are there to avoid fruitlessly contacting the api:
+
+ if (get_option('patreon-client-id', false)
+ && get_option('patreon-client-secret', false)
+ && get_option('patreon-creators-access-token', false)
+ ) {
+ // Credentials are in. Go.
+
+ $creator_id = self::getPatreonCreatorID();
+ }
+ if (isset($creator_id)) {
+ // Creator id acquired. Update.
+ update_option('patreon-creator-id', $creator_id);
+ }
+ }
+ }
+
+ public static function check_creator_tiers()
+ {
+ // Check if creator tier info doesnt exist. This will make sure the new version is compatible with existing installs and will show the tiers in locked interface text from the get go
+
+ // When we move to webhooks, this code can be changed to read from the already present creator details
+
+ $creator_tiers = get_option('patreon-creator-tiers', false);
+
+ if (!$creator_tiers or '' == $creator_tiers or !is_array($creator_tiers['included'][1])) {
+ // Refresh tiers if this is not a lite plan. We dont want this on every page load.
+
+ if (get_option('patreon-creator-has-tiers', 'yes')) {
+ // Trigger an update of creator tiers
+ self::update_creator_tiers_from_api();
+ }
+ }
+ }
+
+ public static function checkPatreonCreatorURL()
+ {
+ // Check if creator url doesnt exist.
+
+ if (!get_option('patreon-creator-url', false) or '' == get_option('patreon-creator-url', false)) {
+ // Making sure access credentials are there to avoid fruitlessly contacting the api:
+
+ if (get_option('patreon-client-id', false)
+ && get_option('patreon-client-secret', false)
+ && get_option('patreon-creators-access-token', false)
+ ) {
+ // Credentials are in. Go.
+ $creator_url = self::getPatreonCreatorURL();
+ }
+ if (isset($creator_url)) {
+ // Creator id acquired. Update.
+ update_option('patreon-creator-url', $creator_url);
+ }
+ }
+ }
+
+ public static function check_plugin_activation_date_for_existing_installs()
+ {
+ // Checks if plugin first activation date is saved for existing installs. Its here for backwards compatibility for existing installs before this version (1.2.5), and in case this meta info is lost in the db for any reason
+
+ $plugin_first_activated = get_option('patreon-plugin-first-activated', 0);
+
+ if (0 == $plugin_first_activated) {
+ // If no date was set, set it to now
+ update_option('patreon-plugin-first-activated', time());
+ update_option('patreon-existing-installation', true);
+ }
+ }
+
+ public static function checkPatreonCampaignID()
+ {
+ // Check if campaign id doesnt exist.
+
+ if (!get_option('patreon-campaign-id', false) or '' == get_option('patreon-campaign-id', false)) {
+ // Making sure access credentials are there to avoid fruitlessly contacting the api:
+
+ if (get_option('patreon-client-id', false)
+ && get_option('patreon-client-secret', false)
+ && get_option('patreon-creators-access-token', false)
+ ) {
+ // Credentials are in. Go.
+ $campaign_id = self::getPatreonCampaignID();
+ }
+ if (isset($campaign_id)) {
+ // Creator id acquired. Update.
+ update_option('patreon-campaign-id', $campaign_id);
+ }
+ }
+ }
+
+ public static function checkPatreonCreatorName()
+ {
+ // This function checks and saves creator's full name, name, surname and campaign name. These are used in post locking interface
+ if (!get_option('patreon-campaign-name', false) or '' == get_option('patreon-campaign-name', false)) {
+ // Making sure access credentials are there to avoid fruitlessly contacting the api:
+
+ if (get_option('patreon-client-id', false) && get_option('patreon-client-secret', false) && get_option('patreon-creators-access-token', false)) {
+ // Credentials are in. Go.
+ $creator_info = self::getPatreonCreatorInfo();
+ }
+ if (isset($creator_info['data'][0]['attributes']['name'])) {
+ update_option('patreon-campaign-name', $creator_info['data'][0]['attributes']['name']);
+ }
+ if (isset($creator_info['included'][0]['attributes']['full_name'])) {
+ // Creator id acquired. Update.
+ update_option('patreon-creator-full-name', $creator_info['included'][0]['attributes']['full_name']);
+ }
+ if (isset($creator_info['included'][0]['attributes']['first_name'])) {
+ // Creator id acquired. Update.
+ update_option('patreon-creator-first-name', $creator_info['included'][0]['attributes']['first_name']);
+ }
+ if (isset($creator_info['included'][0]['attributes']['last_name'])) {
+ // Creator id acquired. Update.
+ update_option('patreon-creator-last-name', $creator_info['included'][0]['attributes']['last_name']);
+ }
+ }
+ }
+
+ public static function getPatreonCreatorInfo()
+ {
+ $api_client = new Patreon_API(get_option('patreon-creators-access-token', false));
$user_response = $api_client->fetch_creator_info();
- if ( empty( $user_response ) OR $user_response == 'throttled_locally' ) {
- return false;
- }
-
- if( isset( $user_response['errors'] ) && is_array( $user_response['errors'] ) ) {
-
- foreach( $user_response['errors'] as $error ) {
-
- if ( $error['code'] == 1 ) {
-
- if( self::refresh_creator_access_token() ) {
- return $api_client->fetch_creator_info();
- }
-
- }
-
- }
-
- }
-
- return $user_response;
-
- }
- public static function refresh_creator_access_token() {
- /* refresh creators token if error 1 */
- $refresh_token = get_option( 'patreon-creators-refresh-token', false );
-
- if( $refresh_token == false ) {
- return false;
- }
-
- $oauth_client = new Patreon_Oauth;
- $tokens = $oauth_client->refresh_token( $refresh_token, site_url() . '/patreon-authorization/' );
-
- if( isset( $tokens['refresh_token'] ) && isset( $tokens['access_token'] ) ) {
-
- update_option( 'patreon-creators-refresh-token', $tokens['refresh_token'] );
- update_option( 'patreon-creators-access-token', $tokens['access_token'] );
-
- return $tokens;
- }
-
- return false;
- }
- public static function check_creator_token_expiration() {
- /* Checks if creator's token is expired or if expire date is missing. Then attempts refreshing the token */
-
- $refresh_token = get_option( 'patreon-creators-refresh-token', false );
-
- if ( $refresh_token == false ) {
- return false;
- }
-
- $expiration = get_option( 'patreon-creators-refresh-token-expiration', false );
-
- if ( !$expiration OR $expiration <= ( time() + ( 60 * 60 * 24 * 7 ) ) ) {
-
- if ( $tokens = self::refresh_creator_access_token() ) {
-
- update_option( 'patreon-creators-refresh-token-expiration', time() + $tokens['expires_in'] );
- update_option( 'patreon-creators-access-token-scope', $tokens['scope'] );
-
- return true;
- }
-
- }
-
- return false;
- }
- public static function getPatreonCreatorID() {
-
- $creator_info = self::getPatreonCreatorInfo();
-
- if ( isset( $creator_info['data'][0]['relationships']['creator']['data']['id'] ) ) {
- return $creator_info['data'][0]['relationships']['creator']['data']['id'];
- }
+ if (empty($user_response) or 'throttled_locally' == $user_response) {
+ return false;
+ }
+
+ if (isset($user_response['errors']) && is_array($user_response['errors'])) {
+ foreach ($user_response['errors'] as $error) {
+ if (1 == $error['code']) {
+ if (self::refresh_creator_access_token()) {
+ return $api_client->fetch_creator_info();
+ }
+ }
+ }
+ }
+
+ return $user_response;
+ }
+
+ public static function refresh_creator_access_token()
+ {
+ /* refresh creators token if error 1 */
+ $refresh_token = get_option('patreon-creators-refresh-token', false);
+
+ if (false == $refresh_token) {
+ return false;
+ }
+
+ $oauth_client = new Patreon_Oauth();
+ $tokens = $oauth_client->refresh_token($refresh_token, site_url().'/patreon-authorization/');
+
+ if (isset($tokens['refresh_token']) && isset($tokens['access_token'])) {
+ update_option('patreon-creators-refresh-token', $tokens['refresh_token']);
+ update_option('patreon-creators-access-token', $tokens['access_token']);
+
+ return $tokens;
+ }
+
+ return false;
+ }
+
+ public static function check_creator_token_expiration()
+ {
+ /* Checks if creator's token is expired or if expire date is missing. Then attempts refreshing the token */
+
+ $refresh_token = get_option('patreon-creators-refresh-token', false);
+
+ if (false == $refresh_token) {
+ return false;
+ }
+
+ $expiration = get_option('patreon-creators-refresh-token-expiration', false);
+
+ if (!$expiration or $expiration <= (time() + (60 * 60 * 24 * 7))) {
+ if ($tokens = self::refresh_creator_access_token()) {
+ update_option('patreon-creators-refresh-token-expiration', time() + $tokens['expires_in']);
+ update_option('patreon-creators-access-token-scope', $tokens['scope']);
+
+ return true;
+ }
+ }
return false;
+ }
+
+ public static function getPatreonCreatorID()
+ {
+ $creator_info = self::getPatreonCreatorInfo();
+
+ if (isset($creator_info['data'][0]['relationships']['creator']['data']['id'])) {
+ return $creator_info['data'][0]['relationships']['creator']['data']['id'];
+ }
- }
- public static function getPatreonCreatorURL() {
+ return false;
+ }
- $creator_info = self::getPatreonCreatorInfo();
+ public static function getPatreonCreatorURL()
+ {
+ $creator_info = self::getPatreonCreatorInfo();
- if ( isset( $creator_info['included'][0]['attributes']['url'] ) ) {
- return $creator_info['included'][0]['attributes']['url'];
- }
+ if (isset($creator_info['included'][0]['attributes']['url'])) {
+ return $creator_info['included'][0]['attributes']['url'];
+ }
return false;
-
- }
- public static function getPatreonCampaignID() {
+ }
- $creator_info = self::getPatreonCreatorInfo();
+ public static function getPatreonCampaignID()
+ {
+ $creator_info = self::getPatreonCreatorInfo();
- if ( isset( $creator_info['data'][0]['id'] ) ) {
- return $creator_info['data'][0]['id'];
- }
+ if (isset($creator_info['data'][0]['id'])) {
+ return $creator_info['data'][0]['id'];
+ }
return false;
-
- }
- public static function getUserPatronage( $user = false ) {
-
- $using_current_user = false;
-
- // If user is not given, try to get the current user attribute ID will be 0 if there is no logged in user
- if ( $user == false ) {
- $using_current_user = true;
- $user = wp_get_current_user();
- }
-
- // If still no user object, return false
- if ( $user->ID == 0 ) {
- return false;
- }
-
- // Compatibility - use the old current_user_pledge_amount_static if user is not given and we are using the current user
- if ( $using_current_user ) {
-
- if ( self::$current_user_pledge_amount != -1 ) {
- return self::$current_user_pledge_amount;
- }
- }
-
- if ( isset( self::$patreon_pledge_info_cache[$user->ID] ) ) {
- return self::$patreon_pledge_info_cache[$user->ID];
- }
-
- $creator_id = get_option( 'patreon-creator-id', false );
-
- if ( $creator_id == false ) {
- return false;
- }
-
- /* get current users meta data */
- $user_meta = get_user_meta( $user->ID ) ;
- $user_response = self::getPatreonUser( $user );
-
- if ( $user_response == false ) {
- return false;
- }
-
- $pledge = false;
- if ( array_key_exists( 'included', $user_response ) ) {
-
- foreach ( $user_response['included'] as $obj ) {
-
- if ( isset( $obj["type"] ) && $obj["type"] == "pledge" && $obj["relationships"]["creator"]["data"]["id"] == $creator_id ) {
- $pledge = $obj;
- break;
- }
-
- }
-
- }
-
- if ( isset( $pledge['attributes']['declined_since']) && !is_null( $pledge['attributes']['declined_since'] ) ) {
- do_action('ptrn/declined_since', $pledge, $pledge['attributes']['declined_since']);
- // return false;
- }
-
- if ( $pledge != false ) {
-
- $pledge_level = self::getUserPatronageLevel( $pledge );
-
- // Compatibility - use the old current_user_pledge_amount_static if user is not given and we are using the current user
- if ( $using_current_user ) {
- self::$current_user_pledge_amount = $pledge_level;
- }
-
- Patreon_Wordpress::add_to_patreon_pledge_info_cache( $user->ID, $pledge_level );
-
- return $pledge_level;
- }
-
- return false;
-
- }
- public static function getUserPatronageDuration( $user = false ) {
-
- if ( self::$current_user_patronage_duration != -1 ) {
- return self::$current_user_patronage_duration;
- }
-
- if ( !$user ) {
- $user = wp_get_current_user();
- }
-
- $pledge_days = false;
-
- $user_response = self::getPatreonUser( $user );
-
- if ( isset( $user_response['included'][0]['attributes']['pledge_relationship_start'] ) ) {
-
- $pledge_days = floor( ( time() - strtotime( $user_response['included'][0]['attributes']['pledge_relationship_start'] ) ) / 60 / 60 / 24 );
-
- }
-
- return $pledge_days;
-
- }
- public static function get_user_pledge_relationship_start( $user = false ) {
-
- if ( self::$current_user_pledge_relationship_start != -1 ) {
- return self::$current_user_pledge_relationship_start;
- }
-
- if ( !$user ) {
- $user = wp_get_current_user();
- }
-
- $pledge_days = false;
- $user_response = self::getPatreonUser( $user );
-
- if ( isset( $user_response['included'][0]['attributes']['pledge_relationship_start'] ) ) {
- return strtotime( $user_response['included'][0]['attributes']['pledge_relationship_start'] );
- }
-
- return 0;
-
- }
- public static function get_user_lifetime_patronage( $user = false ) {
-
- if ( self::$current_user_lifetime_patronage != -1 ) {
- return self::$current_user_lifetime_patronage;
- }
-
- if ( !$user ) {
- $user = wp_get_current_user();
- }
-
- $lifetime_patronage = false;
- $user_response = self::getPatreonUser( $user );
-
- if ( isset( $user_response['included'][0]['attributes']['lifetime_support_cents'] ) ) {
- $lifetime_patronage = $user_response['included'][0]['attributes']['lifetime_support_cents'];
- }
-
- return $lifetime_patronage;
-
- }
- public static function checkDeclinedPatronage( $user ) {
-
- if ( self::$current_user_patronage_declined != -1 ) {
- return self::$current_user_patronage_declined;
- }
-
- if ( !$user ) {
- $user = wp_get_current_user();
- }
-
- $user_response = self::getPatreonUser( $user );
-
- // If no user exists, the patronage cannot have been declined.
- if ( !$user_response ) {
- return self::$current_user_patronage_declined = false;
- }
-
- $creator_id = get_option( 'patreon-creator-id', false );
-
- $pledge = false;
- if ( array_key_exists( 'included', $user_response ) ) {
-
- foreach ( $user_response['included'] as $obj ) {
-
- if ( $obj["type"] == "pledge" && $obj["relationships"]["creator"]["data"]["id"] == $creator_id ) {
- $pledge = $obj;
- break;
- }
-
- }
-
- }
-
- if ( isset( $pledge['attributes']['declined_since']) && !is_null( $pledge['attributes']['declined_since'] ) ) {
-
- do_action( 'ptrn/declined_since', $pledge, $pledge['attributes']['declined_since'] );
- return self::$current_user_patronage_declined = true;
-
- }
- else {
- return self::$current_user_patronage_declined = false;
- }
- }
- public static function getUserPatronageLevel( $pledge ) {
-
- // Exception - for lite tier creators, use currently_entitled_amount_cents until there is a better way to match custom $ without tiers to local info:
-
- if ( get_option( 'patreon-creator-has-tiers', 'yes' ) == 'no' ) {
-
- if( isset( $pledge['attributes']['amount_cents'] ) ) {
- return $pledge['attributes']['amount_cents'];
- }
- // Catch all from old function format
- return 0;
-
- }
-
- // Get currently entitled tiers:
-
- $currently_entitled_tiers = $pledge['relationships']['currently_entitled_tiers']['data'];
-
- if ( !is_array( $currently_entitled_tiers ) ) {
- return 0;
- }
-
- // Get creator's saved tiers
-
- $creator_tiers = get_option( 'patreon-creator-tiers', false );
-
- if ( !$creator_tiers OR $creator_tiers == '' OR !is_array( $creator_tiers['included'][1] ) ) {
-
- // No local tiers. Return 0
- return 0;
-
- }
-
- $creator_tiers = $creator_tiers['included'];
-
- // Using max entitled tier's value for backwards compatibility. This may be changed in future for exact tier match checks.
- $max_entitled_tier_value = 0;
-
- // Manually iterate arrays to make it easier to modify the logic in future
- // Iterate the currently_entitled_tiers array to cover for any possibility in case there may be more than one tier entitled
-
- foreach ( $currently_entitled_tiers as $key => $value ) {
-
- foreach ( $creator_tiers as $c_key => $c_value ) {
-
- // If the checked tier value is higher than the known max value, set the max value to current
-
- if ( $currently_entitled_tiers[$key]['id'] == $creator_tiers[$c_key]['id'] ) {
-
- // Local tier matches this tier. Check if the amount value is greater, if so, set to that value
-
- if ( $creator_tiers[$c_key]['attributes']['amount_cents'] > $max_entitled_tier_value ) {
- $max_entitled_tier_value = $creator_tiers[$c_key]['attributes']['amount_cents'];
- }
-
- }
-
- }
-
- }
-
- // Check whether the pledge value that comes in is higher than the max entitled tier value - for custom pledge amounts
- // This is not currency aware, but should cover cases in which there is large difference in between the custom pledge and the local tier level for which the content is locked for
- if ( isset( $pledge['attributes']['amount_cents'] )) {
- $pledge_amount = $pledge['attributes']['amount_cents'];
-
- // Convert annual pledges into a monthly total
- if (isset($pledge['attributes']['pledge_cadence']) AND $pledge['attributes']['pledge_cadence'] > 1) {
- $pledge_amount = $pledge_amount/ $pledge['attributes']['pledge_cadence'];
- }
-
- if ($pledge_amount > $max_entitled_tier_value ) {
- $max_entitled_tier_value = $pledge_amount;
- }
- }
-
- return $max_entitled_tier_value;
-
- }
- public static function isPatron( $user = false ) {
-
- if( self::$current_user_is_patron != -1 ) {
- return self::$current_user_is_patron;
- }
-
- // If user is not given, try to get the current user attribute ID will be 0 if there is no logged in user
- if ( $user == false ) {
- $user = wp_get_current_user();
- }
-
- $user_patronage = self::getUserPatronage();
-
- if( is_numeric( $user_patronage ) && $user_patronage > 0 ) {
- return self::$current_user_is_patron = true;
- }
- else {
- return self::$current_user_is_patron = false;
- }
-
- }
- public static function enqueueAdminScripts() {
-
- wp_enqueue_script( 'patreon-admin-js', PATREON_PLUGIN_ASSETS . '/js/admin.js', array( 'jquery' ), PATREON_WORDPRESS_VERSION, true );
- wp_localize_script( 'patreon-admin-js', 'pw_admin_js', array(
- 'patreon_wordpress_assets_url' => PATREON_PLUGIN_ASSETS,
- 'patreon_wordpress_nonce_post_sync' => wp_create_nonce( 'patreon_wordpress_nonce_post_sync' ),
- ) );
-
- // Load image related functions only if image feature is on:
-
- if ( get_option( 'patreon-enable-file-locking', false ) ) {
-
- wp_enqueue_script( 'patreon-admin-image-functions-js', PATREON_PLUGIN_ASSETS . '/js/admin_image_functions.js', array( 'jquery' ), PATREON_WORDPRESS_VERSION, true );
- wp_localize_script( 'patreon-admin-image-functions-js', 'admin_image_functions', array( 'patreon_wordpress_assets_url' => PATREON_PLUGIN_ASSETS, ) );
-
- }
-
-
-
- }
- public static function AfterUpdateActions( $upgrader_object, $options = false ) {
-
- // In this function we perform actions after update.
-
- if ( !$options OR !is_array( $options ) ) {
- // Not an update.
- return;
- }
-
- // Check if this plugin was updated:
- if ( $options['action'] == 'update' && $options['type'] == 'plugin' ) {
-
- $got_updated = false;
-
- if( isset( $options['plugins'] ) ) {
-
- // Multi plugin update. Iterate:
- // Iterate through the plugins being updated and check if ours is there
- foreach( $options['plugins'] as $plugin ) {
-
- if( $plugin == PATREON_WORDPRESS_PLUGIN_SLUG ) {
- $got_updated = true;
- }
-
- }
-
- }
- if( isset( $options['plugin'] ) ) {
-
- // Single plugin update
- if( $options['plugin'] == PATREON_WORDPRESS_PLUGIN_SLUG ) {
- $got_updated = true;
- }
-
- }
-
- if( $got_updated ) {
-
- // This section is used to do any tasks need doing after plugin updates. Any code changes to this part would take effect not immediately in the same version, but in the next update cycle since WP would use the new code only after plugin has been updated once.
-
- }
-
-
- }
-
- }
- public static function add_privacy_policy_section() {
-
- wp_add_privacy_policy_content( 'Patreon WordPress', PATREON_PRIVACY_POLICY_ADDENDUM );
-
- }
- public static function AdminMessages() {
-
- // This function processes any message or notification to display once after updates.
-
- // Skip showing any notice if setup is being done
-
- if ( isset( $_REQUEST['page'] ) AND $_REQUEST['page'] == 'patreon_wordpress_setup_wizard' ) {
- return;
- }
-
- if ( apply_filters( 'ptrn/suspend_notices', false ) ) {
- return;
- }
-
- $show_site_disconnect_success_notice = get_option( 'patreon-show-site-disconnect-success-notice', false );
-
- // Queue this message immediately after activation if not already shown
-
- if( $show_site_disconnect_success_notice ) {
- // Info notice - not permanent - doesnt require nonce verification
- ?>
-
-
-
-
-
-
We must connect your site to Patreon to enable Patreon features. Please click here to start the setup wizard
-
-
-
-
Your site's connection to Patreon must be upgraded to ensure that Patreon features will work! Please click here to start the setup wizard to reconnect your site again
-
-
-
-
Did Patreon WordPress help your site? Help creators like yourself find out about it by giving us a good rating!
-
-
-
-
There are important issues affecting your Patreon integration. Please visit health check page to see the issues and solutions.
-
-
-
-
Your Patreon client details were successfully saved!
-
Patreon WordPress is now ready to go and your site is connected to Patreon! You can now lock any post by using the "Patreon Level" meta box in your post editor!
-
-
-
-
Sorry - couldn't connect your site to Patreon
-
Patreon WordPress wasn't able to contact Patreon with the app details you provided. This may be because there is an error in the app details, or because there is something preventing proper connectivity in between your site/server and Patreon API. You can get help by visiting our support forum here
-
- checked ) ) {
- return $plugin_check_data;
- }
-
- if ( isset( $plugin_check_data->response[PATREON_WORDPRESS_PLUGIN_SLUG] ) AND
- version_compare( PATREON_WORDPRESS_VERSION, $plugin_check_data->response[PATREON_WORDPRESS_PLUGIN_SLUG]->new_version, '<' )
- ) {
-
- update_option( 'patreon-wordpress-update-available', 1 );
- }
-
- return $plugin_check_data;
-
- }
- public function dismiss_admin_notice() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- return;
- }
-
- // Mapping what comes from REQUEST to a given value avoids potential security problems and allows custom actions depending on notice
-
- if ( $_REQUEST['notice_id'] == 'patreon-wordpress-update-available' ) {
- delete_option( 'patreon-wordpress-update-available' );
- }
-
- if ( $_REQUEST['notice_id'] == 'patreon-addon-upsell-shown' ) {
- if ( !isset($_REQUEST['patreon_wordpress_nonce_patron_pro_addon_notice_shown']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_patron_pro_addon_notice_shown'] ), 'patreon_wordpress_nonce_patron_pro_addon_notice_shown' ) ) {
- return;
- }
-
- update_option( 'patreon-addon-upsell-shown', true);
-
- // Set the last notice shown date
- self::set_last_non_system_notice_shown_date();
- }
- if ( $_REQUEST['notice_id'] == 'patron_content_manager_pitch_shown' ) {
- if ( !isset($_REQUEST['patreon_wordpress_nonce_patron_content_manager_addon_notice_shown']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_patron_content_manager_addon_notice_shown'] ), 'patreon_wordpress_nonce_patron_content_manager_addon_notice_shown' ) ) {
- return;
- }
- update_option( 'patron_content_manager_pitch_shown', true);
-
- // Set the last notice shown date
- self::set_last_non_system_notice_shown_date();
- }
-
- // Mapping what comes from REQUEST to a given value avoids potential security problems
- if ( $_REQUEST['notice_id'] == 'patreon_setup_needed_notice' ) {
- if ( !isset($_REQUEST['patreon_wordpress_nonce_setup_needed']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_setup_needed'] ), 'patreon_wordpress_nonce_setup_needed' ) ) {
- return;
- }
- update_option( 'patreon-setup-wizard-notice-dismissed', true );
- delete_option( 'patreon-wordpress-app-credentials-success');
- delete_option( 'patreon-wordpress-app-credentials-failure');
- }
-
- // Mapping what comes from REQUEST to a given value avoids potential security problems
- if ( $_REQUEST['notice_id'] == 'patreon_wordpress_patreon_api_version_update_notice' ) {
- if ( !isset($_REQUEST['patreon_wordpress_nonce_patreon_api_version_update']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_patreon_api_version_update'] ), 'patreon_wordpress_nonce_patreon_api_version_update' ) ) {
- return;
- }
-
- update_option( 'api-version-update-notice-dismissed', true );
- update_option( 'api-version-update-notice-dismissed-time', time());
- }
-
- // Mapping what comes from REQUEST to a given value avoids potential security problems
- if ( $_REQUEST['notice_id'] == 'patreon-rate-plugin-notice-shown' ) {
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_rate_plugin_notice']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_rate_plugin_notice'] ), 'patreon_wordpress_nonce_rate_plugin_notice' ) ) {
- return;
- }
- update_option( 'patreon-rate-plugin-notice-shown', true );
-
- // Set the last notice shown date
- self::set_last_non_system_notice_shown_date();
- }
-
- // Mapping what comes from REQUEST to a given value avoids potential security problems
- if ( $_REQUEST['notice_id'] == 'patreon-critical-issues' ) {
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_plugin_critical_issues']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_plugin_critical_issues'] ), 'patreon_wordpress_nonce_plugin_critical_issues' ) ) {
- return;
- }
- update_option( 'patreon-critical-issues', true );
-
- // Set the last notice shown date
- self::set_last_system_notice_shown_date();
- }
-
- }
- public function start_post_import() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- echo 'need_admin_privileges';
- exit;
- }
-
- if( !check_ajax_referer( 'patreon_wordpress_nonce_post_sync', 'patreon_wordpress_nonce_post_sync' ) ) {
- echo 'nonce_fail';
- exit;
- }
-
- update_option( 'patreon-post-import-in-progress', true );
- delete_option( 'patreon-post-import-next-cursor' );
-
- echo 'Success';
- exit;
-
- }
- public function import_next_batch_of_posts() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- echo 'need_admin_privileges';
- exit;
- }
-
- if( !check_ajax_referer( 'patreon_wordpress_nonce_post_sync', 'patreon_wordpress_nonce_post_sync' ) ) {
- echo 'nonce_fail';
- exit;
- }
-
- // Check the last time this function was triggered:
-
- $last_triggered = get_option( 'patreon-manual-import-batch-last-triggered', 0 );
-
- // Abort if last triggered time is within 10 seconds. Works for cases in which it was never triggered.
- if ( ( $last_triggered + 10 ) > time() ) {
- echo 'time_limit_error';
- exit;
- }
-
- $last_triggered = get_option( 'patreon-manual-import-batch-last-triggered', 0 );
-
- // Abort if last triggered time is within 10 seconds. Works for cases in which it was never triggered.
- if ( ( $last_triggered + 10 ) > time() ) {
- echo 'time_limit_error';
- exit;
- }
-
- // Trigger next batch of import
-
- $import_return = self::$patreon_content_sync->import_posts_from_patreon( array( 'manual_import' => true ) );
-
- // Set last triggered time
-
- update_option( 'patreon-manual-import-batch-last-triggered', time() );
-
- // If success, exit to success
-
- if ( $import_return == 'imported_posts' ) {
- echo 'imported_posts';
- exit;
- }
-
- // If did not import any posts
-
- if ( $import_return == 'did_not_import_any_post' ) {
- echo 'did_not_import_any_post';
- exit;
- }
- // There was an issue getting posts
-
- if ( $import_return == 'couldnt_get_posts' ) {
- echo 'couldnt_get_posts';
- exit;
- }
- if ( $import_return == 'throttled_internally' ) {
- echo 'throttled_internally';
- exit;
- }
-
- // Import ended
-
- if ( $import_return == 'post_import_ended' ) {
- echo 'post_import_ended';
- exit;
- }
-
- if ( $import_return == 'no_ongoing_post_import' ) {
- // This means no post import ongoing
- echo 'no_ongoing_post_import';
- exit;
- }
-
- if ( $import_return == 'expired_or_lost_cursor_deleted' ) {
- // This means no post import ongoing
- echo 'expired_or_lost_cursor_deleted';
- exit;
- }
-
-
- }
- public function cancel_manual_post_import() {
-
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- echo 'need_admin_privileges';
- exit;
- }
-
- if( !check_ajax_referer( 'patreon_wordpress_nonce_post_sync', 'patreon_wordpress_nonce_post_sync' ) ) {
- echo 'nonce_fail';
- exit;
- }
-
-
- update_option( 'patreon-post-import-in-progress', false );
- delete_option( 'patreon-post-import-next-cursor' );
-
- if ( !get_option( 'patreon-post-import-in-progress', false ) AND !get_option( 'patreon-post-import-next-cursor', false ) ) {
- echo 'manual_post_import_canceled';
- exit;
- }
-
- echo 'couldnt_cancel_manual_post_import';
- exit;
-
-
- }
- public function save_post_sync_category() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- echo 'You have to be an admin user to set this setting';
- exit;
- }
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_save_post_sync_options'] ) ) ) {
- echo 'Form security field expired - please refresh the page and try again';
- exit;
- }
-
- if ( !(
- isset( $_REQUEST['patreon_sync_post_type'] ) AND
- isset( $_REQUEST['patreon_sync_post_category'] ) AND
- isset( $_REQUEST['patreon_sync_post_term'] )
- )
- ) {
- echo 'Please select all fields';
- exit;
- }
- // Check for empty cases
- if (
- $_REQUEST['patreon_sync_post_type'] == '-' OR
- $_REQUEST['patreon_sync_post_category'] == '-' OR
- $_REQUEST['patreon_sync_post_term'] == '-'
- ) {
- echo 'Please select all fields';
- exit;
- }
-
- update_option( 'patreon-sync-post-type', $_REQUEST['patreon_sync_post_type'] );
- update_option( 'patreon-sync-post-category', $_REQUEST['patreon_sync_post_category'] );
- update_option( 'patreon-sync-post-term', $_REQUEST['patreon_sync_post_term'] );
-
- echo 'Saved!';
- exit;
-
- }
- public function set_post_author_for_post_sync() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- echo 'You have to be logged as an admin to set this setting';
- exit;
- }
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_save_post_sync_options'] ) ) ) {
- echo 'Form expired - please refresh the page and try again';
- exit;
- }
-
- if ( !(
- isset( $_REQUEST['patreon_post_author_for_post_sync'] ) AND
- $_REQUEST['patreon_post_author_for_post_sync'] != ''
- )
- ) {
- echo 'A valid user must be selected';
- exit;
- }
-
- update_option( 'patreon-post-author-for-synced-posts', $_REQUEST['patreon_post_author_for_post_sync'] );
-
- echo 'Saved!';
- exit;
-
- }
- public function set_update_posts_option() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- return;
- }
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_save_post_sync_options'] ) ) ) {
- echo 'Form expired - please refresh the page and try again';
- exit;
- }
-
- if ( $_REQUEST['update_posts_option_value'] == 'yes' ) {
- update_option( 'patreon-update-posts', 'yes' );
- }
-
- if ( $_REQUEST['update_posts_option_value'] == 'no' ) {
- update_option( 'patreon-update-posts', 'no' );
- }
-
- echo 'Saved!';
- exit;
-
- }
- public function set_delete_posts_option() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- return;
- }
-
- if ( !isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_nonce_save_post_sync_options'] ) ) ) {
- echo 'Form expired - please refresh the page and try again';
- exit;
- }
-
- if ( $_REQUEST['delete_posts_option_value'] == 'yes' ) {
- update_option( 'patreon-remove-deleted-posts', 'yes' );
- }
-
-
- if ( $_REQUEST['delete_posts_option_value'] == 'no' ) {
- update_option( 'patreon-remove-deleted-posts', 'no' );
- }
-
- echo 'Saved!';
- exit;
-
- }
- public function toggle_check_api_credentials_on_setting_save( $old_value, $new_value ) {
-
- // This function fires after any of the client details are updated.
- // Doesnt need capability checks - should be allowed to be used programmatically
-
- // This filter only runs when settings are actually updated, but just in case:
- // Try contacting the api
- if( $new_value != $old_value ) {
-
- // One of access credentials were updated. Set a flag to do an api connectivity check
- update_option( 'patreon-wordpress-do-api-connectivity-check', 1 );
-
- }
-
- }
-
- public function post_credential_update_api_connectivity_check() {
-
- // This function checks if the saved app credentials are valid if the check toggle is set
- // Doesnt need capability checks - should be allowed to be used programmatically
-
- if( get_option( 'patreon-wordpress-do-api-connectivity-check', false ) ) {
-
- $result = self::check_api_connection();
- delete_option( 'patreon-wordpress-do-api-connectivity-check' );
- }
-
- }
-
- public static function check_api_connection() {
-
- // Just attempts to connect to API with given credentials, and returns result
-
- // Currently can verify only if creator's access token and refresh token are false. If the access token is false and refresh token is not, the system already refreshes the access token automatically. If only refresh token is false, then the existing correct access token will check true. In future a better check should be implemented
-
- $api_client = new Patreon_API( get_option( 'patreon-creators-access-token' , false ) );
- $creator_response = $api_client->fetch_creator_info();
-
- $creator_access = false;
- $client_access = false;
-
- if ( isset( $creator_response ['included'][0]['id'] ) AND $creator_response ['included'][0]['id'] != '' ) {
- // Got creator id. Credentials must be valid
-
- // Success - set flag
- // update_option( 'patreon-wordpress-app-credentials-success', 1 );
-
- $creator_access = true;
-
- }
-
- // Try to do a creator's token refresh
-
- if ( !$creator_access AND $tokens = self::refresh_creator_access_token() ) {
-
- update_option( 'patreon-creators-refresh-token-expiration', time() + $tokens['expires_in'] );
- update_option( 'patreon-creators-access-token-scope', $tokens['scope'] );
-
- // Try again:
-
- $api_client = new Patreon_API( get_option( 'patreon-creators-access-token' , false ) );
- $creator_response = $api_client->fetch_creator_info();
-
- if ( isset( $creator_response ['included'][0]['id'] ) AND $creator_response ['included'][0]['id'] != '' ) {
-
- // Got creator id. Credentials must be valid
- // Success - set flag
-
- $creator_access = true;
-
- }
-
- }
-
- // Here some check for client id and secret may be entered in future - currently only checks creator access token
-
- if ( $creator_access ) {
-
- update_option( 'patreon-wordpress-app-credentials-success', 1 );
- return;
- }
-
- // All flopped. Set failure flag
- update_option( 'patreon-wordpress-app-credentials-failure', 1 );
-
- }
-
- public function toggle_option() {
-
- if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
- return;
- }
-
- $current_user = wp_get_current_user();
-
- $option_to_toggle = sanitize_key( $_REQUEST['toggle_id'] );
-
- // Bail out if the option to be toggled is not in the allowed options
- if ( !array_key_exists($option_to_toggle, Patreon_Frontend::$allowed_toggles) ) {
- return;
- }
-
- // Bail out if the option to be toggled is not in the allowed options
- if ( !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_advanced_options_toggle_nonce'] ) ) ) {
- return;
- }
-
- $current_value = get_user_meta( $current_user->ID, $option_to_toggle, true );
-
- $new_value = 'off';
-
- if( !$current_value OR $current_value == 'off' ) {
- $new_value = 'on';
- }
-
- update_user_meta( $current_user->ID, $option_to_toggle, $new_value );
- exit;
- }
- public static function add_to_lock_or_not_results( $post_id, $result ) {
- // Manages the lock_or_not post id <-> lock info var cache. The cache is run in a FIFO basis to prevent memory bloat in WP installs which may have long post listings. What it does is snip the first element in array and add the newly added var in the end
-
- // If the lock or not array is large than 50, snip the first item
-
- if ( count( self::$lock_or_not ) > 50 ) {
- array_shift( self::$lock_or_not );
- }
-
- // Add the sent element at the end:
-
- return self::$lock_or_not[$post_id] = $result;
-
- }
- public static function add_plugin_action_links( $links ) {
-
- // Adds action links to plugin listing in WP plugin admin
-
- $links = array_merge( array(
- '' . __( 'Settings', 'textdomain' ) . ''), $links );
-
- return $links;
-
-
- }
- public static function lock_or_not( $post_id = false ) {
-
- // This function has the logic which decides if a post should be locked. It can be called inside or outside the loop
-
- // If the caching var is initialized, consider using it:
- if ( count( self::$lock_or_not ) > 0 ) {
-
- if( !$post_id ) {
-
- global $post;
-
- if ( isset( $post->ID ) ) {
- $post_id = $post->ID;
- }
-
- }
-
- // If post id could be acquired, check if this post's result was already cached:
-
- if ( $post_id AND isset( self::$lock_or_not[$post_id] ) ) {
- return self::$lock_or_not[$post_id];
- }
-
- }
-
- $user = wp_get_current_user();
- $user_pledge_relationship_start = Patreon_Wordpress::get_user_pledge_relationship_start( $user );
- $user_patronage = Patreon_Wordpress::getUserPatronage( $user );
- $user_response = Patreon_Wordpress::getPatreonUser( $user );
- $is_patron = Patreon_Wordpress::isPatron( $user );
- $user_lifetime_patronage = Patreon_Wordpress::get_user_lifetime_patronage( $user );
- $declined = Patreon_Wordpress::checkDeclinedPatronage( $user );
- $active_patron_at_post_date = false;
- $post_locked_with = array();
-
- // Just bail out if this is not the main query for content and no post id was given
- if ( !is_main_query() AND !$post_id ) {
-
- return self::add_to_lock_or_not_results( $post_id, apply_filters(
- 'ptrn/lock_or_not',
- array(
- 'lock' => false,
- 'reason' => 'no_post_id_no_main_query',
- ),
- $post_id,
- $declined,
- $user,
- $user_response,
- $post_locked_with
- )
- );
-
- }
-
- // If post it received, get that post. If no post id received, try to get post from global
- if ( $post_id ) {
- $post = get_post( $post_id );
- }
- else {
- // If post could be acquired from global,
- global $post;
- }
-
- // First check if entire site is locked, get the level for locking.
-
- $patreon_level = get_option( 'patreon-lock-entire-site', false );
-
- // Check if specific level is given for this post:
-
- $post_level = '';
-
- if ( isset( $post->ID ) ) {
- $post_level = get_post_meta( $post->ID, 'patreon-level', true );
- }
-
- // get post meta returns empty if no value is found. If so, set the value to 0.
-
- if ( $post_level == '' ) {
- $post_level = 0;
- }
-
- // Check if post was set for active patrons only
- $patreon_active_patrons_only = get_post_meta( $post->ID, 'patreon-active-patrons-only', true );
-
- // Check if specific total patronage is given for this post:
- $post_total_patronage_level = get_post_meta( $post->ID, 'patreon-total-patronage-level', true );
-
- $post_locked_with = array(
- 'site_patreon_level' => $patreon_level,
- 'post_level' => $post_level,
- 'active_patrons_only' => $patreon_active_patrons_only,
- 'post_total_patronage_level' => $post_total_patronage_level,
-
- );
-
- $exclude = array(
- );
-
- // Enables 3rd party plugins to modify the post types excluded from locking
- $exclude = apply_filters( 'ptrn/filter_excluded_posts', $exclude );
-
- if ( isset( $post->ID ) AND in_array( get_post_type( $post->ID ), $exclude ) ) {
-
- return self::add_to_lock_or_not_results( $post_id, apply_filters(
- 'ptrn/lock_or_not',
- array(
- 'lock' => false,
- 'reason' => 'post_type_excluded_from_locking',
- ),
- $post_id,
- $declined,
- $user,
- $user_response,
- $post_locked_with
- )
- );
-
- }
-
- // Check if both post level and site lock level are set to 0 or nonexistent. If so return normal content.
-
- if ( $post_level == 0
- && ( !$patreon_level
- || $patreon_level == 0 )
- ) {
-
- return self::add_to_lock_or_not_results( $post_id, apply_filters(
- 'ptrn/lock_or_not',
- array(
- 'lock' => false,
- 'reason' => 'post_is_public',
- ),
- $post_id,
- $declined,
- $user,
- $user_response,
- $post_locked_with
- )
- );
- }
-
- // If we are at this point, then this post is protected.
-
- // Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
- // It is independent of the plugin load order since it checks if it is defined.
- // It can be defined by any plugin until right before the_content filter is run.
-
- // If post level is not 0, override patreon level and hence site locking value with post's. This will allow Creators to lock entire site and then set a different value for individual posts for access. Ie, site locking is $5, but one particular post can be $10, and it will require $10 to see.
-
- if ( $post_level !=0 ) {
- $patreon_level = $post_level;
- }
-
- if ( apply_filters( 'ptrn/bypass_filtering', defined( 'PATREON_BYPASS_FILTERING' ) ) ) {
-
- return self::add_to_lock_or_not_results( $post_id, apply_filters(
- 'ptrn/lock_or_not',
- array(
- 'lock' => false,
- 'reason' => 'lock_bypassed_by_filter',
- ),
- $post_id,
- $declined,
- $user,
- $user_response,
- $post_locked_with
- )
- );
- }
-
- if ( current_user_can( 'manage_options' ) ) {
-
- // Here we need to put a notification to admins so they will know they can see the content because they are admin_login_with_patreon_disabled
-
- return self::add_to_lock_or_not_results( $post_id, apply_filters(
- 'ptrn/lock_or_not',
- array(
- 'lock' => false,
- 'reason' => 'show_to_admin_users',
- ),
- $post_id,
- $declined,
- $user,
- $user_response,
- $post_locked_with
- )
- );
-
- }
-
-
- $hide_content = true;
- $reason = 'active_pledge_not_enough';
-
- // Check if user is logged in
-
- if ( !is_user_logged_in() ) {
-
- $hide_content = true;
- $reason = 'user_not_logged_in';
-
- }
-
- if ( $declined ) {
-
- // $hide_content = true;
- // $reason = 'payment_declined';
-
- }
-
- if ( is_user_logged_in() AND !$is_patron ) {
-
- $hide_content = true;
- $reason = 'not_a_patron';
-
- }
-
- if ( !( $user_patronage == false
- || $user_patronage < ( $patreon_level * 100 ) ) AND is_user_logged_in() ) {
-
- $hide_content = false;
- $reason = 'valid_patron';
- // Seems valid patron. Lets see if active patron option was set and the user fulfills it
-
- if ( $patreon_active_patrons_only == '1'
- AND $user_pledge_relationship_start >= strtotime( get_the_date( '', $post->ID ) ) ) {
- $hide_content = true;
- $reason = 'not_active_patron_at_post_date';
- $active_patron_at_post_date = false;
- }
- else {
- $hide_content = false;
- $active_patron_at_post_date = true;
- }
-
- }
-
- if ( $post_total_patronage_level !='' AND $post_total_patronage_level > 0) {
-
- // Total patronage set if user has lifetime patronage over this level, we let him see the content
- if( $user_lifetime_patronage >= $post_total_patronage_level * 100 ) {
- $hide_content = false;
- $reason = 'patron_fulfills_total_historical_pledge_requirement';
- }
-
- }
-
- $result = array(
- 'lock' => $hide_content,
- 'reason' => $reason,
- 'patreon_level' => $patreon_level,
- 'post_total_patronage_level' => $post_total_patronage_level,
- 'patreon_active_patrons_only' => $patreon_active_patrons_only,
- 'active_patron_at_post_date' => $active_patron_at_post_date,
- 'user_is_patron' => $is_patron,
- 'user_active_pledge' => $user_patronage,
- 'user_total_historical_pledge' => $user_lifetime_patronage,
- );
-
- $result = apply_filters( 'ptrn/lock_or_not', $result , $post_id, $declined, $user, $user_response, $post_locked_with );
-
- return self::add_to_lock_or_not_results( $post_id, $result);
-
- }
-
- public static function collect_app_info() {
-
- // Collects app information from WP site to be used in client settins at Patreon
-
- $parsed_home_url = parse_url( get_bloginfo( 'url' ) );
-
- $company_domain = $parsed_home_url['host'];
-
- $app_info = array(
- 'name' => get_bloginfo( 'name' ),
- 'description' => 'Patreon app for ' . get_bloginfo( 'name' ),
- 'author_name' => get_bloginfo( 'name' ),
- 'domain' => $company_domain,
- 'privacy_policy_url' => '',
- 'tos_url' => '',
- 'icon_url' => PATREON_PLUGIN_ASSETS . '/img/patreon_wordpress_app_icon.png',
- 'redirect_uris' => site_url( '/patreon-authorization/' ),
- 'version' => '2',
- 'parent_client_id' => PATREON_PLUGIN_CLIENT_ID,
- );
-
- return apply_filters( 'ptrn/filter_collect_app_info_result', $app_info );
- }
-
- public static function check_setup() {
-
- // Checks if setup was done and does necessary procedures
-
- if( is_admin() AND current_user_can( 'manage_options' ) AND !is_network_admin() ) {
-
- // Check if redirect to setup wizard flag was set.
- $redirect_to_setup_wizard = get_option( 'patreon-redirect_to_setup_wizard', false );
-
- // Apply filter so 3rd party addons can implement their own wizards
-
- $redirect_to_setup_wizard = apply_filters( 'ptrn/redirect_to_setup_override', $redirect_to_setup_wizard );
-
- if( $redirect_to_setup_wizard ) {
-
- // Redirect to setup wizard was set. Set necessary flags and redirect to setup wizard page
-
- // The below flag will allow hiding notices or avoiding recursive redirections
- update_option( 'patreon-setup_is_being_done', true );
-
- // Toggle redirect flag off. If the user skips the wizard we will just show a notice in admin and not force subsequent redirections
- update_option( 'patreon-redirect_to_setup_wizard', false );
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0') );
- exit;
- }
-
- }
-
- }
-
- public static function setup_wizard() {
-
- // Handles setup wizard and reconnect wizard screens
-
- $setup_message = PATREON_SETUP_INITIAL_MESSAGE;
-
- if ( !isset( $_REQUEST['setup_stage'] ) OR $_REQUEST['setup_stage'] == '0' ) {
-
- // This should be allowed to be viewed without a nonce
-
- $requirements_check = Patreon_Compatibility::check_requirements();
- $requirement_notices = '';
-
- if ( is_array( $requirements_check ) AND count( $requirements_check ) > 0 ) {
- $requirement_notices .= PATREON_ENSURE_REQUIREMENTS_MET;
- foreach ( $requirements_check as $key => $value ) {
- $requirement_notices .= '• ' . Patreon_Frontend::$messages_map[$requirements_check[$key]].' ';
- }
- }
-
- // Delete v1 related details in case a v1 site owner initiated setup wizard for whatsoever reason
- if ( get_option( 'patreon-installation-api-version', false ) == '1' ) {
-
- $options_to_delete = array(
- 'patreon-custom-page-name',
- 'patreon-fetch-creator-id',
- 'patreon-creator-tiers',
- 'patreon-creator-last-name',
- 'patreon-creator-first-name',
- 'patreon-creator-full-name',
- 'patreon-creator-url',
- 'patreon-campaign-id',
- 'patreon-creators-refresh-token-expiration',
- 'patreon-creator-id',
- 'patreon-setup-wizard-last-call-result',
- 'patreon-creators-refresh-token',
- 'patreon-creators-access-token',
- 'patreon-client-secret',
- 'patreon-client-id',
- 'patreon-setup_is_being_done',
- 'patreon-setup-done',
- 'patreon-currency-sign',
- );
-
- // Delete override - proceed with deleting local options
-
- foreach ( $options_to_delete as $key => $value ) {
- delete_option( $options_to_delete[$key] );
- }
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-can-use-api-v2', true );
-
- // Set custom setup message telling old v1 install users to delete their client at clients page before starting setup
-
- $setup_message = PATREON_ADMIN_MESSAGE_V1_CLIENT_ATTEMPTING_V2_SETUP;
-
- }
-
- $config_info = self::collect_app_info();
- $config_input = '';
-
- foreach ( $config_info as $key => $value ) {
- $config_input .= '';
-
- }
-
- if ( isset( $_REQUEST['patreon_message'] ) AND $_REQUEST['patreon_message'] != '' ) {
- $setup_message = Patreon_Frontend::$messages_map[$_REQUEST['patreon_message']];
-
- }
-
- // Create state var needed for identifying connection attempt
-
- $state = array(
- 'patreon_action' => 'connect_site',
- );
-
- echo '
';
-
- }
-
- }
-
- public static function check_plugin_exists( $plugin_dir ) {
-
- // Simple function to check if a plugin is installed (may be active, or not active) in the WP instalation
-
- // Plugin dir is the wp's plugin dir together with the plugin's dir
-
- if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_dir ) ) {
- return true;
- }
- }
-
- public static function check_plugin_active( $full_plugin_slug ) {
- // Simple function to check if a plugin is installed (may be active, or not active) in the WP instalation
-
- // Plugin slug is the plugin dir together with the plugin's file which has the plugin header
-
- if ( is_plugin_active( $full_plugin_slug ) ) {
- return true;
- }
- }
- public static function activate( $network_wide ) {
-
- // Kicks in after activation of the plugin and does necessary actions
-
- // We check if it is multisite, and if this is a network activation
-
- if ( is_multisite() AND $network_wide ) {
- // True. This means that plugin was activated by an admin for all sites on the network, so we dont trigger setup wizard
- return;
- }
-
- // Checking if $plugin_first_activated value exists prevents resetting of this value on every plugin deactivation/activation
-
- $plugin_first_activated = get_option( 'patreon-plugin-first-activated', 0 );
-
- if ( $plugin_first_activated == 0 ) {
-
- update_option( 'patreon-plugin-first-activated', time() );
-
- // Currently we are only going to move new installations to v2. Later this flag below can be removed and setup notice can be popped for any install which does not have the app credentials saved.
-
- update_option( 'patreon-installation-api-version', '2' );
- }
-
- // Check if setup was done and put up a redirect flag if not
-
- $patreon_setup_done = get_option( 'patreon-setup-done', false );
-
- // Check if this site is a v2 site
- $api_version = get_option( 'patreon-installation-api-version', false );
-
- if( !$patreon_setup_done ) {
- // Setup complete flag not received. Set flag for redirection in next page load
- update_option( 'patreon-redirect_to_setup_wizard', true );
- }
-
- // Check and add rewrite rules for image/file locking if enabled and not present. Will check if .htaccess exists, will check if rules are present, add if not and refresh if so
-
- Patreon_Protect::addPatreonRewriteRules();
-
- global $wp_rewrite;
- $wp_rewrite->flush_rules();
-
- }
-
- public static function deactivate() {
-
- // Kicks in after deactivation of the plugin and does necessary actions
-
- // Remove Image/file protection related htaccess rules if they were added
-
- remove_action( 'generate_rewrite_rules', array( 'Patreon_Routing', 'add_rewrite_rules' ) );
-
- // Remove htaccess rules if they are in
-
- Patreon_Protect::removePatreonRewriteRules();
-
- global $wp_rewrite;
- $wp_rewrite->flush_rules();
-
- }
-
- public static function check_days_after_last_non_system_notice( $days ) {
- // Calculates if $days many days passed after last non system notice was showed. Used in deciding if and when to show admin wide notices
-
- $last_non_system_notice_shown_date = get_option( 'patreon-last-non-system-notice-shown-date', 0 );
-
- // Calculate if $days days passed since last notice was shown
- if ( ( time() - $last_non_system_notice_shown_date ) > ( $days * 24 * 3600 ) ) {
- // More than $days days. Set flag
- return true;
- }
-
- return false;
-
- }
-
- public static function check_days_after_last_system_notice( $days ) {
- // Calculates if $days many days passed after last non system notice was showed. Used in deciding if and when to show admin wide notices
-
- $last_non_system_notice_shown_date = get_option( 'patreon-last-system-notice-shown-date', 0 );
-
- // Calculate if $days days passed since last notice was shown
- if ( ( time() - $last_non_system_notice_shown_date ) > ( $days * 24 * 3600 ) ) {
- // More than $days days. Set flag
- return true;
- }
-
- return false;
-
- }
-
- public static function calculate_days_after_first_activation( $days ) {
-
- // Used to calculate days passed after first plugin activation.
-
- $plugin_first_activated = get_option( 'patreon-plugin-first-activated', 0 );
-
- // Calculate if $days days passed since last notice was shown
- if ( ( time() - $plugin_first_activated ) > ( $days * 24 * 3600 ) ) {
- // More than $days days. Set flag
- return true;
- }
-
- return false;
-
- }
-
- public static function set_last_non_system_notice_shown_date() {
-
- // Sets the last non system notice shown date to now whenever called. Used for decicing when to show admin wide notices that are not related to functionality.
-
- update_option( 'patreon-last-non-system-notice-shown-date', time() );
-
- }
-
- public static function set_last_system_notice_shown_date() {
-
- // Sets the last non system notice shown date to now whenever called. Used for decicing when to show admin wide notices that are not related to functionality.
-
- update_option( 'patreon-last-system-notice-shown-date', time() );
-
- }
-
- public static function populate_patreon_level_select_from_ajax() {
- // This function accepts the ajax request from the metabox and calls the relevant function to populate the tiers select
-
- if( !current_user_can( 'manage_options' ) ) {
- echo 'Not enough permissions';
- exit;
- }
-
- if ( !isset($_POST['patreon_wordpress_nonce_populate_tier_dropdown']) OR !wp_verify_nonce( $_POST['patreon_wordpress_nonce_populate_tier_dropdown'], 'patreon_wordpress_nonce_populate_tier_dropdown') ) {
- echo 'Form security field expired - please refresh the page and try again';
- exit;
- }
-
- // Just bail out if the action is not relevant, just in case
- if ( !isset( $_REQUEST['action'] ) OR $_REQUEST['action'] != 'patreon_wordpress_populate_patreon_level_select' ) {
- return;
- }
-
- // If post id was not passed, exit with error
- if ( !isset( $_REQUEST['pw_post_id'] ) OR $_REQUEST['pw_post_id'] == '' ) {
- echo 'Error: Could not get post id';
- exit;
- }
-
- Patreon_Wordpress::update_creator_tiers_from_api();
-
- $post = get_post( $_REQUEST['pw_post_id'] );
-
- echo Patreon_Wordpress::make_tiers_select( $post );
- exit;
-
- }
- public static function make_tiers_select( $post = false, $args = array() ) {
-
- if ( !$post ) {
- global $post;
- }
-
- // This function makes a select box with rewards and reward ids from creator's campaign to be used in post locking and site locking
-
- // Get updated tiers from db
- $creator_tiers = get_option( 'patreon-creator-tiers', false );
-
- // Set the select to default
- $select_options = PATREON_TEXT_YOU_HAVE_NO_REWARDS_IN_THIS_CAMPAIGN;
- // 1st element is 'everyone' and 2nd element is 'Patrons' (with cent amount 1) in the rewards array.
-
- if ( isset( $creator_tiers['included'] ) AND is_array( $creator_tiers['included'] ) ) {
-
- $select_options = '';
-
- // Lets get the current Patreon level for the post:
- $patreon_level = get_post_meta( $post->ID, 'patreon-level', true );
-
- $tier_count = 1;
-
- // Flag for determining if the matching tier was found during iteration of tiers
- $matching_level_found = false;
-
- foreach( $creator_tiers['included'] as $key => $value ) {
-
- // If its not a reward element, continue, just to make sure
-
- if(
- !isset( $creator_tiers['included'][$key]['type'] )
- OR ( $creator_tiers['included'][$key]['type'] != 'reward' AND $creator_tiers['included'][$key]['type'] != 'tier' )
- ) {
- continue;
- }
-
- $reward = $creator_tiers['included'][$key];
-
- // Special conditions for label for element 0, which is 'everyone' and '1, which is 'patron only'
-
- if ( $reward['id'] == -1 ) {
- $label = PATREON_TEXT_EVERYONE;
- }
- if ( $reward['id'] == 0 ) {
- $label = PATREON_TEXT_ANY_PATRON;
- }
-
- // Use title if exists, and cents amount converted to dollar for any other reward level
- if ( $reward['id'] > 0 ) {
-
- $tier_title = 'Tier ' . $tier_count;
-
- $tier_count++;
-
- if ( $reward['attributes']['title'] != '' ) {
-
- $tier_title = $reward['attributes']['title'];
-
- // If the title is too long, snip it
- if ( strlen( $tier_title ) > 23 ) {
- $tier_title = substr( $tier_title , 0 , 23 ) .'...';
- }
-
- }
-
- $label = $tier_title . ' - $' . ( $reward['attributes']['amount_cents'] / 100 );
- }
-
- $selected = '';
-
- if ( ( $reward['attributes']['amount_cents'] / 100 ) >= $patreon_level AND !$matching_level_found ) {
-
- // Matching level was present, but now found. Set selected and toggle flag.
- // selected = selected for XHTML compatibility
- $selected = ' selected="selected"';
-
- $matching_level_found = true;
-
- // Check if a precise amount is set for this content. If so, add the actual locking amount in parantheses
-
- if ( ( $reward['attributes']['amount_cents'] / 100 ) != $patreon_level ) {
-
- $label .= ' ($'.$patreon_level.' exact)';
-
- }
-
- }
-
- $select_options .= '';
- }
-
- }
-
- return apply_filters( 'ptrn/post_locking_tier_select', $select_options, $post );
-
- }
- public static function update_creator_tiers_from_api() {
-
- // Does an update of creator tiers from the api
-
- if ( get_option( 'patreon-client-id', false )
- && get_option( 'patreon-client-secret', false )
- && get_option( 'patreon-creators-access-token' , false )
- ) {
- // Credentials are in. Go.
-
- $api_client = new Patreon_API( get_option( 'patreon-creators-access-token', false ) );
- $creator_info = $api_client->fetch_tiers();
-
- }
-
- if ( isset( $creator_info ) AND $creator_info == 'throttled_locally' ) {
- // Return by doing nothing until the api can be contacted again
- return;
- }
-
- if ( isset( $creator_info ) AND isset( $creator_info['included'] ) AND is_array( $creator_info['included'] ) AND isset( $creator_info['included'][1]['type'] ) AND $creator_info['included'][1]['type'] == 'reward' ) {
-
- // Creator info acquired. Update.
- // We want to sort tiers according to their $ level.
-
- usort( $creator_info['included'], function( $a, $b ) {
- return $a['attributes']['amount_cents'] - $b['attributes']['amount_cents'];
- } );
-
- array_walk_recursive( $creator_info, 'self::format_creator_info_array' );
-
- update_option( 'patreon-creator-tiers', $creator_info );
- update_option( 'patreon-creator-has-tiers', 'yes' );
- }
- else {
-
- // Creator doesnt have tiers. Save empty array so local checker functions can know there are no tiers
- update_option( 'patreon-creator-tiers', array() );
- update_option( 'patreon-creator-has-tiers', 'no' );
- }
-
- }
- public static function get_page_name() {
-
- // This wrapper function wraps the means to get the page name of the creator's campaign
- // Currently it uses tier details which are already put into the db to pull the page name. In future it can be made pull the name from another source
-
- $creator_tiers = get_option( 'patreon-creator-tiers', false );
-
- if ( !$creator_tiers OR $creator_tiers == '' ) {
- // Creator tier info not in. bail out
- return false;
- }
-
- // Creator tier info in. Get the page name
-
- if ( isset( $creator_tiers['data'][0]['attributes']['name'] ) AND isset( $creator_tiers['data'][0]['attributes']['name'] ) != '' ) {
-
- // We have the name. Return
-
- return $creator_tiers['data'][0]['attributes']['name'];
-
- }
-
- }
- public static function format_creator_info_array( &$value, $key ) {
-
- // Checks creator info array and formats/cleans as necessary
-
- if ( trim( $key ) == 'description' ) {
- // update_option refuses to save entire creator info array if there are extensive formatting in tier descriptions. base64ing them circumvents this issue
- $value = base64_encode( $value );
- }
-
- }
- public static function order_independent_actions_to_run_on_init_start() {
-
- // This function runs on init at order 0, and allows any action that does not require to be run in a particular order or any other function or operation to be run.
-
- if ( isset( $_REQUEST['patreon_wordpress_action'] ) AND $_REQUEST['patreon_wordpress_action'] == 'disconnect_site_from_patreon' AND is_admin() AND current_user_can( 'manage_options' ) ) {
-
-
- if ( !isset($_REQUEST['patreon_wordpress_disconnect_from_patreon_nonce']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_disconnect_from_patreon_nonce'] ) ) ) {
- wp_die('Form security field expired - please refresh the page and try again');
- }
-
- // Admin side, user is admin level. Perform action:
-
- // To disconnect the site from a particular creator account, we will delete all options related to creator account, but we will leave other plugin settings and post gating values untouched
-
- $options_to_delete = array(
- 'patreon-custom-page-name',
- 'patreon-fetch-creator-id',
- 'patreon-creator-tiers',
- 'patreon-creator-last-name',
- 'patreon-creator-first-name',
- 'patreon-creator-full-name',
- 'patreon-creator-url',
- 'patreon-campaign-id',
- 'patreon-creators-refresh-token-expiration',
- 'patreon-creator-id',
- 'patreon-setup-wizard-last-call-result',
- 'patreon-creators-refresh-token',
- 'patreon-creators-access-token',
- 'patreon-client-secret',
- 'patreon-client-id',
- 'patreon-setup_is_being_done',
- 'patreon-setup-done',
- 'patreon-currency-sign',
- );
-
- // Ask the API to delete this client:
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
- $client_id = get_option( 'patreon-client-id', false );
-
- // Exceptions until v1 v2 transition is complete
-
- $api_version = get_option( 'patreon-installation-api-version' );
-
- if ( $api_version == '1' ) {
-
- // Delete override - proceed with deleting local options
-
- foreach ( $options_to_delete as $key => $value ) {
- delete_option( $options_to_delete[$key] );
- }
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-can-use-api-v2', true );
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0') );
- exit;
- }
-
- // Exceptions EOF
-
- if ( $creator_access_token AND $client_id ) {
-
- // Create new api object
-
- $api_client = new Patreon_API( $creator_access_token );
-
- $params = array(
- 'data' => array(
- 'type' => 'oauth-client',
- 'parent_client_id' => PATREON_PLUGIN_CLIENT_ID,
- )
- );
-
- $client_result = $api_client->delete_client( json_encode( $params ) );
-
- if ( isset( $client_result['response']['code'] ) AND $client_result['response']['code'] == '204' ) {
-
- // Delete succeeded proceed with deleting local options
-
- foreach ( $options_to_delete as $key => $value ) {
- delete_option( $options_to_delete[$key] );
- }
-
- update_option( 'patreon-show-site-disconnect-success-notice', true );
-
- wp_redirect( admin_url( 'admin.php?page=patreon-plugin') );
- exit;
-
- }
-
- // Delete no go. Do error handling here
-
- // We redirect to error page.
-
- wp_redirect( admin_url( 'admin.php?page=patreon-plugin-admin-message&patreon_admin_message_title=client_delete_error_title&patreon_admin_message_content=client_delete_error_content' ) );
- exit;
-
- }
-
-
- }
-
- if ( isset( $_REQUEST['patreon_wordpress_action'] ) AND $_REQUEST['patreon_wordpress_action'] == 'disconnect_site_from_patreon_for_reconnection' AND is_admin() AND current_user_can( 'manage_options' ) ) {
-
- // We dont need to do this anymore. So for the time being, just redirect to the connection wizard for compatibility concerns. The entire block can be removed after a few versions
-
- // Redirect to connect wizard
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0') );
- exit;
-
- if ( !isset($_REQUEST['patreon_wordpress_reconnect_to_patreon_nonce']) OR !wp_verify_nonce( sanitize_key( $_REQUEST['patreon_wordpress_reconnect_to_patreon_nonce'] ) ) ) {
- wp_die('Form security field expired - please refresh the page and try again');
- }
-
- // Admin side, user is admin level. Perform action:
-
- // To disconnect the site from a particular creator account, we will delete all options related to creator account, but we will leave other plugin settings and post gating values untouched
-
- $options_to_delete = array(
- 'patreon-custom-page-name',
- 'patreon-fetch-creator-id',
- 'patreon-creator-tiers',
- 'patreon-creator-last-name',
- 'patreon-creator-first-name',
- 'patreon-creator-full-name',
- 'patreon-creator-url',
- 'patreon-campaign-id',
- 'patreon-creators-refresh-token-expiration',
- 'patreon-creator-id',
- 'patreon-setup-wizard-last-call-result',
- 'patreon-creators-refresh-token',
- 'patreon-creators-access-token',
- 'patreon-client-secret',
- 'patreon-client-id',
- 'patreon-setup_is_being_done',
- 'patreon-setup-done',
- 'patreon-currency-sign',
- );
-
- // Ask the API to delete this client:
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
- $client_id = get_option( 'patreon-client-id', false );
-
- // Exceptions until v1 v2 transition is complete
-
- $api_version = get_option( 'patreon-installation-api-version' );
-
- if ( $api_version == '1' ) {
-
- // Delete override - proceed with deleting local options
-
- foreach ( $options_to_delete as $key => $value ) {
- delete_option( $options_to_delete[$key] );
- }
-
- update_option( 'patreon-installation-api-version', '2' );
- update_option( 'patreon-can-use-api-v2', true );
-
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0') );
- exit;
- }
- // Exceptions EOF
-
- if ( $creator_access_token AND $client_id ) {
-
- // Create new api object
-
- $api_client = new Patreon_API( $creator_access_token );
-
- $params = array(
- 'data' => array(
- 'type' => 'oauth-client',
- 'parent_client_id' => PATREON_PLUGIN_CLIENT_ID,
- )
- );
-
- $client_result = $api_client->delete_client( json_encode( $params ) );
-
- if ( isset( $client_result['response']['code'] ) AND $client_result['response']['code'] == '204' ) {
-
- // Delete succeeded proceed with deleting local options
-
- foreach ( $options_to_delete as $key => $value ) {
- delete_option( $options_to_delete[$key] );
- }
-
- // Redirect to connect wizard
- wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0') );
- exit;
-
- }
-
- // If we are here, delete did not succeed. do error handling here.
-
- // We redirect to error page.
-
- wp_redirect( admin_url( 'admin.php?page=patreon-plugin-admin-message&patreon_admin_message_title=client_reconnect_delete_error_title&patreon_admin_message_content=client_reconnect_delete_error_content' ) );
- exit;
-
- }
-
- }
-
- }
-
- public static function log_connection_error( $error = false ) {
-
- if ( !$error ) {
- return;
- }
-
- // Get the last 50 connection errors log
- // Init in case it does not exist, get value if exists - will return empty array if it does not exist.
-
- $last_50_conn_errors = get_option( 'patreon-last-50-conn-errors', array() );
-
- // If array is 50 or longer, pop it
-
- if( count( $last_50_conn_errors ) >= 50 ) {
- array_pop( $last_50_conn_errors );
- }
-
- // Add the error message to last 50 connection errors with time
-
- array_unshift (
- $last_50_conn_errors,
- array (
- 'date' => time(),
- 'error' => $error
- )
- );
-
- // Update option
- update_option( 'patreon-last-50-conn-errors', $last_50_conn_errors );
-
- }
-
- public static function is_content_gated_with_pw( $post_id ) {
-
- // Checks if a post is gated with PW
-
- $post = get_post( $post_id );
-
- // Take into account excluded posts.
-
- $exclude = array(
- );
-
- // Enables 3rd party plugins to modify the post types excluded from locking
- $exclude = apply_filters( 'ptrn/filter_excluded_posts', $exclude );
-
- if ( isset( $post->ID ) AND in_array( get_post_type( $post->ID ), $exclude ) ) {
- // Excluded from gating. Therefore return false
- return false;
- }
-
- // First check if entire site is locked, get the level for locking.
-
- $patreon_level = get_option( 'patreon-lock-entire-site', false );
-
- // Check if specific level is given for this post:
-
- $post_level = '';
-
- if ( isset( $post->ID ) ) {
- $post_level = get_post_meta( $post->ID, 'patreon-level', true );
- }
-
- // get post meta returns empty if no value is found. If so, set the value to 0.
-
- if ( $post_level == '' ) {
- $post_level = 0;
- }
-
- // Check if both post level and site lock level are set to 0 or nonexistent. If so return normal content.
-
- if ( $post_level == 0
- && ( !$patreon_level
- || $patreon_level == 0 )
- ) {
- // Post is public
- return false;
- }
-
- // If we are at this point, it means the post is gated with PW. Return true.
- return true;
-
- }
-
- public function make_post_type_select( $selected_post_type = 'post' ) {
-
- $post_types = get_post_types();
- $select = '';
-
- foreach( $post_types as $key => $value ) {
-
- $selected = '';
- $obj = get_post_type_object( $key );
-
- if ( $key == $selected_post_type ) {
- $selected = ' selected';
- }
-
- $select .= '';
-
- }
-
- return $select;
-
- }
-
- public static function add_to_patreon_user_info_cache( $user_id, $user_info ) {
-
- // This function manages the array that is used as the cache for info of Patreon users in a given page run. What it does is to accept the id of the WP user and a given Patreon user info, then add it to the a cache array
-
- // If the cache array is larger than 50, snip the first item. This may be increased in future
-
- if ( !empty( self::$patreon_user_info_cache ) && (count( self::$patreon_user_info_cache ) > 50) ) {
- array_shift( self::$patreon_user_info_cache );
- }
-
- // Add the new request and return it
-
- return self::$patreon_user_info_cache[$user_id] = $user_info;
-
- }
- public static function add_to_patreon_pledge_info_cache( $user_id, $pledge ) {
-
- // This function manages the array that is used as the cache for info of Patreon users in a given page run. What it does is to accept the id of the WP user and a given Patreon user info, then add it to the a cache array
-
- // If the cache array is larger than 50, snip the first item. This may be increased in future
-
- if ( !empty( self::$patreon_pledge_info_cache ) && (count( self::$patreon_pledge_info_cache ) > 50) ) {
- array_shift( self::$patreon_pledge_info_cache );
- }
-
- // Add the new request and return it
-
- return self::$patreon_pledge_info_cache[$user_id] = $pledge;
-
- }
-
- public function make_taxonomy_select( $selected_post_type = 'post', $selected_taxonomy = 'category' ) {
-
- $return = true;
- $select = '';
-
- if ( isset( $_REQUEST['patreon_wordpress_post_type'] ) ) {
- $selected_post_type = $_REQUEST['patreon_wordpress_post_type'];
- $return = false;
- }
-
- $taxonomies = get_object_taxonomies( $selected_post_type );
-
- foreach( $taxonomies as $key => $value ) {
-
- $selected = '';
-
- $taxonomy = get_taxonomy( $taxonomies[$key] );
-
- if ( is_object( $taxonomy ) ) {
-
- if ( $taxonomy->name == $selected_taxonomy AND $return ) {
- $selected = ' selected';
- }
-
- $select .= '';
- }
-
- }
-
- if ( $return ) {
- return $select;
- }
-
- echo $select;
- exit;
-
- }
- public function make_term_select( $selected_post_type = 'post', $selected_taxonomy = 'category', $selected_term = 1 ) {
-
- $return = true;
- $select = '';
-
- if ( isset( $_REQUEST['patreon_sync_post_category'] ) ) {
- $selected_taxonomy = $_REQUEST['patreon_sync_post_category'];
- $return = false;
- }
-
- $terms = get_terms( $selected_taxonomy,
- array(
- 'hide_empty' => false,
- 'parent' => 0,
- 'orderby' =>
- 'description',
- 'order' => 'ASC',
- )
- );
-
- foreach( $terms as $key => $value ) {
-
- $selected = '';
-
- if ( count( $terms ) > 0 ) {
-
- if ( $terms[$key]->term_id == $selected_term AND $return ) {
- $selected = ' selected';
- }
-
- $select .= '';
- }
-
- }
-
- if ( $return ) {
- return $select;
- }
-
- echo $select;
- exit;
-
- }
- public function make_user_select( $selected_user = 1 ) {
-
- $return = true;
- $select = '';
-
- if ( isset( $_REQUEST['patreon-post-author-for-synced-posts'] ) ) {
- $selected_user = $_REQUEST['patreon-post-author-for-synced-posts'];
- $return = false;
- }
-
- $args = array(
- 'role__in' => array( 'Super Admin', 'Administrator', 'Editor', 'Author', 'Contributor'),
- 'orderby' => 'user_nicename',
- 'order' => 'ASC'
- );
-
- $users = get_users( $args );
+ }
- // Track how many users we list to prevent resource limit violations
- $user_count = 1;
-
- $existing_author_found = false;
-
- if ( count( $users ) > 0 ) {
-
- foreach( $users as $key => $value ) {
-
- $selected = '';
-
- if ( $users[$key]->data->ID == $selected_user AND $return ) {
- $selected = ' selected';
- $existing_author_found = true;
- }
-
- $select .= '';
-
- if ( $user_count > 100 ) {
- break;
- }
- $user_count++;
-
- }
-
- }
- // If a different user than which users were filtered with $args was selected before $args was added to get_users above, get that user and manually add it to the select box:
-
- if ( !$existing_author_found ) {
-
- $existing_author = get_user_by( 'ID', $selected_user );
-
- if ( $existing_author ) {
-
- $select .= '';
- }
- }
-
-
- if ( $return ) {
- return $select;
- }
-
- echo $select;
- exit;
-
- }
- public function get_file_id_from_media_library( $filename ) {
-
- global $wpdb;
-
- $query = $wpdb->prepare(
- "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_name = %s",
- $filename
- );
-
- if ( $wpdb->get_var( $query ) !== null ) {
- return $wpdb->get_var( $query );
- }
-
- return false;
-
- }
- public function get_remote_image_hash( $image_url ) {
-
- global $wpdb;
-
- $image_hash = false;
- $image_response = wp_remote_get( $image_url, array( 'timeout' => 3 ) );
- $image_content = wp_remote_retrieve_body($image_response);
-
- if ( $image_content != '' ) {
- $image_hash = md5( $image_content );
- }
-
- return $image_hash;
-
- }
-
- public function get_images_info_from_content( $content ) {
-
- if ( $content == '' ) {
- return false;
- }
-
- // Get images out of content using dom
- $dom_document = new domDocument;
- $save_errors = libxml_use_internal_errors( true );
- $dom_document->loadHTML( $content );
- $dom_document->preserveWhiteSpace = false;
- $images = $dom_document->getElementsByTagName( 'img' );
- libxml_use_internal_errors( $save_errors );
-
- $parsed_images_info = array();
-
- foreach ( $images as $image ) {
-
- $url = $image->getAttribute( 'src' );
-
- $details = parse_url( $url );
-
- $exploded_path = array_reverse( explode( '/', $details['path'] ) );
-
- // First element is the Patreon given filename, second is unique identifier
-
- $extension = pathinfo( $exploded_path[0], PATHINFO_EXTENSION );
-
- // Use only unique identifier for now
- $parsed_images_info[] = array(
- 'filename' => $exploded_path[1] . '.' . $extension,
- 'name' => $exploded_path[1],
- 'extension' => $extension,
- 'url' => $url,
- );
-
- }
-
- if ( count( $parsed_images_info ) > 0 ) {
- return $parsed_images_info;
- }
-
- return false;
- }
- public function download_insert_media( $url, $filename ) {
-
- require_once(ABSPATH . '/wp-admin/includes/file.php');
- require_once(ABSPATH . '/wp-admin/includes/media.php');
- require_once(ABSPATH . '/wp-admin/includes/image.php');
-
- $temp_file = download_url( $url, 3 );
-
- if ( !is_wp_error( $temp_file ) ) {
-
- $file_type_info = wp_check_filetype_and_ext( $temp_file, $filename );
-
- $wp_upload_dir = wp_upload_dir();
-
-
- $file_array = array( //array to mimic $_FILES
- 'name' => $filename, //isolates and outputs the file name from its absolute path
- 'type' => $file_type_info['type'], // get mime type of image file
- 'tmp_name' => $temp_file, //this field passes the actual path to the image
- 'error' => 0, //normally, this is used to store an error, should the upload fail. but since this isnt actually an instance of $_FILES we can default it to zero here
- 'size' => filesize( $temp_file ) //returns image filesize in bytes
- );
-
- $attachment_id = media_handle_sideload( $file_array ); //the actual image processing, that is, move to upload directory, generate thumbnails and image sizes and writing into the database happens here
-
- if ( is_wp_error( $attachment_id ) ) {
- // Insert any error handling here
- return false;
- }
-
- return $attachment_id;
-
- }
-
- }
- public function check_post_sync_webhook() {
-
- if (is_admin()) {
- return;
- }
-
- if ( get_option( 'patreon-creator-access-token-401', false ) ) {
- return;
- }
-
- $api_version = get_option( 'patreon-installation-api-version', '1' );
-
- if ( $api_version != '2' ) {
- return;
- }
-
- // Avoid infinite redirects due to https url check at add_patreon_webhook function in api class checking patreon-webhook uri
- if (strpos( $_SERVER['REQUEST_URI'], 'patreon-webhook' ) !== false ) {
- return;
- }
-
- if ( get_option( 'patreon-sync-posts', 'no' ) == 'no' ) {
-
- // Checks if post sync is enabled and posts webhook in case it is not posted
- $existing_hook = get_option( 'patreon-post-sync-webhook', array() );
-
- // If there is an existing webhook, delete it.
-
- if ( get_option( 'patreon-post-sync-webhook-saved', false ) ) {
-
- $existing_hook = get_option( 'patreon-post-sync-webhook', false );
-
- if ( !$existing_hook ) {
- return;
- }
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
-
- $api_client = new Patreon_API( $creator_access_token );
-
- $webhook_delete = $api_client->delete_post_webhook( $existing_hook['data']['id'] );
-
- // If delete is successful remove the local info about webhook
- if ( is_array( $webhook_delete ) AND isset( $webhook_delete['response']['code'] ) AND $webhook_delete['response']['code'] == '204' ) {
-
- update_option( 'patreon-post-sync-webhook-saved', false );
- delete_option( 'patreon-post-sync-webhook');
-
- }
-
- }
-
- return;
- }
-
- // If webhook already added, skip
-
- if ( get_option( 'patreon-post-sync-webhook-saved', false ) ) {
- return;
- }
-
- $creator_access_token = get_option( 'patreon-creators-access-token', false );
-
- $api_client = new Patreon_API( $creator_access_token );
-
- $webhook_added = $api_client->add_post_webhook();
-
- if ( is_array( $webhook_added ) AND isset( $webhook_added['data']['type'] ) AND $webhook_added['data']['type'] == 'webhook' ) {
-
- // Save webhook info
-
- update_option( 'patreon-post-sync-webhook', $webhook_added );
- update_option( 'patreon-post-sync-webhook-saved', true );
-
- }
-
- }
-
- public function get_all_headers() {
-
- // Gets headers on an incoming request
-
- $headers = [];
- foreach ($_SERVER as $name => $value) {
- if (substr($name, 0, 5) == 'HTTP_') {
- $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
- }
- }
- return $headers;
-
- }
-
- // Adds Patreon cron schedule if needed
-
- public function add_patreon_cron_schedules( $schedules ) {
-
- $schedules['patreon_five_minute_cron_schedule'] = array(
- 'interval' => 300, // 5 min
- 'display' => __( 'Patreon cron - every five minutes' ),
- );
-
- return $schedules;
-
- }
-
- public function patreon_five_minute_cron_job() {
-
- // Check if post sync is on just in case if the cron job somehow persisted despite sync being disabled
-
- if ( get_option( 'patreon-post-import-in-progress', false ) ) {
-
- self::$patreon_content_sync->import_posts_from_patreon();
-
- }
-
- }
- public function creator_has_tiers() {
-
- // Checks if creator has tiers locally. This is a way to identify lite plans and avoid hammering the api with tier requests
-
- $creator_tiers = get_option( 'patreon-creator-tiers', false );
-
- if ( !$creator_tiers OR $creator_tiers == '' OR !is_array( $creator_tiers['included'][1] ) ) {
- return false;
- }
- else {
- return true;
- }
-
- }
-
+ public static function getUserPatronage($user = false)
+ {
+ $using_current_user = false;
+
+ // If user is not given, try to get the current user attribute ID will be 0 if there is no logged in user
+ if (false == $user) {
+ $using_current_user = true;
+ $user = wp_get_current_user();
+ }
+
+ // If still no user object, return false
+ if (0 == $user->ID) {
+ return false;
+ }
+
+ // Compatibility - use the old current_user_pledge_amount_static if user is not given and we are using the current user
+ if ($using_current_user) {
+ if (-1 != self::$current_user_pledge_amount) {
+ return self::$current_user_pledge_amount;
+ }
+ }
+
+ if (isset(self::$patreon_pledge_info_cache[$user->ID])) {
+ return self::$patreon_pledge_info_cache[$user->ID];
+ }
+
+ $creator_id = get_option('patreon-creator-id', false);
+
+ if (false == $creator_id) {
+ return false;
+ }
+
+ /* get current users meta data */
+ $user_meta = get_user_meta($user->ID);
+ $user_response = self::getPatreonUser($user);
+
+ if (false == $user_response) {
+ return false;
+ }
+
+ $pledge = false;
+ if (array_key_exists('included', $user_response)) {
+ foreach ($user_response['included'] as $obj) {
+ if (isset($obj['type']) && 'pledge' == $obj['type'] && $obj['relationships']['creator']['data']['id'] == $creator_id) {
+ $pledge = $obj;
+ break;
+ }
+ }
+ }
+
+ if (isset($pledge['attributes']['declined_since']) && !is_null($pledge['attributes']['declined_since'])) {
+ do_action('ptrn/declined_since', $pledge, $pledge['attributes']['declined_since']);
+ // return false;
+ }
+
+ if (false != $pledge) {
+ $pledge_level = self::getUserPatronageLevel($pledge);
+
+ // Compatibility - use the old current_user_pledge_amount_static if user is not given and we are using the current user
+ if ($using_current_user) {
+ self::$current_user_pledge_amount = $pledge_level;
+ }
+
+ Patreon_Wordpress::add_to_patreon_pledge_info_cache($user->ID, $pledge_level);
+
+ return $pledge_level;
+ }
+
+ return false;
+ }
+
+ public static function getUserPatronageDuration($user = false)
+ {
+ if (-1 != self::$current_user_patronage_duration) {
+ return self::$current_user_patronage_duration;
+ }
+
+ if (!$user) {
+ $user = wp_get_current_user();
+ }
+
+ $pledge_days = false;
+
+ $user_response = self::getPatreonUser($user);
+
+ if (isset($user_response['included'][0]['attributes']['pledge_relationship_start'])) {
+ $pledge_days = floor((time() - strtotime($user_response['included'][0]['attributes']['pledge_relationship_start'])) / 60 / 60 / 24);
+ }
+
+ return $pledge_days;
+ }
+
+ public static function get_user_pledge_relationship_start($user = false)
+ {
+ if (-1 != self::$current_user_pledge_relationship_start) {
+ return self::$current_user_pledge_relationship_start;
+ }
+
+ if (!$user) {
+ $user = wp_get_current_user();
+ }
+
+ $pledge_days = false;
+ $user_response = self::getPatreonUser($user);
+
+ if (isset($user_response['included'][0]['attributes']['pledge_relationship_start'])) {
+ return strtotime($user_response['included'][0]['attributes']['pledge_relationship_start']);
+ }
+
+ return 0;
+ }
+
+ public static function get_user_lifetime_patronage($user = false)
+ {
+ if (-1 != self::$current_user_lifetime_patronage) {
+ return self::$current_user_lifetime_patronage;
+ }
+
+ if (!$user) {
+ $user = wp_get_current_user();
+ }
+
+ $lifetime_patronage = false;
+ $user_response = self::getPatreonUser($user);
+
+ if (isset($user_response['included'][0]['attributes']['lifetime_support_cents'])) {
+ $lifetime_patronage = $user_response['included'][0]['attributes']['lifetime_support_cents'];
+ }
+
+ return $lifetime_patronage;
+ }
+
+ public static function checkDeclinedPatronage($user)
+ {
+ if (-1 != self::$current_user_patronage_declined) {
+ return self::$current_user_patronage_declined;
+ }
+
+ if (!$user) {
+ $user = wp_get_current_user();
+ }
+
+ $user_response = self::getPatreonUser($user);
+
+ // If no user exists, the patronage cannot have been declined.
+ if (!$user_response) {
+ return self::$current_user_patronage_declined = false;
+ }
+
+ $creator_id = get_option('patreon-creator-id', false);
+
+ $pledge = false;
+ if (array_key_exists('included', $user_response)) {
+ foreach ($user_response['included'] as $obj) {
+ if ('pledge' == $obj['type'] && $obj['relationships']['creator']['data']['id'] == $creator_id) {
+ $pledge = $obj;
+ break;
+ }
+ }
+ }
+
+ if (isset($pledge['attributes']['declined_since']) && !is_null($pledge['attributes']['declined_since'])) {
+ do_action('ptrn/declined_since', $pledge, $pledge['attributes']['declined_since']);
+
+ return self::$current_user_patronage_declined = true;
+ } else {
+ return self::$current_user_patronage_declined = false;
+ }
+ }
+
+ public static function getUserPatronageLevel($pledge)
+ {
+ // Exception - for lite tier creators, use currently_entitled_amount_cents until there is a better way to match custom $ without tiers to local info:
+
+ if ('no' == get_option('patreon-creator-has-tiers', 'yes')) {
+ if (isset($pledge['attributes']['amount_cents'])) {
+ return $pledge['attributes']['amount_cents'];
+ }
+
+ // Catch all from old function format
+ return 0;
+ }
+
+ // Get currently entitled tiers:
+
+ $currently_entitled_tiers = $pledge['relationships']['currently_entitled_tiers']['data'];
+
+ if (!is_array($currently_entitled_tiers)) {
+ return 0;
+ }
+
+ // Get creator's saved tiers
+
+ $creator_tiers = get_option('patreon-creator-tiers', false);
+
+ if (!$creator_tiers or '' == $creator_tiers or !is_array($creator_tiers['included'][1])) {
+ // No local tiers. Return 0
+ return 0;
+ }
+
+ $creator_tiers = $creator_tiers['included'];
+
+ // Using max entitled tier's value for backwards compatibility. This may be changed in future for exact tier match checks.
+ $max_entitled_tier_value = 0;
+
+ // Manually iterate arrays to make it easier to modify the logic in future
+ // Iterate the currently_entitled_tiers array to cover for any possibility in case there may be more than one tier entitled
+
+ foreach ($currently_entitled_tiers as $key => $value) {
+ foreach ($creator_tiers as $c_key => $c_value) {
+ // If the checked tier value is higher than the known max value, set the max value to current
+
+ if ($currently_entitled_tiers[$key]['id'] == $creator_tiers[$c_key]['id']) {
+ // Local tier matches this tier. Check if the amount value is greater, if so, set to that value
+
+ if ($creator_tiers[$c_key]['attributes']['amount_cents'] > $max_entitled_tier_value) {
+ $max_entitled_tier_value = $creator_tiers[$c_key]['attributes']['amount_cents'];
+ }
+ }
+ }
+ }
+
+ // Check whether the pledge value that comes in is higher than the max entitled tier value - for custom pledge amounts
+ // This is not currency aware, but should cover cases in which there is large difference in between the custom pledge and the local tier level for which the content is locked for
+ if (isset($pledge['attributes']['amount_cents'])) {
+ $pledge_amount = $pledge['attributes']['amount_cents'];
+
+ // Convert annual pledges into a monthly total
+ if (isset($pledge['attributes']['pledge_cadence']) and $pledge['attributes']['pledge_cadence'] > 1) {
+ $pledge_amount = $pledge_amount / $pledge['attributes']['pledge_cadence'];
+ }
+
+ if ($pledge_amount > $max_entitled_tier_value) {
+ $max_entitled_tier_value = $pledge_amount;
+ }
+ }
+
+ return $max_entitled_tier_value;
+ }
+
+ public static function isPatron($user = false)
+ {
+ if (-1 != self::$current_user_is_patron) {
+ return self::$current_user_is_patron;
+ }
+
+ // If user is not given, try to get the current user attribute ID will be 0 if there is no logged in user
+ if (false == $user) {
+ $user = wp_get_current_user();
+ }
+
+ $user_patronage = self::getUserPatronage();
+
+ if (is_numeric($user_patronage) && $user_patronage > 0) {
+ return self::$current_user_is_patron = true;
+ } else {
+ return self::$current_user_is_patron = false;
+ }
+ }
+
+ public static function enqueueAdminScripts()
+ {
+ wp_enqueue_script('patreon-admin-js', PATREON_PLUGIN_ASSETS.'/js/admin.js', ['jquery'], PATREON_WORDPRESS_VERSION, true);
+ wp_localize_script('patreon-admin-js', 'pw_admin_js', [
+ 'patreon_wordpress_assets_url' => PATREON_PLUGIN_ASSETS,
+ 'patreon_wordpress_nonce_post_sync' => wp_create_nonce('patreon_wordpress_nonce_post_sync'),
+ ]);
+
+ // Load image related functions only if image feature is on:
+
+ if (get_option('patreon-enable-file-locking', false)) {
+ wp_enqueue_script('patreon-admin-image-functions-js', PATREON_PLUGIN_ASSETS.'/js/admin_image_functions.js', ['jquery'], PATREON_WORDPRESS_VERSION, true);
+ wp_localize_script('patreon-admin-image-functions-js', 'admin_image_functions', ['patreon_wordpress_assets_url' => PATREON_PLUGIN_ASSETS]);
+ }
+ }
+
+ public static function AfterUpdateActions($upgrader_object, $options = false)
+ {
+ // In this function we perform actions after update.
+
+ if (!$options or !is_array($options)) {
+ // Not an update.
+ return;
+ }
+
+ // Check if this plugin was updated:
+ if ('update' == $options['action'] && 'plugin' == $options['type']) {
+ $got_updated = false;
+
+ if (isset($options['plugins'])) {
+ // Multi plugin update. Iterate:
+ // Iterate through the plugins being updated and check if ours is there
+ foreach ($options['plugins'] as $plugin) {
+ if (PATREON_WORDPRESS_PLUGIN_SLUG == $plugin) {
+ $got_updated = true;
+ }
+ }
+ }
+ if (isset($options['plugin'])) {
+ // Single plugin update
+ if (PATREON_WORDPRESS_PLUGIN_SLUG == $options['plugin']) {
+ $got_updated = true;
+ }
+ }
+
+ if ($got_updated) {
+ // This section is used to do any tasks need doing after plugin updates. Any code changes to this part would take effect not immediately in the same version, but in the next update cycle since WP would use the new code only after plugin has been updated once.
+ }
+ }
+ }
+
+ public static function add_privacy_policy_section()
+ {
+ wp_add_privacy_policy_content('Patreon WordPress', PATREON_PRIVACY_POLICY_ADDENDUM);
+ }
+
+ public static function AdminMessages()
+ {
+ // This function processes any message or notification to display once after updates.
+
+ // Skip showing any notice if setup is being done
+
+ if (isset($_REQUEST['page']) and 'patreon_wordpress_setup_wizard' == $_REQUEST['page']) {
+ return;
+ }
+
+ if (apply_filters('ptrn/suspend_notices', false)) {
+ return;
+ }
+
+ $show_site_disconnect_success_notice = get_option('patreon-show-site-disconnect-success-notice', false);
+
+ // Queue this message immediately after activation if not already shown
+
+ if ($show_site_disconnect_success_notice) {
+ // Info notice - not permanent - doesnt require nonce verification
+ ?>
+
+
+
+
+
+
We must connect your site to Patreon to enable Patreon features. Please click here to start the setup wizard
+
+
+
+
Your site's connection to Patreon must be upgraded to ensure that Patreon features will work! Please click here to start the setup wizard to reconnect your site again
+
+
+
+
Did Patreon WordPress help your site? Help creators like yourself find out about it by giving us a good rating!
+
+
+
+
There are important issues affecting your Patreon integration. Please visit health check page to see the issues and solutions.
+
+
+
+
Your Patreon client details were successfully saved!
+
Patreon WordPress is now ready to go and your site is connected to Patreon! You can now lock any post by using the "Patreon Level" meta box in your post editor!
+
+
+
+
Sorry - couldn't connect your site to Patreon
+
Patreon WordPress wasn't able to contact Patreon with the app details you provided. This may be because there is an error in the app details, or because there is something preventing proper connectivity in between your site/server and Patreon API. You can get help by visiting our support forum here
+
+ checked)) {
+ return $plugin_check_data;
+ }
+
+ if (isset($plugin_check_data->response[PATREON_WORDPRESS_PLUGIN_SLUG])
+ and version_compare(PATREON_WORDPRESS_VERSION, $plugin_check_data->response[PATREON_WORDPRESS_PLUGIN_SLUG]->new_version, '<')
+ ) {
+ update_option('patreon-wordpress-update-available', 1);
+ }
+
+ return $plugin_check_data;
+ }
+
+ public function dismiss_admin_notice()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ return;
+ }
+
+ // Mapping what comes from REQUEST to a given value avoids potential security problems and allows custom actions depending on notice
+
+ if ('patreon-wordpress-update-available' == $_REQUEST['notice_id']) {
+ delete_option('patreon-wordpress-update-available');
+ }
+
+ if ('patreon-addon-upsell-shown' == $_REQUEST['notice_id']) {
+ if (!isset($_REQUEST['patreon_wordpress_nonce_patron_pro_addon_notice_shown']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_patron_pro_addon_notice_shown']), 'patreon_wordpress_nonce_patron_pro_addon_notice_shown')) {
+ return;
+ }
+
+ update_option('patreon-addon-upsell-shown', true);
+
+ // Set the last notice shown date
+ self::set_last_non_system_notice_shown_date();
+ }
+ if ('patron_content_manager_pitch_shown' == $_REQUEST['notice_id']) {
+ if (!isset($_REQUEST['patreon_wordpress_nonce_patron_content_manager_addon_notice_shown']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_patron_content_manager_addon_notice_shown']), 'patreon_wordpress_nonce_patron_content_manager_addon_notice_shown')) {
+ return;
+ }
+ update_option('patron_content_manager_pitch_shown', true);
+
+ // Set the last notice shown date
+ self::set_last_non_system_notice_shown_date();
+ }
+
+ // Mapping what comes from REQUEST to a given value avoids potential security problems
+ if ('patreon_setup_needed_notice' == $_REQUEST['notice_id']) {
+ if (!isset($_REQUEST['patreon_wordpress_nonce_setup_needed']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_setup_needed']), 'patreon_wordpress_nonce_setup_needed')) {
+ return;
+ }
+ update_option('patreon-setup-wizard-notice-dismissed', true);
+ delete_option('patreon-wordpress-app-credentials-success');
+ delete_option('patreon-wordpress-app-credentials-failure');
+ }
+
+ // Mapping what comes from REQUEST to a given value avoids potential security problems
+ if ('patreon_wordpress_patreon_api_version_update_notice' == $_REQUEST['notice_id']) {
+ if (!isset($_REQUEST['patreon_wordpress_nonce_patreon_api_version_update']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_patreon_api_version_update']), 'patreon_wordpress_nonce_patreon_api_version_update')) {
+ return;
+ }
+
+ update_option('api-version-update-notice-dismissed', true);
+ update_option('api-version-update-notice-dismissed-time', time());
+ }
+
+ // Mapping what comes from REQUEST to a given value avoids potential security problems
+ if ('patreon-rate-plugin-notice-shown' == $_REQUEST['notice_id']) {
+ if (!isset($_REQUEST['patreon_wordpress_nonce_rate_plugin_notice']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_rate_plugin_notice']), 'patreon_wordpress_nonce_rate_plugin_notice')) {
+ return;
+ }
+ update_option('patreon-rate-plugin-notice-shown', true);
+
+ // Set the last notice shown date
+ self::set_last_non_system_notice_shown_date();
+ }
+
+ // Mapping what comes from REQUEST to a given value avoids potential security problems
+ if ('patreon-critical-issues' == $_REQUEST['notice_id']) {
+ if (!isset($_REQUEST['patreon_wordpress_nonce_plugin_critical_issues']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_plugin_critical_issues']), 'patreon_wordpress_nonce_plugin_critical_issues')) {
+ return;
+ }
+ update_option('patreon-critical-issues', true);
+
+ // Set the last notice shown date
+ self::set_last_system_notice_shown_date();
+ }
+ }
+
+ public function start_post_import()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ echo 'need_admin_privileges';
+ exit;
+ }
+
+ if (!check_ajax_referer('patreon_wordpress_nonce_post_sync', 'patreon_wordpress_nonce_post_sync')) {
+ echo 'nonce_fail';
+ exit;
+ }
+
+ update_option('patreon-post-import-in-progress', true);
+ delete_option('patreon-post-import-next-cursor');
+
+ echo 'Success';
+ exit;
+ }
+
+ public function import_next_batch_of_posts()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ echo 'need_admin_privileges';
+ exit;
+ }
+
+ if (!check_ajax_referer('patreon_wordpress_nonce_post_sync', 'patreon_wordpress_nonce_post_sync')) {
+ echo 'nonce_fail';
+ exit;
+ }
+
+ // Check the last time this function was triggered:
+
+ $last_triggered = get_option('patreon-manual-import-batch-last-triggered', 0);
+
+ // Abort if last triggered time is within 10 seconds. Works for cases in which it was never triggered.
+ if (($last_triggered + 10) > time()) {
+ echo 'time_limit_error';
+ exit;
+ }
+
+ $last_triggered = get_option('patreon-manual-import-batch-last-triggered', 0);
+
+ // Abort if last triggered time is within 10 seconds. Works for cases in which it was never triggered.
+ if (($last_triggered + 10) > time()) {
+ echo 'time_limit_error';
+ exit;
+ }
+
+ // Trigger next batch of import
+
+ $import_return = self::$patreon_content_sync->import_posts_from_patreon(['manual_import' => true]);
+
+ // Set last triggered time
+
+ update_option('patreon-manual-import-batch-last-triggered', time());
+
+ // If success, exit to success
+
+ if ('imported_posts' == $import_return) {
+ echo 'imported_posts';
+ exit;
+ }
+
+ // If did not import any posts
+
+ if ('did_not_import_any_post' == $import_return) {
+ echo 'did_not_import_any_post';
+ exit;
+ }
+ // There was an issue getting posts
+
+ if ('couldnt_get_posts' == $import_return) {
+ echo 'couldnt_get_posts';
+ exit;
+ }
+ if ('throttled_internally' == $import_return) {
+ echo 'throttled_internally';
+ exit;
+ }
+
+ // Import ended
+
+ if ('post_import_ended' == $import_return) {
+ echo 'post_import_ended';
+ exit;
+ }
+
+ if ('no_ongoing_post_import' == $import_return) {
+ // This means no post import ongoing
+ echo 'no_ongoing_post_import';
+ exit;
+ }
+
+ if ('expired_or_lost_cursor_deleted' == $import_return) {
+ // This means no post import ongoing
+ echo 'expired_or_lost_cursor_deleted';
+ exit;
+ }
+ }
+
+ public function cancel_manual_post_import()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ echo 'need_admin_privileges';
+ exit;
+ }
+
+ if (!check_ajax_referer('patreon_wordpress_nonce_post_sync', 'patreon_wordpress_nonce_post_sync')) {
+ echo 'nonce_fail';
+ exit;
+ }
+
+ update_option('patreon-post-import-in-progress', false);
+ delete_option('patreon-post-import-next-cursor');
+
+ if (!get_option('patreon-post-import-in-progress', false) and !get_option('patreon-post-import-next-cursor', false)) {
+ echo 'manual_post_import_canceled';
+ exit;
+ }
+
+ echo 'couldnt_cancel_manual_post_import';
+ exit;
+ }
+
+ public function save_post_sync_category()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ echo 'You have to be an admin user to set this setting';
+ exit;
+ }
+
+ if (!isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']))) {
+ echo 'Form security field expired - please refresh the page and try again';
+ exit;
+ }
+
+ if (!(
+ isset($_REQUEST['patreon_sync_post_type'])
+ and isset($_REQUEST['patreon_sync_post_category'])
+ and isset($_REQUEST['patreon_sync_post_term'])
+ )
+ ) {
+ echo 'Please select all fields';
+ exit;
+ }
+ // Check for empty cases
+ if (
+ '-' == $_REQUEST['patreon_sync_post_type']
+ or '-' == $_REQUEST['patreon_sync_post_category']
+ or '-' == $_REQUEST['patreon_sync_post_term']
+ ) {
+ echo 'Please select all fields';
+ exit;
+ }
+
+ update_option('patreon-sync-post-type', $_REQUEST['patreon_sync_post_type']);
+ update_option('patreon-sync-post-category', $_REQUEST['patreon_sync_post_category']);
+ update_option('patreon-sync-post-term', $_REQUEST['patreon_sync_post_term']);
+
+ echo 'Saved!';
+ exit;
+ }
+
+ public function set_post_author_for_post_sync()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ echo 'You have to be logged as an admin to set this setting';
+ exit;
+ }
+
+ if (!isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']))) {
+ echo 'Form expired - please refresh the page and try again';
+ exit;
+ }
+
+ if (!(
+ isset($_REQUEST['patreon_post_author_for_post_sync'])
+ and '' != $_REQUEST['patreon_post_author_for_post_sync']
+ )
+ ) {
+ echo 'A valid user must be selected';
+ exit;
+ }
+
+ update_option('patreon-post-author-for-synced-posts', $_REQUEST['patreon_post_author_for_post_sync']);
+
+ echo 'Saved!';
+ exit;
+ }
+
+ public function set_update_posts_option()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ return;
+ }
+
+ if (!isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']))) {
+ echo 'Form expired - please refresh the page and try again';
+ exit;
+ }
+
+ if ('yes' == $_REQUEST['update_posts_option_value']) {
+ update_option('patreon-update-posts', 'yes');
+ }
+
+ if ('no' == $_REQUEST['update_posts_option_value']) {
+ update_option('patreon-update-posts', 'no');
+ }
+
+ echo 'Saved!';
+ exit;
+ }
+
+ public function set_delete_posts_option()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ return;
+ }
+
+ if (!isset($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']) or !wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_nonce_save_post_sync_options']))) {
+ echo 'Form expired - please refresh the page and try again';
+ exit;
+ }
+
+ if ('yes' == $_REQUEST['delete_posts_option_value']) {
+ update_option('patreon-remove-deleted-posts', 'yes');
+ }
+
+ if ('no' == $_REQUEST['delete_posts_option_value']) {
+ update_option('patreon-remove-deleted-posts', 'no');
+ }
+
+ echo 'Saved!';
+ exit;
+ }
+
+ public function toggle_check_api_credentials_on_setting_save($old_value, $new_value)
+ {
+ // This function fires after any of the client details are updated.
+ // Doesnt need capability checks - should be allowed to be used programmatically
+
+ // This filter only runs when settings are actually updated, but just in case:
+ // Try contacting the api
+ if ($new_value != $old_value) {
+ // One of access credentials were updated. Set a flag to do an api connectivity check
+ update_option('patreon-wordpress-do-api-connectivity-check', 1);
+ }
+ }
+
+ public function post_credential_update_api_connectivity_check()
+ {
+ // This function checks if the saved app credentials are valid if the check toggle is set
+ // Doesnt need capability checks - should be allowed to be used programmatically
+
+ if (get_option('patreon-wordpress-do-api-connectivity-check', false)) {
+ $result = self::check_api_connection();
+ delete_option('patreon-wordpress-do-api-connectivity-check');
+ }
+ }
+
+ public static function check_api_connection()
+ {
+ // Just attempts to connect to API with given credentials, and returns result
+
+ // Currently can verify only if creator's access token and refresh token are false. If the access token is false and refresh token is not, the system already refreshes the access token automatically. If only refresh token is false, then the existing correct access token will check true. In future a better check should be implemented
+
+ $api_client = new Patreon_API(get_option('patreon-creators-access-token', false));
+ $creator_response = $api_client->fetch_creator_info();
+
+ $creator_access = false;
+ $client_access = false;
+
+ if (isset($creator_response['included'][0]['id']) and '' != $creator_response['included'][0]['id']) {
+ // Got creator id. Credentials must be valid
+
+ // Success - set flag
+ // update_option( 'patreon-wordpress-app-credentials-success', 1 );
+
+ $creator_access = true;
+ }
+
+ // Try to do a creator's token refresh
+
+ if (!$creator_access and $tokens = self::refresh_creator_access_token()) {
+ update_option('patreon-creators-refresh-token-expiration', time() + $tokens['expires_in']);
+ update_option('patreon-creators-access-token-scope', $tokens['scope']);
+
+ // Try again:
+
+ $api_client = new Patreon_API(get_option('patreon-creators-access-token', false));
+ $creator_response = $api_client->fetch_creator_info();
+
+ if (isset($creator_response['included'][0]['id']) and '' != $creator_response['included'][0]['id']) {
+ // Got creator id. Credentials must be valid
+ // Success - set flag
+
+ $creator_access = true;
+ }
+ }
+
+ // Here some check for client id and secret may be entered in future - currently only checks creator access token
+
+ if ($creator_access) {
+ update_option('patreon-wordpress-app-credentials-success', 1);
+
+ return;
+ }
+
+ // All flopped. Set failure flag
+ update_option('patreon-wordpress-app-credentials-failure', 1);
+ }
+
+ public function toggle_option()
+ {
+ if (!(is_admin() && current_user_can('manage_options'))) {
+ return;
+ }
+
+ $current_user = wp_get_current_user();
+
+ $option_to_toggle = sanitize_key($_REQUEST['toggle_id']);
+
+ // Bail out if the option to be toggled is not in the allowed options
+ if (!array_key_exists($option_to_toggle, Patreon_Frontend::$allowed_toggles)) {
+ return;
+ }
+
+ // Bail out if the option to be toggled is not in the allowed options
+ if (!wp_verify_nonce(sanitize_key($_REQUEST['patreon_wordpress_advanced_options_toggle_nonce']))) {
+ return;
+ }
+
+ $current_value = get_user_meta($current_user->ID, $option_to_toggle, true);
+
+ $new_value = 'off';
+
+ if (!$current_value or 'off' == $current_value) {
+ $new_value = 'on';
+ }
+
+ update_user_meta($current_user->ID, $option_to_toggle, $new_value);
+ exit;
+ }
+
+ public static function add_to_lock_or_not_results($post_id, $result)
+ {
+ // Manages the lock_or_not post id <-> lock info var cache. The cache is run in a FIFO basis to prevent memory bloat in WP installs which may have long post listings. What it does is snip the first element in array and add the newly added var in the end
+
+ // If the lock or not array is large than 50, snip the first item
+
+ if (count(self::$lock_or_not) > 50) {
+ array_shift(self::$lock_or_not);
+ }
+
+ // Add the sent element at the end:
+
+ return self::$lock_or_not[$post_id] = $result;
+ }
+
+ public static function add_plugin_action_links($links)
+ {
+ // Adds action links to plugin listing in WP plugin admin
+
+ $links = array_merge([
+ ''.__('Settings', 'textdomain').''], $links);
+
+ return $links;
+ }
+
+ public static function lock_or_not($post_id = false)
+ {
+ // This function has the logic which decides if a post should be locked. It can be called inside or outside the loop
+
+ // If the caching var is initialized, consider using it:
+ if (count(self::$lock_or_not) > 0) {
+ if (!$post_id) {
+ global $post;
+
+ if (isset($post->ID)) {
+ $post_id = $post->ID;
+ }
+ }
+
+ // If post id could be acquired, check if this post's result was already cached:
+
+ if ($post_id and isset(self::$lock_or_not[$post_id])) {
+ return self::$lock_or_not[$post_id];
+ }
+ }
+
+ $user = wp_get_current_user();
+ $user_pledge_relationship_start = Patreon_Wordpress::get_user_pledge_relationship_start($user);
+ $user_patronage = Patreon_Wordpress::getUserPatronage($user);
+ $user_response = Patreon_Wordpress::getPatreonUser($user);
+ $is_patron = Patreon_Wordpress::isPatron($user);
+ $user_lifetime_patronage = Patreon_Wordpress::get_user_lifetime_patronage($user);
+ $declined = Patreon_Wordpress::checkDeclinedPatronage($user);
+ $active_patron_at_post_date = false;
+ $post_locked_with = [];
+
+ // Just bail out if this is not the main query for content and no post id was given
+ if (!is_main_query() and !$post_id) {
+ return self::add_to_lock_or_not_results(
+ $post_id,
+ apply_filters(
+ 'ptrn/lock_or_not',
+ [
+ 'lock' => false,
+ 'reason' => 'no_post_id_no_main_query',
+ ],
+ $post_id,
+ $declined,
+ $user,
+ $user_response,
+ $post_locked_with
+ )
+ );
+ }
+
+ // If post it received, get that post. If no post id received, try to get post from global
+ if ($post_id) {
+ $post = get_post($post_id);
+ } else {
+ // If post could be acquired from global,
+ global $post;
+ }
+
+ // First check if entire site is locked, get the level for locking.
+
+ $patreon_level = get_option('patreon-lock-entire-site', false);
+
+ // Check if specific level is given for this post:
+
+ $post_level = '';
+
+ if (isset($post->ID)) {
+ $post_level = get_post_meta($post->ID, 'patreon-level', true);
+ }
+
+ // get post meta returns empty if no value is found. If so, set the value to 0.
+
+ if ('' == $post_level) {
+ $post_level = 0;
+ }
+
+ // Check if post was set for active patrons only
+ $patreon_active_patrons_only = get_post_meta($post->ID, 'patreon-active-patrons-only', true);
+
+ // Check if specific total patronage is given for this post:
+ $post_total_patronage_level = get_post_meta($post->ID, 'patreon-total-patronage-level', true);
+
+ $post_locked_with = [
+ 'site_patreon_level' => $patreon_level,
+ 'post_level' => $post_level,
+ 'active_patrons_only' => $patreon_active_patrons_only,
+ 'post_total_patronage_level' => $post_total_patronage_level,
+ ];
+
+ $exclude = [
+ ];
+
+ // Enables 3rd party plugins to modify the post types excluded from locking
+ $exclude = apply_filters('ptrn/filter_excluded_posts', $exclude);
+
+ if (isset($post->ID) and in_array(get_post_type($post->ID), $exclude)) {
+ return self::add_to_lock_or_not_results(
+ $post_id,
+ apply_filters(
+ 'ptrn/lock_or_not',
+ [
+ 'lock' => false,
+ 'reason' => 'post_type_excluded_from_locking',
+ ],
+ $post_id,
+ $declined,
+ $user,
+ $user_response,
+ $post_locked_with
+ )
+ );
+ }
+
+ // Check if both post level and site lock level are set to 0 or nonexistent. If so return normal content.
+
+ if (0 == $post_level
+ && (!$patreon_level
+ || 0 == $patreon_level)
+ ) {
+ return self::add_to_lock_or_not_results(
+ $post_id,
+ apply_filters(
+ 'ptrn/lock_or_not',
+ [
+ 'lock' => false,
+ 'reason' => 'post_is_public',
+ ],
+ $post_id,
+ $declined,
+ $user,
+ $user_response,
+ $post_locked_with
+ )
+ );
+ }
+
+ // If we are at this point, then this post is protected.
+
+ // Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
+ // It is independent of the plugin load order since it checks if it is defined.
+ // It can be defined by any plugin until right before the_content filter is run.
+
+ // If post level is not 0, override patreon level and hence site locking value with post's. This will allow Creators to lock entire site and then set a different value for individual posts for access. Ie, site locking is $5, but one particular post can be $10, and it will require $10 to see.
+
+ if (0 != $post_level) {
+ $patreon_level = $post_level;
+ }
+
+ if (apply_filters('ptrn/bypass_filtering', defined('PATREON_BYPASS_FILTERING'))) {
+ return self::add_to_lock_or_not_results(
+ $post_id,
+ apply_filters(
+ 'ptrn/lock_or_not',
+ [
+ 'lock' => false,
+ 'reason' => 'lock_bypassed_by_filter',
+ ],
+ $post_id,
+ $declined,
+ $user,
+ $user_response,
+ $post_locked_with
+ )
+ );
+ }
+
+ if (current_user_can('manage_options')) {
+ // Here we need to put a notification to admins so they will know they can see the content because they are admin_login_with_patreon_disabled
+
+ return self::add_to_lock_or_not_results(
+ $post_id,
+ apply_filters(
+ 'ptrn/lock_or_not',
+ [
+ 'lock' => false,
+ 'reason' => 'show_to_admin_users',
+ ],
+ $post_id,
+ $declined,
+ $user,
+ $user_response,
+ $post_locked_with
+ )
+ );
+ }
+
+ $hide_content = true;
+ $reason = 'active_pledge_not_enough';
+
+ // Check if user is logged in
+
+ if (!is_user_logged_in()) {
+ $hide_content = true;
+ $reason = 'user_not_logged_in';
+ }
+
+ if ($declined) {
+ // $hide_content = true;
+ // $reason = 'payment_declined';
+ }
+
+ if (is_user_logged_in() and !$is_patron) {
+ $hide_content = true;
+ $reason = 'not_a_patron';
+ }
+
+ if (!(false == $user_patronage
+ || $user_patronage < ($patreon_level * 100)) and is_user_logged_in()) {
+ $hide_content = false;
+ $reason = 'valid_patron';
+ // Seems valid patron. Lets see if active patron option was set and the user fulfills it
+
+ if ('1' == $patreon_active_patrons_only
+ and $user_pledge_relationship_start >= strtotime(get_the_date('', $post->ID))) {
+ $hide_content = true;
+ $reason = 'not_active_patron_at_post_date';
+ $active_patron_at_post_date = false;
+ } else {
+ $hide_content = false;
+ $active_patron_at_post_date = true;
+ }
+ }
+
+ if ('' != $post_total_patronage_level and $post_total_patronage_level > 0) {
+ // Total patronage set if user has lifetime patronage over this level, we let him see the content
+ if ($user_lifetime_patronage >= $post_total_patronage_level * 100) {
+ $hide_content = false;
+ $reason = 'patron_fulfills_total_historical_pledge_requirement';
+ }
+ }
+
+ $result = [
+ 'lock' => $hide_content,
+ 'reason' => $reason,
+ 'patreon_level' => $patreon_level,
+ 'post_total_patronage_level' => $post_total_patronage_level,
+ 'patreon_active_patrons_only' => $patreon_active_patrons_only,
+ 'active_patron_at_post_date' => $active_patron_at_post_date,
+ 'user_is_patron' => $is_patron,
+ 'user_active_pledge' => $user_patronage,
+ 'user_total_historical_pledge' => $user_lifetime_patronage,
+ ];
+
+ $result = apply_filters('ptrn/lock_or_not', $result, $post_id, $declined, $user, $user_response, $post_locked_with);
+
+ return self::add_to_lock_or_not_results($post_id, $result);
+ }
+
+ public static function collect_app_info()
+ {
+ // Collects app information from WP site to be used in client settins at Patreon
+
+ $parsed_home_url = parse_url(get_bloginfo('url'));
+
+ $company_domain = $parsed_home_url['host'];
+
+ $app_info = [
+ 'name' => get_bloginfo('name'),
+ 'description' => 'Patreon app for '.get_bloginfo('name'),
+ 'author_name' => get_bloginfo('name'),
+ 'domain' => $company_domain,
+ 'privacy_policy_url' => '',
+ 'tos_url' => '',
+ 'icon_url' => PATREON_PLUGIN_ASSETS.'/img/patreon_wordpress_app_icon.png',
+ 'redirect_uris' => site_url('/patreon-authorization/'),
+ 'version' => '2',
+ 'parent_client_id' => PATREON_PLUGIN_CLIENT_ID,
+ ];
+
+ return apply_filters('ptrn/filter_collect_app_info_result', $app_info);
+ }
+
+ public static function check_setup()
+ {
+ // Checks if setup was done and does necessary procedures
+
+ if (is_admin() and current_user_can('manage_options') and !is_network_admin()) {
+ // Check if redirect to setup wizard flag was set.
+ $redirect_to_setup_wizard = get_option('patreon-redirect_to_setup_wizard', false);
+
+ // Apply filter so 3rd party addons can implement their own wizards
+
+ $redirect_to_setup_wizard = apply_filters('ptrn/redirect_to_setup_override', $redirect_to_setup_wizard);
+
+ if ($redirect_to_setup_wizard) {
+ // Redirect to setup wizard was set. Set necessary flags and redirect to setup wizard page
+
+ // The below flag will allow hiding notices or avoiding recursive redirections
+ update_option('patreon-setup_is_being_done', true);
+
+ // Toggle redirect flag off. If the user skips the wizard we will just show a notice in admin and not force subsequent redirections
+ update_option('patreon-redirect_to_setup_wizard', false);
+
+ wp_redirect(admin_url('admin.php?page=patreon_wordpress_setup_wizard&setup_stage=0'));
+ exit;
+ }
+ }
+ }
+
+ public static function setup_wizard()
+ {
+ // Handles setup wizard and reconnect wizard screens
+
+ $setup_message = PATREON_SETUP_INITIAL_MESSAGE;
+
+ if (!isset($_REQUEST['setup_stage']) or '0' == $_REQUEST['setup_stage']) {
+ // This should be allowed to be viewed without a nonce
+
+ $requirements_check = Patreon_Compatibility::check_requirements();
+ $requirement_notices = '';
+
+ if (is_array($requirements_check) and count($requirements_check) > 0) {
+ $requirement_notices .= PATREON_ENSURE_REQUIREMENTS_MET;
+ foreach ($requirements_check as $key => $value) {
+ $requirement_notices .= '• '.Patreon_Frontend::$messages_map[$requirements_check[$key]].' ';
+ }
+ }
+
+ // Delete v1 related details in case a v1 site owner initiated setup wizard for whatsoever reason
+ if ('1' == get_option('patreon-installation-api-version', false)) {
+ $options_to_delete = [
+ 'patreon-custom-page-name',
+ 'patreon-fetch-creator-id',
+ 'patreon-campaign-name',
+ 'patreon-creator-tiers',
+ 'patreon-creator-last-name',
+ 'patreon-creator-first-name',
+ 'patreon-creator-full-name',
+ 'patreon-creator-url',
+ 'patreon-campaign-id',
+ 'patreon-creators-refresh-token-expiration',
+ 'patreon-creator-id',
+ 'patreon-setup-wizard-last-call-result',
+ 'patreon-creators-refresh-token',
+ 'patreon-creators-access-token',
+ 'patreon-client-secret',
+ 'patreon-client-id',
+ 'patreon-setup_is_being_done',
+ 'patreon-setup-done',
+ 'patreon-currency-sign',
+ ];
+
+ // Delete override - proceed with deleting local options
+
+ foreach ($options_to_delete as $key => $value) {
+ delete_option($options_to_delete[$key]);
+ }
+
+ update_option('patreon-installation-api-version', '2');
+ update_option('patreon-can-use-api-v2', true);
+
+ // Set custom setup message telling old v1 install users to delete their client at clients page before starting setup
+
+ $setup_message = PATREON_ADMIN_MESSAGE_V1_CLIENT_ATTEMPTING_V2_SETUP;
+ }
+
+ $config_info = self::collect_app_info();
+ $config_input = '';
+
+ foreach ($config_info as $key => $value) {
+ $config_input .= '';
+ }
+
+ if (isset($_REQUEST['patreon_message']) and '' != $_REQUEST['patreon_message']) {
+ $setup_message = Patreon_Frontend::$messages_map[$_REQUEST['patreon_message']];
+ }
+
+ // Create state var needed for identifying connection attempt
+
+ $state = [
+ 'patreon_action' => 'connect_site',
+ ];
+
+ echo '
';
+ }
+ if (isset($_REQUEST['setup_stage']) and 'post_sync_2' == $_REQUEST['setup_stage']) {
+ $setup_message = PATREON_POST_SYNC_4;
+
+ // Check if any post sync field
+
+ if (!isset($_POST['patreon_wordpress_nonce_save_post_sync_options']) or !wp_verify_nonce($_POST['patreon_wordpress_nonce_save_post_sync_options'])) {
+ echo '
Form security field expired - please refresh the page and try again
';
+ exit;
+ }
+
+ if (isset($_REQUEST['patreon_message']) and '' != $_REQUEST['patreon_message']) {
+ $setup_message = Patreon_Frontend::$messages_map[$_REQUEST['patreon_message']];
+ }
+
+ update_option('patreon-post-sync-set-up', true);
+
+ // If post sync is on and no post import was done, start a post import:
+
+ if (get_option('patreon-sync-posts', false) and !get_option('patreon-first-post-import-started', false)) {
+ update_option('patreon-post-import-in-progress', true);
+ update_option('patreon-first-post-import-started', true);
+ delete_option('patreon-post-import-next-cursor');
+ }
+
+ echo '
';
+ }
+
+ echo Patreon_Frontend::login_widget();
+
+ echo $after_widget;
+ }
+
+ /** @see WP_Widget::update -- do not rename this */
+ public function update($new_instance, $old_instance)
+ {
+ $instance = $old_instance;
+ $instance['title'] = strip_tags($new_instance['title']);
+ $instance['message'] = strip_tags($new_instance['message']);
+
+ return $instance;
+ }
+
+ /** @see WP_Widget::form -- do not rename this */
+ public function form($instance)
+ {
+ $instance = wp_parse_args((array) $instance, ['title' => PATREON_LOGIN_WIDGET_NAME, 'message' => '']);
+ $title = esc_attr($instance['title']);
+ $message = esc_attr($instance['message']);
+
+ ?>
-
-
+
+
-
-
-
+
+
+
+
+
+
-
-
-
-
- %%creator%% Patreon at %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or more' );
-define( "PATREON_TEXT_OVER_BUTTON_1A", 'To view this content, you must upgrade your tier to %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or higher at %%creator%% Patreon. Upgrade below to unlock this content.' );
-define( "PATREON_TEXT_OVER_BUTTON_2", 'Edit your pledge to %%creator%% to %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or more to access this content. You\'re currently pledging %%currency_sign_front%%%%currentpledgelevel%%%%currency_sign_behind%%.' );
-define( "PATREON_TEXT_OVER_BUTTON_3", 'Please update your Patreon payment method to access this content.' );
-define( "PATREON_VALID_PATRON_POST_FOOTER_TEXT", 'This content is available exclusively to members of %%creator%% Patreon at %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or more.' );
-define( "PATREON_TEXT_UNDER_BUTTON_1", '' );
-define( "PATREON_TEXT_UNDER_BUTTON_2", 'Already a qualifying Patreon member? Refresh to access this content.' );
-define( "PATREON_TEXT_UNDER_BUTTON_3", 'Already updated? Refresh to access this content.' );
-define( "PATREON_CANT_LOGIN_STRICT_OAUTH", 'Sorry, couldn\'t log you in with Patreon because you have to be logged in to '.get_bloginfo('NAME').' first' );
-define( "PATREON_LOGIN_WITH_WORDPRESS_NOW", 'You can now login with your wordpress username/password.' );
-define( "PATREON_CANT_LOGIN_NONCES_DONT_MATCH", 'Sorry. Aborted Patreon login for security because security cookies dont match.' );
-define( "PATREON_CANT_LOGIN_DUE_TO_API_ERROR", 'Sorry. Login aborted due to an API error.' );
-define( "PATREON_CANT_LOGIN_DUE_TO_API_ERROR_CHECK_CREDENTIALS", 'Sorry. Login aborted due to an API error. Please check API credentials.' );
-define( "PATREON_WEIRD_REDIRECTION_AT_LOGIN", 'This redirect should not have happened. Please contact site administration.' );
-define( "PATREON_COULDNT_CREATE_WP_ACCOUNT", 'Sorry. Could not create a WordPress account. Please contact site administration.' );
-define( "PATREON_API_CREDENTIALS_MISSING", 'Sorry. Could not login because API credentials are missing or incorrect. Please contact site administration.' );
-define( "PATREON_ADMIN_LOGIN_WITH_PATREON_DISABLED", 'Sorry. Logging in Administrators via Patreon is turned off in options. Please login with your WordPress account first' );
-define( "PATREON_EMAIL_EXISTS_LOGIN_WITH_WP_FIRST", 'Sorry. A WordPress user with the same email you use at Patreon exists. Please log into this site with your WordPress user first, and then log in with Patreon to link these two accounts...' );
-define( "PATREON_LOGIN_WITH_PATREON_DISABLED", 'Sorry. Logging in with Patreon is disabled in this Website. Please contact administrator.' );
-define( "PATREON_ADMIN_BYPASSES_FILTER_MESSAGE", 'This content is for Patrons only, it\'s not locked for you because you are an Administrator' );
-define( "PATREON_CREATOR_BYPASSES_FILTER_MESSAGE", 'This content is for Patrons only, it\'s not locked for you because you are logged in as the Patreon creator' );
-define( "PATREON_NO_LOCKING_LEVEL_SET_FOR_THIS_POST", 'Post is already public. If you would like to lock this post, please set a pledge level for it' );
-define( "PATREON_NO_POST_ID_TO_UNLOCK_POST", 'Sorry - could not get the post id for this locked post' );
-define( "PATREON_WORDPRESS_VERSION", '1.9.6' );
-define( "PATREON_WORDPRESS_BETA_STRING", '' );
-define( "PATREON_WORDPRESS_PLUGIN_SLUG", plugin_basename( __FILE__ ) );
-define( "PATREON_PRIVACY_POLICY_ADDENDUM", '
Patreon features in this website
In order to enable you to use this website with Patreon services, we save certain functionally important Patreon information about you in this website if you log in with Patreon.
+define('PATREON_HOST', 'patreon.com');
+define('PATREON_PLUGIN_URL', plugin_dir_url(__FILE__));
+define('PATREON_PLUGIN_ASSETS', plugin_dir_url(__FILE__).'assets');
+define('PATREON_PLUGIN_ASSETS_DIR', plugin_dir_path(__FILE__).'assets');
+define('PATREON_PLUGIN_LOCKED_IMAGE_CACHE_DIR', $patreon_locked_image_cache_dir);
+define('PATREON_TEXT_CONNECT', 'Connect with Patreon');
+define('PATREON_TEXT_REFRESH', 'Refresh');
+define('PATREON_TEXT_NOT_PATRON', 'Not a Patron?');
+define('PATREON_TEXT_ALREADY_PATRON', 'Already a Patron?');
+define('PATREON_TEXT_BECOME_PATRON', 'Become a Patron!');
+define('PATREON_TEXT_SUPPORT_ON_PATREON', 'Support on Patreon');
+define('PATREON_TEXT_MISTAKEN_PATRON', 'Patron but can\'t see?');
+define('PATREON_TEXT_PLEDGE_NOT_ENOUGH', 'Upgrade your Pledge!');
+define('PATREON_TEXT_UPGRADE_PLEDGE', 'Upgrade Pledge');
+define('PATREON_TEXT_UNLOCK_WITH_PATREON', 'Unlock with Patreon');
+define('PATREON_TEXT_UPDATE_PLEDGE', 'Update Pledge');
+define('PATREON_TEXT_LOCKED_POST', 'This content is available exclusively to Patreon members.');
+define('PATREON_TEXT_OVER_BUTTON_1', 'To view this content, you must be a member of %%creator%% Patreon at %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or more');
+define('PATREON_TEXT_OVER_BUTTON_1A', 'To view this content, you must upgrade your tier to %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or higher at %%creator%% Patreon. Upgrade below to unlock this content.');
+define('PATREON_TEXT_OVER_BUTTON_2', 'Edit your pledge to %%creator%% to %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or more to access this content. You\'re currently pledging %%currency_sign_front%%%%currentpledgelevel%%%%currency_sign_behind%%.');
+define('PATREON_TEXT_OVER_BUTTON_3', 'Please update your Patreon payment method to access this content.');
+define('PATREON_VALID_PATRON_POST_FOOTER_TEXT', 'This content is available exclusively to members of %%creator%% Patreon at %%currency_sign_front%%%%pledgelevel%%%%currency_sign_behind%% or more.');
+define('PATREON_TEXT_UNDER_BUTTON_1', '');
+define('PATREON_TEXT_UNDER_BUTTON_2', 'Already a qualifying Patreon member? Refresh to access this content.');
+define('PATREON_TEXT_UNDER_BUTTON_3', 'Already updated? Refresh to access this content.');
+define('PATREON_CANT_LOGIN_STRICT_OAUTH', 'Sorry, couldn\'t log you in with Patreon because you have to be logged in to '.get_bloginfo('NAME').' first');
+define('PATREON_LOGIN_WITH_WORDPRESS_NOW', 'You can now login with your WordPress username/password.');
+define('PATREON_CANT_LOGIN_NONCES_DONT_MATCH', 'Sorry. Aborted Patreon login for security because security cookies dont match.');
+define('PATREON_CANT_LOGIN_DUE_TO_API_ERROR', 'Sorry. Login aborted due to an API error.');
+define('PATREON_CANT_LOGIN_DUE_TO_API_ERROR_CHECK_CREDENTIALS', 'Sorry. Login aborted due to an API error. Please check API credentials.');
+define('PATREON_WEIRD_REDIRECTION_AT_LOGIN', 'This redirect should not have happened. Please contact site administration.');
+define('PATREON_COULDNT_CREATE_WP_ACCOUNT', 'Sorry. Could not create a WordPress account. Please contact site administration.');
+define('PATREON_API_CREDENTIALS_MISSING', 'Sorry. Could not login because API credentials are missing or incorrect. Please contact site administration.');
+define('PATREON_ADMIN_LOGIN_WITH_PATREON_DISABLED', 'Sorry. Logging in Administrators via Patreon is turned off in options. Please login with your WordPress account first');
+define('PATREON_EMAIL_EXISTS_LOGIN_WITH_WP_FIRST', 'Sorry. A WordPress user with the same email you use at Patreon exists. Please log into this site with your WordPress user first, and then log in with Patreon to link these two accounts...');
+define('PATREON_LOGIN_WITH_PATREON_DISABLED', 'Sorry. Logging in with Patreon is disabled in this Website. Please contact administrator.');
+define('PATREON_ADMIN_BYPASSES_FILTER_MESSAGE', 'This content is for Patrons only, it\'s not locked for you because you are an Administrator');
+define('PATREON_CREATOR_BYPASSES_FILTER_MESSAGE', 'This content is for Patrons only, it\'s not locked for you because you are logged in as the Patreon creator');
+define('PATREON_NO_LOCKING_LEVEL_SET_FOR_THIS_POST', 'Post is already public. If you would like to lock this post, please set a pledge level for it');
+define('PATREON_NO_POST_ID_TO_UNLOCK_POST', 'Sorry - could not get the post id for this locked post');
+define('PATREON_WORDPRESS_VERSION', '1.9.6');
+define('PATREON_WORDPRESS_BETA_STRING', '');
+define('PATREON_WORDPRESS_PLUGIN_SLUG', plugin_basename(__FILE__));
+define('PATREON_PRIVACY_POLICY_ADDENDUM', '
Patreon features in this website
In order to enable you to use this website with Patreon services, we save certain functionally important Patreon information about you in this website if you log in with Patreon.
These include your Patreon user id, Patreon username, your first, last names and your vanity name. Additionally, the id of your campaign at Patreon and your campaign\'s Patreon URL are also saved.
-If you request that your data be deleted from this website, this data will also be deleted and Patreon functionality will not work. You would need to register on this website and log in to this website with Patreon again in order to re-populate this data and have Patreon functionality working again.' );
-define( "PATREON_NO_CODE_RECEIVED_FROM_PATREON", "Sorry - No authorization code received from Patreon." );
-define( "PATREON_NO_FLOW_ACTION_PROVIDED", "Nothing to do since no Patreon action was requested." );
-define( "PATREON_DIRECT_UNLOCKS_NOT_ON", 'In order to use this feature, direct unlocks need to be turned on. Please refer to the developer documentation. ' );
-define( "PATREON_TEXT_LOCKED_POST_ACTIVE_PATRONS", 'while having been active patrons on %%post_date%%' );
-define( "PATREON_TEXT_LOCKED_POST_ACTIVE_PATRONS_WITH_TOTAL_PLEDGE", 'or Patrons with total pledge of %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%%' );
-define( "PATREON_CONTRIBUTION_REQUIRED_STOP_MARK", '!' );
-define( "PATREON_TEXT_OVER_BUTTON_4", ' while having been active patrons on %%post_date%%' );
-define( "PATREON_TEXT_OVER_BUTTON_5", ' or Patrons with total %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% or more pledge' );
-define( "PATREON_TEXT_OVER_BUTTON_6", ' on Patreon' );
-define( "PATREON_TEXT_OVER_BUTTON_7", 'This content is available exclusively to members of %%creator%% Patreon at the time of posting. Become a patron at %%currency_sign_front%%%%pledgelevel%%%%currency_sign_fbehind%% or more to get exclusive content like this in the future.' );
-define( "PATREON_TEXT_OVER_BUTTON_8", 'This content is available exclusively to members of %%creator%% Patreon at the time of posting. Your account does not fulfill the requirements. Become a patron to get exclusive content like this in the future.' );
-define( "PATREON_TEXT_OVER_BUTTON_9", 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total. Upgrade below to unlock this content.' );
-define( "PATREON_TEXT_OVER_BUTTON_10", 'This content is available exclusively to members of %%creator%% Patreonat %%tier_level%% or higher tier at the time this content was posted, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total.' );
-define( "PATREON_TEXT_OVER_BUTTON_11", 'This content is available exclusively to members at %%tier_level%% or higher tier at %%creator%% Patreon at the time this content was posted.' );
-define( "PATREON_TEXT_OVER_BUTTON_12", 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total.' );
-define( "PATREON_TEXT_OVER_BUTTON_13", 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier at the time this content was posted, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total.' );
-define( "PATREON_TEXT_OVER_BUTTON_14", 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier at the time this content was posted, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total. Your account currently does not qualify.' );
-define( "PATREON_TEXT_OVER_BUTTON_15", 'To view this content, you must be a member of %%creator%% Patreon' );
-define( "PATREON_COULDNT_ACQUIRE_USER_DETAILS", 'Sorry. Could not acquire your info from Patreon. Please try again later.' );
-define( "PATREON_PRETTY_PERMALINKS_NEED_TO_BE_ENABLED", 'Pretty permalinks are required for Patreon WordPress to work. Please visit permalink options page and set an option that is different from "Plain"' );
-define( "PATREON_ENSURE_REQUIREMENTS_MET", '
Please ensure following requirements are met before starting setup:
' );
-define( "PATREON_ERROR_MISSING_CREDENTIALS", 'One or more of app credentials were not received. Please try again.' );
-define( "PATREON_SETUP_INITIAL_MESSAGE", 'By connecting your site to Patreon, you can bring Patreon features to your website & post member-only content at your website to increase your patrons and monthly revenue. We will now take you to Patreon in order to automatically connect your site. Please make sure you are logged in to your creator account at Patreon before starting, or register here if you don\'t already have a creator account.' );
-define( "PATREON_SETUP_SUCCESS_MESSAGE", 'Great! Your site is now connected to Patreon!' );
-define( "PATREON_RECONNECT_SUCCESS_MESSAGE", 'Great! We successfully reconnected your site to Patreon!' );
-define( "PATREON_TEXT_EVERYONE", 'Everyone' );
-define( "PATREON_TEXT_ANY_PATRON", 'Any patron' );
+If you request that your data be deleted from this website, this data will also be deleted and Patreon functionality will not work. You would need to register on this website and log in to this website with Patreon again in order to re-populate this data and have Patreon functionality working again.');
+define('PATREON_NO_CODE_RECEIVED_FROM_PATREON', 'Sorry - No authorization code received from Patreon.');
+define('PATREON_NO_FLOW_ACTION_PROVIDED', 'Nothing to do since no Patreon action was requested.');
+define('PATREON_TEXT_LOCKED_POST_ACTIVE_PATRONS', 'while having been active patrons on %%post_date%%');
+define('PATREON_TEXT_LOCKED_POST_ACTIVE_PATRONS_WITH_TOTAL_PLEDGE', 'or Patrons with total pledge of %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%%');
+define('PATREON_CONTRIBUTION_REQUIRED_STOP_MARK', '!');
+define('PATREON_TEXT_OVER_BUTTON_4', ' while having been active patrons on %%post_date%%');
+define('PATREON_TEXT_OVER_BUTTON_5', ' or Patrons with total %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% or more pledge');
+define('PATREON_TEXT_OVER_BUTTON_6', ' on Patreon');
+define('PATREON_TEXT_OVER_BUTTON_7', 'This content is available exclusively to members of %%creator%% Patreon at the time of posting. Become a patron at %%currency_sign_front%%%%pledgelevel%%%%currency_sign_fbehind%% or more to get exclusive content like this in the future.');
+define('PATREON_TEXT_OVER_BUTTON_8', 'This content is available exclusively to members of %%creator%% Patreon at the time of posting. Your account does not fulfill the requirements. Become a patron to get exclusive content like this in the future.');
+define('PATREON_TEXT_OVER_BUTTON_9', 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total. Upgrade below to unlock this content.');
+define('PATREON_TEXT_OVER_BUTTON_10', 'This content is available exclusively to members of %%creator%% Patreonat %%tier_level%% or higher tier at the time this content was posted, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total.');
+define('PATREON_TEXT_OVER_BUTTON_11', 'This content is available exclusively to members at %%tier_level%% or higher tier at %%creator%% Patreon at the time this content was posted.');
+define('PATREON_TEXT_OVER_BUTTON_12', 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total.');
+define('PATREON_TEXT_OVER_BUTTON_13', 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier at the time this content was posted, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total.');
+define('PATREON_TEXT_OVER_BUTTON_14', 'This content is available exclusively to members of %%creator%% Patreon at %%tier_level%% or higher tier at the time this content was posted, or having at least %%currency_sign_front%%%%total_pledge%%%%currency_sign_behind%% pledged in total. Your account currently does not qualify.');
+define('PATREON_TEXT_OVER_BUTTON_15', 'To view this content, you must be a member of %%creator%% Patreon');
+define('PATREON_COULDNT_ACQUIRE_USER_DETAILS', 'Sorry. Could not acquire your info from Patreon. Please try again later.');
+define('PATREON_PRETTY_PERMALINKS_NEED_TO_BE_ENABLED', 'Pretty permalinks are required for Patreon WordPress to work. Please visit permalink options page and set an option that is different from "Plain"');
+define('PATREON_ENSURE_REQUIREMENTS_MET', '
Please ensure following requirements are met before starting setup:
');
+define('PATREON_ERROR_MISSING_CREDENTIALS', 'One or more of app credentials were not received. Please try again.');
+define('PATREON_SETUP_INITIAL_MESSAGE', 'By connecting your site to Patreon, you can bring Patreon features to your website & post member-only content at your website to increase your patrons and monthly revenue. We will now take you to Patreon in order to automatically connect your site. Please make sure you are logged in to your creator account at Patreon before starting, or register here if you don\'t already have a creator account.');
+define('PATREON_SETUP_SUCCESS_MESSAGE', 'Great! Your site is now connected to Patreon!');
+define('PATREON_RECONNECT_SUCCESS_MESSAGE', 'Great! We successfully reconnected your site to Patreon!');
+define('PATREON_TEXT_EVERYONE', 'Everyone');
+define('PATREON_TEXT_ANY_PATRON', 'Any patron');
// Common identificator for WP installations - just for reference, does not do anything
-define( "PATREON_PLUGIN_CLIENT_ID", '40otjXLPiUL023m_FAX5XRkRYVRF0DT62cHKH7NjyNsYYFZMYHxqzWoqbEtt-22l' );
-define( "PATREON_SITE_DISCONNECTED_FROM_PATREON_HEADING", 'Disconnection successful!' );
-define( "PATREON_SITE_DISCONNECTED_FROM_PATREON_TEXT", 'You successfully disconnected your site from Patreon. Now you can connect another creator account to your site.' );
-define( "PATREON_NO_AUTH_FOR_CLIENT_CREATION", 'We weren\'t able to get the go ahead from Patreon while attempting to connect your site. Please wait a minute and try again. If this situation persists, contact support.' );
-define( "PATREON_NO_ACQUIRE_CLIENT_DETAILS", 'We weren\'t able to get to get the token for connecting your site to Patreon for the time being. Please wait a while and try again and contact support if this situation persists.' );
-define( "PATREON_NO_CREDENTIALS_RECEIVED", 'We weren\'t able to connect your site to Patreon because Patreon sent back empty credentials. Please wait a while and try again and contact support if this situation persists.' );
-define( "PATREON_RECONNECT_INITIAL_MESSAGE", 'We will now (re)connect your site to Patreon. This will refresh your site\'s connection and all app credentials. Patron only content in your website will be accessible to everyone until reconnection is complete. We will now take you to Patreon in order to automatically reconnect your site. Please make sure you are logged into your creator account at Patreon before starting.' );
-define( "PATREON_ADMIN_MESSAGE_DEFAULT_TITLE", 'All\'s cool' );
-define( "PATREON_ADMIN_MESSAGE_DEFAULT_CONTENT", 'Pretty much nothing to report.' );
-define( "PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_TITLE", 'Sorry, couldn\'t disconnect your site' );
-define( "PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_CONTENT", 'Please wait a few minutes and try again. If this issue persists, you can visit your your app/clients page and delete the app/client for this site. Then you can save empty values for details in "Connection details" tab in "Patreon settings" menu at your site. This would manually disconnect your site from Patreon. Then, you can reconnect your site to another Patreon account or to the same account.' );
-define( "PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_TITLE", 'Sorry, couldn\'t disconnect your site before reconnecting it' );
-define( "PATREON_TEXT_YOU_HAVE_NO_REWARDS_IN_THIS_CAMPAIGN", 'You have no tiers in this campaign' );
-define( "PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_CONTENT", 'Please wait a few minutes and try again. If this issue persists, you can visit your your app/clients page and delete the app/client for this site. Then you can save empty values for details in "Connection details" tab in "Patreon settings" menu at your site. This would manually disconnect your site from Patreon. Then, you can reconnect your site to another Patreon account or to the same account.' );
-define( "PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED_HEADING", 'WP Super Cache caches pages for logged in users' );
-define( "PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED", 'This could cause logged in patrons to see a content they unlocked still as locked because they may be served a cached version of the page.
Solution
Please visit this WP Super Cache settings page and turn the option "Disable caching for visitors who have a cookie set in their browser." or "Disable caching for logged in visitors. (Recommended)" on and click "Update Status" button to save WP Super Cache settings' );
-define( "PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED_HEADING", 'WP Super Cache caches treats logged in users as anonymous' );
-define( "PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED", 'This could cause logged in patrons to be treated anonymous and cause them to get served a cached version of the unlocked content, therefore preventing them from accessing the content they unlocked.
Solution
Please visit this WP Super Cache settings page and turn the option "Make known users anonymous so they’re served supercached static files." off and click "Update Status" button to save WP Super Cache settings' );
-define( "PATREON_PRETTY_PERMALINKS_ARE_OFF_HEADING", 'Pretty permalinks are turned off' );
-define( "PATREON_PRETTY_PERMALINKS_ARE_OFF", 'Pretty permalinks are off in your WP installation. This would cause content unlock flows to fail when the user is returning from Patreon.
Solution
Please visit permalink settings page and select any option other than "Plain" and click "Save Changes". Please note that this will change the link structure of your site.' );
-define( "PATREON_LAST_50_CONNECTION_ERRORS", 'These are the last 50 connection issues encountered by your site when contacting Patreon API. These are here for general info on health of the connection of your WP site to Patreon API. They only constitute an error if there are a lot of recent ones. Healthiest integrations should have a number of them (up to 50) in the long run.' );
-define( "PATREON_LAST_50_CONNECTION_ERRORS_HEADING", 'Last 50 connection errors' );
-define( "PATREON_FEED_ACTION_TEXT", ' - Click "Read more" to unlock this content at the source' );
-define( "PATREON_LOGIN_WIDGET_NAME", 'Login with Patreon' );
-define( "PATREON_LOGIN_WIDGET_DESC", 'Have your users login with Patreon or connect their Patreon account.' );
-define( "PATREON_LOGIN_WIDGET_LOGOUT", 'You are logged in. %%click_here%% to logout.' );
-define( "PATREON_CLICK_HERE", 'Click here' );
-define( "PATREON_ADMIN_MESSAGE_V1_CLIENT_ATTEMPTING_V2_SETUP", 'Your site is using the old v1 version of Patreon connection. Since v1 apps can\'t be reconnected automatically, please visit your app/clients page at Patreon and delete the app/client that shows up for this site. After this, you can continue with setup.' );
-define( "PATREON_POST_SYNC_0", 'If you want, you can sync your posts from Patreon to your WP site and simplify your workflow. You can import all your existing posts to your site and import new and updated posts on the go.' );
-define( "PATREON_POST_SYNC_1", 'Choose how you want to sync your posts from Patreon' );
-define( "PATREON_POST_SYNC_2", 'This will overwrite content and formatting in your local posts with the ones in your Patreon posts. Recommended: Yes' );
-define( "PATREON_POST_SYNC_3", 'Delete local post if when you delete matching post at Patreon. Recommended: No' );
-define( "PATREON_POST_SYNC_4", 'Your post sync choices were saved! Existing posts will be imported every 5 minutes until all are imported, and new/updated posts will also be synced. You can change these settings, start a new import or turn post sync on/off in "Patreon Settings" menu.' );
-define( "PATREON_POST_SYNC_5", 'Choose a post type and category to put synced posts in - this will only affect existing posts that will be imported or posts you make in future' );
-define( "PATREON_POST_SYNC_6", 'Choose the author to be used in synced posts. This will only affect newly imported posts' );
-define( "PATREON_ALL_POST_CATEGORY_FIELDS_MUST_BE_SELECTED", 'Please select all post category fields. All 3 post category fields must be present and selected' );
-define( "PATREON_API_VERSION_WARNING", 'Your plugin is still using API v1! This will cause errors when you use post sync feature! Please read this guide to upgrade your plugin to API v2 before activating post sync.' );
-define( "PATREON_WARNING_IMPORTANT", 'Important: ' );
-define( "PATREON_WARNING_POST_SYNC_SET_WITHOUT_API_V2", 'Important: Post syncing from Patreon is set to on, but your site is using API v1. Post sync wont work without API v2. Follow this guide to upgrade your site to API v2 or disable post sync here in settings' );
+define('PATREON_PLUGIN_CLIENT_ID', '40otjXLPiUL023m_FAX5XRkRYVRF0DT62cHKH7NjyNsYYFZMYHxqzWoqbEtt-22l');
+define('PATREON_SITE_DISCONNECTED_FROM_PATREON_HEADING', 'Disconnection successful!');
+define('PATREON_SITE_DISCONNECTED_FROM_PATREON_TEXT', 'You successfully disconnected your site from Patreon. Now you can connect another creator account to your site.');
+define('PATREON_NO_AUTH_FOR_CLIENT_CREATION', 'We weren\'t able to get the go ahead from Patreon while attempting to connect your site. Please wait a minute and try again. If this situation persists, contact support.');
+define('PATREON_NO_ACQUIRE_CLIENT_DETAILS', 'We weren\'t able to get to get the token for connecting your site to Patreon for the time being. Please wait a while and try again and contact support if this situation persists.');
+define('PATREON_NO_CREDENTIALS_RECEIVED', 'We weren\'t able to connect your site to Patreon because Patreon sent back empty credentials. Please wait a while and try again and contact support if this situation persists.');
+define('PATREON_RECONNECT_INITIAL_MESSAGE', 'We will now (re)connect your site to Patreon. This will refresh your site\'s connection and all app credentials. Patron only content in your website will be accessible to everyone until reconnection is complete. We will now take you to Patreon in order to automatically reconnect your site. Please make sure you are logged into your creator account at Patreon before starting.');
+define('PATREON_ADMIN_MESSAGE_DEFAULT_TITLE', 'All\'s cool');
+define('PATREON_ADMIN_MESSAGE_DEFAULT_CONTENT', 'Pretty much nothing to report.');
+define('PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_TITLE', 'Sorry, couldn\'t disconnect your site');
+define('PATREON_ADMIN_MESSAGE_CLIENT_DELETE_ERROR_CONTENT', 'Please wait a few minutes and try again. If this issue persists, you can visit your your app/clients page and delete the app/client for this site. Then you can save empty values for details in "Connection details" tab in "Patreon settings" menu at your site. This would manually disconnect your site from Patreon. Then, you can reconnect your site to another Patreon account or to the same account.');
+define('PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_TITLE', 'Sorry, couldn\'t disconnect your site before reconnecting it');
+define('PATREON_TEXT_YOU_HAVE_NO_REWARDS_IN_THIS_CAMPAIGN', 'You have no tiers in this campaign');
+define('PATREON_ADMIN_MESSAGE_CLIENT_RECONNECT_DELETE_ERROR_CONTENT', 'Please wait a few minutes and try again. If this issue persists, you can visit your your app/clients page and delete the app/client for this site. Then you can save empty values for details in "Connection details" tab in "Patreon settings" menu at your site. This would manually disconnect your site from Patreon. Then, you can reconnect your site to another Patreon account or to the same account.');
+define('PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED_HEADING', 'WP Super Cache caches pages for logged in users');
+define('PATREON_WP_SUPER_CACHE_LOGGED_IN_USERS_ENABLED', 'This could cause logged in patrons to see a content they unlocked still as locked because they may be served a cached version of the page.
Solution
Please visit this WP Super Cache settings page and turn the option "Disable caching for visitors who have a cookie set in their browser." or "Disable caching for logged in visitors. (Recommended)" on and click "Update Status" button to save WP Super Cache settings');
+define('PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED_HEADING', 'WP Super Cache caches treats logged in users as anonymous');
+define('PATREON_WP_SUPER_CACHE_MAKE_KNOWN_ANON_ENABLED', 'This could cause logged in patrons to be treated anonymous and cause them to get served a cached version of the unlocked content, therefore preventing them from accessing the content they unlocked.
Solution
Please visit this WP Super Cache settings page and turn the option "Make known users anonymous so they’re served supercached static files." off and click "Update Status" button to save WP Super Cache settings');
+define('PATREON_PRETTY_PERMALINKS_ARE_OFF_HEADING', 'Pretty permalinks are turned off');
+define('PATREON_PRETTY_PERMALINKS_ARE_OFF', 'Pretty permalinks are off in your WP installation. This would cause content unlock flows to fail when the user is returning from Patreon.
Solution
Please visit permalink settings page and select any option other than "Plain" and click "Save Changes". Please note that this will change the link structure of your site.');
+define('PATREON_LAST_50_CONNECTION_ERRORS', 'These are the last 50 connection issues encountered by your site when contacting Patreon API. These are here for general info on health of the connection of your WP site to Patreon API. They only constitute an error if there are a lot of recent ones. Healthiest integrations should have a number of them (up to 50) in the long run.');
+define('PATREON_LAST_50_CONNECTION_ERRORS_HEADING', 'Last 50 connection errors');
+define('PATREON_FEED_ACTION_TEXT', ' - Click "Read more" to unlock this content at the source');
+define('PATREON_LOGIN_WIDGET_NAME', 'Login with Patreon');
+define('PATREON_LOGIN_WIDGET_DESC', 'Have your users login with Patreon or connect their Patreon account.');
+define('PATREON_LOGIN_WIDGET_LOGOUT', 'You are logged in. %%click_here%% to logout.');
+define('PATREON_CLICK_HERE', 'Click here');
+define('PATREON_ADMIN_MESSAGE_V1_CLIENT_ATTEMPTING_V2_SETUP', 'Your site is using the old v1 version of Patreon connection. Since v1 apps can\'t be reconnected automatically, please visit your app/clients page at Patreon and delete the app/client that shows up for this site. After this, you can continue with setup.');
+define('PATREON_POST_SYNC_0', 'If you want, you can sync your posts from Patreon to your WP site and simplify your workflow. You can import all your existing posts to your site and import new and updated posts on the go.');
+define('PATREON_POST_SYNC_1', 'Choose how you want to sync your posts from Patreon');
+define('PATREON_POST_SYNC_2', 'This will overwrite content and formatting in your local posts with the ones in your Patreon posts. Recommended: Yes');
+define('PATREON_POST_SYNC_3', 'Delete local post if when you delete matching post at Patreon. Recommended: No');
+define('PATREON_POST_SYNC_4', 'Your post sync choices were saved! Existing posts will be imported every 5 minutes until all are imported, and new/updated posts will also be synced. You can change these settings, start a new import or turn post sync on/off in "Patreon Settings" menu.');
+define('PATREON_POST_SYNC_5', 'Choose a post type and category to put synced posts in - this will only affect existing posts that will be imported or posts you make in future');
+define('PATREON_POST_SYNC_6', 'Choose the author to be used in synced posts. This will only affect newly imported posts');
+define('PATREON_ALL_POST_CATEGORY_FIELDS_MUST_BE_SELECTED', 'Please select all post category fields. All 3 post category fields must be present and selected');
+define('PATREON_API_VERSION_WARNING', 'Your plugin is still using API v1! This will cause errors when you use post sync feature! Please read this guide to upgrade your plugin to API v2 before activating post sync.');
+define('PATREON_WARNING_IMPORTANT', 'Important: ');
+define('PATREON_WARNING_POST_SYNC_SET_WITHOUT_API_V2', 'Important: Post syncing from Patreon is set to on, but your site is using API v1. Post sync wont work without API v2. Follow this guide to upgrade your site to API v2 or disable post sync here in settings');
-require( 'classes/patreon_wordpress.php' );
+require 'classes/patreon_wordpress.php';
-register_activation_hook( __FILE__, array( 'Patreon_Wordpress', 'activate' ) );
+register_activation_hook(__FILE__, ['Patreon_Wordpress', 'activate']);
-$Patreon_Wordpress = new Patreon_Wordpress;
-
-require( 'includes/patreon_widgets.php' );
+$Patreon_Wordpress = new Patreon_Wordpress();
+require 'includes/patreon_widgets.php';