PluginProbe ʕ •ᴥ•ʔ
Post Affiliate Pro / trunk
Post Affiliate Pro vtrunk
1.29.5 1.29.4 1.29.3 1.29.0 1.29.1 1.29.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.5.0 1.5.1 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.1 1.6.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 trunk 1.0.1 1.0.10 1.0.11 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.10.1 1.10.2 1.10.3 1.10.4 1.11.1 1.11.2 1.12.1 1.12.2 1.12.3 1.12.4 1.12.5 1.13.0 1.13.1 1.13.2 1.13.3 1.13.4 1.13.5 1.13.6 1.14.0 1.14.1 1.14.2 1.15.0 1.15.1 1.15.2 1.15.3 1.15.4 1.15.5 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.17.0 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.19.0 1.19.1 1.19.10 1.19.11 1.19.12 1.19.13 1.19.14 1.19.15 1.19.16 1.19.17 1.19.2 1.19.3 1.19.4 1.19.5 1.19.6 1.19.7 1.19.8 1.19.9 1.2.0 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.28 1.2.3 1.2.31 1.2.32 1.2.33 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.20.0 1.20.1 1.21.0 1.21.1 1.21.2 1.21.3 1.21.4 1.21.5 1.21.6 1.21.7 1.22.0 1.22.1 1.22.2 1.23.0 1.23.1 1.23.2 1.23.3 1.23.4 1.23.5 1.23.6 1.23.7 1.23.8 1.24.1 1.24.2 1.24.3 1.24.4 1.24.5 1.24.6 1.24.7 1.24.8 1.24.9 1.25.0 1.26.0 1.26.1 1.26.10 1.26.11 1.26.2 1.26.3 1.26.4 1.26.5 1.26.6 1.26.7 1.26.8 1.26.9 1.27.0 1.27.1 1.27.10 1.27.11 1.27.12 1.27.13 1.27.14 1.27.15 1.27.2 1.27.3 1.27.5 1.27.6 1.27.7 1.27.8 1.27.9 1.28.0 1.28.1 1.28.2 1.28.3
postaffiliatepro / postaffiliatepro.php
postaffiliatepro Last commit date
Form 3 weeks ago Shortcode 3 weeks ago Template 2 months ago Util 2 months ago Widget 2 months ago resources 3 months ago Base.class.php 2 months ago PapApi.class.php 3 weeks ago postaffiliatepro.php 3 weeks ago readme.txt 3 weeks ago screenshot-1.png 3 years ago screenshot-2.png 3 years ago screenshot-3.png 3 years ago screenshot-4.png 3 years ago screenshot-5.png 3 years ago screenshot-6.png 3 years ago screenshot-7.png 3 years ago
postaffiliatepro.php
750 lines
1 <?php
2 /*
3 Plugin Name: Post Affiliate Pro
4 Plugin URI: https://www.postaffiliatepro.com/
5 Description: Easily integrate your WordPress site with a Post Affiliate Pro account
6 Author: QualityUnit
7 Version: 1.29.5
8 Author URI: https://www.qualityunit.com/
9 License: GNUGPL
10 */
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 if (!defined('PAP_PLUGIN_VERSION')) {
16 define('PAP_PLUGIN_VERSION', '1.29.5');
17 }
18
19 if (!defined('PAP_PLUGIN_NAME')) {
20 define('PAP_PLUGIN_NAME', 'postaffiliatepro');
21 }
22
23 include WP_PLUGIN_DIR . '/' . PAP_PLUGIN_NAME . '/Base.class.php';
24
25 if (!class_exists('postaffiliatepro')) {
26 class postaffiliatepro extends postaffiliatepro_Base {
27 const API_FILE = '/postaffiliatepro/PapApi.class.php';
28
29 //configuration pages and settings
30 //general page
31 const GENERAL_SETTINGS_PAGE_NAME = 'pap_config_general_page';
32 const PAP_URL_SETTING_NAME = 'pap-url';
33 const PAP_MERCHANT_NAME_SETTING_NAME = 'pap-merchant-name';
34 const PAP_MERCHANT_PASSWORD_SETTING_NAME = 'pap-merchant-password';
35 const HASHED_TRACKING_SCRIPT = 'hashed-tracking-script';
36 const PAP_VERSION = 'pap-version';
37 const ASYNC_ENABLED = 'async-enabled';
38
39 //signup options
40 const SIGNUP_SETTINGS_PAGE_NAME = 'pap_config_signup_page';
41
42 const SIGNUP_INTEGRATION_ENABLED_SETTING_NAME = 'pap-sugnup-integration-enabled';
43 const SIGNUP_DEFAULT_PARENT_SETTING_NAME = 'pap-sugnup-default-parent';
44 const SIGNUP_AFFILIATE_USERNAME = 'pap-sugnup-affiliate-username';
45 const SIGNUP_DEFAULT_STATUS_SETTING_NAME = 'pap-sugnup-default-status';
46 const SIGNUP_CAMPAIGNS_SETTINGS_SETTING_NAME = 'pap-sugnup-campaigns-settings';
47 const SIGNUP_INTEGRATION_USE_PHOTO = 'pap-sugnup-use-photo';
48 const SIGNUP_INTEGRATION_SAVE_LEVEL = 'pap-sugnup-save-level';
49 const SIGNUP_INTEGRATION_NO_SAVE_LEVEL = 'pap-signup-no-save-levels';
50
51 //click tracking integration page
52 const CLICK_TRACKING_SETTINGS_PAGE_NAME = 'pap_config_click_tracking_page';
53
54 const CLICK_TRACKING_ENABLED_SETTING_NAME = 'pap-click-tracking-enabled';
55 const CLICK_TRACKING_ACCOUNT_SETTING_NAME = 'pap-click-tracking-account';
56 const CLICK_TRACKING_CAMPAIGN = 'pap-click-tracking-capaign';
57
58 const DEFAULT_ACCOUNT_NAME = 'default1';
59
60 //special integrations page
61 const INTEGRATIONS_SETTINGS_PAGE_NAME = 'pap-integrations-config-page';
62
63 public function __construct() {
64 if (!$this->apiFileExists()) {
65 return;
66 }
67 $this->includePapApiFile();
68
69 // Stamp all outgoing PAP calls with the plugin identity.
70 // Uses Gpf_Net_Http_Request::setDefaultUserAgent() (PapApi.class.php) which only
71 // sets the HTTP transport header — does NOT affect the visitor UA query param
72 // that Pap_Api_Tracker forwards to track.php.
73 Gpf_Net_Http_Request::setDefaultUserAgent('PostAffiliateProWordPressPlugin/' . PAP_PLUGIN_VERSION);
74
75 $this->initUtils();
76 $this->initForms();
77 $this->initWidgets();
78 $this->initPlugin();
79 $this->initShortcodes();
80 }
81
82 private function initUtils() {
83 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Util/CampaignHelper.class.php';
84 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Util/TopAffiliatesHelper.class.php';
85 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Util/ContactForm7Helper.class.php';
86 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Util/UpdateDB.class.php';
87 }
88
89 private function initForms() {
90 $path = WP_PLUGIN_DIR . '/postaffiliatepro/Form/';
91 require_once $path.'Base.class.php';
92 require_once $path.'Settings/General.class.php';
93 require_once $path.'Settings/Signup.class.php';
94 require_once $path.'Settings/AdditionalOptions.class.php';
95 require_once $path.'Settings/Debugging.class.php';
96 require_once $path.'Settings/Campaigns.class.php';
97 require_once $path.'Settings/CampaignInfo.class.php';
98 require_once $path.'Settings/ClickTracking.class.php';
99 require_once $path.'Settings/EDD.class.php';
100 require_once $path.'Settings/LifterLMS.class.php';
101 require_once $path.'Settings/Integrations.class.php';
102 require_once $path.'Settings/ContactForm7.class.php';
103 require_once $path.'Settings/Marketpress.class.php';
104 require_once $path.'Settings/MemberPress.class.php';
105 require_once $path.'Settings/Membership2.class.php';
106 require_once $path.'Settings/PMPro.class.php';
107 require_once $path.'Settings/PPBuyNowButton.class.php';
108 require_once $path.'Settings/RestrictContentPro.class.php';
109 require_once $path.'Settings/S2Member.class.php';
110 require_once $path.'Settings/SimplePayPro.class.php';
111 require_once $path.'Settings/StripePayments.class.php';
112 require_once $path.'Settings/SureCart.class.php';
113 require_once $path.'Settings/WishListMember.class.php';
114 require_once $path.'Settings/WooComm.class.php';
115 require_once $path.'Settings/WPEasyCart.class.php';
116 require_once $path.'Settings/WPPayForms.class.php';
117 }
118
119 private function initWidgets() {
120 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Widget/top-affiliates-block.php';
121 }
122
123 private function initShortcodes() {
124 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Shortcode/Cache.class.php';
125 require_once WP_PLUGIN_DIR . '/postaffiliatepro/Shortcode/Affiliate.class.php';
126 }
127
128 private function initPlugin() {
129 add_action('admin_init', array($this, 'initSettings'));
130 add_filter('admin_head', array($this, 'initAdminHeader'), 99);
131 add_action('admin_menu', array($this, 'addPrimaryConfigMenu'));
132 add_filter('plugin_action_links_'.plugin_basename(__FILE__), array($this, 'addSettingsLinkIntoPlugin'));
133 add_action('user_register', array($this, 'onNewUserRegistration'), 99);
134 add_action('mgm_user_register', array($this, 'onNewUserRegistration'), 99); //fix to work with magic members
135 add_action('woocommerce_created_customer', array($this, 'onNewUserRegistrationWoo'), 99, 3); //fix to work with WooCommerce registration
136 add_action('register_form', array($this, 'addHiddenFieldToRegistrationForm'));
137 add_action('profile_update', array($this, 'onUpdateExistingUser'));
138 add_filter('wp_footer', array($this, 'insertIntegrationCodeToFooter'), 99);
139 add_action('upgrader_process_complete', array($this, 'afterUpdateMaintenance'), 99, 2);
140 }
141
142 private function includePapApiFile() {
143 require_once WP_PLUGIN_DIR . self::API_FILE;
144 }
145
146 private function apiFileExists() {
147 return @file_exists(WP_PLUGIN_DIR . self::API_FILE);
148 }
149
150 private function getPapIconURL() {
151 return $this->getImgUrl() . 'menu-icon.png';
152 }
153
154 public function initSettings() {
155 $args = array(
156 'type' => 'string',
157 'group' => self::GENERAL_SETTINGS_PAGE_NAME,
158 'label' => 'Post Affiliate Pro URL',
159 'description' => __('root URL of your Post Affiliate Pro installation', 'postaffiliatepro').', e.g. https://myaccount.postaffiliatepro.com/',
160 'sanitize_callback' => array($this, 'sanitizeUrl'),
161 'show_in_rest' => false,
162 );
163 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::PAP_URL_SETTING_NAME, $args);
164 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::PAP_MERCHANT_NAME_SETTING_NAME, ['sanitize_callback' => 'sanitize_text_field']);
165 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::PAP_MERCHANT_PASSWORD_SETTING_NAME);
166 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::CLICK_TRACKING_ACCOUNT_SETTING_NAME, ['sanitize_callback' => 'sanitize_text_field']);
167 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::HASHED_TRACKING_SCRIPT);
168 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::PAP_VERSION);
169 register_setting(self::GENERAL_SETTINGS_PAGE_NAME, self::ASYNC_ENABLED);
170
171 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_INTEGRATION_ENABLED_SETTING_NAME);
172 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_DEFAULT_PARENT_SETTING_NAME, ['sanitize_callback' => 'sanitize_text_field']);
173 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_AFFILIATE_USERNAME, ['sanitize_callback' => 'sanitize_text_field']);
174 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_DEFAULT_STATUS_SETTING_NAME, ['sanitize_callback' => 'sanitize_text_field']);
175 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_CAMPAIGNS_SETTINGS_SETTING_NAME);
176 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_INTEGRATION_USE_PHOTO);
177 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_INTEGRATION_SAVE_LEVEL);
178 register_setting(self::SIGNUP_SETTINGS_PAGE_NAME, self::SIGNUP_INTEGRATION_NO_SAVE_LEVEL, ['sanitize_callback' => 'sanitize_text_field']);
179
180 register_setting(self::CLICK_TRACKING_SETTINGS_PAGE_NAME, self::CLICK_TRACKING_ENABLED_SETTING_NAME);
181 register_setting(self::CLICK_TRACKING_SETTINGS_PAGE_NAME, self::CLICK_TRACKING_CAMPAIGN, ['sanitize_callback' => 'sanitize_text_field']);
182
183 // ask for a review notice
184 $this->askForReview();
185 }
186
187 public static function getPAPTrackJSDynamicCode() {
188 $tracker = 'trackjs.js';
189 if (self::isHashed()) {
190 $tracker = get_option(self::HASHED_TRACKING_SCRIPT);
191 }
192 return '<script type="text/javascript" id="pap_x2s6df8d" src="//'.
193 self::parseServerPathForClickTrackingCode().'scripts/'.$tracker.'">
194 </script>';
195 }
196
197 public static function getPAPTrackJSAsyncCode($content) {
198 $tracker = 'trackjs.js';
199 if (self::isHashed()) {
200 $tracker = get_option(self::HASHED_TRACKING_SCRIPT);
201 }
202
203 $result = '<script type="text/javascript">
204 (function(d,t) {
205 var script = d.createElement(t); script.id= "pap_x2s6df8d"; script.async = true;
206 script.src = "//'.self::parseServerPathForClickTrackingCode().'scripts/'.$tracker.'";
207 script.onload = script.onreadystatechange = function() {
208 var rs = this.readyState; if (rs && (rs != "complete") && (rs != "loaded")) return;';
209 $result .= $content . '}
210 var theBody = document.getElementsByTagName("body")[0];
211 theBody.insertBefore(script, theBody.childNodes[0]);
212 })(document, "script");</script>';
213 return $result;
214 }
215
216 public static function parseServerPathForClickTrackingCode() {
217 return str_replace(['http://','https://'], '', get_option(self::PAP_URL_SETTING_NAME));
218 }
219
220 public static function parseSaleScriptPath() {
221 $tracker = 'sale.php';
222
223 $url = get_option(self::PAP_URL_SETTING_NAME);
224 if (self::isHashed()) {
225 $tracker = str_replace('j', 's', get_option(self::HASHED_TRACKING_SCRIPT));
226 }
227 return $url.'scripts/'.$tracker;
228 }
229
230 public static function isHashed() {
231 $hash = get_option(self::HASHED_TRACKING_SCRIPT);
232 if ($hash != '' && $hash != '0') {
233 return true;
234 }
235 return false;
236 }
237
238 public function insertIntegrationCodeToFooter($content) {
239 // exit if feed
240 if (is_feed()) {
241 return false;
242 }
243
244 if (get_option(self::CLICK_TRACKING_ENABLED_SETTING_NAME) != 'true') {
245 return $content;
246 }
247
248 $contentToAsync = 'PostAffTracker.setAccountId(\''.self::getAccountName().'\');';
249
250 $campaignId = get_option(self::CLICK_TRACKING_CAMPAIGN);
251 if (!empty($campaignId)) {
252 $contentToAsync .= "window.CampaignID = '$campaignId';\n";
253 }
254
255 $contentToAsync .= 'try {PostAffTracker.track();} catch (err) { }';
256
257 if (get_option(self::ASYNC_ENABLED) != 'true') {
258 $result = self::getPAPTrackJSDynamicCode()."<script type='text/javascript'>\n";
259 $result .= $contentToAsync . "</script>\n";
260 } else {
261 $result = self::getPAPTrackJSAsyncCode($contentToAsync);
262 }
263
264 echo $result.$content;
265 }
266
267 public function afterUpdateMaintenance($upgrader_object, $options) {
268 if (isset($options['translations']) && isset($options['translations']['version'])) {
269 $this->_log('After "' . $options['type'] . '" update to version ' . $options['translations']['version']);
270 }
271
272 $this->getPapVersion();
273 }
274
275 private function resolveParentAffiliateFromCookie(Pap_Api_Session $session, Pap_Api_Affiliate $affiliate) {
276 if (!empty(sanitize_text_field(wp_unslash($_REQUEST['pap_parent'])))) {
277 $parent = sanitize_text_field(wp_unslash($_REQUEST['pap_parent']));
278 $affiliate->setParentUserId($parent);
279 $this->_log("Parent affiliate resolved from cookies: $parent");
280 return true;
281 }
282
283 $clickTracker = new Pap_Api_ClickTracker($session);
284 try {
285 $clickTracker->track();
286 } catch (Exception $e) {
287 $this->_log('Error running track:' . $e->getMessage());
288 }
289 if ($clickTracker->getAffiliate() != null) {
290 $affiliate->setParentUserId($clickTracker->getAffiliate()->getValue('userid'));
291 } else {
292 $this->_log('Parent affiliate not found from cookie');
293 }
294 }
295
296 private function resolveFirstAndLastName(WP_User $user, Pap_Api_Affiliate $affiliate) {
297 $first_name = $user->first_name;
298 $last_name = $user->last_name;
299 if ($first_name == '') {
300 foreach ($_POST as $key => $value) {
301 if ((strpos($key, 'first') !== false) && (strpos($key, 'name') !== false)) {
302 $first_name = $value;
303 }
304 }
305 }
306 if ($first_name == '') {
307 $first_name = 'empty';
308 }
309
310 if ($last_name == '') {
311 foreach ($_POST as $key => $value) {
312 if ((strpos($key, 'last') !== false) && (strpos($key, 'name') !== false)) {
313 $last_name = $value;
314 }
315 }
316 }
317 if ($last_name == '') {
318 $last_name = 'empty';
319 }
320
321 $affiliate->setFirstname($first_name);
322 $affiliate->setLastname($last_name);
323 }
324
325 /**
326 * @return Pap_Api_Affiliate
327 */
328 private function initAffiliate(WP_User $user, Pap_Api_Session $session) {
329 $affiliate = new Pap_Api_AffiliateSignup($session);
330 $affiliate->setUsername($user->user_email);
331 if (get_option(self::SIGNUP_AFFILIATE_USERNAME) == 'username') {
332 $affiliate->setUsername($user->user_nicename);
333 }
334
335 $affiliate->setRefid($user->user_nicename);
336 $this->resolveFirstAndLastName($user, $affiliate);
337 $affiliate->setNotificationEmail($user->user_email);
338 if (get_option(self::SIGNUP_INTEGRATION_SAVE_LEVEL) != '' && get_option(self::SIGNUP_INTEGRATION_SAVE_LEVEL) > 0) {
339 $affiliate->setData(get_option(self::SIGNUP_INTEGRATION_SAVE_LEVEL), $user->user_level);
340 }
341 if (get_option(self::SIGNUP_INTEGRATION_USE_PHOTO) == 'true') {
342 $affiliate->setPhoto(site_url('/avatar/user-'.$user->ID.'-96.png'));
343 if (is_multisite()) {
344 $affiliate->setPhoto(network_site_url('/avatar/user-'.$user->ID.'-96.png'));
345 }
346 }
347 return $affiliate;
348 }
349
350 private function setParentToAffiliate(Pap_Api_Affiliate $affiliate, Pap_Api_Session $session) {
351 $parentSignup = get_option(self::SIGNUP_DEFAULT_PARENT_SETTING_NAME);
352 if (!empty($parentSignup) && $parentSignup != 'from_cookie') {
353 $affiliate->setParentUserId($parentSignup);
354 }
355 if ($parentSignup == 'from_cookie') {
356 $this->resolveParentAffiliateFromCookie($session, $affiliate);
357 }
358 }
359
360 private function setStatusToAffiliate($affiliateId, Pap_Api_Session $session) {
361 $status = get_option(self::SIGNUP_DEFAULT_STATUS_SETTING_NAME);
362 if (!empty($status)) {
363 $request = new Gpf_Rpc_Request('Pap_Merchants_User_AffiliateForm', 'changeStatus', $session);
364 $request->addParam('status', $status);
365 $request->addParam('ids', new Gpf_Rpc_Array(array($affiliateId)));
366 $request->addParam('dontSendNotification', 'N');
367 try {
368 $request->sendNow();
369 } catch(Exception $e) {
370 $this->_log('Affiliate status change after signup failed: '.$e->getMessage());
371 return false;
372 }
373
374 $response = $request->getStdResponse();
375
376 if ($response->success != 'Y') {
377 $this->_log('Cannot change affiliate status after signup: '.$response->errorMessage);
378 }
379 }
380 return true;
381 }
382
383 private function signupIntegrationEnabled() {
384 return get_option(self::SIGNUP_INTEGRATION_ENABLED_SETTING_NAME) == 'true';
385 }
386
387 public function onNewUserRegistrationWoo($user_id, $new_customer_data, $password_generated) {
388 // user info should include first name and last name, we can call the standard function
389 $this->onNewUserRegistration($user_id);
390 }
391
392 public function onNewUserRegistration($user_id) {
393 if (!$this->signupIntegrationEnabled()) {
394 $this->_log('Signup integration disabled - skipping new affiliate creation');
395 return;
396 }
397 $session = $this->getApiSession();
398 if ($session === null || $session === '0') {
399 $this->_log('We have no session to PAP installation! Registration of PAP user cancelled.');
400 return;
401 }
402
403 $wpuser = new WP_User($user_id);
404 $affiliate = $this->initAffiliate($wpuser, $session);
405
406 // check the new user role
407 if (get_option(self::SIGNUP_INTEGRATION_NO_SAVE_LEVEL) != '') {
408 $noSaveLevels = get_option(self::SIGNUP_INTEGRATION_NO_SAVE_LEVEL);
409 $noSaveLevels = str_replace(', ', ',', $noSaveLevels);
410 $noSaveLevels = explode(',', $noSaveLevels);
411 if (in_array($wpuser->user_level, $noSaveLevels)) {
412 $this->_log('No affiliate account will be created for the user '.$wpuser->user_email.' with role '.$wpuser->user_level);
413 return;
414 }
415 }
416
417 $this->setParentToAffiliate($affiliate, $session);
418 $affiliate->setField('parentUserIdValidate', 'N');
419
420 try {
421 if ($affiliate->add()) {
422 $this->_log('Affiliate account for user '.$wpuser->user_email.' created successfully');
423
424 $this->setStatusToAffiliate($affiliate->getUserid(), $session);
425 } else {
426 $this->_log('Cannot save affiliate: '.$affiliate->getMessage());
427 return;
428 }
429 } catch (Exception $e) {
430 $this->_log('Error adding affiliate' . $e->getMessage());
431 return;
432 }
433
434 $this->processCampaigns($affiliate);
435 }
436
437 public static function addHiddenFieldToRegistrationForm() {
438 if (get_option(self::PAP_URL_SETTING_NAME) == '') {
439 return false;
440 }
441 echo '<input type="hidden" name="pap_parent" value="" id="pap_xa77cb50a" />';
442
443 $contentToAsync = 'PostAffTracker.writeAffiliateToCustomField(\'pap_xa77cb50a\');';
444
445 if (get_option(self::ASYNC_ENABLED) != 'true') {
446 $result = postaffiliatepro::getPAPTrackJSDynamicCode().'<script type="text/javascript">';
447 $result .= $contentToAsync . "</script>\n";
448 } else {
449 $result = postaffiliatepro::getPAPTrackJSAsyncCode($contentToAsync);
450 }
451
452 echo $result;
453 }
454
455 public static function addHiddenFieldToPaymentForm($return = false) {
456 $result = '<!-- Post Affiliate Pro integration snippet -->';
457 $result .= '<input type="hidden" name="pap_IP" value="'.self::getRemoteIp().'" />';
458 $result .= '<input type="hidden" name="pap_custom" value="" id="pap_dx8vc2s5" />';
459
460 $contentToAsync = 'PostAffTracker.setAccountId(\''.self::getAccountName().'\');
461 PostAffTracker.notifySale();';
462
463 if (get_option(self::ASYNC_ENABLED) != 'true') {
464 $result .= postaffiliatepro::getPAPTrackJSDynamicCode().'<script type="text/javascript">';
465 $result .= $contentToAsync . "</script>\n";
466 } else {
467 $result .= postaffiliatepro::getPAPTrackJSAsyncCode($contentToAsync);
468 }
469
470 $result .= '<!-- /Post Affiliate Pro integration snippet -->';
471
472 if ($return) {
473 return $result;
474 } else {
475 echo $result;
476 }
477 }
478
479 public static function getRemoteIp() {
480 if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && ($ip = filter_var(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR']), FILTER_VALIDATE_IP))) {
481 $ipAddresses = explode(',', $ip);
482 foreach ($ipAddresses as $ipAddress) {
483 $ipAddress = trim($ipAddress);
484 if (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
485 return $ipAddress;
486 }
487 }
488 }
489 if (isset($_SERVER['REMOTE_ADDR']) && ($ip = filter_var(wp_unslash($_SERVER['REMOTE_ADDR']), FILTER_VALIDATE_IP))) {
490 return $ip;
491 }
492 return '';
493 }
494
495 private function getCampaignOption($campaignId, $name) {
496 $value = get_option(self::SIGNUP_CAMPAIGNS_SETTINGS_SETTING_NAME);
497 if (!is_array($value)) {
498 return '';
499 }
500 if (!array_key_exists($name . '-' . $campaignId, $value)) {
501 return '';
502 }
503 return $value[$name . '-' . $campaignId];
504 }
505
506 private function assignToCampaign(Pap_Api_Affiliate $affiliate, $campaignId, $sendNotification) {
507 try {
508 $affiliate->assignToPrivateCampaign($campaignId, ($sendNotification=='true')?true:false);
509 } catch (Exception $e) {
510 $this->_log('Unable to assign user to private campaign ' . $campaignId . ', problem: ' . $e->getMessage());
511 }
512 }
513
514 private function processCampaigns(Pap_Api_Affiliate $affiliate) {
515 $campaigns = $this->getCampaignHelper()->getCampaignsList();
516 if ($campaigns === null) {
517 return;
518 }
519 foreach ($campaigns as $campaign) {
520 if ($campaign->get(postaffiliatepro_Util_CampaignHelper::CAMPAIGN_TYPE) != postaffiliatepro_Util_CampaignHelper::CAMPAIGN_TYPE_PUBLIC) {
521 if ($this->getCampaignOption($campaign->get(postaffiliatepro_Util_CampaignHelper::CAMPAIGN_ID), postaffiliatepro_Form_Settings_CampaignInfo::ADD_TO_CAMPAIGN) == 'true') {
522 $this->assignToCampaign($affiliate, $campaign->get(postaffiliatepro_Util_CampaignHelper::CAMPAIGN_ID),
523 $this->getCampaignOption($campaign->get(postaffiliatepro_Util_CampaignHelper::CAMPAIGN_ID), postaffiliatepro_Form_Settings_CampaignInfo::SEND_NOTIFICATION_EMAIL));
524 }
525 }
526 }
527 }
528
529 public function onUpdateExistingUser($user_id) {
530 if (!$this->signupIntegrationEnabled()) {
531 $this->_log('Signup integration disabled - will not update existing affiliate');
532 return;
533 }
534 $session = $this->getApiSession();
535 if ($session === null || $session === '0') {
536 $this->_log('We have no session to PAP installation! Updating of PAP user cancelled.');
537 return;
538 }
539 $user = new WP_User($user_id);
540 $affiliate = new Pap_Api_Affiliate($session);
541 $affiliate->setRefid($user->user_nicename);
542 try {
543 $affiliate->load();
544 } catch (Exception $e) {
545 // try it with notification email as well
546 $this->_log('Unable to load affiliate by referral ID');
547 try {
548 $affiliate->setRefid('');
549 $affiliate->setNotificationEmail($user->user_email);
550 $affiliate->load();
551 } catch (Exception $e) {
552 // last try - username
553 $this->_log('Unable to load affiliate by notification email');
554 try {
555 $affiliate->setNotificationEmail('');
556 $affiliate->setUsername($user->user_email);
557 $affiliate->load();
558 } catch (Exception $e) {
559 $this->_log('Unable to load affiliate by username');
560 $this->_log('Update of user '.$user->nickname.' cancelled');
561 return;
562 }
563 }
564 }
565 $this->resolveFirstAndLastName($user, $affiliate);
566 $affiliate->setNotificationEmail($user->user_email);
567 if (get_option(self::SIGNUP_INTEGRATION_SAVE_LEVEL) != '' && get_option(self::SIGNUP_INTEGRATION_SAVE_LEVEL) > 0) {
568 $affiliate->setData(get_option(self::SIGNUP_INTEGRATION_SAVE_LEVEL), $user->user_level);
569 }
570 $affiliate->save();
571 }
572
573 public function initAdminHeader($content) {
574 if (!is_feed()) {
575 wp_enqueue_style('pap-style', postaffiliatepro_Base::getCssUrl().'style.css', array(), PAP_PLUGIN_VERSION);
576 }
577 echo $content;
578 }
579
580 public function addSettingsLinkIntoPlugin($links) {
581 return array_merge($links, array('<a href="'.admin_url('admin.php?page=pap-top-level-options-handle').'">'.__('Settings', 'postaffiliatepro').'</a>'));
582 }
583
584 private function askForReview() {
585 $this->handleReviewStatus();
586 $activationTime = get_site_option('pap_active_time');
587 $reviewDismissed = get_site_option('pap_review_dismiss');
588
589 if ($reviewDismissed == 'yes') return;
590
591 $now = time();
592 if (!$activationTime) {
593 $activationTime = $now;
594 add_site_option('pap_active_time', $now);
595 }
596
597 if ($now - $activationTime > 864000) { // 10 days
598 add_action('admin_notices', array($this, 'reviewNoticeMessage'));
599 }
600 }
601
602 private function handleReviewStatus() {
603 if (!is_admin() || ! current_user_can('manage_options') || !isset( $_GET['_wpnonce']) ||
604 !wp_verify_nonce(sanitize_key(wp_unslash($_GET['_wpnonce'])), 'pap-review-nonce')) {
605 return;
606 }
607
608 if (isset($_GET['pap_review_later'])) {
609 // set time to now
610 update_site_option('pap_active_time', time());
611 }
612
613 if (isset($_GET['pap_review_dismiss'])) {
614 add_site_option('pap_review_dismiss', 'yes');
615 }
616 }
617
618 public function reviewNoticeMessage() {
619 $url = '';
620 if (isset($_SERVER['REQUEST_URI'])) {
621 $scheme = (wp_parse_url(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])), PHP_URL_QUERY))?'&':'?';
622 $url = sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])) . $scheme;
623 }
624 $dismissUrl = wp_nonce_url($url . 'pap_review_dismiss=yes', 'pap-review-nonce');
625 $laterUrl = wp_nonce_url($url . 'pap_review_later=yes', 'pap-review-nonce' );
626
627 echo '<div class="pap-review-notice-wrapper">
628 <div class="pap-review-notice">
629 <div class="pap-review-text">
630 <h2>We would like to know <span class="highlight">your opinion</span></h2>
631 <p>You\'ve been using <span class="highlight">Post Affiliate Pro WordPress Plugin</span> for some time. We hope all is working as supposed and the setup was smooth. Would you mind taking a few minutes to write a review on WordPress.org?<br />Any review works =) Thank you!</p>
632 <div class="reviewButton"><a href="https://wordpress.org/support/plugin/postaffiliatepro/reviews/#new-post" target="_blank"><span class="dashicons dashicons-external"></span>Sure! I\'d love to!</a></div>
633 <div class="reviewButton"><a href="' . $dismissUrl .'"><span class="dashicons dashicons-smiley"></span>I\'ve already left a review</a></div>
634 <div class="reviewButton"><a href="' . $laterUrl .'"><span class="dashicons dashicons-calendar-alt"></span>Will rate it later</a></div>
635 <div class="reviewButton"><a href="' . $dismissUrl .'"><span class="dashicons dashicons-dismiss"></span>Hide this forever</a></div>
636 <div class="pap-review-help"><a href="https://www.postaffiliatepro.com/" target="_blank"><span class="dashicons dashicons-external"></span>Help! I have some technical issues.</a></div>
637 </div>
638 </div>
639 </div>';
640 }
641
642 // sanitize URL before saving it
643 public function sanitizeUrl($url) {
644 $url = esc_url_raw(trim(strtolower($url)));
645
646 if (substr($url, -1) !== '/') {
647 $url .= '/';
648 }
649
650 if (strpos($url, 'http://') === 0) {
651 $url = str_replace('http://', 'https://', $url);
652 } elseif (strpos($url, '/') === 0) {
653 $url = substr_replace($url, 'https://', 0, 1);
654 }
655 if (strpos($url, 'https://') === false) {
656 $url = 'https://'.$url;
657 }
658
659 // 1. Must be a structurally valid URL
660 if (!filter_var($url,FILTER_VALIDATE_URL)) {
661 add_settings_error(
662 self::PAP_URL_SETTING_NAME,
663 self::PAP_URL_SETTING_NAME.'-error',
664 __('This is not a valid URL! Changes were not saved.', 'postaffiliatepro')
665 );
666 return get_option(self::PAP_URL_SETTING_NAME); // Keep the old value
667 }
668
669 // 2. Must use http or https only
670 $parsed = wp_parse_url($url);
671 if (!in_array(strtolower($parsed['scheme'] ?? ''), ['http', 'https'], true)) {
672 add_settings_error(
673 self::PAP_URL_SETTING_NAME,
674 self::PAP_URL_SETTING_NAME.'-error',
675 __('The URL must use http or https!', 'postaffiliatepro')
676 );
677 return get_option(self::PAP_URL_SETTING_NAME);
678 }
679
680 // 3. Resolve hostname and block private/reserved IPs (the SSRF fix)
681 $host = $parsed['host'] ?? '';
682 $ip = gethostbyname($host);
683
684 if (!filter_var($ip, FILTER_VALIDATE_IP)) {
685 add_settings_error(
686 self::PAP_URL_SETTING_NAME,
687 self::PAP_URL_SETTING_NAME.'-error',
688 __('Hostname could not be resolved from the URL!', 'postaffiliatepro')
689 );
690 return get_option(self::PAP_URL_SETTING_NAME);
691 }
692
693 $flags = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
694 if (!filter_var($ip, FILTER_VALIDATE_IP, $flags)) {
695 add_settings_error(
696 self::PAP_URL_SETTING_NAME,
697 self::PAP_URL_SETTING_NAME.'-error',
698 __('The URL must point to a public external address!', 'postaffiliatepro')
699 );
700 return get_option(self::PAP_URL_SETTING_NAME);
701 }
702
703 return $url;
704 }
705
706 public function addPrimaryConfigMenu() {
707 $handle = 'pap-top-level-options-handle';
708 add_menu_page('Post Affiliate Pro', 'Post Affiliate Pro', 'manage_options', $handle, array($this, 'printGeneralConfigPage'), $this->getPapIconURL());
709 add_submenu_page($handle, __('General','postaffiliatepro'), __('General','postaffiliatepro'), 'manage_options', $handle, array($this, 'printGeneralConfigPage'));
710
711 if (!$this->isPluginSet()) {
712 return;
713 }
714
715 add_submenu_page($handle, __('Click tracking','postaffiliatepro'), __('Click tracking','postaffiliatepro'), 'manage_options', 'click-tracking-config-page', array($this, 'printClickTrackingConfigPage'));
716 add_submenu_page($handle, __('Signup','postaffiliatepro'), __('Signup options','postaffiliatepro'), 'manage_options', 'signup-config-page', array($this, 'printSignupConfigPage'));
717 add_submenu_page($handle, __('Additional options','postaffiliatepro'), __('Additional options','postaffiliatepro'), 'manage_options', 'additional-options-page', array($this, 'printAdditionalOptionsPage'));
718
719 add_menu_page(__('Integrations','postaffiliatepro'), __('Integrations','postaffiliatepro'), 'manage_options', 'integrations-config-page-handle', array($this, 'printIntegrationsConfigPage'), $this->getPapIconURL());
720 add_submenu_page('integrations-config-page-handle', __('General', 'postaffiliatepro'), __('General', 'postaffiliatepro'), 'manage_options', 'integrations-config-page-handle', array($this, 'printIntegrationsConfigPage'));
721 }
722
723 public function printGeneralConfigPage() {
724 $form = new postaffiliatepro_Form_Settings_General();
725 $form->render();
726 }
727
728 public function printSignupConfigPage() {
729 $form = new postaffiliatepro_Form_Settings_Signup();
730 $form->render();
731 }
732
733 public function printClickTrackingConfigPage() {
734 $form = new postaffiliatepro_Form_Settings_ClickTracking();
735 $form->render();
736 }
737
738 public function printAdditionalOptionsPage() {
739 $form = new postaffiliatepro_Form_Settings_AdditionalOptions();
740 $form->render();
741 }
742
743 public function printIntegrationsConfigPage() {
744 $form = new postaffiliatepro_Form_Settings_Integrations();
745 $form->render();
746 }
747 }
748 }
749
750 $postaffiliatepro = new postaffiliatepro();