PluginProbe
PowerPress Podcasting plugin by Blubrry / trunk
PowerPress Podcasting plugin by Blubrry vtrunk
11.17.9 11.17.8 11.17.7 11.17.6 11.17.4 11.17.3 11.17.2 11.17.1 11.17 11.16.11 11.16.10 11.16.9 11.16.8 11.16.7 11.16.6 11.16.5 11.16.4 11.16.3 11.16.2 11.16.1 11.9.13 11.9.14 11.9.15 11.9.16 11.9.17 All 383 releases
powerpress / powerpress-network.php

powerpress-network.php in PowerPress Podcasting plugin by Blubrry trunk, at powerpress-network.php

1,129 lines 48.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /***
3 * @package PowerPressNetwork
4 */
5
6
7
8 if ( !function_exists('add_action') )
9 die("access denied.");
10
11 require_once(dirname(__FILE__).'/api-data-transfer-bus.class.php');
12 require_once(dirname(__FILE__).'/powerpressadmin-auth.class.php');
13 require_once(dirname(__FILE__).'/views/components/network-page-select.php');
14 require_once(dirname(__FILE__).'/views/components/network-list-section.php');
15
16 class PowerPressNetwork
17 {
18 private $display;
19 private $apiBus;
20 private $parent_slug;
21
22 function __construct($parent_slug)
23 {
24 $this->parent_slug = $parent_slug;
25 $this->init();
26 $this->apiBus = new PowerpressNetworkDataBus();
27
28 add_action('admin_menu', array($this, 'checkUpdateProgram'));
29 add_action('wp_ajax_add_program_to_network', array($this, 'addProgramToNetwork'));
30 add_action('wp_ajax_ppn_page_action', array($this, 'ajaxPageAction'));
31 }
32
33 function init()
34 {
35
36 if (!is_admin()) {
37 require_once(dirname(__FILE__) . '/shortcodes/ShortCode.php');
38 require_once(dirname(__FILE__) . '/shortcodes/Application.php');
39 require_once(dirname(__FILE__) . '/shortcodes/ListPreview.php');
40 require_once(dirname(__FILE__) . '/shortcodes/List.php');
41 require_once(dirname(__FILE__) . '/shortcodes/Program.php');
42 require_once(dirname(__FILE__) . '/shortcodes/Grid.php');
43
44 }
45
46 if (is_admin() && isset($_GET['page']) && $_GET['page'] == 'powerpress') {
47 $key = $_GET['key'] ?? '';
48
49 switch ($key) {
50 case 'programs':
51 include(dirname(__FILE__) . '/admin/programs.php');
52 break;
53 case 'lists':
54 include(dirname(__FILE__) . '/admin/lists.php');
55 break;
56 //case 'link':
57 //include(dirname(__FILE__) . '/admin/link.php');
58 //break;
59 //case 'index':
60 //include(dirname(__FILE__) . '/admin/index.php');
61 //break;
62 case 'base':
63 include(dirname(__FILE__) . '/admin/base.php');
64 break;
65 case 'applications':
66 include(dirname(__FILE__) . '/admin/applications.php');
67 break;
68 default:
69 break;
70 }
71
72 }
73
74 }
75
76 function getAccessToken()
77 {
78 // Look at the creds and use the latest access token, if its not the latest refresh it...
79 $creds = get_option('powerpress_network_creds');
80 if( !empty($creds['access_token']) && !empty($creds['access_expires']) && $creds['access_expires'] > time() ) { // If access token did not expire
81 return $creds['access_token'];
82 }
83
84 if( !empty($creds['refresh_token']) && !empty($creds['client_id']) && !empty($creds['client_secret']) ) {
85
86 // Create new access token with refresh token here...
87 $auth = new PowerPressAuth();
88 $resultTokens = $auth->getAccessTokenFromRefreshToken($creds['refresh_token'], $creds['client_id'], $creds['client_secret']);
89
90 if( !empty($resultTokens['access_token']) && !empty($resultTokens['expires_in']) ) {
91 powerpress_save_settings( array('access_token'=>$resultTokens['access_token'], 'access_expires'=>( time() + $resultTokens['expires_in'] - 10 ) ), 'powerpress_network_creds');
92
93 return $resultTokens['access_token'];
94 }
95 }
96
97 // If we failed to get credentials, return false
98 return false;
99 }
100
101 function requestAPI($requestUrl, $auth = false, $post = false)
102 {
103 $accessToken = '';
104 if( $auth ) {
105 $accessToken = $this->getAccessToken();
106 if( empty($accessToken) )
107 return false;
108 }
109
110 // Equivelant command line argument to run command
111 //mail('c', 'dd', "curl ". (is_array($post) ? '-d "'.implode("&", $post) .'" ' : '') ."-H \"Authorization: Bearer $accessToken\" \"$requestUrl\"");
112 $auth = new PowerPressAuth();
113 $response = $auth->api($accessToken, $requestUrl, $post);
114
115 if( $response === false ) {
116 powerpress_page_message_add_error( __('Error: ' . $auth->getLastError(), 'powerpress') );
117 }
118 return $response;
119 }
120
121 static function getHTML($filename, $props, $networkInfo, $accountInfo, $shows_html = '', $groups_html = '', $requests_html = '', $secondary_props = array(), $manage_html = '')
122 {
123 if (is_file(dirname(__FILE__) . '/shortcodes/views/' . $filename)) {
124 ob_start();
125 include(dirname(__FILE__) . '/shortcodes/views/' . $filename);
126 $html = ob_get_contents();
127 ob_end_clean();
128 return $html;
129 } else if (is_file(dirname(__FILE__) . '/admin/' . $filename)) {
130 ob_start();
131 include(dirname(__FILE__) . '/admin/' . $filename);
132 $html = ob_get_contents();
133 ob_end_clean();
134 return $html;
135 } else {
136 return "<div><strong>View for $filename unavailable.</strong></div>";
137 }
138 }
139
140 static function powerpress_network_plugin_url()
141 {
142 $local_path = __FILE__;
143 if (DIRECTORY_SEPARATOR == '\\') { // Win32 fix
144 $local_path = basename(dirname(__FILE__)) . '/' . basename(__FILE__);
145
146 if (strpos(__FILE__, 'C:\\') === 0 && strstr($local_path, 'mu-plugins')) {
147 $local_path = __FILE__;
148 $local_path = substr($local_path, 2);
149 $local_path = str_replace('\\', '/', $local_path);
150 }
151
152 if (strstr(__FILE__, 'mu-plugins')) {
153 // mu-plugins URL!
154 return content_url() . '/mu-plugins/' . dirname($local_path) . '/';
155 }
156 }
157
158 $plugin_url = plugins_url('', $local_path);
159 return $plugin_url . '/';
160 }
161
162 public function setDisplay()
163 {
164 $this->display = $this->action_admin_init();
165 }
166
167 static function createPage()
168 {
169 $originalMap = get_option('powerpress_network_map');
170 $map = $originalMap ?: array();
171 $pageCreated = false;
172 $target = null;
173 $postID = null;
174 if ($_POST['target'] == "Program"){
175 $target = "p-".$_POST['targetId'];
176 } else if ($_POST['target'] == "List") {
177 $target = "l-".$_POST['targetId'];
178 } else if ($_POST['target'] == "Application") {
179 $target = "Application";
180 } else if ($_POST['target'] == "Homepage") {
181 $target = "Homepage";
182 }
183 // homepage + application reuse existing page if published;
184 // programs and lists always create fresh so users can replace pages (dupe page name, unique slug)
185 $isSingleton = in_array($_POST['target'], ['Homepage', 'Application'], true);
186 if ($isSingleton && isset($map[$target])) {
187 $postID = $map[$target];
188 if (get_post_status($postID) == 'publish') {
189 $pageCreated = true;
190 }
191 }
192
193 if (!$pageCreated){
194 global $user_ID;
195 $page['post_type'] = 'page';
196 $page['post_content'] = $_POST['content'];
197 $page['post_parent'] = 0;
198 $page['post_author'] = $user_ID;
199 $page['post_status'] = 'publish';
200 $page['post_title'] = __(htmlspecialchars($_POST['pageTitle']), 'powerpress');
201 $postID = wp_insert_post($page);
202 if ($postID != 0){
203 $map[$target] = $postID;
204 if($originalMap === null) {
205 add_option('powerpress_network_map', $map);
206 } else{
207 update_option('powerpress_network_map', $map);
208 }
209 }
210 }
211 return $postID;
212 }
213
214 /**
215 * ensure page content has the correct ppn shortcode after linking
216 *
217 * if a stale shortcode of the same type exists (wrong id), replace it.
218 * if no matching shortcode exists, append one.
219 * if the correct shortcode is already present, do nothing.
220 */
221 private static function ensureShortcodeOnPage($postID, $target, $targetId) {
222 if (empty($postID) || empty($target) || empty($targetId)) return;
223
224 $post = get_post($postID);
225 if (!$post) return;
226
227 $content = $post->post_content;
228
229 if ($target === 'List') {
230 $changed = false;
231
232 // fix stale ppn-gridview (skip id="all")
233 $gridShortcode = "[ppn-gridview id=\"{$targetId}\" rows=\"100\" cols=\"3\"]";
234 $gridPattern = '/\[ppn-gridview\s+id="(?!all)[^"]*"[^\]]*\]/';
235 if (strpos($content, $gridShortcode) === false && preg_match($gridPattern, $content)) {
236 $content = preg_replace($gridPattern, $gridShortcode, $content, 1);
237 $changed = true;
238 }
239
240 // fix stale ppn-list (skip id="all")
241 $listPattern = '/\[ppn-list\s+id="(?!all)[^"]*"[^\]]*\]/';
242 if (preg_match($listPattern, $content, $m)) {
243 // preserve existing attrs (style, etc) but swap the id
244 $fixed = preg_replace('/id="[^"]*"/', "id=\"{$targetId}\"", $m[0], 1);
245 $content = str_replace($m[0], $fixed, $content);
246 $changed = true;
247 }
248
249 // if no ppn shortcode found at all, append default gridview
250 if (!$changed && strpos($content, $gridShortcode) === false) {
251 $content = trim($content) . "\n\n" . $gridShortcode;
252 }
253 } elseif ($target === 'Program') {
254 $shortcode = "[ppn-program id=\"{$targetId}\"]";
255 if (strpos($content, $shortcode) !== false) return;
256 // replace stale ppn-program w/ different id
257 $pattern = '/\[ppn-program\s+id\s*=\s*"?[^"\]]*"?\]/';
258 if (preg_match($pattern, $content)) {
259 $content = preg_replace($pattern, $shortcode, $content, 1);
260 } else {
261 $content = trim($content) . "\n\n" . $shortcode;
262 }
263 } else {
264 return;
265 }
266
267 wp_update_post([
268 'ID' => $postID,
269 'post_content' => $content,
270 ]);
271 }
272
273 private function handlePageAction($createUrl)
274 {
275 $option = get_option('powerpress_network_map');
276 $postID = 0;
277 if (isset($_POST['pageAction']) && $_POST['pageAction'] == 'unlink'){
278 if ($_POST['target'] == 'List') {
279 unset($option['l-' . $_POST['targetId']]);
280 $this->removeOption('link_page_list');
281 } else if ($_POST['target'] == 'Program') {
282 unset($option['p-' . $_POST['targetId']]);
283 $this->removeOption('link_page_program');
284 }
285 update_option ('powerpress_network_map', $option);
286 powerpress_page_message_add_notice(__('Page unlinked.', 'powerpress'));
287 } else if (isset($_POST['pageAction']) && $_POST['pageAction'] == 'clearSiteCache') {
288 $network_id = get_option('powerpress_network_id');
289 if ($network_id) {
290 $cacheNameBase = 'ppn-cache n-' . $network_id;
291 // base network data
292 $this->apiBus->clearCache($cacheNameBase);
293 // applications
294 $this->apiBus->clearCache($cacheNameBase . '-a');
295 // programs
296 $this->apiBus->clearCache($cacheNameBase . '-p');
297 // lists
298 $this->apiBus->clearCache($cacheNameBase . '-l');
299 powerpress_page_message_add_notice(__('Cache cleared.', 'powerpress'));
300 }
301 } else {
302 if (empty($_POST['pageID'])) {
303 $postID = $this->createPage();
304 } else{
305 $postID = $_POST['pageID'];
306 if ($_POST['target'] == 'List') {
307 $option['l-' . $_POST['targetId']] = $postID;
308 } else if ($_POST['target'] == 'Program') {
309 $option['p-' . $_POST['targetId']] = $postID;
310 }
311 update_option('powerpress_network_map', $option);
312 // update shortcode on page if user opted in
313 if (!empty($_POST['updateShortcode'])) {
314 self::ensureShortcodeOnPage($postID, $_POST['target'], $_POST['targetId']);
315 }
316 }
317 if ($postID != 0) {
318 if ($_POST['target'] == 'List') {
319 $option['l-' . $_POST['targetId']] = $postID;
320 $this->insertOption('link_page_list', get_permalink($postID));
321 } else if ($_POST['target'] == 'Program') {
322 $option['p-' . $_POST['targetId']] = $postID;
323 $this->insertOption('link_page_program', get_permalink($postID));
324 }
325 powerpress_page_message_add_notice(__('Page linked successfully.', 'powerpress'));
326 }
327 }
328 if ($postID!= 0 && !(isset($_POST['redirectUrl']) && $_POST['redirectUrl'] == 'false')) {
329 header('location: ' . $createUrl . 'wp-admin/post.php?post=' . $postID . '&action=edit');
330 exit;
331 }
332 }
333
334 private function handleCodeReturn()
335 {
336 if( empty($_GET['state']) || empty($_GET['code']) ) {
337 powerpress_page_message_add_error( __('An error occurred linking your account. Missing parameters.', 'powerpress') );
338 return false;
339 }
340
341 $tempClient = get_option('powerpress_network_temp_client');
342 if( $_GET['state'] != $tempClient['state'] ) {
343 powerpress_page_message_add_error( __('An error occurred linking your account. State does not match.', 'powerpress') );
344 return false;
345 }
346 $redirectUri = admin_url('admin.php?page=network-plugin');
347 $auth = new PowerPressAuth();
348
349 // Get the client ID for this installation
350 $resultClient = $auth->issueClient($_GET['code'], $tempClient['temp_client_id'], $tempClient['temp_client_secret'], $redirectUri);
351 if( $resultClient === false || empty($resultClient['client_id']) || empty($resultClient['client_secret']) ) {
352 if( !empty($resultClient['error_description']) )
353 powerpress_page_message_add_error( $resultClient['error_description'] );
354 else if( !empty($resultClient['error']) )
355 powerpress_page_message_add_error( $resultClient['error'] );
356 else
357 powerpress_page_message_add_error( __('Error issuing client:','powerpress') .' '.$auth->GetLastError() . $auth->getDebugInfo() );
358 return false;
359 }
360
361 // Get the access and refresh token for this client
362 $resultTokens = $auth->getAccessTokenFromCode( $_GET['code'], $resultClient['client_id'], $resultClient['client_secret'], $redirectUri);
363 if( $resultTokens === false || empty($resultTokens['access_token']) || empty($resultTokens['refresh_token']) ) {
364 if( !empty($resultTokens['error_description']) )
365 powerpress_page_message_add_error( $resultTokens['error_description'] );
366 else if( !empty($resultTokens['error']) )
367 powerpress_page_message_add_error( $resultTokens['error'] );
368 else
369 powerpress_page_message_add_error( __('Error retrieving access token:','powerpress') .' '.$auth->GetLastError() );
370 return false;
371 }
372
373 $props = array();
374 $props['code'] = $_GET['code'];
375 $props['client_id'] = $resultClient['client_id'];
376 $props['client_secret'] = $resultClient['client_secret'];
377 $props['access_token'] = $resultTokens['access_token'];
378 $props['access_expires'] = ( time() + $resultTokens['expires_in'] - 10 );
379 $props['refresh_token'] = $resultTokens['refresh_token'];
380 ////update_option('network_general', $props);
381 powerpress_save_settings( $props, 'powerpress_network_creds');
382
383 powerpress_page_message_add_notice( __('Account linked successfully.', 'powerpress') );
384 return;
385 }
386
387 private function checkSignin()
388 {
389 $accessToken = $this->getAccessToken();
390 if( !empty($accessToken) )
391 return true;
392
393 return false;
394 }
395
396 public function action_admin_init()
397 {
398 // Only do anything if we are in the network page..unlink a
399 if(empty($_GET['page']) || $_GET['page'] != 'network-plugin' )
400 return;
401
402 // Move wp-admin code here that processes things before any HTML is sent back by the server.
403 $apiArray = powerpress_get_api_array();
404 $apiUrl = $apiArray[0];
405 $createUrl = get_home_url() . '/';
406 $creds = array();
407
408 // verify nonce on all POST requests
409 if (!empty($_POST) && !wp_verify_nonce($_POST['_ppn_nonce'] ?? '', 'powerpress')) {
410 powerpress_page_message_add_error(__('Please refresh and try again.', 'powerpress'));
411 return;
412 }
413
414 if (isset ($_POST['target']) || isset($_POST['clearSiteCache'])) {
415 $this->handlePageAction($createUrl);
416 }
417 if (isset($_GET['code'])) {
418 $this->handleCodeReturn();
419 }
420
421 if (isset($_POST['unlinkAccount'])){
422 // flush network cache
423 PowerpressNetworkDataBus::clearCacheByLike("ppn-cache %");
424
425 delete_option('powerpress_network_creds');
426 delete_option('powerpress_network_id');
427 delete_option('powerpress_network_title');
428 delete_option('powerpress_network_temp_client');
429 powerpress_page_message_add_notice(__('Account unlinked.', 'powerpress'));
430 }
431
432 if( !empty($_POST['ppn-action']) ) {
433 switch( $_POST['ppn-action']) {
434 case 'link-account': {
435 // Link account action requested
436 if (isset($_POST['signinRequest'])) {
437
438 $auth = new PowerPressAuth();
439 $result = $auth->getTemporaryCredentials();
440
441 // Okay we got it!
442 if( $result !== false && !empty($result['temp_client_id']) && !empty($result['temp_client_secret']) ) {
443 $state = md5( rand(0, 999999) . time() );
444 update_option('powerpress_network_temp_client', array('temp_client_id' => $result['temp_client_id'], 'temp_client_secret' =>$result['temp_client_secret'], 'state'=>$state ));
445 header('location:' . $auth->getApiUrl() . 'oauth2/authorize?response_type=code&client_id=' . $result['temp_client_id'] . '&redirect_uri=' . $createUrl . 'wp-admin/admin.php?page=network-plugin&state='.$state );
446 exit;
447 }
448
449 // Handle error here
450 if( !empty($result['error_description']) )
451 powerpress_page_message_add_error( $result['error_description'] );
452 else if( !empty($result['error']) )
453 powerpress_page_message_add_error( $result['error'] );
454 else
455 powerpress_page_message_add_error( __('Error creating temporary client:','powerpress') .' '.$auth->GetLastError() );
456 }
457 }; break;
458
459 case 'create-network': {
460 $requestUrl = '/2/powerpress/network/create/';
461 $props = $this->requestAPI($requestUrl, true, $_POST);
462
463 // save network to wpdb
464 if(!empty($props['network_id'])){ // new network ID
465 // hard clear stale data from old network (THIS WILL NEED UPDATE IF WE GO MULTI NETWORK)
466 PowerpressNetworkDataBus::clearCacheByLike('ppn-cache %');
467 delete_option('powerpress_network_map');
468 delete_option('powerpress_network');
469 delete_option('powerpress_network_id');
470 delete_option('powerpress_network_title');
471
472 // setup new network
473 update_option('powerpress_network_id', $props['network_id']);
474 if (!empty($props['network_title'])) {
475 update_option('powerpress_network_title', $props['network_title']);
476 }
477 if (isset($props['network_description'])) {
478 $ppnSettings = get_option('powerpress_network', []);
479 $ppnSettings['network_description'] = $props['network_description'];
480 update_option('powerpress_network', $ppnSettings);
481 }
482 powerpress_page_message_add_notice(__('Network successfully created.', 'powerpress-network'));
483 } else {
484 powerpress_page_message_add_error(__('Could not create network. Please check the title and description and try again.', 'powerpress-network'));
485 }
486 } break;
487
488 case 'set-network-id': {
489 $networkId = $_POST['networkId'];
490 $requestUrl = '/2/powerpress/network/' . $networkId;
491 $props = $this->requestAPI($requestUrl, true);
492
493
494 //$props = PowerpressNetworkDataBus::getCacheOrCallAPI($creds, $cacheName, $requestUrl, $needDirectAPI);
495 if( !empty($props['network_id']) ) {
496 update_option('powerpress_network_id', $networkId);
497 if( !empty($props['network_title']) )
498 update_option('powerpress_network_title', $props['network_title']);
499 // store network description for homepage display
500 if( isset($props['network_description']) ) {
501 $ppnSettings = get_option('powerpress_network', []);
502 $ppnSettings['network_description'] = $props['network_description'];
503 update_option('powerpress_network', $ppnSettings);
504 }
505 powerpress_page_message_add_notice(__('Network linked successfully.', 'powerpress'));
506 }
507
508 // NETWORK NOT ON BLUBRRY DB
509 elseif (!empty($props['error']) && $props['error'] == 'Your account does not have the network with specified id') {
510 // API confirms network is gone, scrub from local list cache
511 $this->scrubStaleNetwork($networkId);
512 powerpress_page_message_add_error(__('That network no longer exists. It has been removed from your list.', 'powerpress'));
513 }
514
515
516 else {
517 powerpress_page_message_add_error(__('Could not find that network. Please check the ID and try again.', 'powerpress'));
518 }
519
520 }; break;
521
522 case 'save-tos-url': {
523 $tosRaw = trim($_POST['ppn_tos_url'] ?? '');
524
525 if ($tosRaw && stripos($tosRaw, 'http') !== 0) {
526 $tosRaw = 'https://' . $tosRaw;
527 }
528
529 if ($tosRaw && !filter_var($tosRaw, FILTER_VALIDATE_URL)) {
530 http_response_code(400);
531 break;
532 }
533 $tosUrl = esc_url_raw($tosRaw);
534 $ppnSettings = get_option('powerpress_network', []);
535 $ppnSettings['tos_url'] = $tosUrl;
536 update_option('powerpress_network', $ppnSettings);
537 powerpress_page_message_add_notice(__('Terms of service URL saved.', 'powerpress'));
538 }; break;
539
540 case 'unset-network-id': {
541 // flush network cache
542 $oldNetworkId = get_option('powerpress_network_id');
543 if ($oldNetworkId) {
544 delete_option("ppn-cache n-{$oldNetworkId}");
545 PowerpressNetworkDataBus::clearCacheByLike("ppn-cache n-{$oldNetworkId}-%");
546 delete_option("ppn-cache n");
547 }
548 delete_option('powerpress_network_id');
549 delete_option('powerpress_network_title');
550 $networkId = '';
551 powerpress_page_message_add_notice(__('Network unlinked.', 'powerpress'));
552 }; break;
553 }
554 }
555 $passSignIn = $this->checkSignin();
556
557 $status = null;
558 $props = array();
559 $program_props = array();
560 $list_props = array();
561 $application_props = array();
562 $needDirectAPI = false;
563
564
565 $networkInfo = get_option('powerpress_network', array());
566 $networkId = get_option('powerpress_network_id');
567 $networkTitle = get_option('powerpress_network_title');
568 $networkInfo['network_id'] = $networkId;
569 $networkInfo['network_title'] = $networkTitle;
570 //echo "PowerPress Network ID: $networkId <br />";
571
572 $accountInfo = $this->apiBus->getNetworkOwnerInformation($apiUrl, $creds, $networkInfo, $needDirectAPI);
573
574 // pull the network record on admin load w/ 1 hour throttle
575 if (!empty($networkId) && !get_transient('ppn_network_info_synced')) {
576 set_transient('ppn_network_info_synced', 1, HOUR_IN_SECONDS);
577 self::syncNetworkInfo($this->apiBus->getSpecificNetworkInAccount($apiUrl, $creds, $networkInfo, true), $networkInfo);
578 $networkTitle = $networkInfo['network_title'];
579 }
580
581 if ($passSignIn) { //If the user pass the signin section
582 if ( empty($networkId) ){
583 $status = 'List Networks';
584 } else {
585 if (isset($_GET['status']) && ($_GET['status'] != 'List Networks' ) ) {
586 $status = htmlspecialchars($_GET['status']);
587 } else {
588 $status = 'Select Choice';
589 }
590 }
591 //echo "- - - - - - - - - - - - - - - $status <br />";
592 if (isset($_POST['listId'])) {
593 $this->insertOption('list_id', $_POST['listId']);
594 $networkInfo = get_option('powerpress_network');
595 $networkInfo['network_id'] = $networkId;
596 $networkInfo['network_title'] = $networkTitle;
597 }
598 if (isset($_POST['programId'])){
599 $this->insertOption('program_id', $_POST['programId']);
600 $networkInfo = get_option('powerpress_network');
601 $networkInfo['network_id'] = $networkId;
602 $networkInfo['network_title'] = $networkTitle;
603 }
604 if (isset($_POST['linkPageProgram'])){
605 $this->insertOption('link_page_program', $_POST['linkPageProgram']);
606 $networkInfo = get_option('powerpress_network');
607 $networkInfo['network_id'] = $networkId;
608 $networkInfo['network_title'] = $networkTitle;
609 }
610 if (isset($_POST['linkPageList'])){
611 $this->insertOption('link_page_list', $_POST['linkPageList']);
612 $networkInfo = get_option('powerpress_network');
613 $networkInfo['network_id'] = $networkId;
614 $networkInfo['network_title'] = $networkTitle;
615 }
616 if (isset($_POST['needDirectAPI']) && $_POST['needDirectAPI'] == true){
617 // flush all ppn-cache options
618 global $wpdb;
619 $wpdb->query(
620 $wpdb->prepare("DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", 'ppn-cache %')
621 );
622 }
623
624 if (isset($_POST['editNetworkTitle'])) { //Edit Network record
625 $update = [
626 'editNetworkTitle' => mb_substr(wp_unslash($_POST['editNetworkTitle']), 0, 255),
627 'editNetworkDescription' => mb_substr(wp_unslash($_POST['editNetworkDescription'] ?? ''), 0, 255),
628 ];
629 $props = $this->apiBus->updateNetwork($apiUrl, $creds, $networkInfo, $update);
630 $needDirectAPI = true;
631
632 if ($props !== false && empty($props['danger'])) {
633 // set local data for shortcodes
634 update_option('powerpress_network_title', $props['network_title']);
635 $this->insertOption('network_description', $props['network_description']);
636 delete_option('ppn-cache n');
637 delete_option('ppn-cache n-'.$networkInfo['network_id']);
638 $networkInfo['network_title'] = $props['network_title'];
639 $networkTitle = $props['network_title'];
640
641 // set transient to prevent load sync + send success to user
642 set_transient('ppn_network_info_synced', 1, HOUR_IN_SECONDS);
643 update_option('ppn_manage_notice', ['text' => __('Network details saved.', 'powerpress'), 'error' => false]);
644 } else {
645 $failure = (is_array($props) && !empty($props['alert'])) ? $props['alert'] : __('Could not save network details. Please try again.', 'powerpress');
646 update_option('ppn_manage_notice', ['text' => $failure, 'error' => true]);
647 }
648 }
649
650 if (isset ($_POST['changeOrCreate']) && $_POST['changeOrCreate'] == true) {
651 if (isset($_POST['newListTitle'])) { //Create New List
652 $create = array(
653 'newListTitle' => mb_substr(wp_unslash($_POST['newListTitle']), 0, 255),
654 'newListDescription' => mb_substr(wp_unslash($_POST['newListDescription']), 0, 255),
655 );
656 $props = $this->apiBus->createNewList($apiUrl, $creds, $networkInfo, $create);
657 $needDirectAPI = true;
658 delete_option('ppn-cache n-'.$networkInfo['network_id'].'-l');
659 if ($props !== false && empty($props['danger'])) {
660 powerpress_page_message_add_notice(__('Group created successfully.', 'powerpress'));
661 // save list_id so manage list view can load new group
662 if (!empty($props['list_id'])) {
663 $this->insertOption('list_id', $props['list_id']);
664 $networkInfo['list_id'] = $props['list_id'];
665 }
666 }
667 }
668
669 if (isset($_POST['editListTitle'])) { //Edit List
670 $update = array(
671 'editListTitle' => mb_substr(wp_unslash($_POST['editListTitle']), 0, 255),
672 'editListDescription'=> mb_substr(wp_unslash($_POST['editListDescription']), 0, 255)
673 );
674 $props = $this->apiBus->updateList($apiUrl, $creds, $networkInfo, $update);
675 $needDirectAPI = true;
676 delete_option("ppn-cache n-{$networkInfo['network_id']}-l");
677 }
678
679 if (isset($_POST['requestAction'])) { //Change List
680 $needDirectAPI = true;
681 if ($_POST['requestAction'] == 'delete') {
682 if (isset($_POST['listId'])) {
683 $this->insertOption('list_id', $_POST['listId']);
684 $networkInfo = get_option('powerpress_network');
685 $networkInfo['network_id'] = $networkId;
686 $networkInfo['network_title'] = $networkTitle;
687 $props = $this->apiBus->deleteSpecificList($apiUrl, $creds, $networkInfo);
688
689 if ($props !== false && empty($props['danger'])) {
690 powerpress_page_message_add_notice(__('Group deleted.', 'powerpress'));
691 }
692 }
693
694 else if (isset($_POST['target']) && $_POST['target'] == 'program') {
695 $networkInfo = get_option('powerpress_network');
696 $networkInfo['network_title'] = get_option('powerpress_network_title');
697 $networkInfo['network_id'] = get_option('powerpress_network_id');
698 $networkInfo['program_id'] = $_POST['targetId'];
699 $props = $this->apiBus->removeSpecificProgramInNetwork($apiUrl, $creds, $networkInfo, true);
700
701 if ($props !== false && empty($props['danger'])) {
702 powerpress_page_message_add_notice(__('Show removed from network.', 'powerpress'));
703 }
704 }
705 }
706
707 else if ($_POST['requestAction'] == 'save') {
708 $props = $this->apiBus->updateProgramsInSpecificList($apiUrl, $creds, $networkInfo, $_POST['program'] ?? []);
709 if ($props !== false && empty($props['danger'])) {
710 powerpress_page_message_add_notice(__('Group saved successfully.', 'powerpress'));
711 }
712 }
713
714 else if ($_POST['requestAction'] == 'add' && $_POST['list_id'] ) {
715 $networkInfo['list_id'] = $_POST['list_id'];
716 $props = $this->apiBus->addProgramToList($apiUrl, $creds, $networkInfo, $_POST['program']);
717 if ($props !== false && empty($props['danger'])) {
718 powerpress_page_message_add_notice(__('Show added to group.', 'powerpress'));
719 }
720 }
721 }
722
723
724 if (isset($_POST['appAction'])) {
725 $needDirectAPI = true;
726 $action = array(
727 'appAction' => htmlspecialchars($_POST['appAction']),
728 'applicantId' => intval($_POST['applicantId'])
729 );
730 $props = $this->apiBus->changeApplicationStatus($apiUrl, $creds, $networkInfo, $action);
731 // clear programs cache so newly approved programs can be viewed
732 $networkId = get_option('powerpress_network_id');
733 $this->apiBus->clearCache('ppn-cache n-'.$networkId.'-p');
734 }
735 }
736
737 // display api error
738 if (is_array($props)) {
739 if (!empty($props['danger']) && !empty($props['alert'])) {
740 powerpress_page_message_add_error($props['alert']);
741 } else if (!empty($props['error'])) {
742 powerpress_page_message_add_error($props['error']);
743 }
744 }
745
746 $requestUrl = null;
747 // $status = 'Manage Program';
748 // $_POST['linkPage'] = 'Nothing here';
749 switch ($status) {
750 case 'List Networks':
751 $props = $this->apiBus->getNetworksInAccount($creds);
752 if (isset($_POST['linkNetwork']) && $_POST['linkNetwork'] == 'unlink') {
753 delete_option('powerpress_network');
754 }
755 break;
756
757 case 'Select Choice':
758 // pre-flight probe
759 $probe = $this->requestAPI('/2/powerpress/network/' . absint($networkId), true);
760 if (is_array($probe) && !empty($probe['error']) && $probe['error'] == 'Your account does not have the network with specified id') {
761 $this->scrubStaleNetwork($networkId);
762 delete_option('powerpress_network_id');
763 delete_option('powerpress_network_title');
764
765 powerpress_page_message_add_error(__('That network no longer exists. It has been removed from your list.', 'powerpress'));
766
767 $networkId = '';
768 $networkTitle = '';
769 $networkInfo['network_id'] = '';
770 $networkInfo['network_title'] = '';
771 $status = 'List Networks';
772 $props = $this->apiBus->getNetworksInAccount($creds);
773 break;
774 }
775
776 $program_props = $this->apiBus->getProgramsInNetwork($apiUrl, $creds, $networkInfo, $needDirectAPI );
777 $list_props = $this->apiBus->getListsInNetwork($apiUrl, $creds, $networkInfo, $needDirectAPI );
778 $application_props = $this->apiBus->getApplicantsInNetwork($apiUrl, $creds, $networkInfo, true );
779 $props = $this->apiBus->getSpecificNetworkInAccount($apiUrl, $creds, $networkInfo, $needDirectAPI );
780 $networkInfo = get_option('powerpress_network', array());
781 $networkInfo['network_id'] = $networkId;
782 $networkInfo['network_title'] = $networkTitle;
783 break;
784
785 case 'List Programs':
786 $props = $this->apiBus->getProgramsInNetwork($apiUrl, $creds, $networkInfo, $needDirectAPI );
787 break;
788
789 case 'List Lists':
790 $props = $this->apiBus->getListsInNetwork($apiUrl, $creds, $networkInfo, $needDirectAPI );
791 $networkInfo = get_option('powerpress_network');
792 $networkInfo['network_id'] = $networkId;
793 $networkInfo['network_title'] = $networkTitle;
794 break;
795
796 case 'List Applicants':
797 $props = $this->apiBus->getApplicantsInNetwork($apiUrl, $creds, $networkInfo, true );
798 break;
799
800 case 'Manage List':
801 if (!empty($props['list_id'])) {
802 $networkInfo['list_id'] = $props['list_id'];
803 }
804 $props = $this->apiBus->getSpecificListInNetwork($apiUrl, $creds, $networkInfo, $needDirectAPI );
805 $networkInfo = get_option ('powerpress_network');
806 $networkInfo['network_id'] = $networkId;
807 $networkInfo['network_title'] = $networkTitle;
808 // clean up bad page link if page was deleted
809 if (!empty($networkInfo['link_page_list'])) {
810 $map = get_option('powerpress_network_map', []);
811 $mapKey = 'l-' . $networkInfo['list_id'];
812 $pageId = $map[$mapKey] ?? 0;
813 if (!$pageId || get_post_status($pageId) !== 'publish') {
814 $this->removeOption('link_page_list');
815 $networkInfo['link_page_list'] = '';
816 if ($pageId) {
817 unset($map[$mapKey]);
818 update_option('powerpress_network_map', $map);
819 }
820 }
821 }
822 break;
823
824 case 'Manage Program':
825 $props = $this->apiBus->getSpecificProgramInNetwork($apiUrl, $creds, $networkInfo, $needDirectAPI);
826 $networkInfo = get_option ('powerpress_network');
827 $networkInfo['network_id'] = $networkId;
828 $networkInfo['network_title'] = $networkTitle;
829 // clean up stale page link if page was deleted
830 if (!empty($networkInfo['link_page_program'])) {
831 $map = get_option('powerpress_network_map', []);
832 $mapKey = 'p-' . ($networkInfo['program_id'] ?? '');
833 $pageId = $map[$mapKey] ?? 0;
834 if (!$pageId || get_post_status($pageId) !== 'publish') {
835 $this->removeOption('link_page_program');
836 $networkInfo['link_page_program'] = '';
837 if ($pageId) {
838 unset($map[$mapKey]);
839 update_option('powerpress_network_map', $map);
840 }
841 }
842 }
843 break;
844 }
845
846 }
847 $return = array();
848 $return['status'] = $status;
849 $return['props'] = $props;
850 $return['program_props'] = $program_props;
851 $return['list_props'] = $list_props;
852 $return['application_props'] = $application_props;
853 $return['accountInfo'] = $accountInfo;
854 $return['network_info'] = $networkInfo;
855 return $return;
856
857 }
858
859 static function removeOption ($key)
860 {
861 $result = get_option ('powerpress_network');
862 unset ($result[$key]);
863 update_option('powerpress_network', $result);
864 }
865
866 /** refresh the cached network title and description from api response */
867 static function syncNetworkInfo($fetched, array &$networkInfo): void {
868 if (!is_array($fetched) || !empty($fetched['error'])) {
869 return;
870 }
871
872 foreach (['network_title', 'network_description'] as $key) {
873 if (!array_key_exists($key, $fetched)) {
874 continue;
875 }
876
877 $value = (string) $fetched[$key];
878 if ($value === (string) ($networkInfo[$key] ?? '')) {
879 continue;
880 }
881
882 $networkInfo[$key] = $value;
883 if ($key === 'network_title') {
884 update_option('powerpress_network_title', $value);
885 continue;
886 }
887
888 self::insertOption($key, $value);
889 }
890 }
891
892 static function insertOption ($key, $value)
893 {
894 $result = get_option ('powerpress_network');
895 $result[$key] = $value;
896 update_option('powerpress_network', $result);
897 }
898
899 function display_plugin()
900 {
901 if( function_exists('powerpress_page_message_print') )
902 powerpress_page_message_print();
903
904 $status = $this->display['status'];
905 $props = $this->display['props'];
906 $program_props = $this->display['program_props'];
907 $list_props = $this->display['list_props'];
908 $application_props = $this->display['application_props'];
909 $accountInfo = $this->display['accountInfo'];
910 $networkInfo = $this->display['network_info'];
911 ?>
912
913 <div class="ppn-admin">
914 <?php
915 $ppn_nonce = wp_create_nonce('powerpress');
916 echo '<script>var ppnNonce = ' . json_encode($ppn_nonce) . ';</script>';
917 switch ($status) {
918 case 'List Networks':
919 echo $this->getHTML('networks.php', $props, $networkInfo, $accountInfo);
920 break;
921 case 'Select Choice':
922 $shows_html = $this->getHTML('programs.php', $program_props, $networkInfo, $accountInfo, '', '', '', $list_props);
923 $groups_html = $this->getHTML('lists.php', $list_props, $networkInfo, $accountInfo);
924 $requests_html = $this->getHTML('applications.php', $application_props, $networkInfo, $accountInfo);
925 $manage_html = $this->getHTML('managenetwork.php', $props, $networkInfo, $accountInfo);
926 echo $this->getHTML('base.php', $props, $networkInfo, $accountInfo, $shows_html, $groups_html, $requests_html, [], $manage_html);
927 break;
928 case 'List Programs':
929 echo $this->getHTML('programs.php', $props, $networkInfo, $accountInfo);
930 break;
931 case 'List Lists':
932 echo $this->getHTML('lists.php', $props, $networkInfo, $accountInfo);
933 break;
934 case 'List Applicants':
935 echo $this->getHTML('applications.php', $props, $networkInfo, $accountInfo);
936 break;
937 case 'Create List':
938 case 'Manage List':
939 echo $this->getHTML('managelist.php', $props, $networkInfo, $accountInfo);
940 break;
941 case 'Manage Program':
942 echo $this->getHTML('manageprogram.php', $props, $networkInfo, $accountInfo);
943 break;
944 default:
945 echo $this->getHTML('signin.php', $props, $networkInfo, $accountInfo);
946 break;
947 }
948 ?>
949 </div>
950 <?php
951 }
952
953 static function updateMeta ($meta_key)
954 {
955 global $wpdb;
956 $update = serialize(['last_update' => time(), 'need_update' => true]);
957 $wpdb->update(
958 "{$wpdb->prefix}postmeta",
959 ['meta_value' => $update],
960 ['meta_key' => $meta_key]
961 );
962 }
963
964 function checkUpdateProgram()
965 {
966 $option = get_option ('powerpress_network_creds');
967 if (empty($option)){
968 return;
969 }
970
971 if (!wp_next_scheduled ( 'updateProgram' )) {
972 wp_schedule_event(time(), 'hourly', 'updateProgram');
973 }
974
975 $timeExecute = wp_next_scheduled('updateProgram');
976 if (time() >= $timeExecute){
977 $networkId = get_option('powerpress_network_id');
978 if( empty($networkId) )
979 return;
980 $apiArray = powerpress_get_api_array();
981 $apiUrl = $apiArray[0];
982 // $post = false; // array('grant_type'=>'client_credentials', 'access_token'=>$accessToken );
983 $requestUrl = $apiUrl.'2/powerpress/network/'.$networkId.'/update?since='.($timeExecute - 24*60*60);
984 $programUpdate = $this->requestAPI($requestUrl);
985 foreach ($programUpdate ?: [] as $program) {
986 PowerPressNetwork::updateMeta($program);
987 }
988 }
989 }
990
991 function addProgramToNetwork() {
992 // verify nonce
993 if(!wp_verify_nonce($_POST['nonce'] ?? '', 'powerpress')){
994 wp_send_json_error('error');
995 }
996
997 // verify options management
998 if(!current_user_can('manage_options')){
999 wp_send_json_error('error');
1000 }
1001
1002 // collect POST values
1003 $networkID = htmlspecialchars($_POST['network_id'] ?? '');
1004 $programID = htmlspecialchars($_POST['program_id'] ?? ''); // currently don't need
1005
1006 if(empty($networkID)) {
1007 wp_send_json_error('error');
1008 }
1009
1010 // compare networkID from POST to networkID from PP
1011 if((string)get_option('powerpress_network_id') !== $networkID){
1012 wp_send_json_error('error');
1013 }
1014
1015 // verify programID is not ALREADY within the network
1016 $requestUrl = '/2/powerpress/network/' . $networkID . '/exists-program/';
1017 $result = $this->requestAPI($requestUrl, true, $_POST);
1018
1019 if(!$result || isset($result['error'])){
1020 wp_send_json_error('error');
1021 }
1022
1023 if($result['exists']){
1024 wp_send_json_error('error');
1025 }
1026
1027 // program does not exist within network, add it
1028 $requestUrl = '/2/powerpress/network/' . $networkID . '/programs/add/';
1029 $result = $this->requestAPI($requestUrl, true, $_POST);
1030
1031 if(empty($result) || isset($result['error'])){
1032 wp_send_json_error('error');
1033 }
1034
1035 $programTitle = sanitize_text_field(wp_unslash($_POST['program_title'] ?? ''));
1036 $networkTitle = wp_unslash(get_option('powerpress_network_title', ''));
1037 $msg = sprintf(__('%s added to %s', 'powerpress'), $programTitle, $networkTitle);
1038 wp_send_json_success(['message' => $msg]);
1039 }
1040
1041 function ajaxPageAction() {
1042 // init checks
1043 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'powerpress')) {
1044 wp_send_json_error('invalid nonce');
1045 }
1046
1047 if (!current_user_can('manage_options')) {
1048 wp_send_json_error('insufficient permissions');
1049 }
1050
1051 $target = sanitize_text_field($_POST['target'] ?? '');
1052 $targetId = sanitize_text_field($_POST['targetId'] ?? '');
1053 $mode = sanitize_text_field($_POST['mode'] ?? '');
1054
1055 if (empty($target)) {
1056 wp_send_json_error('missing target');
1057 }
1058
1059 $option = get_option('powerpress_network_map') ?: [];
1060
1061 if ($mode === 'link') {
1062 // LINK EXISTING PAGE
1063 $pageId = intval($_POST['pageID'] ?? 0);
1064 if (!$pageId) {
1065 wp_send_json_error('missing page ID');
1066 }
1067
1068 if ($target === 'List') {
1069 $option["l-{$targetId}"] = $pageId;
1070 } elseif ($target === 'Program') {
1071 $option["p-{$targetId}"] = $pageId;
1072 }
1073 update_option('powerpress_network_map', $option);
1074
1075 if (!empty($_POST['updateShortcode'])) {
1076 self::ensureShortcodeOnPage($pageId, $target, $targetId);
1077 }
1078 $postId = $pageId;
1079 } else {
1080 // CREATE NEW PAGE
1081 $postId = self::createPage();
1082 }
1083
1084 if (!$postId) {
1085 wp_send_json_error('page creation failed');
1086 }
1087
1088 // store link in network info
1089 if ($target === 'List') {
1090 $this->insertOption('link_page_list', get_permalink($postId));
1091 } elseif ($target === 'Program') {
1092 $this->insertOption('link_page_program', get_permalink($postId));
1093 }
1094
1095 wp_send_json_success([
1096 'post_id' => $postId,
1097 'permalink' => get_permalink($postId),
1098 'edit_url' => admin_url("post.php?post={$postId}&action=edit"),
1099 ]);
1100 }
1101
1102 /**
1103 * clear local state for a network
1104 * acts as a sync when network removed from blubrry server but still exists on local wp db
1105 * scrubs network etnry from list cache and deletes network caches
1106 */
1107 private function scrubStaleNetwork($networkId) {
1108 $networkId = absint($networkId);
1109 if (!$networkId) {
1110 return;
1111 }
1112
1113 // clean cache
1114 $cache = get_option('ppn-cache n', []);
1115 if (!empty($cache['data']) && is_array($cache['data'])) {
1116 $cache['data'] = array_values(
1117 array_filter($cache['data'], function($n) use ($networkId) {
1118 return empty($n['network_id']) || (int)$n['network_id'] !== $networkId;
1119 })
1120 );
1121 update_option('ppn-cache n', $cache);
1122 }
1123
1124 delete_option("ppn-cache n-{$networkId}");
1125 PowerpressNetworkDataBus::clearCacheByLike("ppn-cache n-{$networkId}-%");
1126 }
1127
1128 }
1129