PluginProbe
PowerPress Podcasting plugin by Blubrry / 11.17.3
PowerPress Podcasting plugin by Blubrry v11.17.3
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.php

powerpress.php in PowerPress Podcasting plugin by Blubrry 11.17.3, at powerpress.php

6,702 lines 276.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Blubrry PowerPress
4 Plugin URI: https://blubrry.com/services/powerpress-plugin/
5 Description: <a href="https://blubrry.com/services/powerpress-plugin/" target="_blank">Blubrry PowerPress</a> is the No. 1 Podcasting plugin for WordPress. Developed by podcasters for podcasters; features include Simple and Advanced modes, multiple audio/video player options, subscribe to podcast tools, podcast SEO features, and more! Fully supports Apple Podcasts (previously iTunes), Google Podcasts, Spotify, and Blubrry Podcasting directories, as well as all podcast applications and clients.
6 Version: 11.17.3
7 Author: Blubrry
8 Author URI: https://blubrry.com/
9 Requires at least: 3.6
10 Tested up to: 7.0
11 Text Domain: powerpress
12 Change Log:
13 Please see readme.txt for detailed change log.
14
15 Contributors:
16 Angelo Mandato, CIO Blubrry - Plugin founder, architect and lead developer
17 See readme.txt for full list of contributors.
18
19 Credits:
20 getID3(), License: GPL 2.0+ by James Heinrich <info [at] getid3.org> http://www.getid3.org
21 Note: getid3.php analyze() function modified to prevent redundant filesize() function call.
22
23 Copyright 2008-2019 Blubrry (https://blubrry.com)
24
25 License: GPL (http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt)
26
27 This project uses source that is GPL licensed.
28 */
29
30 use Mpdf\Tag\A;
31
32 if( !function_exists('add_action') ) {
33 header( 'Status: 403 Forbidden' );
34 header( 'HTTP/1.1 403 Forbidden' );
35 exit();
36 }
37
38 /**
39 * Updated version of a function originally added by the WordPress.org Plugins Review team in response to an incident with versions 11.9.3 to 11.9.4 where users were auto-created
40 * This resets passwords for these auto-created users. Query was updated for efficiency.
41 */
42 function PowerPress_PRT_incidence_response_notice() {
43 global $PowerPress_PRT_incidence_response_usernames;
44 ?>
45 <div class="notice notice-warning">
46 <h3><?php esc_html_e( 'Action Required: Please verify user accounts', 'powerpress' ); ?></h3>
47 <p><?php esc_html_e( 'On June 28th, an unauthorized update of PowerPress was released using a compromised account.
48 That version (11.9.3 to 11.9.4), contained malicious code that created users with administrative privileges. It was quickly replaced with a fixed version about an hour later.
49 As a security measure, the passwords of all such accounts were invalidated to prevent access.
50 ', 'powerpress' ); ?>
51 <?php printf(
52 esc_html__( 'To remove this message, please verify all admin users and remove users with login names %s.', 'powerpress' ),
53 esc_html(implode(', ', $PowerPress_PRT_incidence_response_usernames))
54 ); ?>
55
56 <?php if(function_exists('add_footer_script')){
57 esc_html_e( 'In addition, a function called "add_footer_script" may have been modified/added to the functions.php file in your theme. This will have to be manually checked. Updating to a new version or re-installing your theme will also fix this issue.', 'powerpress' );
58 } ?> </p>
59 <p><?php esc_html_e( 'We would like to thank the community and the WordPress team for their help in getting this detected and fixed quickly.', 'powerpress' ); ?></p>
60 <p><?php _e( 'Our support lines are open. If you need help or have any questions, please reach out via our <a href="https://blubrry.com/contact"> contact form. </a>', 'powerpress' ); ?></p>
61 </div>
62 <?php
63 }
64 function PowerPress_PRT_incidence_response() {
65 global $PowerPress_PRT_incidence_response_usernames;
66 $check_completed = get_option('powerpress_user_check_completed');
67 if ($check_completed) {
68 return;
69 }
70 // They tried to create those users.
71 $affectedusernames = ['PluginAUTH', 'PluginGuest', 'Options'];
72
73 $page = 1;
74 $showWarning = false;
75
76 do {
77 $args = array (
78 'role' => 'administrator',
79 'date_query' => array(
80 array(
81 'after' => '2024-06-27 00:00:00',
82 'inclusive' => true,
83 ),
84 ),
85 'number' => 5000,
86 'paged' => $page
87 );
88
89 $user_query = new WP_User_Query($args);
90 $users = $user_query->get_results();
91 if (!$users) {
92 break;
93 }
94 foreach ($users as $user) {
95 if (7 === strlen($user->user_login)) {
96 $affectedusernames[] = $user->user_login;
97 }
98 }
99
100 if (!empty($affectedusernames)) {
101 foreach ($affectedusernames as $affectedusername) {
102 $user = get_user_by('login', $affectedusername);
103 if ($user) {
104 // Affected users had an email on the form <username>@example.com
105 if ($user->user_email === $affectedusername . '@example.com') {
106 // We set an invalid password hash to invalidate the user login.
107 $temphash = 'PRT_incidence_response_230624';
108 if ($user->user_pass !== $temphash) {
109 global $wpdb;
110 $wpdb->update(
111 $wpdb->users,
112 array(
113 'user_pass' => $temphash,
114 'user_activation_key' => '',
115 ),
116 array('ID' => $user->ID)
117 );
118 clean_user_cache($user);
119 }
120 $PowerPress_PRT_incidence_response_usernames[] = $user->user_login;
121 $showWarning = true;
122 }
123 }
124 }
125 }
126 $page++;
127 } while (!empty($users));
128 if($showWarning){
129 add_action( 'admin_notices', 'PowerPress_PRT_incidence_response_notice' );
130 } else {
131 add_option('powerpress_user_check_completed', true);
132 }
133 }
134 add_action('init', 'PowerPress_PRT_incidence_response');
135
136 // WP_PLUGIN_DIR (REMEMBER TO USE THIS DEFINE IF NEEDED)
137 define('POWERPRESS_VERSION', '11.17.3' );
138
139 // Translation support:
140 if ( !defined('POWERPRESS_ABSPATH') )
141 define('POWERPRESS_ABSPATH', dirname(__FILE__) );
142
143
144
145 /////////////////////////////////////////////////////
146 // The following define options should be placed in your
147 // wp-config.php file so the setting is not disrupted when
148 // you upgrade the plugin.
149 /////////////////////////////////////////////////////
150
151 if( !defined('POWERPRESS_BLUBRRY_API_URL') )
152 define('POWERPRESS_BLUBRRY_API_URL', 'http://api.blubrry.com/');
153
154 // Replace validator service with one that is more reliable here:
155 define('POWERPRESS_FEEDVALIDATOR_URL', 'https://castfeedvalidator.com/?url=');
156
157 if( !defined('POWERPRESS_CONTENT_ACTION_PRIORITY') )
158 define('POWERPRESS_CONTENT_ACTION_PRIORITY', 10 );
159
160 // Added so administrators can customize what capability is needed for PowerPress
161 if( !defined('POWERPRESS_CAPABILITY_MANAGE_OPTIONS') )
162 define('POWERPRESS_CAPABILITY_MANAGE_OPTIONS', 'manage_options');
163 if( !defined('POWERPRESS_CAPABILITY_EDIT_PAGES') )
164 define('POWERPRESS_CAPABILITY_EDIT_PAGES', 'edit_pages');
165
166 // Define variables, advanced users could define these in their own wp-config.php so lets not try to re-define
167 if( !defined('POWERPRESS_LINK_SEPARATOR') )
168 define('POWERPRESS_LINK_SEPARATOR', '|');
169 if( !defined('POWERPRESS_TEXT_SEPARATOR') )
170 define('POWERPRESS_TEXT_SEPARATOR', ':');
171 if( !defined('PHP_EOL') )
172 define('PHP_EOL', "\n"); // We need this variable defined for new lines.
173 if( defined('POWERPRESS_DEBUG') ) {
174 if( !defined('PHP_EOL_WEB') ) {
175 define('PHP_EOL_WEB', "\n"); // Helps with readability
176 }
177 } else {
178 if( !defined('PHP_EOL_WEB') ) {
179 define('PHP_EOL_WEB', ''); // We don't necessarily need new lines for web output
180 }
181 }
182
183 if( !defined('POWERPRESS_SUBSCRIBE') )
184 define('POWERPRESS_SUBSCRIBE', true);
185 if(!defined('POWERPRESS_NEW_APPLE_CATEGORIES')) {
186 define('POWERPRESS_NEW_APPLE_CATEGORIES', true);
187 }
188 // Set regular expression values for determining mobile devices
189 if( !defined('POWERPRESS_MOBILE_REGEX') )
190 define('POWERPRESS_MOBILE_REGEX', 'iphone|ipod|ipad|aspen|android|blackberry|opera mini|webos|incognito|webmate|silk');
191
192 // TRUSTED DOMAINS
193 if( !defined('POWERPRESS_TRUSTED_DOMAINS') ) {
194 if( defined('POWERPRESS_LOCAL_DEV') && POWERPRESS_LOCAL_DEV ) {
195 define('POWERPRESS_TRUSTED_DOMAINS', array('blubrry.com', 'blubrry.biz', 'blubrry.local'));
196 } else {
197 define('POWERPRESS_TRUSTED_DOMAINS', array('blubrry.com'));
198 }
199 }
200
201 $powerpress_feed = NULL; // DO NOT CHANGE
202
203 function powerpress_content($content)
204 {
205 global $post, $g_powerpress_excerpt_post_id;
206
207 if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) || isset($GLOBALS['podcast_channel_active']) || defined('PODCASTING_VERSION') )
208 return $content;
209
210 if( empty($post->ID) || !is_object($post) )
211 return $content;
212
213 if( defined('POWERPRESS_DO_ENCLOSE_FIX') )
214 $content = preg_replace('/\<!--.*added by PowerPress.*-->/im', '', $content );
215
216 if( is_feed() )
217 return $content; // We don't want to do anything to the feed
218
219 if( function_exists('post_password_required') )
220 {
221 if( post_password_required($post) )
222 return $content;
223 }
224
225 // PowerPress settings:
226 $GeneralSettings = get_option('powerpress_general', array());
227
228 // No player or links to add to content...
229 if( !empty($GeneralSettings['disable_appearance']) )
230 return $content;
231
232 // check for themes/plugins where we know we need to do this...
233 if( empty($GeneralSettings['player_aggressive']) )
234 {
235 if( !empty($GLOBALS['fb_ver']) && version_compare($GLOBALS['fb_ver'], '1.0', '<=') ) {
236 $GeneralSettings['player_aggressive'] = 1;
237 }
238 if( defined('JETPACK__VERSION') && version_compare(JETPACK__VERSION, '2.0', '>=') ) {
239 $GeneralSettings['player_aggressive'] = 1; // Jet pack still doesn't behave with PowerPress the_content
240 }
241 if( defined('WPSEO_VERSION') ) {
242 $GeneralSettings['player_aggressive'] = 4;
243 }
244 }
245
246 if( !empty($GeneralSettings['player_aggressive']) )
247 {
248 if( $GeneralSettings['player_aggressive'] == 4 )
249 {
250 $in_http_head = powerpress_in_wp_head();
251 if( $in_http_head === true )
252 return $content;
253 }
254 else if( $GeneralSettings['player_aggressive'] == 2 ) // If we do not have theme issues then lets keep this logic clean. and only display playes after the wp_head only
255 {
256 if( empty($GLOBALS['powerpress_wp_head_completed']) )
257 return $content;
258 }
259 else // method 1 or 3...
260 {
261 if( strstr($content, '<!--powerpress_player-->') !== false )
262 return $content; // The players were already added to the content
263
264 if( $GeneralSettings['player_aggressive'] != 3 && $g_powerpress_excerpt_post_id > 0 )
265 $g_powerpress_excerpt_post_id = 0; // Hack, set this to zero so it always goes past...
266
267 if( $GeneralSettings['player_aggressive'] == 3 )
268 $GeneralSettings['player_aggressive'] = 1; // remainder of the system will function as normal
269 }
270 }
271
272 // Problem: If the_excerpt is used instead of the_content, both the_exerpt and the_content will be called here.
273 // Important to note, get_the_excerpt will be called before the_content is called, so we add a simple little hack
274 if( current_filter() == 'get_the_excerpt' )
275 {
276 $g_powerpress_excerpt_post_id = $post->ID;
277 return $content; // We don't want to do anything to this content yet...
278 }
279 else if( current_filter() == 'the_content' && $g_powerpress_excerpt_post_id == $post->ID )
280 {
281 return $content; // We don't want to do anything to this excerpt content in this call either...
282 }
283 else if( class_exists('custom_post_widget') && powerpress_in_custom_post_widget() )
284 {
285 return $content; // Custom Post Widget compatibility
286 }
287
288
289 if( !isset($GeneralSettings['custom_feeds']) )
290 $GeneralSettings['custom_feeds'] = array('podcast'=>'Default Podcast Feed');
291 if( empty($GeneralSettings['custom_feeds']['podcast']) )
292 $GeneralSettings['custom_feeds']['podcast'] = 'Default Podcast Feed';
293
294 // Re-order so the default podcast episode is the top most...
295 $Temp = $GeneralSettings['custom_feeds'];
296 $GeneralSettings['custom_feeds'] = array();
297 $GeneralSettings['custom_feeds']['podcast'] = 'Default Podcast Feed';
298 foreach( $Temp as $feed_slug=> $feed_title )
299 {
300 if( $feed_slug == 'podcast' )
301 continue;
302 $GeneralSettings['custom_feeds'][ $feed_slug ] = $feed_title;
303 }
304
305 // Handle post type feeds....
306 if( !empty($GeneralSettings['posttype_podcasting']) )
307 {
308 $post_type = get_query_var('post_type');
309 if ( is_array( $post_type ) ) {
310 $post_type = reset( $post_type ); // get first element in array
311 }
312
313 // Get the feed slugs and titles for this post type
314 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$post_type, array());
315 // Loop through this array of post type settings...
316 if( !empty($PostTypeSettingsArray) )
317 {
318 switch($post_type)
319 {
320 case 'post':
321 case 'page': {
322 // Do nothing!, we want the default podcast to appear in these post types
323 }; break;
324 default: {
325 if( !empty($post_type) && empty($PostTypeSettingsArray['podcast']) )
326 unset($GeneralSettings['custom_feeds']['podcast']); // special case, we do not want an accidental podcast episode to appear in a custom post type if the feature is enabled
327 }; break;
328 }
329
330 if (is_array($PostTypeSettingsArray)) {
331 foreach ($PostTypeSettingsArray as $feed_slug => $postTypeSettings) {
332 if (!empty($postTypeSettings['title']))
333 $GeneralSettings['custom_feeds'][$feed_slug] = $postTypeSettings['title'];
334 else
335 $GeneralSettings['custom_feeds'][$feed_slug] = $feed_slug;
336 }
337 }
338 }
339 }
340
341 if( !isset($GeneralSettings['display_player']) )
342 $GeneralSettings['display_player'] = 1;
343 if( !isset($GeneralSettings['player_function']) )
344 $GeneralSettings['player_function'] = 1;
345 if( !isset($GeneralSettings['podcast_link']) )
346 $GeneralSettings['podcast_link'] = 1;
347
348 // The blog owner doesn't want anything displayed, so don't bother wasting anymore CPU cycles
349 if( $GeneralSettings['display_player'] == 0 )
350 return $content;
351
352 if( current_filter() == 'the_excerpt' && empty($GeneralSettings['display_player_excerpt']) )
353 return $content; // We didn't want to modify this since the user didn't enable it for excerpts
354
355 if( !empty($GeneralSettings['hide_player_more']) && strstr($content, 'class="more-link"') )
356 return $content; // We do not want to add players and links if the read-more class found
357
358 // Figure out which players are alerady in the body of the page...
359 $ExcludePlayers = array();
360 if( isset($GeneralSettings['disable_player']) )
361 $ExcludePlayers = $GeneralSettings['disable_player']; // automatically disable the players configured
362
363 if( !empty($GeneralSettings['process_podpress']) && strstr($content, '[display_podcast]') )
364 return $content;
365
366 if( preg_match_all('/(.?)\[(powerpress)\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)/s', $content, $matches) )
367 {
368 if( isset($matches[3]) && is_array($matches[3]) )
369 {
370 foreach ($matches[3] as $key => $row) {
371 $attributes = shortcode_parse_atts($row);
372 if (isset($attributes['url'])) {
373 // not a problem...
374 } else if (isset($attributes['feed'])) {
375 // we want to exclude this feed from the links aera...
376 $ExcludePlayers[$attributes['feed']] = true;
377 } else {
378 // we don't want to include any players below...
379 $ExcludePlayers = $GeneralSettings['custom_feeds'];
380 }
381 }
382 }
383 }
384
385 $new_content = '';
386 if ( is_array($GeneralSettings['custom_feeds']) ) {
387 // LOOP HERE TO DISPLAY EACH MEDIA TYPE
388 foreach ($GeneralSettings['custom_feeds'] as $feed_slug => $feed_title) {
389 // Get the enclosure data
390 $EpisodeData = powerpress_get_enclosure_data($post->ID, $feed_slug);
391
392 if (!$EpisodeData && !empty($GeneralSettings['process_podpress']) && $feed_slug == 'podcast')
393 $EpisodeData = powerpress_get_enclosure_data_podpress($post->ID);
394
395 if (!$EpisodeData || !$EpisodeData['url'])
396 continue;
397
398 // Just in case, if there's no URL lets escape!
399 if (!$EpisodeData['url'])
400 continue;
401
402 // If the player is not already inserted in the body of the post using the shortcode...
403 //if( preg_match('/\[powerpress(.*)\]/is', $content) == 0 )
404 if (!isset($ExcludePlayers[$feed_slug])) // If the player is not in our exclude list because it's already in the post body somewhere...
405 {
406 if (isset($GeneralSettings['premium_caps']) && $GeneralSettings['premium_caps'] && !powerpress_premium_content_authorized($feed_slug)) {
407 $new_content .= powerpress_premium_content_message($post->ID, $feed_slug, $EpisodeData);
408 } else {
409 if ($GeneralSettings['player_function'] != 3 && $GeneralSettings['player_function'] != 0) // Play in new window only or disabled
410 {
411 do_action('wp_powerpress_player_scripts');
412 $AddDefaultPlayer = empty($EpisodeData['no_player']);
413
414 if ($EpisodeData && !empty($EpisodeData['embed'])) {
415 $new_content .= SanitizeEmbed(trim($EpisodeData['embed']));
416 if (!empty($GeneralSettings['embed_replace_player']))
417 $AddDefaultPlayer = false;
418 }
419
420 if ($AddDefaultPlayer) {
421 $image = '';
422 if (isset($EpisodeData['image']) && $EpisodeData['image'] != '')
423 $image = $EpisodeData['image'];
424
425 $new_content .= apply_filters('powerpress_player', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData);
426 }
427 }
428
429 if (!isset($EpisodeData['no_links'])) {
430 do_action('wp_powerpress_player_scripts');
431 $new_content .= apply_filters('powerpress_player_links', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData);
432 $new_content .= apply_filters('powerpress_player_subscribe_links', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData);
433 }
434 }
435 }
436 }
437 }
438
439 if( $new_content == '' )
440 return $content;
441
442 switch( $GeneralSettings['display_player'] )
443 {
444 case 1: { // Below posts
445 return $content.$new_content.( !empty($GeneralSettings['player_aggressive']) && $GeneralSettings['player_aggressive'] == 1 ?'<!--powerpress_player-->':'');
446 }; break;
447 case 2: { // Above posts
448 return ( !empty($GeneralSettings['player_aggressive']) && $GeneralSettings['player_aggressive'] == 1 ?'<!--powerpress_player-->':'').$new_content.$content;
449 }; break;
450 }
451 return $content;
452 }//end function
453
454
455 add_filter('get_the_excerpt', 'powerpress_content', (POWERPRESS_CONTENT_ACTION_PRIORITY - 1) );
456 add_filter('the_content', 'powerpress_content', POWERPRESS_CONTENT_ACTION_PRIORITY);
457 if( !defined('POWERPRESS_NO_THE_EXCERPT') )
458 add_filter('the_excerpt', 'powerpress_content', POWERPRESS_CONTENT_ACTION_PRIORITY);
459
460 /* Specail case fix Yoast bug which messes up the HTML */
461 function powerpress_yoast_gawp_fix($content)
462 {
463 $content= preg_replace(
464 array('/return powerpress\_pinw\(\"/', '/return powerpress\_embed\_winplayer\(\"/', '/return powerpress\_show\_embed\(\"/', '/return powerpress\_embed\_html5v\(\"/', '/return powerpress\_embed\_html5a\(\"/', ),
465 array('return powerpress_pinw(\'', 'return powerpress_embed_winplayer(\'', 'return powerpress_show_embed(\'', 'return powerpress_embed_html5v(\'', 'return powerpress_embed_html5a(\'' ),
466 $content);
467
468 return $content;
469 }
470
471 function powerpress_header()
472 {
473 // PowerPress settings:
474 $Powerpress = get_option('powerpress_general', array());
475 if( !isset($Powerpress['custom_feeds']) )
476 $Powerpress['custom_feeds'] = array('podcast'=>'Default Podcast Feed');
477
478 if( empty($Powerpress['disable_appearance']) || $Powerpress['disable_appearance'] == false )
479 {
480 if( !isset($Powerpress['player_function']) || $Powerpress['player_function'] > 0 ) // Don't include the player in the header if it is not needed...
481 {
482 $PowerpressPluginURL = powerpress_get_root_url();
483 ?>
484 <script type="text/javascript"><!--
485 <?php
486 $new_window_width = 420;
487 $new_window_height = 240;
488
489 if( isset($Powerpress['new_window_width']) && $Powerpress['new_window_width'] > 0 )
490 $new_window_width = $Powerpress['new_window_width'];
491 else if( isset($Powerpress['new_window_width']) )
492 $new_window_width = 420;
493
494 if( isset($Powerpress['new_window_height']) && $Powerpress['new_window_height'] > 0 )
495 $new_window_height = $Powerpress['new_window_height'];
496 else if( isset($Powerpress['new_window_height']) )
497 $new_window_height = 240;
498
499 if( empty($Powerpress['new_window_nofactor']) )
500 {
501 $new_window_width += 40;
502 $new_window_height += 80;
503 }
504
505 ?>
506 function powerpress_pinw(pinw_url){window.open(pinw_url, 'PowerPressPlayer','toolbar=0,status=0,resizable=1,width=<?php echo ($new_window_width); ?>,height=<?php echo ($new_window_height); ?>'); return false;}
507 //-->
508
509 // tabnab protection
510 window.addEventListener('load', function () {
511 // make all links have rel="noopener noreferrer"
512 document.querySelectorAll('a[target="_blank"]').forEach(link => {
513 link.setAttribute('rel', 'noopener noreferrer');
514 });
515 });
516 </script>
517 <?php
518 }
519 }
520
521 if( !empty($Powerpress['feed_links']) )
522 {
523 if( is_home() ) {
524 $feed_slug = 'podcast';
525 $href = get_feed_link($feed_slug);
526 // Podcast default and channel feed settings
527 $Settings = get_option('powerpress_feed_'. $feed_slug, array());
528
529 if( empty($Settings) && $feed_slug == 'podcast' )
530 $Settings = get_option('powerpress_feed', array()); // Get the main feed settings
531
532 if( empty($Settings['title']) )
533 $Settings['title'] = get_bloginfo_rss('name'); // Get blog title
534
535 // Get the default podcast feed...
536 echo '<link rel="alternate" type="' . feed_content_type() . '" title="' . esc_attr( $Settings['title'] ) . '" href="' . esc_url( $href ) . '" />' . "\n";
537 } else if( is_category() ) {
538
539 $category_id = get_query_var('cat');
540 if( $category_id ) {
541 $Settings = get_option('powerpress_cat_feed_'.$category_id, array() );
542 if( empty($Settings['title']) ) {
543 $Settings['title'] = get_cat_name( $category_id ); // Get category title
544 $Settings['title'] .= ' '. apply_filters( 'document_title_separator', '-' ) .' ';
545 $Settings['title'] .= get_bloginfo_rss('name');
546 }
547 if( empty($Settings['title']) ) {
548 $Settings['title'] = get_bloginfo_rss('name'); // Get blog title, best we can do
549 }
550
551 if( !empty($Settings['feed_redirect_url']) )
552 $Settings['feed_url'] = $Settings['feed_redirect_url'];
553 else if( !empty($Powerpress['cat_casting_podcast_feeds']) )
554 $Settings['feed_url'] = get_category_feed_link($category_id, 'podcast');
555 else
556 $Settings['feed_url'] = get_category_feed_link( $category_id ); // Get category feed URL
557
558 // Get the category podcast feed...
559 echo '<link rel="alternate" type="' . feed_content_type() . '" title="' . esc_attr( $Settings['title'] ) . '" href="' . esc_url( $Settings['feed_url'] ) . '" />' . "\n";
560 }
561 }
562 }
563 }
564
565 add_action('wp_head', 'powerpress_header');
566
567 function powerpress_wp_head_completed()
568 {
569 $GLOBALS['powerpress_wp_head_completed'] = true;
570 }
571
572 add_action('wp_head', 'powerpress_wp_head_completed', 100000);
573
574 function powerpress_exit_on_http_head($return)
575 {
576 if( is_feed() )
577 {
578 // Set the content type for HTTP headers...
579 header('Content-Type: ' . feed_content_type('rss-http') . '; charset=' . get_option('blog_charset'), true);
580
581 // Needs authentication?
582 $GeneralSettings = get_option('powerpress_general', array());
583 if( !empty($GeneralSettings['premium_caps']) )
584 {
585 $feed_slug = get_query_var('feed');
586 $FeedSettings = get_option('powerpress_feed_'.$feed_slug, array());
587 if( !empty($FeedSettings['premium']) )
588 {
589 return false; // Let the logic further into PowerPress authenticate this HEAD request
590 }
591 }
592 }
593 return $return;
594 }
595
596 add_filter('exit_on_http_head', 'powerpress_exit_on_http_head' );
597
598 function powerpress_rss2_ns()
599 {
600 if( !powerpress_is_podcast_feed() )
601 return;
602
603 // Okay, lets add the namespace
604 echo 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"'.PHP_EOL;
605
606 // Add the Podcast Index namespace
607 echo 'xmlns:podcast="https://podcastindex.org/namespace/1.0"'.PHP_EOL;
608
609 if( !defined('POWERPRESS_RAWVOICE_RSS') || POWERPRESS_RAWVOICE_RSS != false )
610 {
611 echo 'xmlns:rawvoice="https://blubrry.com/developer/rawvoice-rss/"'.PHP_EOL;
612 }
613 }
614
615 function SanitizeEmbed($html) {
616 $allowed_attrs = [
617 'src' => true,
618 'width' => true,
619 'height' => true,
620 'frameborder' => true,
621 'allow' => true,
622 'sandbox' => true,
623 'referrerpolicy' => true,
624 'loading' => true,
625 'allowfullscreen' => true,
626 'title' => true,
627 'scrolling' => true,
628 'alt' => true,
629 ];
630 $allowed_html = [
631 'iframe' => $allowed_attrs,
632 'div' => $allowed_attrs,
633 ];
634 return wp_kses($html, $allowed_html, ['http', 'https']);
635 }
636
637
638 function powerpress_check_for_chartable()
639 {
640 $found_chartable = false;
641 $General = get_option('powerpress_general');
642 if (!empty($General['redirect1'])) {
643 if (is_chartable_url($General['redirect1'])) {
644 update_option('powerpress_chartable_check', 'has_chartable');
645 $found_chartable = true;
646 }
647 }
648 if (!empty($General['redirect2'])) {
649 if (is_chartable_url($General['redirect2'])) {
650 update_option('powerpress_chartable_check', 'has_chartable');
651 $found_chartable = true;
652 }
653 }
654 if (!empty($General['redirect3'])) {
655 if (is_chartable_url($General['redirect3'])) {
656 update_option('powerpress_chartable_check', 'has_chartable');
657 $found_chartable = true;
658 }
659 }
660
661 // if we haven't found chartable in the saved redirects, check all media just to be safe
662 if (!$found_chartable) {
663 global $wpdb;
664 $query = "SELECT meta_id, post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE meta_key LIKE \"%enclosure\"";
665 $results_data = $wpdb->get_results($query, ARRAY_A);
666 foreach ($results_data as $idx => $data) {
667 $meta_parts = explode("\n", $data['meta_value']);
668 $post_enclosure_url = $meta_parts[0];
669 if (strpos($post_enclosure_url, 'chrt.fm') !== false || strpos($post_enclosure_url, 'chtbl.com') !== false) {
670 update_option('powerpress_chartable_check', 'has_chartable');
671 $found_chartable = true;
672 }
673 }
674 }
675
676 if (!$found_chartable) {
677 update_option('powerpress_chartable_check', 'no_chartable');
678 }
679 }
680 add_action('powerpress_check_for_chartable_hook', 'powerpress_check_for_chartable');
681
682 if (!function_exists('buildRedirect')) {
683 function buildRedirect($redirects)
684 {
685 $redirect_result = '';
686 for ($x = 3; $x >= 0; $x--) {
687 $key = sprintf('redirect%d', $x);
688 if (!empty($redirects[$key])) {
689 if (preg_match('/^https?:\/\/(.*)$/', trim($redirects[$key]), $matches) == 0)
690 continue;
691
692 if (is_chartable_url($redirects[$key])) {
693 continue;
694 }
695
696 $redirectClean = $matches[1];
697 if (substr($redirectClean, -1, 1) != '/') // Rediercts need to end with a slash /.
698 $redirectClean .= '/';
699
700 if (!empty($redirectClean)) {
701 if (strpos($redirectClean, '/') == 0) // Not a valid redirect URL
702 continue;
703
704 if (!strstr($redirect_result, $redirectClean)) // If the redirect is not already added...
705 $redirect_result = $redirectClean . $redirect_result;
706 }
707 }
708 }
709 return 'https://' . $redirect_result;
710 }
711 }
712
713 if (!function_exists('powerpress_getAccessToken')) {
714 function powerpress_getAccessToken()
715 {
716 // Look at the creds and use the latest access token, if its not the latest refresh it...
717 $creds = get_option('powerpress_creds', array());
718 if (!empty($creds['access_token']) && !empty($creds['access_expires']) && $creds['access_expires'] > time()) { // If access token did not expire
719 return $creds['access_token'];
720 }
721
722 if (!empty($creds['refresh_token']) && !empty($creds['client_id']) && !empty($creds['client_secret'])) {
723
724 // Create new access token with refresh token here...
725 require_once(POWERPRESS_ABSPATH.'/powerpressadmin-auth.class.php');
726 $auth = new PowerPressAuth();
727 $resultTokens = $auth->getAccessTokenFromRefreshToken($creds['refresh_token'], $creds['client_id'], $creds['client_secret']);
728
729 if (!empty($resultTokens['access_token']) && !empty($resultTokens['expires_in'])) {
730 powerpress_save_settings(
731 array(
732 'access_token' => $resultTokens['access_token'],
733 'access_expires' => (time() + $resultTokens['expires_in'] - 10)
734 ),
735 'powerpress_creds'
736 );
737
738 return $resultTokens['access_token'];
739 }
740
741 // false means every api url failed, creds are still good
742 if ($resultTokens === false) {
743 powerpress_page_message_add_error(__('Blubrry Hosting Error: could not reach the authorization service.', 'powerpress'));
744 return false;
745 }
746
747 //if their refresh token is expired, sign them out so they can re-authenticate
748 delete_option('powerpress_creds');
749 powerpress_page_message_add_error(__('Your account has been logged out. Please sign-in again.', 'powerpress'));
750 }
751
752 // If we failed to get credentials, return false
753 return false;
754 }
755 }
756
757 // ===================
758 // BLUBRRY API HELPERS
759 // ===================
760
761 function IPAddressIsPublic($ip) {
762 if (empty($ip) || !is_string($ip))
763 return false;
764
765 // check IP for hostname is not in LAN
766 $longip = ip2long($ip);
767 if ($longip === false) {
768 return false;
769 }
770 if ($longip >= ip2long('192.168.0.0') && $longip <= ip2long('192.168.255.255')) {
771 return false;
772 }
773 // current network 0.0.0.0/8
774 if ($longip >= ip2long('0.0.0.0') && $longip <= ip2long('0.255.255.255')) {
775 return false;
776 }
777 // private 10.0.0.0/8
778 if ($longip >= ip2long('10.0.0.0') && $longip <= ip2long('10.255.255.255')) {
779 return false;
780 }
781 // private 172.16.0.0/12
782 if ($longip >= ip2long('172.16.0.0') && $longip <= ip2long('172.31.255.255')) {
783 return false;
784 }
785 // private 192.168.0.0/16
786 if ($longip >= ip2long('192.168.0.0') && $longip <= ip2long('192.168.255.255')) {
787 return false;
788 }
789 // link-local 169.254.0.0/16
790 if ($longip >= ip2long('169.254.0.0') && $longip <= ip2long('169.254.255.255')) {
791 return false;
792 }
793 // CGN/shared 100.64.0.0/10
794 if ($longip >= ip2long('100.64.0.0') && $longip <= ip2long('100.127.255.255')) {
795 return false;
796 }
797 // multicast 224.0.0.0/4
798 if ($longip >= ip2long('224.0.0.0') && $longip <= ip2long('239.255.255.255')) {
799 return false;
800 }
801 // broadcast
802 if ($longip === ip2long('255.255.255.255')) {
803 return false;
804 }
805
806 return true;
807 }
808
809 function SSRFCheck($url, $feed_slug, $echo_error = false, $media_label = "media url") {
810 // validate url parameter
811 if (!is_string($url) || empty($url)) return false;
812
813 $GeneralSettings = get_option('powerpress_general', []);
814 // Set the arguments for a HEAD request
815 $args = array(
816 'method' => 'HEAD',
817 'redirection' => 0, // Do not follow redirects
818 'headers' => array(
819 'User-Agent' => 'WordPress/PowerPress ' . POWERPRESS_VERSION, // Custom User-Agent header
820 ),
821 );
822 $redirect_count = 0;
823 $ssrf_valid = true;
824 do {
825 $UrlParts = parse_url($url);
826 if (!is_array($UrlParts) || empty($UrlParts['host'])) {
827 $ssrf_valid = false;
828 break;
829 }
830 $media_hostname = $UrlParts['host'];
831
832 // check if hostname is a trusted domain (bypass SSRF checks)
833 $is_trusted = false;
834 if( defined('POWERPRESS_TRUSTED_DOMAINS') ) {
835 foreach( POWERPRESS_TRUSTED_DOMAINS as $trusted_domain ) {
836 if( preg_match('/\.' . preg_quote($trusted_domain, '/') . '$/i', $media_hostname) ) {
837 $is_trusted = true;
838 break;
839 }
840 }
841 }
842
843 if( !$is_trusted ) {
844 $ip = gethostbyname($media_hostname);
845 // if DNS resolution failed, $ip equals hostname
846 if ($ip === $media_hostname) {
847 $ssrf_valid = false;
848 }
849 // check IP for hostname is not in LAN
850 if ($ssrf_valid && empty($GeneralSettings['powerpress_self_hosted_media']) && !IPAddressIsPublic($ip)) {
851 $ssrf_valid = false;
852 }
853 }
854 if ($ssrf_valid) {
855 $response = wp_safe_remote_head($url, $args);
856 if (is_wp_error($response)) {
857 $error_message = $response->get_error_message();
858 if ($error_message) {
859 powerpress_page_message_add_error($error_message);
860 }
861 $httpCode = 500;
862 $headers = array();
863 } else {
864 $headers = wp_remote_retrieve_headers($response);
865 $httpCode = wp_remote_retrieve_response_code($response);
866 }
867 $url = false;
868 if ($httpCode >= 300 && $httpCode < 400) {
869 if (isset($headers['location'])) {
870 $url = $headers['location'];
871 }
872 }
873 } else {
874 $url = false;
875 }
876 $redirect_count++;
877 } while ($url != false && $redirect_count <= 12);
878
879 if (!$ssrf_valid) {
880 $error = __("Invalid {$media_label}. Please ensure that your url is formatted correctly, e.g https://example.com/filename.mp3.", "powerpress");
881 if ($media_label == "media url") {
882 $error .= " " . __("You can still publish this episode, but will need to enter filesize and duration manually.", 'powerpress');
883 }
884 if ($echo_error) {
885 echo "$feed_slug\n";
886 echo $error;
887 } else {
888 powerpress_page_message_add_error($error);
889 }
890 return false;
891 }
892
893 return true;
894 }
895
896 function powerpress_remote_fopen($url, $basic_auth = false, $post_args = array(), $timeout = 15, $custom_request = false, $force_curl=false )
897 {
898 unset($GLOBALS['g_powerpress_remote_error']);
899 unset($GLOBALS['g_powerpress_remote_errorno']);
900
901 if( ($force_curl || (defined('POWERPRESS_CURL') && POWERPRESS_CURL) ) && function_exists( 'curl_init' ) )
902 {
903 $curl = curl_init();
904 curl_setopt($curl, CURLOPT_URL, $url);
905 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
906 curl_setopt($curl, CURLOPT_HEADER, 0);
907
908 if ( version_compare( PHP_VERSION, '5.3.0') < 0 )
909 {
910 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Follow location redirection
911 curl_setopt($curl, CURLOPT_MAXREDIRS, 12); // Location redirection limit
912 }
913 else if ( !ini_get('open_basedir') )
914 {
915 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Follow location redirection
916 curl_setopt($curl, CURLOPT_MAXREDIRS, 12); // Location redirection limit
917 }
918 else
919 {
920 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
921 curl_setopt($curl, CURLOPT_MAXREDIRS, 0 );
922 }
923
924 curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 2 ); // Connect time out
925 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); // The maximum number of seconds to execute.
926 curl_setopt($curl, CURLOPT_USERAGENT, 'Blubrry PowerPress/'.POWERPRESS_VERSION);
927 curl_setopt($curl, CURLOPT_FAILONERROR, true);
928 if( preg_match('/^https:\/\//i', $url) != 0 )
929 {
930 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2 );
931 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true );
932 curl_setopt($curl, CURLOPT_CAINFO, ABSPATH . WPINC . '/certificates/ca-bundle.crt');
933 }
934 // HTTP Authentication
935 if( $basic_auth )
936 {
937 curl_setopt( $curl, CURLOPT_HTTPHEADER, array('Authorization: Basic '.$basic_auth) );
938 }
939 // HTTP Post:
940 if( is_array($post_args) && count($post_args) > 0 )
941 {
942 $post_query = '';
943 foreach( $post_args as $name => $value )
944 {
945 if( $post_query != '' )
946 $post_query .= '&';
947 $post_query .= $name;
948 $post_query .= '=';
949 $post_query .= urlencode($value);
950 }
951 curl_setopt($curl, CURLOPT_POST, 1);
952 curl_setopt($curl, CURLOPT_POSTFIELDS, $post_query);
953 }
954 else if( $custom_request )
955 {
956 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $custom_request);
957 }
958
959 $content = curl_exec($curl);
960 $error = curl_errno($curl);
961 $error_msg = curl_error($curl);
962 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
963
964
965 if (version_compare(PHP_VERSION, '8.0', '<')) {
966 curl_close($curl);
967 } else {
968 unset($curl);
969 }
970 if( $error )
971 {
972 $GLOBALS['g_powerpress_remote_error'] = $error_msg;
973 $GLOBALS['g_powerpress_remote_errorno'] = $http_code;
974 //echo 'error: '.$content;
975
976 $decoded = json_decode($content);
977 if( !empty($decoded) )
978 return $content; // We can still return the error from the server at least
979 return false;
980 }
981 else if( $http_code > 399 )
982 {
983 //echo '40x';
984 $GLOBALS['g_powerpress_remote_error'] = "HTTP $http_code";
985 $GLOBALS['g_powerpress_remote_errorno'] = $http_code;
986 switch( $http_code )
987 {
988 case 400: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Bad Request", 'powerpress'); break;
989 case 401: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Unauthorized (Check that your username and password are correct)", 'powerpress'); break;
990 case 402: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Payment Required", 'powerpress'); break;
991 case 403: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Forbidden", 'powerpress'); break;
992 case 404: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Not Found", 'powerpress'); break;
993 }
994
995 $decoded = json_decode($content);
996 if( !empty($decoded) )
997 return $content; // We can still return the error from the server at least
998 return false;
999 }
1000 return $content;
1001 }
1002
1003 if( $force_curl )
1004 return false; // Do not continue, we wanted to use cURL
1005
1006 $options = array();
1007 $options['timeout'] = $timeout;
1008 $options['user-agent'] = 'Blubrry PowerPress/'.POWERPRESS_VERSION;
1009 if( $basic_auth )
1010 $options['headers']['Authorization'] = 'Basic '.$basic_auth;
1011
1012 if( is_array($post_args) && count($post_args) > 0 )
1013 {
1014 $options['body'] = $post_args;
1015 $response = wp_remote_post( $url, $options );
1016 }
1017 else if($custom_request) {
1018 $options['method'] = $custom_request;
1019 $response = wp_remote_request($url,$options);
1020 }
1021 else
1022 {
1023 $response = wp_remote_get( $url, $options );
1024 }
1025
1026 if ( is_wp_error( $response ) )
1027 {
1028 $GLOBALS['g_powerpress_remote_errorno'] = $response->get_error_code();
1029 $GLOBALS['g_powerpress_remote_error'] = $response->get_error_message();
1030 return false;
1031 }
1032
1033 if( isset($response['response']['code']) && $response['response']['code'] > 399 )
1034 {
1035 $GLOBALS['g_powerpress_remote_error'] = "HTTP ".$response['response']['code'];
1036 $GLOBALS['g_powerpress_remote_errorno'] = $response['response']['code'];
1037 switch( $response['response']['code'] )
1038 {
1039 case 400: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Bad Request", 'powerpress'); break;
1040 case 401: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Unauthorized (Check that your username and password are correct)", 'powerpress'); break;
1041 case 402: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Payment Required", 'powerpress'); break;
1042 case 403: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Forbidden", 'powerpress'); break;
1043 case 404: $GLOBALS['g_powerpress_remote_error'] .= ' '. __("Not Found", 'powerpress'); break;
1044 default: $GLOBALS['g_powerpress_remote_error'] .= ' '.$response['response']['message'];
1045 }
1046 }
1047
1048 return $response['body'];
1049 }
1050
1051 function powerpress_json_decode($value)
1052 {
1053 if (empty($value)) {
1054 return [];
1055 } elseif (is_array($value)) {
1056 return $value;
1057 } else {
1058 return json_decode($value, true);
1059 }
1060 }
1061
1062 // =====================
1063 // ERROR MESSAGE HELPERS
1064 // =====================
1065
1066 function powerpress_message_allowed_html()
1067 {
1068 $attrs = ['href' => [], 'target' => [], 'rel' => [], 'class' => [], 'style' => [], 'id' => []];
1069
1070 return [
1071 'a' => $attrs,
1072 'p' => $attrs,
1073 'div' => $attrs,
1074 'span' => $attrs,
1075 'strong' => $attrs,
1076 'em' => $attrs,
1077 'br' => [],
1078 'details' => $attrs,
1079 'summary' => $attrs,
1080 'pre' => $attrs,
1081 ];
1082 }
1083
1084 function powerpress_page_message_store($html, $prepend = false)
1085 {
1086 $messages = get_option('powerpress_errors');
1087 if (!is_array($messages))
1088 $messages = [];
1089
1090 // jquery re-orders with first as last, so notices go on the front
1091 if ($prepend)
1092 array_unshift($messages, $html);
1093 else
1094 $messages[] = $html;
1095
1096 update_option('powerpress_errors', $messages);
1097 }
1098
1099 function powerpress_page_message_add_error($msg, $classes = 'inline', $debug = [])
1100 {
1101 $msg = wp_kses($msg, powerpress_message_allowed_html());
1102 $msg .= powerpress_message_details($debug);
1103
1104 powerpress_page_message_store("<div class='error powerpress-error {$classes}'>{$msg}</div>");
1105 }
1106
1107 function powerpress_page_message_add_notice($msg, $classes = 'inline')
1108 {
1109 $msg = wp_kses($msg, powerpress_message_allowed_html());
1110
1111 powerpress_page_message_store("<div class='updated fade powerpress-notice {$classes}'>{$msg}</div>", true);
1112 }
1113
1114 function powerpress_page_message_print()
1115 {
1116 $messages = get_option('powerpress_errors');
1117 if (empty($messages) || !is_array($messages))
1118 return;
1119
1120 delete_option('powerpress_errors');
1121
1122 foreach ($messages as $message)
1123 echo $message;
1124 }
1125
1126 function powerpress_message_details($debug)
1127 {
1128 if (empty($debug) || !is_array($debug))
1129 return '';
1130
1131 $details = [];
1132
1133 if (!empty($debug['feed_slug']))
1134 $details[] = 'Feed: ' . esc_html($debug['feed_slug']);
1135
1136 if (!empty($debug['media_file']))
1137 $details[] = 'File: ' . esc_html($debug['media_file']);
1138
1139 if (!empty($GLOBALS['g_powerpress_remote_error']))
1140 $details[] = 'Response: ' . esc_html($GLOBALS['g_powerpress_remote_error']);
1141
1142 if (empty($details))
1143 return '';
1144
1145 $details_text = implode("\n", $details);
1146 $link_text = __('Show Details', 'powerpress');
1147
1148 return "<details class='powerpress-error-details'><summary>{$link_text}</summary><pre>{$details_text}</pre></details>";
1149 }
1150
1151 if (!function_exists('powerpress_clear_blubrry_caches')) {
1152 function powerpress_clear_blubrry_caches($program_keyword = '') {
1153 delete_transient('powerpress_programs_list');
1154 delete_transient('powerpress_programs_api_error');
1155 delete_transient('powerpress_no_stats_programs');
1156 if (!empty($program_keyword)) {
1157 delete_transient('powerpress_program_info_' . md5($program_keyword));
1158 }
1159 }
1160 }
1161
1162 if (!function_exists('powerpress_save_settings')) {
1163 function powerpress_save_settings($SettingsNew = false, $field = 'powerpress_general')
1164 {
1165 if ($field == 'powerpress_taxonomy_podcasting' || $field == 'powerpress_itunes_featured') { // No merging settings for these fields...
1166 update_option($field, $SettingsNew);
1167 return;
1168 }
1169 // Save general settings
1170 if ($SettingsNew) {
1171 $Settings = get_option($field);
1172 if (!is_array($Settings))
1173 $Settings = [];
1174 foreach ($SettingsNew as $key => $value) {
1175 $Settings[$key] = $value;
1176 }
1177 if ($field == 'powerpress_general' && !isset($Settings['timestamp']))
1178 $Settings['timestamp'] = time();
1179
1180 if (isset($Settings['value_recipients'])) {
1181 unset(
1182 $Settings['value_pubkey'],
1183 $Settings['value_split'],
1184 $Settings['value_lightning'],
1185 $Settings['value_custom_key'],
1186 $Settings['value_custom_value'],
1187 $Settings['value_is_fee'],
1188 $Settings['value_fee']
1189 );
1190 }
1191
1192 // Special case fields, if they are empty, we can delete them., this will keep the Settings array uncluttered
1193 if (isset($Settings['feed_links']) && $Settings['feed_links'] == 0) // If set to default value, no need to save it in the database
1194 unset($Settings['feed_links']);
1195 // We can unset settings that are set to their defaults to save database size...
1196 if ($field == 'powerpress_general') {
1197 if (isset($SettingsNew['new_episode_box_flag'])) {
1198 /* Switch the settings over to the actual field name (to fix FCGI mode problem with older versions of PHP.
1199 if (isset($SettingsNew['ebititle'])) {
1200 if ($SettingsNew['ebititle'] == 'false') {
1201 $Settings['new_episode_box_itunes_title'] = 2;
1202 } else {
1203 $Settings['new_episode_box_itunes_title'] = 1;
1204 $SettingsNew['new_episode_box_itunes_title'] = 1;
1205 }
1206 unset($Settings['ebititle']);
1207 }
1208
1209 if (isset($SettingsNew['ebinst'])) {
1210 if ($SettingsNew['ebinst'] == 'false') {
1211 $Settings['new_episode_box_itunes_nst'] = 2;
1212 } else {
1213 $Settings['new_episode_box_itunes_nst'] = 1;
1214 $SettingsNew['new_episode_box_itunes_nst'] = 1;
1215 }
1216 unset($Settings['ebinst']);
1217 }*/
1218
1219 if (!isset($SettingsNew['new_episode_box_embed']))
1220 $Settings['new_episode_box_embed'] = 2;
1221 if (!isset($SettingsNew['new_embed_replace_player']))
1222 $Settings['new_embed_replace_player'] = 2;
1223 if (!isset($SettingsNew['new_episode_box_no_player']))
1224 $Settings['new_episode_box_no_player'] = 2;
1225 if (!isset($SettingsNew['new_episode_box_no_links']))
1226 $Settings['new_episode_box_no_links'] = 2;
1227 if (!isset($SettingsNew['new_episode_box_no_player_and_links']))
1228 $Settings['new_episode_box_no_player_and_links'] = 2;
1229 if (!isset($SettingsNew['new_episode_box_cover_image']))
1230 $Settings['new_episode_box_cover_image'] = 2;
1231 if (!isset($SettingsNew['new_episode_box_player_size']))
1232 $Settings['new_episode_box_player_size'] = 2;
1233 if (!isset($SettingsNew['new_episode_box_subtitle']))
1234 $Settings['new_episode_box_subtitle'] = 2;
1235 if (!isset($SettingsNew['new_episode_box_summary']))
1236 $Settings['new_episode_box_summary'] = 2;
1237 if (!isset($SettingsNew['new_episode_box_author']))
1238 $Settings['new_episode_box_author'] = 2;
1239 if (!isset($SettingsNew['new_episode_box_explicit']))
1240 $Settings['new_episode_box_explicit'] = 2;
1241 if (!isset($SettingsNew['new_episode_box_pci']))
1242 $Settings['new_episode_box_pci'] = 2;
1243 if (!isset($SettingsNew['new_episode_box_block']))
1244 $Settings['new_episode_box_block'] = 2;
1245 if (!isset($SettingsNew['new_episode_box_itunes_image']))
1246 $Settings['new_episode_box_itunes_image'] = 2;
1247 if (!isset($SettingsNew['new_episode_box_order']))
1248 $Settings['new_episode_box_order'] = 2;
1249 if (!isset($SettingsNew['new_episode_box_itunes_title']))
1250 $Settings['new_episode_box_itunes_title'] = 2;
1251 if (!isset($SettingsNew['new_episode_box_itunes_nst']))
1252 $Settings['new_episode_box_itunes_nst'] = 2;
1253 if (!isset($SettingsNew['new_episode_box_gp_explicit']))
1254 $Settings['new_episode_box_gp_explicit'] = 2;
1255 if (!isset($SettingsNew['new_episode_box_feature_in_itunes']))
1256 $Settings['new_episode_box_feature_in_itunes'] = 2;
1257 } elseif (isset($SettingsNew['pp-gen-settings-tabs'])) {
1258 if (!isset($SettingsNew['skip_to_episode_settings']) || empty($SettingsNew['skip_to_episode_settings']))
1259 unset($Settings['skip_to_episode_settings']);
1260 if (!isset($SettingsNew['display_player_excerpt']) || empty($SettingsNew['display_player_excerpt']))
1261 unset($Settings['display_player_excerpt']);
1262 if (!isset($SettingsNew['hide_player_more']) || empty($SettingsNew['hide_player_more']))
1263 unset($Settings['hide_player_more']);
1264 if (!isset($SettingsNew['podcast_embed']) || empty($SettingsNew['podcast_embed']))
1265 unset($Settings['podcast_embed']);
1266 if (!isset($SettingsNew['subscribe_links']) || empty($SettingsNew['subscribe_links']))
1267 unset($Settings['subscribe_links']);
1268 if (!isset($SettingsNew['new_window_no_factor']) || empty($SettingsNew['new_window_no_factor']))
1269 unset($Settings['new_window_no_factor']);
1270 } elseif (isset($SettingsNew['powerpress_bplayer_settings'])) {
1271 unset($Settings['powerpress_bplayer_settings']);
1272 if (!isset($SettingsNew['new_episode_box_itunes_image']) || empty($SettingsNew['new_episode_box_itunes_image']))
1273 $Settings['new_episode_box_itunes_image'] = 2;
1274 if (isset($SettingsNew['bp_episode_image']) && empty($SettingsNew['bp_episode_image']))
1275 unset($Settings['bp_episode_image']);
1276 }
1277
1278
1279 if (isset($Settings['videojs_css_class']) && empty($Settings['videojs_css_class']))
1280 unset($Settings['videojs_css_class']);
1281 if (isset($Settings['cat_casting']) && empty($Settings['cat_casting']))
1282 unset($Settings['cat_casting']);
1283 if (isset($Settings['posttype_podcasting']) && empty($Settings['posttype_podcasting']))
1284 unset($Settings['posttype_podcasting']);
1285 if (isset($Settings['taxonomy_podcasting']) && empty($Settings['taxonomy_podcasting']))
1286 unset($Settings['taxonomy_podcasting']);
1287 if (isset($Settings['playlist_player']) && empty($Settings['playlist_player']))
1288 unset($Settings['playlist_player']);
1289 if (isset($Settings['seo_feed_title']) && empty($Settings['seo_feed_title']))
1290 unset($Settings['seo_feed_title']);
1291 if (isset($Settings['subscribe_feature_email']) && empty($Settings['subscribe_feature_email']))
1292 unset($Settings['subscribe_feature_email']);
1293 if (isset($Settings['poster_image_video']) && empty($Settings['poster_image_video']))
1294 unset($Settings['poster_image_video']);
1295 if (isset($Settings['poster_image_audio']) && empty($Settings['poster_image_audio']))
1296 unset($Settings['poster_image_audio']);
1297 if (isset($Settings['itunes_image_audio']) && empty($Settings['itunes_image_audio']))
1298 unset($Settings['itunes_image_audio']);
1299 if (isset($Settings['network_mode']) && empty($Settings['network_mode']))
1300 unset($Settings['network_mode']);
1301 if (isset($Settings['use_caps']) && empty($Settings['use_caps']))
1302 unset($Settings['use_caps']);
1303 } else // Feed or player settings...
1304 {
1305 if (isset($Settings['itunes_block']) && $Settings['itunes_block'] == 0)
1306 unset($Settings['itunes_block']);
1307 if (isset($Settings['itunes_complete']) && $Settings['itunes_complete'] == 0)
1308 unset($Settings['itunes_complete']);
1309 if (isset($Settings['maximize_feed']) && $Settings['maximize_feed'] == 0)
1310 unset($Settings['maximize_feed']);
1311 if (isset($Settings['unlock_podcast']) && $Settings['unlock_podcast'] == 0)
1312 unset($Settings['unlock_podcast']);
1313 if (isset($Settings['donate_link']) && $Settings['donate_link'] == 0)
1314 unset($Settings['donate_link']);
1315 if (empty($Settings['donate_url']))
1316 unset($Settings['donate_url']);
1317 if (empty($Settings['donate_label']))
1318 unset($Settings['donate_label']);
1319 if (isset($Settings['allow_feed_comments']) && $Settings['allow_feed_comments'] == 0)
1320 unset($Settings['allow_feed_comments']);
1321 if (empty($Settings['episode_itunes_image']))
1322 unset($Settings['episode_itunes_image']);
1323 }
1324
1325 if (!empty($Settings)) {
1326 if (isset($Settings['player'])) {
1327 if ($Settings['player'] == 'blubrrymodern') {
1328 if (!empty($_POST)) {
1329 if (isset($_POST['ModernPlayer']['progress']) && isset($_POST['ModernPlayer']['border']) && isset($_POST['mode'])) {
1330 if ($_POST['mode'] == 'Light' || $_POST['mode'] == 'Dark') {
1331 if (preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i', $_POST['ModernPlayer']['progress']) && preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i', $_POST['ModernPlayer']['border'])) {
1332 $updatedPlayerSettings = ['mode' => $_POST['mode'], 'border' => $_POST['ModernPlayer']['border'], 'progress' => $_POST['ModernPlayer']['progress']];
1333 update_option('powerpress_bplayer', json_encode($updatedPlayerSettings));
1334 }
1335 }
1336 }
1337 }
1338 }
1339 }
1340 }
1341
1342 update_option($field, $Settings);
1343 }
1344 }
1345 }
1346
1347 function powerpress_sync_progad() {
1348
1349 // grab the redirect url prefixes for each feed slug and make an array
1350 $General = get_option('powerpress_general');
1351 // append general redirects to each other (starting with redirect1)
1352 $redirects = array('redirect0'=>'', 'redirect1'=>'', 'redirect2'=>'', 'redirect3'=>'');
1353 if( !empty($General['redirect1']) )
1354 $redirects['redirect1'] = $General['redirect1'];
1355 if( !empty($General['redirect2']) )
1356 $redirects['redirect2'] = $General['redirect2'];
1357 if( !empty($General['redirect3']) )
1358 $redirects['redirect3'] = $General['redirect3'];
1359
1360 // add to redirect array with key 'enclosure'
1361 $main_redirect = buildRedirect($redirects);
1362 $redirect_array = array('enclosure' => $main_redirect);
1363
1364 // then append custom feed redirects to beginning of main feed redirect with _slug:enclosure for each custom feed
1365 // channels
1366 if (!empty($General['custom_feeds'])) {
1367 foreach ($General['custom_feeds'] as $slug => $title) {
1368 $Feed = get_option('powerpress_feed_' . $slug, array());
1369 if (!empty($Feed['redirect'])) {
1370 $redirects['redirect0'] = $Feed['redirect'];
1371 $redirect_array += array('_' . $slug . ':enclosure' => buildRedirect($redirects));
1372 $redirects['redirect0'] = '';
1373 } else {
1374 $redirect_array += array('_' . $slug . ':enclosure' => $main_redirect);
1375 // default stats redirect
1376 }
1377 }
1378 }
1379
1380 // categories
1381 if (!empty($General['custom_cat_feeds'])) {
1382 foreach ($General['custom_cat_feeds'] as $idx => $id) {
1383 $category = get_category($id);
1384 // $category['slug']
1385 $Feed = get_option('powerpress_cat_feed_' . $id, array());
1386 if (!empty($Feed['redirect'])) {
1387 $redirects['redirect0'] = $Feed['redirect'];
1388 $redirect_array += array('_' . $category->slug . ':enclosure' => buildRedirect($redirects));
1389 $redirects['redirect0'] = '';
1390 } else {
1391 $redirect_array += array('_' . $category->slug . ':enclosure' => $main_redirect);
1392 // default stats redirect
1393 }
1394 }
1395 }
1396
1397 // taxonomies
1398 $PowerPressTaxonomies = get_option('powerpress_taxonomy_podcasting', array());
1399 if (!empty($PowerPressTaxonomies)) {
1400 foreach ($PowerPressTaxonomies as $tt_id => $null) {
1401
1402 $taxonomy_type = '';
1403 $term_ID = '';
1404 $tt_id = intval($tt_id); // sanitize for sql
1405
1406 global $wpdb;
1407 $term_info = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = $tt_id", ARRAY_A);
1408 if (!empty($term_info[0]['term_id'])) {
1409 $term_ID = $term_info[0]['term_id'];
1410 $taxonomy_type = $term_info[0]['taxonomy'];
1411 } else {
1412 continue; // we didn't find this taxonomy relationship
1413 }
1414
1415 $Feed = get_option('powerpress_taxonomy_' . $tt_id);
1416 $term_object = get_term( $term_ID, $taxonomy_type, OBJECT, 'edit');
1417 if (!empty($Feed['redirect'])) {
1418 $redirects['redirect0'] = $Feed['redirect'];
1419 $redirect_array += array('_' . $term_object->slug . ':enclosure' => buildRedirect($redirects));
1420 $redirects['redirect0'] = '';
1421 } else {
1422 $redirect_array += array('_' . $term_object->slug . ':enclosure' => $main_redirect);
1423 // default stats redirect
1424 }
1425 }
1426 }
1427
1428 // post types
1429
1430 $post_types = array();
1431 $post_types_wp = get_post_types();
1432 foreach( $post_types_wp as $index => $post_type )
1433 {
1434 if( $post_type == 'redirect_rule' || $post_type == 'attachment' || $post_type == 'nav_menu_item' || $post_type == 'revision' || $post_type == 'action' )
1435 continue;
1436
1437 $post_types[] = $post_type;
1438
1439 }
1440 if (!empty($post_types)) {
1441 foreach ($post_types as $null => $post_type) {
1442 $PostTypeSettingsArray = get_option('powerpress_posttype_' . $post_type, array());
1443 if (empty($PostTypeSettingsArray))
1444 continue;
1445
1446 foreach ($PostTypeSettingsArray as $feed_slug => $Feed) {
1447 if (!empty($Feed['redirect'])) {
1448 $redirects['redirect0'] = $Feed['redirect'];
1449 $redirect_array += array('_' . $feed_slug . ':enclosure' => buildRedirect($redirects));
1450 $redirects['redirect0'] = '';
1451 } else {
1452 $redirect_array += array('_' . $feed_slug . ':enclosure' => $main_redirect);
1453 // default stats redirect
1454 }
1455 }
1456 }
1457 }
1458
1459 // figure out which shows we are enabling/disabling
1460 require_once('powerpressadmin-auth.class.php');
1461 $progad_error = '';
1462 $progad_enable_urls = array();
1463 $progad_disable_urls = array();
1464 $auth = new PowerPressAuth();
1465 $accessToken = powerpress_getAccessToken();
1466 $req_url = sprintf('/2/media/prog_ad_status.json?cache=' . md5(rand(0, 999) . time()));
1467 $req_url .= (defined('POWERPRESS_BLUBRRY_API_QSA') ? '?' . POWERPRESS_BLUBRRY_API_QSA : '');
1468 $req_url .= (defined('POWERPRESS_PUBLISH_PROTECTED') ? '&protected=true' : '');
1469 $progad_enabled_shows = $auth->api($accessToken, $req_url, array(), false, 60 * 30);
1470 if (!$progad_enabled_shows) {
1471 $progad_error = $auth->getLastError();
1472 }
1473 $past_shows_with_progad = get_option('pp_programmatic_enabled_shows');
1474 if (!empty($past_shows_with_progad) && !empty($progad_enabled_shows['programs'])) {
1475 $shows_to_enable = array_diff($progad_enabled_shows['programs'], $past_shows_with_progad);
1476 $shows_to_disable = array_diff($past_shows_with_progad, $progad_enabled_shows['programs']);
1477 } elseif (!empty($past_shows_with_progad) && empty($progad_enabled_shows['programs'])) {
1478 $shows_to_disable = $past_shows_with_progad;
1479 } elseif (!empty($progad_enabled_shows['programs']) && empty($past_shows_with_progad)) {
1480 $shows_to_enable = $progad_enabled_shows['programs'];
1481 }
1482 update_option('pp_programmatic_enabled_shows', $progad_enabled_shows['programs']);
1483
1484 // use the API to get associated URLs for all URLs in any program whose ads were just enabled
1485 if (!empty($shows_to_enable)) {
1486 foreach ($shows_to_enable as $idx => $keyword) {
1487 $req_url = sprintf('/2/media/' . $keyword . '/prog_ad_urls.json?cache=' . md5(rand(0, 999) . time()));
1488 if (defined('POWERPRESS_PROGRAMMATIC_FIX')) {
1489 $req_url .= '&pp_first_release_fix=true';
1490 }
1491 $req_url .= (defined('POWERPRESS_BLUBRRY_API_QSA') ? '?' . POWERPRESS_BLUBRRY_API_QSA : '');
1492 $req_url .= (defined('POWERPRESS_PUBLISH_PROTECTED') ? '&protected=true' : '');
1493 $result_prog = $auth->api($accessToken, $req_url, array(), false, 60 * 30);
1494 if (isset($result_prog['urls']) && is_array($result_prog['urls'])) {
1495 foreach ($result_prog['urls'] as $i => $url_pair) {
1496 // add the redirect to the key before adding this pair
1497 $progad_enable_urls += $url_pair;
1498 }
1499 } elseif (isset($result_prog['message']) && $result_prog['message'] == 'no media') {
1500 // no error--continue
1501 }
1502 else {
1503 $progad_error = $auth->getLastError();
1504 }
1505 }
1506 }
1507
1508 // use the API to get associated URLs for all URLs in any program whose ads were just disabled
1509 if (!empty($shows_to_disable)) {
1510 foreach ($shows_to_disable as $idx => $keyword) {
1511 $req_url = sprintf('/2/media/' . $keyword . '/prog_ad_urls.json?disable=true&cache=' . md5(rand(0, 999) . time()));
1512 $req_url .= (defined('POWERPRESS_BLUBRRY_API_QSA') ? '?' . POWERPRESS_BLUBRRY_API_QSA : '');
1513 $req_url .= (defined('POWERPRESS_PUBLISH_PROTECTED') ? '&protected=true' : '');
1514 $result_prog = $auth->api($accessToken, $req_url, array(), false, 60 * 30);
1515 $progad_error = $auth->getLastError();
1516 if (isset($result_prog['urls']) && is_array($result_prog['urls'])) {
1517 foreach ($result_prog['urls'] as $i => $url_pair) {
1518 // add the redirect to the key before adding this pair
1519 $progad_disable_urls += $url_pair;
1520 }
1521 } elseif (isset($result_prog['message']) && $result_prog['message'] == 'no media') {
1522 // no error--continue
1523 }
1524 else {
1525 $progad_error = $auth->getLastError();
1526 }
1527 }
1528 }
1529
1530 // query the wordpress database to match up the URLs that we need to update
1531 global $wpdb;
1532 $query = "SELECT meta_id, post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE meta_key LIKE \"%enclosure\"";
1533 $results_data = $wpdb->get_results($query, ARRAY_A);
1534 foreach ($results_data as $idx => $data) {
1535 $meta_parts = explode("\n", $data['meta_value']);
1536
1537 if (strpos($meta_parts[0], 'ins.blubrry.com')) {
1538 $parts_array = explode('ins.blubrry.com', $meta_parts[0]);
1539 } else if (strpos($meta_parts[0], 'content3.blubrry.biz')) {
1540 $parts_array = explode('content3.blubrry.biz', $meta_parts[0]);
1541 } else if (strpos($meta_parts[0], 'mc.blubrry.com')) {
1542 $parts_array = explode('mc.blubrry.com', $meta_parts[0]);
1543 } elseif (strpos($meta_parts[0], 'content.blubrry.com')) {
1544 $parts_array = explode('content.blubrry.com', $meta_parts[0]);
1545 } else {
1546 // not Blubrry hosted
1547 continue;
1548 }
1549 $url_without_prefix = $parts_array[1];
1550 $parts_drop_qs = explode('?', $url_without_prefix);
1551 if (!empty($progad_enable_urls) && array_key_exists($parts_drop_qs[0], $progad_enable_urls)) {
1552 // now, if they have a redirect for the feed that this url is in, we need to replace the https://media.blubrry.com/{keyword}/ with those redirects
1553 $progad_url_with_pp_redirect = preg_replace('#https://media.blubrry.com/(.*)/#U', $redirect_array[$data['meta_key']], $progad_enable_urls[$parts_drop_qs[0]]);
1554 // replace the url in the meta_parts array, implode it back together, and update the program meta
1555 $meta_parts[0] = $progad_url_with_pp_redirect;
1556 $new_meta_value = implode("\n", $meta_parts);
1557 update_post_meta($data['post_id'], $data['meta_key'], $new_meta_value);
1558 } else if (!empty($progad_disable_urls) && array_key_exists($parts_drop_qs[0], $progad_disable_urls)) {
1559 $hosting_url_with_pp_redirect = preg_replace('#http(s?)://#U', $redirect_array[$data['meta_key']], $progad_disable_urls[$parts_drop_qs[0]]);
1560 // replace the url in the meta_parts array, implode it back together, and update the program meta
1561 $meta_parts[0] = $hosting_url_with_pp_redirect;
1562 $new_meta_value = implode("\n", $meta_parts);
1563 update_post_meta($data['post_id'], $data['meta_key'], $new_meta_value);
1564 }
1565 }
1566
1567 if ($progad_error) {
1568 update_option("pp_progad_sync_error", __("Error syncing Programmatic Advertising Settings:", 'powerpress') . " " . $progad_error);
1569 } else {
1570 update_option("pp_progad_sync_success", __("Successfully synced Programmatic Advertising Settings from Blubrry.", 'powerpress'));
1571 }
1572 }
1573 add_action('powerpress_sync_progad_hook', 'powerpress_sync_progad');
1574
1575 add_action('rss2_ns', 'powerpress_rss2_ns');
1576 add_action('rss2_ns_powerpress', 'powerpress_rss2_ns');
1577
1578 function powerpress_rss2_head()
1579 {
1580 // disable php notices inside feeds
1581 error_reporting(0);
1582 global $powerpress_feed;
1583
1584 if( !powerpress_is_podcast_feed() )
1585 return; // Not a feed we manage
1586
1587 $feed_slug = get_query_var( 'feed' );
1588 $cat_ID = get_query_var('cat');
1589
1590 $Feed = get_option('powerpress_feed', array()); // Get the main feed settings
1591 $General = get_option('powerpress_general', array());
1592
1593 $feed_url = "";
1594 if( !empty($powerpress_feed['category']) )
1595 {
1596 $CustomFeed = get_option('powerpress_cat_feed_'.$powerpress_feed['category'], array()); // Get the custom podcast feed settings saved in the database
1597 if( !empty($CustomFeed) )
1598 $Feed = powerpress_merge_empty_feed_settings($CustomFeed, $Feed);
1599
1600 if( !empty($General['cat_casting_podcast_feeds']) )
1601 $feed_url = get_category_feed_link($powerpress_feed['category'], 'podcast');
1602 else // Use the old link
1603 $feed_url = get_category_feed_link($powerpress_feed['category']);
1604 }
1605 else if( !empty($powerpress_feed['term_taxonomy_id']) )
1606 {
1607 $CustomFeed = get_option('powerpress_taxonomy_'.$powerpress_feed['term_taxonomy_id'], array()); // Get the taxonomy podcast settings saved in the database
1608 if( !empty($CustomFeed) )
1609 $Feed = powerpress_merge_empty_feed_settings($CustomFeed, $Feed);
1610
1611 global $wpdb;
1612 $term_info = $wpdb->get_results("SELECT term_id, taxonomy FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = " . intval($powerpress_feed['term_taxonomy_id']), ARRAY_A);
1613 $taxonomy_type = $term_info[0]['taxonomy'];
1614 $feed_url = get_term_feed_link($powerpress_feed['term_taxonomy_id'], $taxonomy_type, 'rss2');
1615 }
1616 else if( !empty($powerpress_feed['post_type']) )
1617 {
1618 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$powerpress_feed['post_type'], array()); // Get the post type podcast feed settings saved in the database
1619 if( !empty($PostTypeSettingsArray[ $feed_slug ]) )
1620 {
1621 $CustomFeed = $PostTypeSettingsArray[ $feed_slug ];
1622 $Feed = powerpress_merge_empty_feed_settings($CustomFeed, $Feed, ($feed_slug == 'podcast') );
1623 }
1624
1625 $feed_url = get_post_type_archive_feed_link($powerpress_feed['post_type'], $feed_slug);
1626 }
1627 else if( powerpress_is_custom_podcast_feed() ) // If we're handling a custom podcast feed...
1628 {
1629 $CustomFeed = get_option('powerpress_feed_'.$feed_slug, array()); // Get the custom podcast feed settings saved in the database
1630 $Feed = powerpress_merge_empty_feed_settings($CustomFeed, $Feed, ($feed_slug == 'podcast') );
1631 $feed_url = get_feed_link($feed_slug);
1632 }
1633
1634 if( !isset($Feed['url']) || trim($Feed['url']) == '' )
1635 {
1636 if( is_category() )
1637 $Feed['url'] = get_category_link($cat_ID);
1638 else {
1639
1640 $blogHomepage = get_option('page_for_posts');
1641 if( !empty($blogHomepage) ) {
1642 $Feed['url'] = get_permalink( $blogHomepage );
1643 }
1644
1645 if( empty($Feed['url']) )
1646 $Feed['url'] = get_bloginfo('url');
1647 }
1648 }
1649
1650 $General = get_option('powerpress_general', array());
1651
1652 $feedComment = apply_filters('powerpress_feed_comment', '');
1653 $feedComment = trim($feedComment);
1654 if( !empty($feedComment) )
1655 echo $feedComment.' ';
1656
1657
1658 // Websub!
1659 if(!(defined('POWERPRESS_DISABLE_WEBSUB') && POWERPRESS_DISABLE_WEBSUB )) {
1660 echo "\t<atom:link rel=\"hub\" href=\"https://pubsubhubbub.appspot.com/\" />" . PHP_EOL;
1661 }
1662
1663 // Podcast Index Locked Tag
1664 if (!empty($Feed['pp_enable_feed_lock'])) {
1665 $lockOwner = '';
1666 if (!empty($Feed['email']) && is_email($Feed['email'])) {
1667 $lockOwner = ' owner="' . esc_attr($Feed['email']) . '"';
1668 }
1669 echo "\t<podcast:locked{$lockOwner}>";
1670 if (!empty($Feed['unlock_podcast'])) {
1671 echo "no";
1672 } else {
1673 echo "yes";
1674 }
1675 echo "</podcast:locked>" . PHP_EOL;
1676 }
1677
1678 // add the itunes:new-feed-url tag to feed
1679 if( powerpress_is_custom_podcast_feed() )
1680 {
1681 if( !empty($Feed['itunes_new_feed_url']) )
1682 {
1683 $Feed['itunes_new_feed_url'] = str_replace('&amp;', '&', $Feed['itunes_new_feed_url']);
1684 echo "\t<itunes:new-feed-url>". htmlspecialchars(trim($Feed['itunes_new_feed_url'])) .'</itunes:new-feed-url>'.PHP_EOL;
1685 }
1686 }
1687 else if( !empty($Feed['itunes_new_feed_url']) && ($feed_slug == 'feed' || $feed_slug == 'rss2') ) // If it is the default feed (We don't wnat to apply this to category or tag feeds
1688 {
1689 $Feed['itunes_new_feed_url'] = str_replace('&amp;', '&', $Feed['itunes_new_feed_url']);
1690 echo "\t<itunes:new-feed-url>". htmlspecialchars(trim($Feed['itunes_new_feed_url'])) .'</itunes:new-feed-url>'.PHP_EOL;
1691 }
1692
1693 if( !empty($powerpress_feed['itunes_talent_name']) )
1694 echo "\t<itunes:author>" . esc_html($powerpress_feed['itunes_talent_name']) . '</itunes:author>'.PHP_EOL;
1695
1696 // itunes:explicit is REQUIRED by Apple on channel level
1697 if( !empty($powerpress_feed['explicit']) )
1698 echo "\t".'<itunes:explicit>' . $powerpress_feed['explicit'] . '</itunes:explicit>'.PHP_EOL;
1699
1700 if( !empty($Feed['itunes_block']) )
1701 echo "\t<itunes:block>yes</itunes:block>".PHP_EOL;
1702
1703 if( !empty($Feed['itunes_complete']) )
1704 echo "\t<itunes:complete>yes</itunes:complete>".PHP_EOL;
1705
1706 if( !empty($Feed['itunes_image']) )
1707 {
1708 echo "\t".'<itunes:image href="' . esc_url( powerpress_url_in_feed($Feed['itunes_image']) ) . '" />'.PHP_EOL;
1709 }
1710 else
1711 {
1712 echo "\t".'<itunes:image href="' . powerpress_url_in_feed(powerpress_get_root_url()) . 'itunes_default.jpg" />'.PHP_EOL;
1713 }
1714
1715 if( !empty($Feed['itunes_type']) ) {
1716 echo "\t".'<itunes:type>'. esc_html($Feed['itunes_type']) .'</itunes:type>'.PHP_EOL;
1717 }
1718
1719 if( !empty($Feed['email']) && (!isset($Feed['pp_enable_email']) || $Feed['pp_enable_email'] == 1) && !empty($powerpress_feed['itunes_talent_name']) )
1720 {
1721 echo "\t".'<itunes:owner>'.PHP_EOL;
1722 echo "\t\t".'<itunes:name>' . esc_html($powerpress_feed['itunes_talent_name']) . '</itunes:name>'.PHP_EOL;
1723 echo "\t\t".'<itunes:email>' . esc_html($Feed['email']) . '</itunes:email>'.PHP_EOL;
1724 echo "\t".'</itunes:owner>'.PHP_EOL;
1725 }
1726
1727 if ( !empty($Feed['apple_claim_token'])) {
1728 echo "\t"."<itunes:applepodcastsverify>".esc_html($Feed['apple_claim_token'])."</itunes:applepodcastsverify>".PHP_EOL;
1729 echo "\t".'<podcast:txt purpose="applepodcastsverify">'.esc_html($Feed['apple_claim_token'])."</podcast:txt>".PHP_EOL;
1730 }
1731
1732 if( isset( $Feed['copyright'] ) && trim($Feed['copyright']) !== '' )
1733 {
1734 $Feed['copyright'] = str_replace(array('&copy;', '(c)', '(C)', chr(194) . chr(169), chr(169) ), '&#xA9;', $Feed['copyright']);
1735 echo "\t".'<copyright>'. esc_html($Feed['copyright']) . '</copyright>'.PHP_EOL;
1736 if ( isset( $Feed['copyright_url'] ) && trim($Feed['copyright_url']) !== '' ) {
1737 echo "\t".'<podcast:license url="' . esc_attr(powerpress_url_in_feed($Feed['copyright_url'])) . '">'. esc_html($Feed['copyright']) . '</podcast:license>'.PHP_EOL;
1738 } else {
1739 echo "\t".'<podcast:license>'. esc_html($Feed['copyright']) . '</podcast:license>'.PHP_EOL;
1740 }
1741 }
1742
1743 if (!empty($Feed['txt_tag']) && is_array($Feed['txt_tag']))
1744 {
1745 foreach ($Feed['txt_tag'] as $txt_tag) {
1746 $tag_value = isset($txt_tag['tag']) ? trim($txt_tag['tag']) : '';
1747 if ($tag_value !== '') {
1748 $tag_output = "\t" . '<podcast:txt';
1749
1750 if (!empty($txt_tag['purpose'])) {
1751 $tag_output .= ' purpose="' . esc_attr($txt_tag['purpose']) . '"';
1752 }
1753
1754 $tag_output .= '>' . esc_html($tag_value) . '</podcast:txt>' . PHP_EOL;
1755
1756 echo $tag_output;
1757 }
1758 }
1759 }
1760
1761 $feedMedium = isset($Feed['medium']) && trim((string)$Feed['medium']) !== '' ? $Feed['medium'] : 'podcast';
1762 echo "\t".'<podcast:medium>'. esc_html($feedMedium) . '</podcast:medium>'.PHP_EOL;
1763 $podcast_title_safe = '';
1764 if( version_compare($GLOBALS['wp_version'], '4.4', '<' ) ) {
1765 $podcast_title_safe .= get_bloginfo_rss('name');
1766 }
1767 $podcast_title_safe .= get_wp_title_rss();
1768 if( empty($General['disable_rss_image']) )
1769 {
1770 if(!empty($Feed['itunes_image']) )
1771 {
1772 $rss_image = $Feed['itunes_image'];
1773
1774 echo "\t". '<image>' .PHP_EOL;
1775 echo "\t\t".'<title>' . $podcast_title_safe . '</title>'.PHP_EOL;
1776 echo "\t\t".'<url>' . esc_url( $rss_image ) . '</url>'.PHP_EOL;
1777 echo "\t\t".'<link>'. $Feed['url'] . '</link>' . PHP_EOL;
1778 echo "\t".'</image>' . PHP_EOL;
1779 }
1780 else // Use the default image
1781 {
1782 echo "\t". '<image>' .PHP_EOL;
1783 echo "\t\t".'<title>' . $podcast_title_safe . '</title>'.PHP_EOL;
1784 echo "\t\t".'<url>' . powerpress_get_root_url() . 'rss_default.jpg</url>'.PHP_EOL;
1785 echo "\t\t".'<link>'. $Feed['url'] . '</link>' . PHP_EOL;
1786 echo "\t".'</image>' . PHP_EOL;
1787 }
1788 }
1789
1790 // Handle iTunes categories
1791 $Cat1 = false; $Cat2 = false; $Cat3 = false; $SubCat1 = false; $SubCat2 = false; $SubCat3 = false;
1792 if(defined('POWERPRESS_NEW_APPLE_CATEGORIES') && POWERPRESS_NEW_APPLE_CATEGORIES == true) {
1793 $Categories = powerpress_apple_categories();
1794 for ($i = 1; $i <= 3; $i++) {
1795 if(!empty($Feed['itunes_cat_'.$i]) && empty($Feed['apple_cat_'.$i])) {
1796 $mappings = array('01-00' => '01-00', '01-01' => '01-02', '01-02' => '01-03', '01-03' => '01-04', '01-04' => '01-01',
1797 '01-05' => '01-05', '01-06' => '01-06', '02-00' => '02-00', '02-01' => '12-01', '02-02' => '02-01', '02-03' => '02-03',
1798 '02-04' => '02-00', '02-05' => '02-00', '03-00' => '03-00', '04-00' => '04-00', '04-01' => '04-00', '04-02' => '04-00',
1799 '04-03' => '09-01', '04-04' => '04-03', '04-05' => '04-00', '05-00' => '10-00', '05-01' => '10-02', '05-02' => '10-03',
1800 '05-03' => '10-06', '05-04' => '10-05', '05-05' => '10-05', '06-00' => '06-00', '06-01' => '06-00', '06-02' => '06-00',
1801 '06-03' => '06-00', '06-04' => '06-00', '07-00' => '07-00', '07-01' => '07-01', '07-02' => '07-00', '07-03' => '04-04',
1802 '07-04' => '07-06', '08-00' => '09-00', '09-00' => '11-00', '11-00' => '13-00', '11-01' => '13-01', '11-02' => '13-02',
1803 '11-03' => '13-03', '11-04' => '13-04', '11-05' => '13-05', '11-06' => '13-06', '11-07' => '13-07', '12-00' => '14-00',
1804 '12-01' => '07-03', '12-02' => '14-06', '12-03' => '14-09', '13-00' => '15-00', '13-01' => '08-00', '13-02' => '15-02',
1805 '13-03' => '15-03', '13-04' => '15-04', '14-00' => '16-00', '14-01' => '16-00', '14-02' => '16-00', '14-03' => '16-15',
1806 '14-04' => '16-00', '15-00' => '17-00', '15-01' => '17-00', '15-02' => '12-07', '15-03' => '17-00', '15-04' => '17-00', '16-00' => '19-00');
1807 $Feed['apple_cat_'. $i] = $mappings[$Feed['itunes_cat_'.$i]];
1808
1809 }
1810 }
1811 if (!empty($Feed['apple_cat_1']))
1812 list($Cat1, $SubCat1) = explode('-', $Feed['apple_cat_1']);
1813 if (!empty($Feed['apple_cat_2']))
1814 list($Cat2, $SubCat2) = explode('-', $Feed['apple_cat_2']);
1815 if (!empty($Feed['apple_cat_3']))
1816 list($Cat3, $SubCat3) = explode('-', $Feed['apple_cat_3']);
1817 $googleplay_category_mapping = array(
1818 '01-00' => '01-00',
1819 '02-00' => '02-00',
1820 '03-00' => '03-00',
1821 '04-00' => '04-00',
1822 '05-00' => '13-00',
1823 '06-00' => '06-00',
1824 '07-00' => '07-00',
1825 '08-00' => '13-00',
1826 '09-00' => '08-00',
1827 '10-00' => '05-00',
1828 '11-00' => '09-00',
1829 '12-00' => '10-00',
1830 '13-00' => '11-00',
1831 '14-00' => '12-00',
1832 '15-00' => '13-00',
1833 '16-00' => '14-00',
1834 '17-00' => '15-00',
1835 '18-00' => '13-00',
1836 '19-00' => '16-00',
1837 );
1838 }
1839 else {
1840 $Categories = powerpress_itunes_categories();
1841 if (!empty($Feed['itunes_cat_1']))
1842 list($Cat1, $SubCat1) = explode('-', $Feed['itunes_cat_1']);
1843 if (!empty($Feed['itunes_cat_2']))
1844 list($Cat2, $SubCat2) = explode('-', $Feed['itunes_cat_2']);
1845 if (!empty($Feed['itunes_cat_3']))
1846 list($Cat3, $SubCat3) = explode('-', $Feed['itunes_cat_3']);
1847 $googleplay_category_mapping = array(
1848 '01-00' => '01-00',
1849 '02-00' => '02-00',
1850 '03-00' => '03-00',
1851 '04-00' => '04-00',
1852 '05-00' => '05-00',
1853 '06-00' => '06-00',
1854 '07-00' => '07-00',
1855 '08-00' => '08-00',
1856 '09-00' => '09-00',
1857 '10-00' => '10-00',
1858 '11-00' => '11-00',
1859 '12-00' => '12-00',
1860 '13-00' => '13-00',
1861 '14-00' => '14-00',
1862 '15-00' => '15-00',
1863 '16-00' => '16-00',
1864 );
1865 }
1866
1867 $googleplay_categories = powerpress_googleplay_categories();
1868
1869 $emitted_categories = [];
1870
1871 foreach ([[$Cat1, $SubCat1], [$Cat2, $SubCat2], [$Cat3, $SubCat3]] as [$Cat, $SubCat]) {
1872 if (!$Cat) {
1873 continue;
1874 }
1875
1876 $CatDesc = $Categories[$Cat.'-00'] ?? '';
1877 if ($CatDesc === '') {
1878 continue;
1879 }
1880
1881 $SubCatDesc = $Categories[$Cat.'-'.$SubCat] ?? '';
1882 if ($SubCat == '00') {
1883 $SubCatDesc = '';
1884 }
1885
1886 if (isset($emitted_categories[$CatDesc.'|'.$SubCatDesc])) {
1887 continue;
1888 }
1889 $emitted_categories[$CatDesc.'|'.$SubCatDesc] = true;
1890
1891 echo "\t".'<itunes:category text="'. esc_attr($CatDesc);
1892 if ($SubCatDesc !== '') {
1893 echo '">' . PHP_EOL . "\t\t" . '<itunes:category text="' . esc_attr($SubCatDesc) . '" />' . PHP_EOL;
1894 // End this category set
1895 echo "\t".'</itunes:category>'.PHP_EOL;
1896 } else {
1897 echo '" />'.PHP_EOL;
1898 }
1899 }
1900 // End Handle iTunes categories
1901
1902 // RawVoice RSS Tags
1903 if( !defined('POWERPRESS_RAWVOICE_RSS') || POWERPRESS_RAWVOICE_RSS != false )
1904 {
1905 if( !empty($Feed['parental_rating']) )
1906 echo "\t<rawvoice:rating>". $Feed['parental_rating'] ."</rawvoice:rating>".PHP_EOL;
1907
1908 $locations = [];
1909 if (!empty($Feed['location']) && is_array($Feed['location'])) {
1910 $first = reset($Feed['location']);
1911 if (is_array($first) && (isset($first['address']) || isset($first['location']))) {
1912 // nested format
1913 foreach ($Feed['location'] as $loc) {
1914 if (empty($loc) || !is_array($loc)) continue;
1915 $address = $loc['address'] ?? $loc['location'] ?? '';
1916 if ($address === '') continue;
1917 $locations[] = [
1918 'address' => $address,
1919 'pci_geo' => $loc['pci_geo'] ?? $loc['geo'] ?? '',
1920 'pci_osm' => $loc['pci_osm'] ?? $loc['osm'] ?? '',
1921 'pci_rel' => $loc['pci_rel'] ?? $loc['rel'] ?? '1',
1922 'pci_country' => $loc['pci_country'] ?? $loc['country'] ?? ''
1923 ];
1924 }
1925 } else {
1926 // paralellized (legacy)
1927 foreach ((array)$Feed['location'] as $i => $addr) {
1928 if ($addr === '' || $addr === null) continue;
1929 $locations[] = [
1930 'address' => $addr,
1931 'pci_geo' => $Feed['pci_geo'][$i] ?? $Feed['geo'][$i] ?? '',
1932 'pci_osm' => $Feed['pci_osm'][$i] ?? $Feed['osm'][$i] ?? '',
1933 'pci_rel' => $Feed['pci_rel'][$i] ?? $Feed['rel'][$i] ?? '1',
1934 'pci_country' => $Feed['pci_country'][$i] ?? $Feed['country'][$i] ?? '',
1935 ];
1936 }
1937 }
1938 }
1939
1940 foreach ($locations as $location) {
1941 $address = trim((string)($location['address'] ?? ''));
1942 if ($address === '') continue;
1943
1944 echo "\t<rawvoice:location>" . htmlspecialchars($address) . "</rawvoice:location>\n";
1945 echo "\t<podcast:location";
1946
1947 if (!empty($location['pci_geo'])) echo ' geo="' . htmlspecialchars($location['pci_geo']) . '"';
1948 if (!empty($location['pci_osm'])) echo ' osm="' . htmlspecialchars($location['pci_osm']) . '"';
1949 if (!empty($location['pci_rel'])) echo ' rel="' . ($location['pci_rel'] == '1' ? 'subject' : 'creator') . '"';
1950 if (!empty($location['pci_country'])) echo ' country="' . htmlspecialchars($location['pci_country']) . '"';
1951
1952 echo '>' . htmlspecialchars($address) . "</podcast:location>\n";
1953 }
1954
1955 // ==========================================
1956 // UPDATE FREQUENCY <podcast:updateFrequency>
1957 // <rawvoice:frequency>
1958 // ==========================================
1959
1960 $freq_data = powerpress_normalize_update_frequency(
1961 $Feed['update_frequency'] ?? null,
1962 $Feed['update_frequency_week'] ?? null,
1963 $Feed['update_frequency_month'] ?? null
1964 );
1965
1966 if (!empty($freq_data['freq'])) {
1967 // HANDLE RRULE ATTR
1968 $rrule_parts = ['FREQ=' . $freq_data['freq']];
1969 if (!empty($freq_data['byday']))
1970 $rrule_parts[] = "BYDAY={$freq_data['byday']}";
1971
1972 if (!empty($freq_data['bymonth']))
1973 $rrule_parts[] = "BYMONTH={$freq_data['bymonth']}";
1974
1975 if (!empty($freq_data['bymonthday']))
1976 $rrule_parts[] = "BYMONTHDAY={$freq_data['bymonthday']}";
1977
1978 if (!empty($freq_data['count']))
1979 $rrule_parts[] = 'COUNT=' . (int) $freq_data['count'];
1980
1981 if (!empty($freq_data['interval']))
1982 $rrule_parts[] = 'INTERVAL=' . (int) $freq_data['interval'];
1983
1984 if (!empty($freq_data['until']))
1985 $rrule_parts[] = "UNTIL={$freq_data['until']}";
1986
1987 $rrule = implode(';', $rrule_parts);
1988
1989 // HANDLE DISPLAY FIELD
1990 $display = $freq_data['display'] ?? ucfirst(strtolower($freq_data['freq']));
1991 $attrs = ' rrule="' . esc_attr($rrule) . '"';
1992
1993 // HANDLE COMPLETE ATTR
1994 if (!empty($Feed['itunes_complete'])) {
1995 $attrs .= ' complete="true"';
1996 }
1997
1998 // HANDLE DTSTART ATTR
1999 if (!empty($Feed['dtstart'])) {
2000 $attrs .= ' dtstart="' . esc_attr($Feed['dtstart']) . '"';
2001 }
2002
2003 // OUTPUT PCI TAG
2004 echo "\t<podcast:updateFrequency{$attrs}>" . esc_html($display) . "</podcast:updateFrequency>\n";
2005
2006 // OUTPUT RV TAG
2007 if (!empty($Feed['frequency']) && in_array($freq_data['freq'], ['DAILY', 'WEEKLY', 'MONTHLY'], true)) {
2008 echo "\t<rawvoice:frequency>" . htmlspecialchars($Feed['frequency']) . "</rawvoice:frequency>\n";
2009 }
2010 }
2011
2012 // =====================
2013 // BLOCK <podcast:block>
2014 // <itunes:block>
2015 // =====================
2016
2017 if (isset($Feed['block'])) {
2018 if (isset($Feed['block_all']) && $Feed['block_all'] != 0) {
2019 echo "\t<podcast:block>yes</podcast:block>\n";
2020 echo "\t<itunes:block>yes</itunes:block>\n";
2021 } else {
2022 // block individuals
2023 $blockListStr = $Feed['block_list'] ?? '';
2024 $blockList = !empty($blockListStr) ? explode(';', $blockListStr) : [];
2025
2026 foreach ($blockList as $block) {
2027 if ($block != '') {
2028 echo "\t<podcast:block id=\"" . esc_attr($block) . "\">yes</podcast:block>\n";
2029 if ($block == 'apple') {
2030 echo "\t<itunes:block>yes</itunes:block>\n";
2031 }
2032 }
2033 }
2034 }
2035 }
2036
2037 if (isset($Feed['remote_items']) && !empty($Feed['remote_items'])) {
2038 $existingRemoteItems = $Feed['remote_items'];
2039 $existingPodrollItems = [];
2040 $existingFeedItems = [];
2041
2042 foreach ($existingRemoteItems as $remoteItem) {
2043 $isPodroll = $remoteItem['podroll'] ?? 0;
2044 if ($isPodroll == 1)
2045 $existingPodrollItems[] = $remoteItem;
2046 else
2047 $existingFeedItems[] = $remoteItem;
2048 }
2049
2050 if (!empty($existingPodrollItems)) {
2051 echo "\t<podcast:podroll>\n";
2052
2053 foreach ($existingPodrollItems as $remoteItem) {
2054 $feedGuid = $remoteItem['feed_guid'];
2055 echo "\t\t<podcast:remoteItem feedGuid=\"" . esc_attr($feedGuid) . "\"";
2056
2057 if (!empty($remoteItem['item_guid'])) {
2058 echo " itemGuid=\"" . esc_attr($remoteItem['item_guid']) . "\"";
2059 }
2060
2061 if (!empty($remoteItem['item_link'])) {
2062 echo " feedUrl=\"" . esc_attr($remoteItem['item_link']) . "\"";
2063 }
2064
2065 if (!empty($remoteItem['medium'])) {
2066 echo " medium=\"" . esc_attr($remoteItem['medium']) . "\"";
2067 }
2068
2069 if (!empty($remoteItem['item_title'])) {
2070 echo " title=\"" . esc_attr($remoteItem['item_title']) . "\"";
2071 }
2072 echo " />\n";
2073 }
2074 echo "\t</podcast:podroll>\n";
2075 }
2076
2077 foreach ($existingFeedItems as $remoteItem) {
2078 $feedGuid = $remoteItem['feed_guid'];
2079 $attrStr = "feedGuid=\"" . esc_attr($feedGuid) . "\"";
2080
2081 if (!empty($remoteItem['item_guid'])) {
2082 $itemGuid = $remoteItem['item_guid'] ?? '';
2083 $attrStr .= " itemGuid=\"" . esc_attr($itemGuid) . "\"";
2084 }
2085 if (!empty($remoteItem['item_link'])) {
2086 $attrStr .= " feedUrl=\"" . esc_attr($remoteItem['item_link']) . "\"";
2087 }
2088 if (!empty($remoteItem['medium'])) {
2089 $attrStr .= " medium=\"" . esc_attr($remoteItem['medium']) . "\"";
2090 }
2091 if (!empty($remoteItem['item_title'])) {
2092 $attrStr .= " title=\"" . esc_attr($remoteItem['item_title']) . "\"";
2093 }
2094
2095 echo "\t<podcast:remoteItem $attrStr />\n";
2096 }
2097 }
2098
2099
2100 $value_recipients = [];
2101 if (!empty($Feed['value_recipients']) && is_array($Feed['value_recipients'])) {
2102 foreach ($Feed['value_recipients'] as $value_recipient) {
2103 if (empty($value_recipient['pubkey']) || empty($value_recipient['split'])) {
2104 continue;
2105 }
2106
2107 $value_recipients[] = [
2108 'lightning' => $value_recipient['lightning'] ?? $value_recipient['name'] ?? '',
2109 'split' => $value_recipient['split'],
2110 'address' => $value_recipient['pubkey'],
2111 'customKey' => $value_recipient['custom_key'] ?? $value_recipient['customKey'] ?? '',
2112 'customValue' => $value_recipient['custom_value'] ?? $value_recipient['customValue'] ?? '',
2113 'fee' => (isset($value_recipient['fee']) && $value_recipient['fee'] === 'true') ? 'true' : 'false'
2114 ];
2115 }
2116 } elseif (!empty($Feed['value_pubkey']) && !empty($Feed['value_split'])) {
2117 foreach ($Feed['value_pubkey'] as $id => $pubkey) {
2118 if (empty($pubkey)) continue;
2119 $value_recipients[] = [
2120 'lightning' => $Feed['value_lightning'][$id] ?? '',
2121 'split' => $Feed['value_split'][$id],
2122 'address' => $pubkey,
2123 'customKey' => $Feed['value_custom_key'][$id] ?? '',
2124 'customValue' => $Feed['value_custom_value'][$id] ?? '',
2125 'fee' => isset($Feed['value_is_fee'][$id]) && $Feed['value_is_fee'][$id] ? 'true' : 'false'
2126 ];
2127 }
2128 }
2129
2130 if (!empty($value_recipients)) {
2131 $value_recipients[] = [
2132 'lightning' => 'blubrry@getalby.com',
2133 'split' => 3,
2134 'address' => '03b8a595e4d8e19efa8faa3fbe2524b0d39f7c812415ff831d38a466a4a1bc888a',
2135 'customKey' => '696969',
2136 'customValue' => 'qAHJuqKLmMhTNFualcIj',
2137 'fee' => 'true'
2138 ];
2139
2140 echo "\t".'<podcast:value type="lightning" method="keysend" suggested="0.00000005000">'."\n";
2141
2142 foreach ($value_recipients as $value_recipient) {
2143 $attrStr = 'type="node" split="'.(int)$value_recipient['split'].'" address="'.htmlspecialchars($value_recipient['address']).'"';
2144
2145 if (!empty($value_recipient['lightning'])) {
2146 $attrStr .= ' name="'.htmlspecialchars($value_recipient['lightning']).'"';
2147 }
2148
2149 if (!empty($value_recipient['customKey'])) {
2150 $attrStr .= ' customKey="'.htmlspecialchars($value_recipient['customKey']).'"';
2151 }
2152
2153 if (!empty($value_recipient['customValue'])) {
2154 $attrStr .= ' customValue="'.htmlspecialchars($value_recipient['customValue']).'"';
2155 }
2156
2157 if ($value_recipient['fee'] === 'true') {
2158 $attrStr .= ' fee="true"';
2159 }
2160
2161 echo "\t\t"."<podcast:valueRecipient $attrStr/>\n";
2162 }
2163
2164 echo "\t".'</podcast:value>'."\n";
2165 }
2166
2167 // DONATE
2168 if( !empty($Feed['donate_link']) && !empty($Feed['donate_url']) ) {
2169 echo "\t<rawvoice:donate href=\"" . htmlspecialchars($Feed['donate_url']) . "\">" . htmlspecialchars((empty($Feed['donate_label']) ? '' : $Feed['donate_label'])) . "</rawvoice:donate>" . PHP_EOL;
2170 }
2171 if( !empty($Feed['donate_url']) ) {
2172 echo "\t<podcast:funding url=\"" . htmlspecialchars($Feed['donate_url']) . "\">" . htmlspecialchars((empty($Feed['donate_label']) ? '' : $Feed['donate_label'])) . "</podcast:funding>" . PHP_EOL;
2173 }
2174
2175 // CREDITS
2176 // nested
2177 $channel_credits = [];
2178 if (!empty($Feed['credits'])) {
2179 $channel_credits = $Feed['credits'];
2180 }
2181 // parallelized (legacy)
2182 else if ( !empty($Feed['person_names']) ) {
2183 $personNames = $Feed['person_names'];
2184 $personRoles = $Feed['person_roles'] ?? [];
2185 $personURLs = $Feed['person_urls'] ?? [];
2186 $linkURLs = $Feed['link_urls'] ?? [];
2187
2188 foreach ($personNames as $i => $name) {
2189 if ($name == '') continue;
2190 $channel_credits[] = [
2191 'name' => $name,
2192 'role' => $personRoles[$i] ?? '',
2193 'person_url' => $personURLs[$i] ?? '',
2194 'link_url' => $linkURLs[$i] ?? ''
2195 ];
2196 }
2197 }
2198
2199 // RSS output
2200 foreach ($channel_credits as $credit) {
2201 if (!isset($credit['name']) || trim((string)$credit['name']) === '') continue;
2202
2203 echo "\t<podcast:person";
2204 if (!empty($credit['role'])) {
2205 echo " role=\"" . htmlspecialchars($credit['role']) . "\"";
2206 }
2207 if (!empty($credit['person_url'])) {
2208 $personImg = esc_url($credit['person_url']);
2209 if ($personImg !== '') {
2210 echo " img=\"" . $personImg . "\"";
2211 }
2212 }
2213 if (!empty($credit['link_url'])) {
2214 $personHref = esc_url($credit['link_url']);
2215 if ($personHref !== '') {
2216 echo " href=\"" . $personHref . "\"";
2217 }
2218 }
2219 echo ">" . htmlspecialchars($credit['name']) . "</podcast:person>" . PHP_EOL;
2220
2221 }
2222
2223 echo "\t<podcast:podping usesPodping=\"true\" />" . PHP_EOL;
2224
2225 require_once('uuid5.class.php');
2226 // This will be the same every time, but moved this logic into here to expedite removal of duplicate GUIDs
2227 $guidFeedURL = str_replace("http://", "", str_replace("https://", "", $feed_url));
2228 $guidFeedURL = rtrim($guidFeedURL,"/");
2229 $guid = UUID::v5('ead4c236-bf58-58c6-a2c6-a6b28d128cb6', $guidFeedURL);
2230 if (UUID::is_valid($guid)) {
2231 $Feed['podcast_guid'] = $guid;
2232 }
2233 $guidOverride = isset($Feed['guid_override']) ? trim($Feed['guid_override']) : '';
2234 if (!empty($Feed['guid_override_check']) && $guidOverride !== '' && UUID::is_valid($guidOverride))
2235 echo "\t<podcast:guid>".esc_html($guidOverride)."</podcast:guid>" . PHP_EOL;
2236 elseif (isset($Feed['podcast_guid']) && UUID::is_valid($Feed['podcast_guid']) && $guidFeedURL != '')
2237 echo "\t<podcast:guid>".$Feed['podcast_guid']."</podcast:guid>" . PHP_EOL;
2238
2239 if (isset($Feed['live_item']) && $Feed['live_item']['enabled'] == '1' && UUID::is_valid($Feed['live_item']['guid'])) {
2240 $liveItem = $Feed['live_item'];
2241 $tzName = '';
2242
2243 // 1) RESOLVE TIMEZONE
2244 if (!empty($liveItem['timezone'])) {
2245 $tzName = timezone_name_from_abbr($liveItem['timezone']);
2246 if ($tzName === false && in_array($liveItem['timezone'], timezone_identifiers_list(), true))
2247 $tzName = $liveItem['timezone'];
2248 }
2249
2250 // UTC FALLBACK
2251 if (empty($tzName))
2252 $tzName = 'UTC';
2253
2254 // 2) PARSE START/END: both stay false if either is unparseable
2255 $startDate = false;
2256 $endDate = false;
2257
2258 if (!empty($liveItem['start_date_time']) && !empty($liveItem['end_date_time'])) {
2259 try {
2260 $liveTimeZone = new DateTimeZone($tzName);
2261 $startDate = new DateTime(str_replace('T', ' ', $liveItem['start_date_time']), $liveTimeZone);
2262 $endDate = new DateTime(str_replace('T', ' ', $liveItem['end_date_time']), $liveTimeZone);
2263 } catch (Exception $e) {
2264 $startDate = false;
2265 $endDate = false;
2266 }
2267 }
2268
2269 // 3) EMIT LIVE ITEM, skip when no dates
2270 if ($startDate !== false && $endDate !== false) {
2271 $status = strtolower($liveItem['status']);
2272 $start = $startDate->format('c');
2273 $end = $endDate->format('c');
2274 echo "\t<podcast:liveItem status=\"".esc_attr($status)."\" start=\"".esc_attr($start)."\" end=\"".esc_attr($end)."\">" . PHP_EOL;
2275 echo "\t\t<title>".esc_html($liveItem['title'])."</title>" . PHP_EOL;
2276 echo "\t\t<guid isPermaLink=\"false\">".esc_html($liveItem['guid'])."</guid>" . PHP_EOL;
2277
2278 if (!empty($liveItem['description']))
2279 echo "\t\t<description>".esc_html($liveItem['description'])."</description>" . PHP_EOL;
2280
2281 if (!empty($liveItem['coverart_link']))
2282 echo "\t\t<podcast:images srcset=\"".esc_url($liveItem['cover_art'] ?? '')." 1400w\" />" . PHP_EOL;
2283
2284 // 4) STREAM + LINKS
2285 $EnclosureAttr = 'url="'.esc_url($liveItem['stream_link']).'" ';
2286 $EnclosureAttr .= 'length="5242880" ';
2287 $EnclosureAttr .= 'type="'.esc_attr($liveItem['stream_type']).'"';
2288
2289 echo "\t\t<enclosure $EnclosureAttr />" . PHP_EOL;
2290 echo "\t\t<podcast:alternateEnclosure type=\"".esc_attr($liveItem['stream_type'])."\">" . PHP_EOL;
2291 echo "\t\t\t<podcast:source uri=\"".esc_url($liveItem['stream_link'])."\" />" . PHP_EOL;
2292 echo "\t\t</podcast:alternateEnclosure>" . PHP_EOL;
2293
2294 if (!empty($liveItem['episode_link']))
2295 echo "\t\t<link>".esc_url($liveItem['episode_link'])."</link>" . PHP_EOL;
2296
2297 echo "\t\t<podcast:contentLink href=\"".esc_url($liveItem['fallback_link'])."\">Listen Live!</podcast:contentLink>" . PHP_EOL;
2298 echo "\t\t<podcast:timezone>".esc_html($liveItem['timezone'] ?? '')."</podcast:timezone>" . PHP_EOL;
2299 echo "\t</podcast:liveItem>" . PHP_EOL;
2300 }
2301 }
2302
2303 if( !empty($Feed['itunes_url']) || !empty($Feed['tunein_url']) || !empty($Feed['spotify_url']) ) {
2304 echo "\t<rawvoice:subscribe feed=\"";
2305 self_link();
2306 echo '"';
2307
2308 // Subscribe page // empty($FeedSettings['subscribe_page_link_href']) && empty($FeedSettings['subscribe_page_link_id'])
2309 if( !empty($Feed['subscribe_page_link_id']) ) {
2310 $link = get_page_link($Feed['subscribe_page_link_id']);
2311 if( !empty($link) ) {
2312 echo " html=\"". htmlspecialchars( $link ) .'"';
2313 }
2314 } else if( !empty($Feed['subscribe_page_link_href']) ) {
2315 echo " html=\"". htmlspecialchars( $Feed['subscribe_page_link_href'] ) .'"';
2316 }
2317
2318 if( !empty($Feed['itunes_url']) )
2319 echo " itunes=\"". htmlspecialchars( $Feed['itunes_url'] ) .'"';
2320 if( !empty($Feed['tunein_url']) )
2321 echo " tunein=\"". htmlspecialchars( $Feed['tunein_url'] ) .'"';
2322 if( !empty($Feed['spotify_url']) )
2323 echo " spotify=\"". htmlspecialchars( $Feed['spotify_url'] ) .'"';
2324 if( !empty($Feed['amazon_url']) )
2325 echo " amazon_music=\"". htmlspecialchars( $Feed['amazon_url'] ) .'"';
2326 if( !empty($Feed['pcindex_url']) )
2327 echo " pcindex=\"". htmlspecialchars( $Feed['pcindex_url'] ) .'"';
2328 if( !empty($Feed['iheart_url']) )
2329 echo " iheart=\"". htmlspecialchars( $Feed['iheart_url'] ) .'"';
2330 if( !empty($Feed['pandora_url']) )
2331 echo " pandora=\"". htmlspecialchars( $Feed['pandora_url'] ) .'"';
2332 if( !empty($Feed['deezer_url']) )
2333 echo " deezer=\"". htmlspecialchars( $Feed['deezer_url'] ) .'"';
2334 if( !empty($Feed['jiosaavn_url']) )
2335 echo " jiosaavn=\"". htmlspecialchars( $Feed['jiosaavn_url'] ) .'"';
2336 if( !empty($Feed['podchaser_url']) )
2337 echo " podchaser=\"". htmlspecialchars( $Feed['podchaser_url'] ) .'"';
2338 if( !empty($Feed['gaana_url']) )
2339 echo " gaana=\"". htmlspecialchars( $Feed['gaana_url'] ) .'"';
2340 if( !empty($Feed['anghami_url']) )
2341 echo " anghami=\"". htmlspecialchars( $Feed['anghami_url'] ) .'"';
2342 if( !empty($Feed['youtube_url']) )
2343 echo " youtube=\"". htmlspecialchars( $Feed['youtube_url'] ) .'"';
2344 echo "></rawvoice:subscribe>".PHP_EOL;
2345 }
2346 }
2347 }
2348
2349 add_action('rss2_head', 'powerpress_rss2_head');
2350 add_action('rss2_head_powerpress', 'powerpress_rss2_head');
2351
2352 function powerpress_rss2_item()
2353 {
2354 global $post, $powerpress_feed;
2355
2356 // disable php notices inside feeds
2357 error_reporting(0);
2358
2359 // are we processing a feed that powerpress should handle
2360 if( !powerpress_is_podcast_feed() )
2361 return;
2362
2363 if( function_exists('post_password_required') )
2364 {
2365 if( post_password_required($post) )
2366 return;
2367 }
2368
2369 // Check and see if we're working with a podcast episode
2370 $custom_enclosure = false;
2371 if( powerpress_is_custom_podcast_feed() && get_query_var('feed') !== 'podcast' && !is_category() && !is_tax() && !is_tag() )
2372 {
2373 $EpisodeData = powerpress_get_enclosure_data($post->ID, get_query_var('feed') );
2374 $custom_enclosure = true;
2375 }
2376 else
2377 {
2378 $EpisodeData = powerpress_get_enclosure_data($post->ID, 'podcast');
2379 if( !$EpisodeData && !empty($powerpress_feed['process_podpress']) )
2380 {
2381 $EpisodeData = powerpress_get_enclosure_data_podpress($post->ID);
2382 $custom_enclosure = true;
2383 }
2384 }
2385
2386 // No episode data to include
2387 if( empty($EpisodeData) || empty($EpisodeData['url']) || $EpisodeData['url'] == 'no' )
2388 return;
2389
2390 // If enclosure not added, check to see why...
2391 if( defined('POWERPRESS_ENCLOSURE_FIX') && POWERPRESS_ENCLOSURE_FIX && !$custom_enclosure && $GLOBALS['powerpress_rss_enclosure_post_id'] != $post->ID )
2392 {
2393 $enclosure_in_wp = apply_filters('rss_enclosure', '<enclosure url="' . trim(htmlspecialchars($EpisodeData['url']) . '" length="' . $EpisodeData['size'] . '" type="' . $EpisodeData['type'] . '" />' . "\n") );
2394 if( !$enclosure_in_wp )
2395 $custom_enclosure = true;
2396 }
2397
2398 // Lets print the enclosure tag
2399 if( $custom_enclosure ) // We need to add the enclosure tag here...
2400 {
2401 if (!is_numeric($EpisodeData['size']) || $EpisodeData['size'] <= 0)
2402 $EpisodeData['size'] = 5242880; // Use the dummy 5MB size since we don't have a size to quote
2403
2404 // encode htmlspecialchars if necessary
2405 $decoded = htmlspecialchars_decode($EpisodeData['url']);
2406 if (strlen($decoded) != strlen($EpisodeData['url'])) {
2407 // already encoded
2408 $media_url = $EpisodeData['url'];
2409 } else {
2410 // might need encoded/no risk of double encoding
2411 $media_url = htmlspecialchars($EpisodeData['url']);
2412 }
2413
2414 echo "\t\t" . sprintf('<enclosure url="%s" length="%d" type="%s" />%s',
2415 powerpress_url_in_feed(trim($media_url)),
2416 trim($EpisodeData['size']),
2417 trim($EpisodeData['type']),
2418 PHP_EOL);
2419 }
2420
2421 if (!empty($EpisodeData['alternate_enclosure'])) {
2422 $episode_str = '';
2423
2424 foreach ($EpisodeData['alternate_enclosure'] as $alternate_enclosure) {
2425
2426 $alt_type = trim($alternate_enclosure['type'] ?? '');
2427 if ($alt_type === '') {
2428 continue;
2429 }
2430
2431 $episode_str = '';
2432 $episode_str .= "\t\t<podcast:alternateEnclosure ";
2433 // support both 'length' (new) and 'size' (legacy) field names
2434 $alt_length = $alternate_enclosure['length'] ?? $alternate_enclosure['size'] ?? 0;
2435 if (!empty($alt_length) && $alt_length > 0) {
2436 $episode_str .= ' length="' . esc_attr($alt_length) . '"';
2437 }
2438
2439 $episode_str .= ' type="' . esc_attr($alt_type) . '"';
2440
2441 if (!empty($alternate_enclosure['height'])) {
2442 $episode_str .= ' height="' . esc_attr($alternate_enclosure['height']) . '"';
2443 }
2444
2445 if (!empty($alternate_enclosure['title'])) {
2446 $episode_str .= ' title="' . esc_attr($alternate_enclosure['title']) . '"';
2447 }
2448
2449 if (!empty($alternate_enclosure['lang'])) {
2450 $episode_str .= ' lang="' . esc_attr($alternate_enclosure['lang']) . '"';
2451 }
2452
2453 if (!empty($alternate_enclosure['rel'])) {
2454 $episode_str .= ' rel="' . esc_attr($alternate_enclosure['rel']) . '"';
2455 }
2456
2457 if (!empty($alternate_enclosure['codecs'])) {
2458 $episode_str .= ' codecs="' . esc_attr($alternate_enclosure['codecs']) . '"';
2459 }
2460
2461 if (!empty($alternate_enclosure['bitrate'])) {
2462 $episode_str .= ' bitrate="' . esc_attr($alternate_enclosure['bitrate']) . '"';
2463 }
2464
2465 if (!empty($alternate_enclosure['is_default'])) {
2466 if ($alternate_enclosure['is_default']) {
2467 $episode_str .= ' default="true"';
2468 }
2469 }
2470 $episode_str .= ">\n";
2471
2472 // Process Alternate Enclosure's URI values if present
2473 $episode_str .= "\t\t\t" . sprintf('<podcast:source uri="%s" contentType="%s"/>%s',
2474 powerpress_url_in_feed(trim(htmlspecialchars($alternate_enclosure['url']))),
2475 htmlspecialchars($alt_type),
2476 PHP_EOL);
2477
2478 if (!empty($alternate_enclosure['uris']) && is_array($alternate_enclosure['uris'])) {
2479 foreach ($alternate_enclosure['uris'] as $uri_data) {
2480 $uri_url = !empty($uri_data['uri']) ? trim(htmlspecialchars($uri_data['uri'])) : '';
2481 if (empty($uri_url)) continue;
2482
2483 $uri_type = !empty($uri_data['contentType'])
2484 ? trim(htmlspecialchars($uri_data['contentType']))
2485 : trim(htmlspecialchars(powerpress_get_contenttype($uri_url)));
2486 if ($uri_url === trim(htmlspecialchars($EpisodeData['url'])) || $uri_type === '') continue;
2487
2488 $episode_str .= "\t\t\t" . sprintf('<podcast:source uri="%s" contentType="%s"/>%s ', esc_url(powerpress_url_in_feed($uri_url)), $uri_type, PHP_EOL);
2489 }
2490 }
2491 $episode_str .= "\t\t" . sprintf('</podcast:alternateEnclosure>%s', PHP_EOL);
2492 echo $episode_str;
2493 }
2494 }
2495
2496 if (!empty($EpisodeData['content_link'])) {
2497 foreach ($EpisodeData['content_link'] as $i => $content_link) {
2498 $url = htmlspecialchars($content_link['url']);
2499 $label = htmlspecialchars($content_link['label'] ?? '');
2500
2501 if (filter_var($url, FILTER_VALIDATE_URL)) {
2502 $href_string = 'href="' . trim($url) . '"';
2503 echo "\t\t" . sprintf('<podcast:contentLink %s>%s</podcast:contentLink>%s',
2504 $href_string,
2505 $label,
2506 PHP_EOL);
2507 }
2508 }
2509 }
2510
2511
2512 // episode-level author: only output when ep metabox field is filled or itunes_author_post is checked
2513 $episode_author = '';
2514 if( isset($powerpress_feed['itunes_author_post']) )
2515 $episode_author = get_the_author();
2516 if( !empty( $EpisodeData['author'] ) )
2517 $episode_author = $EpisodeData['author'];
2518
2519 $explicit = $powerpress_feed['explicit'];
2520 $block = false;
2521
2522 if( isset( $EpisodeData['explicit'] ) && is_numeric($EpisodeData['explicit']) )
2523 {
2524 // switching from 'not set' 'yes' 'clean' to 'true' 'false'--for backwards compatibility, 'not set' will now be 'false'
2525 $explicit_array = array("false", "true", "false");
2526 $explicit = $explicit_array[$EpisodeData['explicit']];
2527 }
2528
2529 if( !empty( $EpisodeData['block'] ) )
2530 $block = 'yes';
2531
2532 if( !empty($episode_author) ) {
2533 echo "\t\t<itunes:author>" . esc_html($episode_author) . '</itunes:author>'.PHP_EOL;
2534 }
2535
2536 // itunes episode image
2537 if( !empty( $EpisodeData['itunes_image']) ) {
2538 echo "\t\t".'<itunes:image href="' . esc_url( powerpress_url_in_feed($EpisodeData['itunes_image']) ) . '" />'.PHP_EOL;
2539 } else if( !empty($powerpress_feed['itunes_image']) ) {
2540 echo "\t\t".'<itunes:image href="' . esc_url( powerpress_url_in_feed($powerpress_feed['itunes_image']) ) . '" />'.PHP_EOL;
2541 }
2542
2543 if( !empty($EpisodeData['season']) ) {
2544 echo "\t\t".'<itunes:season>'. esc_html($EpisodeData['season']) .'</itunes:season>'.PHP_EOL;
2545 echo "\t\t".'<podcast:season>'. esc_html($EpisodeData['season']) .'</podcast:season>'.PHP_EOL;
2546 }
2547
2548 if( isset($EpisodeData['episode_no']) && trim((string)$EpisodeData['episode_no']) !== '' ) {
2549 echo "\t\t".'<itunes:episode>'. esc_html(floor($EpisodeData['episode_no'])) .'</itunes:episode>'.PHP_EOL;
2550 if (!empty($EpisodeData['episode_no_display'])) {
2551 echo "\t\t" . '<podcast:episode display="' . esc_html($EpisodeData['episode_no_display']) . '">' . esc_html(floor($EpisodeData['episode_no'])) . '</podcast:episode>' . PHP_EOL;
2552 } else {
2553 echo "\t\t" . '<podcast:episode>' . esc_html(floor($EpisodeData['episode_no'])) . '</podcast:episode>' . PHP_EOL;
2554 }
2555 }
2556
2557 // TXT Tag
2558 if ( !empty($EpisodeData['txt_tag']) && is_array($EpisodeData['txt_tag']) ) {
2559 foreach ($EpisodeData['txt_tag'] as $tag) {
2560 $tag_value = isset($tag['tag']) ? trim($tag['tag']) : '';
2561 if ($tag_value === '') {
2562 continue;
2563 }
2564 echo "\t\t<podcast:txt";
2565
2566 if (!empty($tag['purpose'])) {
2567 $tag_purpose = esc_attr($tag['purpose']);
2568 echo " purpose=\"" . $tag_purpose . "\">";
2569 } else {
2570 echo ">";
2571 }
2572
2573 $tag_content = esc_html($tag_value);
2574 echo $tag_content . "</podcast:txt>" . PHP_EOL;
2575 }
2576 }
2577
2578 if( !empty($EpisodeData['episode_title']) ) {
2579 echo "\t\t".'<itunes:title>'. esc_html($EpisodeData['episode_title']) .'</itunes:title>'.PHP_EOL;
2580 }
2581
2582 if(empty($EpisodeData['episode_type'])) {
2583 $EpisodeData['episode_type'] = 'full';
2584 }
2585 echo "\t\t".'<itunes:episodeType>'. esc_html($EpisodeData['episode_type']) .'</itunes:episodeType>'.PHP_EOL;
2586
2587 // episode explicit only outputs when overriding channel explicit
2588 // clean channel+explicit episode | explicit channel + clean episode
2589 if( !empty($explicit) && $explicit != $powerpress_feed['explicit'] ) {
2590 echo "\t\t<itunes:explicit>" . $explicit . '</itunes:explicit>'.PHP_EOL;
2591 }
2592
2593 $episode_duration = ltrim(trim($EpisodeData['duration'] ?? ''), '0:');
2594 if ($episode_duration !== '' && preg_match('/^\d+$|^\d{1,2}:\d{1,2}$|^\d{1,3}:\d{1,2}:\d{1,2}$/D', $episode_duration)) {
2595 echo "\t\t<itunes:duration>" . esc_html($episode_duration) . '</itunes:duration>'.PHP_EOL;
2596 }
2597
2598 if( $block && $block == 'yes' ) {
2599 echo "\t\t<itunes:block>yes</itunes:block>".PHP_EOL;
2600 }
2601
2602 // Podcast index tags:
2603 if (!empty($EpisodeData['pci_transcript']) && !empty($EpisodeData['pci_transcript_url'])) {
2604 echo "\t\t<podcast:transcript url=\"" . esc_attr($EpisodeData['pci_transcript_url']) . "\"";
2605 $transcript_type = powerpress_get_contenttype($EpisodeData['pci_transcript_url']);
2606 $transcript_language = powerpress_valid_language($EpisodeData['pci_transcript_language'] ?? '');
2607 if (!empty($transcript_language)) {
2608 echo " language=\"" . esc_attr($transcript_language) . "\"";
2609 }
2610 if (empty($transcript_type)) {
2611 $transcript_type = 'text/plain';
2612 }
2613 echo " type=\"" . esc_attr($transcript_type) . "\"";
2614 if (in_array($transcript_type, ['application/srt', 'text/vtt', 'application/json'], true)) {
2615 echo " rel=\"captions\"";
2616 }
2617 echo " />".PHP_EOL;
2618 }
2619 if (!empty($EpisodeData['pci_chapters']) && !empty($EpisodeData['pci_chapters_url'])) {
2620 echo "\t\t<podcast:chapters url=\"" . esc_attr($EpisodeData['pci_chapters_url']) . "\" type=\"application/json+chapters\" />".PHP_EOL;
2621 }
2622
2623 if (!empty($EpisodeData['disable_episode_comments'])) {
2624 echo "\t\t<podcast:socialInteract protocol=\"disabled\" />" . PHP_EOL;
2625 } elseif (!empty($EpisodeData['social_interact']) && is_array($EpisodeData['social_interact'])) {
2626 foreach ($EpisodeData['social_interact'] as $social_interact) {
2627 // Skip Empty Tags
2628 if (empty($social_interact['uri']) || empty($social_interact['protocol']) || $social_interact['protocol'] === 'disabled') {
2629 continue;
2630 }
2631
2632 echo "\t\t<podcast:socialInteract";
2633 echo " protocol=\"" . esc_attr($social_interact['protocol']) . "\"";
2634
2635 if (!empty($social_interact['uri'])) {
2636 echo " uri=\"" . esc_attr($social_interact['uri']) . "\"";
2637 }
2638
2639 if (!empty($social_interact['account_id'])) {
2640 echo " accountId=\"" . esc_attr($social_interact['account_id']) . "\"";
2641 }
2642
2643 if (!empty($social_interact['accountUrl'])) {
2644 echo " accountUrl=\"" . esc_attr($social_interact['accountUrl']) . "\"";
2645 }
2646
2647 if (!empty($social_interact['priority'])) {
2648 echo " priority=\"" . esc_attr($social_interact['priority']) . "\"";
2649 }
2650
2651 echo " />" . PHP_EOL;
2652 }
2653 } elseif (!empty($EpisodeData['social_interact_uri']) && !empty($EpisodeData['social_interact_protocol'])) {
2654 // Legacy social_interact handler
2655 if (!empty($EpisodeData['social_interact_account_id']))
2656 echo "\t\t<podcast:socialInteract uri=\"" . esc_attr($EpisodeData['social_interact_uri']) . "\" protocol=\"".esc_attr($EpisodeData['social_interact_protocol'])."\" accountId=\"".esc_attr($EpisodeData['social_interact_account_id'])."\" />".PHP_EOL;
2657 else
2658 echo "\t\t<podcast:socialInteract uri=\"" . esc_attr($EpisodeData['social_interact_uri']) . "\" protocol=\"".esc_attr($EpisodeData['social_interact_protocol'])."\" />".PHP_EOL;
2659 }
2660
2661 // <podcast:funding>
2662 if (!empty($EpisodeData['donate_url'])) {
2663 echo "\t\t<podcast:funding url=\"".esc_attr($EpisodeData['donate_url'])."\">". (!empty($EpisodeData['donate_label']) ? esc_html($EpisodeData['donate_label']) : '')."</podcast:funding>".PHP_EOL;
2664 }
2665 // legacy naming convention
2666 else if (!empty($EpisodeData['funding_url'])) {
2667 echo "\t\t<podcast:funding url=\"".esc_attr($EpisodeData['funding_url'])."\">".esc_html($EpisodeData['funding_label'])."</podcast:funding>".PHP_EOL;
2668 }
2669
2670 // <podcast:license>
2671 if( isset( $EpisodeData['copyright'] ) && trim($EpisodeData['copyright']) !== '' ) {
2672 $EpisodeData['copyright'] = str_replace(array('&copy;', '(c)', '(C)', chr(194) . chr(169), chr(169) ), '&#xA9;', $EpisodeData['copyright']);
2673 if ( isset( $EpisodeData['copyright_url'] ) && trim($EpisodeData['copyright_url']) !== '' ) {
2674 echo "\t\t".'<podcast:license url="' . esc_attr(powerpress_url_in_feed($EpisodeData['copyright_url'])) . '">'. esc_html($EpisodeData['copyright']) . '</podcast:license>'.PHP_EOL;
2675 } else {
2676 echo "\t\t".'<podcast:license>'. esc_html($EpisodeData['copyright']) . '</podcast:license>'.PHP_EOL;
2677 }
2678 }
2679
2680 // inheritence check
2681 $credits = [];
2682 if (!empty($EpisodeData['inherit_channel_credits'])) {
2683 $Feed = get_option('powerpress_feed', array());
2684
2685 if (!empty($Feed['credits'])) {
2686 $credits = $Feed['credits'];
2687 } else if (!empty($Feed['person_names'])) {
2688 foreach ($Feed['person_names'] as $i => $name) {
2689 if ($name === '') continue;
2690 $credits[] = [
2691 'name' => $name,
2692 'role' => $Feed['person_roles'][$i] ?? '',
2693 'person_url' => $Feed['person_urls'][$i] ?? '',
2694 'link_url' => $Feed['link_urls'][$i] ?? ''
2695 ];
2696 }
2697 }
2698 }
2699 // load episode credits
2700 $episode_credits = [];
2701 // nested
2702 if (!empty($EpisodeData['credits'])) {
2703 $episode_credits = $EpisodeData['credits'];
2704 }
2705 // parallelized (legacy)
2706 else if (!empty($EpisodeData['person_names'])) {
2707 foreach ($EpisodeData['person_names'] as $i => $name) {
2708 if ($name === '') continue;
2709 $episode_credits[] = [
2710 'name' => $name,
2711 'role' => $EpisodeData['person_roles'][$i] ?? '',
2712 'person_url' => $EpisodeData['person_urls'][$i] ?? '',
2713 'link_url' => $EpisodeData['link_urls'][$i] ?? ''
2714 ];
2715 }
2716 }
2717 // print to feed
2718 $credits = array_merge($credits, $episode_credits);
2719 foreach ($credits as $credit) {
2720 if (!isset($credit['name']) || trim((string)$credit['name']) === '') continue;
2721
2722 echo "\t\t<podcast:person";
2723 if (!empty($credit['role'])) {
2724 echo " role=\"" . htmlspecialchars($credit['role']) . "\"";
2725 }
2726 if (!empty($credit['person_url'])) {
2727 $personImg = esc_url($credit['person_url']);
2728 if ($personImg !== '') {
2729 echo " img=\"" . $personImg . "\"";
2730 }
2731 }
2732 if (!empty($credit['link_url'])) {
2733 $personHref = esc_url($credit['link_url']);
2734 if ($personHref !== '') {
2735 echo " href=\"" . $personHref . "\"";
2736 }
2737 }
2738 echo ">" . htmlspecialchars($credit['name']) . "</podcast:person>" . PHP_EOL;
2739 }
2740
2741 if (!empty($EpisodeData['soundbites']) && is_array($EpisodeData['soundbites'])) {
2742 // nested
2743 foreach ($EpisodeData['soundbites'] as $soundbite) {
2744 $start = $soundbite['start'] ?? '';
2745 $duration = $soundbite['duration'] ?? '';
2746 $title = $soundbite['title'] ?? '';
2747
2748 $float_start = (float) $start;
2749 $is_string_float = (strval($float_start) == $start);
2750 if ($start == "" || !$is_string_float || $float_start < 0)
2751 continue;
2752
2753 $float_duration = (float) $duration;
2754 $is_string_float = (strval($float_duration) == $duration);
2755 if ($duration == "" || !$is_string_float || $float_duration <= 0)
2756 continue;
2757
2758 $attrStr = ' startTime="' . $start . '"';
2759 $attrStr .= ' duration="' . $duration . '"';
2760 echo "\t\t<podcast:soundbite$attrStr>" . esc_html($title) . "</podcast:soundbite>" . PHP_EOL;
2761 }
2762 } elseif (!empty($EpisodeData['soundbite_starts'])) {
2763 // parallelized (legacy)
2764 $soundbiteStarts = $EpisodeData['soundbite_starts'];
2765 $soundbiteDurations = $EpisodeData['soundbite_durations'];
2766 $soundbiteTitles = $EpisodeData['soundbite_titles'];
2767
2768 for ($i = 0; $i < count($soundbiteStarts); $i++) {
2769 $start = $soundbiteStarts[$i];
2770 $float_start = (float) $start;
2771 $is_string_float = (strval($float_start) == $start);
2772 if ($start == "" || !$is_string_float || $float_start < 0)
2773 continue;
2774
2775 $duration = $soundbiteDurations[$i];
2776 $float_duration = (float) $duration;
2777 $is_string_float = (strval($float_duration) == $duration);
2778 if ($duration == "" || !$is_string_float || $float_duration <= 0)
2779 continue;
2780
2781 $attrStr = ' startTime="' . $start . '"';
2782 $attrStr .= ' duration="' . $duration . '"';
2783 $title = $soundbiteTitles[$i];
2784 echo "\t\t<podcast:soundbite$attrStr>" . esc_html($title) . "</podcast:soundbite>" . PHP_EOL;
2785 }
2786 }
2787
2788
2789 if( !empty($EpisodeData['location']) ) {
2790 $locations = [];
2791
2792 if (is_array($EpisodeData['location'])) {
2793 $first_location = reset($EpisodeData['location']);
2794
2795 if (is_array($first_location)) {
2796 foreach ($EpisodeData['location'] as $location_item) {
2797 if (empty($location_item) || !is_array($location_item)) {
2798 continue;
2799 }
2800
2801 $address = '';
2802 if (!empty($location_item['location'])) $address = $location_item['location'];
2803 elseif (!empty($location_item['address'])) $address = $location_item['address'];
2804
2805 if (!empty($address)) {
2806 $locations[] = [
2807 'address' => $address,
2808 'geo' => $location_item['geo'] ?? $location_item['pci_geo'] ?? '',
2809 'osm' => $location_item['osm'] ?? $location_item['pci_osm'] ?? '',
2810 'rel' => $location_item['rel'] ?? $location_item['pci_rel'] ?? '1',
2811 'country' => $location_item['country'] ?? $location_item['pci_country'] ?? '',
2812 ];
2813 }
2814 }
2815 } else {
2816 // legacy
2817 foreach ($EpisodeData['location'] as $i => $address) {
2818 if (!empty($address)) {
2819 $locations[] = [
2820 'address' => $address,
2821 'geo' => $EpisodeData['pci_geo'][$i] ?? '',
2822 'osm' => $EpisodeData['pci_osm'][$i] ?? '',
2823 'rel' => $EpisodeData['pci_rel'][$i] ?? '1',
2824 'country' => $EpisodeData['pci_country'][$i] ?? '',
2825 ];
2826 }
2827 }
2828 }
2829 }
2830 foreach ($locations as $location) {
2831 if (empty($location['address'])) {
2832 continue;
2833 }
2834
2835 echo "\t\t<podcast:location";
2836
2837 if (!empty($location['geo'])) echo ' geo="' . htmlspecialchars($location['geo']) . '"';
2838 if (!empty($location['osm'])) echo ' osm="' . htmlspecialchars($location['osm']) . '"';
2839 if (!empty($location['rel'])) echo ' rel="' . ($location['rel'] == '1' ? 'subject' : 'creator') . '"';
2840 if (!empty($location['country'])) echo ' country="' . htmlspecialchars($location['country']) . '"';
2841
2842 echo ">" . htmlspecialchars($location['address']) . "</podcast:location>" . PHP_EOL;
2843 }
2844 }
2845
2846 $recipients = [];
2847 // v4v channel level inheritence
2848 if (isset($EpisodeData['channel_level_recipients'])) {
2849 $Feed = get_option('powerpress_feed', array());
2850 // nested format
2851 if (!empty($Feed['value_recipients'])) {
2852 foreach ($Feed['value_recipients'] as $recipient) {
2853 $pubkey = trim($recipient['pubkey'] ?? '');
2854 $split = (int)($recipient['split'] ?? 0);
2855 if ($pubkey === '' || $split <= 0) continue;
2856
2857 $recipients[] = [
2858 'lightning' => $recipient['lightning'] ?? $recipient['name'] ?? '',
2859 'split' => $split,
2860 'pubkey' => $pubkey,
2861 'customKey' => $recipient['custom_key'] ?? $recipient['customKey'] ?? '',
2862 'customValue' => $recipient['custom_value'] ?? $recipient['customValue'] ?? '',
2863 'fee' => !empty($recipient['fee']) ? 'true' : 'false'
2864 ];
2865 }
2866 // legacy format
2867 } else if ((!empty($Feed['value_pubkey']) && is_array($Feed['value_pubkey']))
2868 && (!empty($Feed['value_split']) && is_array($Feed['value_split']))) {
2869 $pubKeys = (array)$Feed['value_pubkey'];
2870 $splits = (array)$Feed['value_split'];
2871 $lightnings = (array)($Feed['value_lightning'] ?? []);
2872 $customKeys = (array)($Feed['value_custom_key'] ?? []);
2873 $customVals = (array)($Feed['value_custom_value'] ?? []);
2874 $fees = (array)($Feed['value_is_fee'] ?? []);
2875
2876 foreach ($pubKeys as $i => $pubkey) {
2877 $pubkey = trim((string)$pubkey);
2878 $split = (int)($splits[$i] ?? 0);
2879 if ($pubkey === '' || $split <= 0) continue;
2880
2881 $recipients[] = [
2882 'lightning' => (string)($lightnings[$i] ?? ''),
2883 'split' => $split,
2884 'pubkey' => $pubkey,
2885 'customKey' => (string)($customKeys[$i] ?? ''),
2886 'customValue' => (string)($customVals[$i] ?? ''),
2887 'fee' => (isset($fees[$i]) && $fees[$i] === 'true') ? 'true' : 'false',
2888 ];
2889 }
2890 }
2891 }
2892 // nested
2893 if (isset($EpisodeData['value_recipients']) && is_array($EpisodeData['value_recipients'])) {
2894 foreach ($EpisodeData['value_recipients'] as $recipient) {
2895 $pubkey = trim($recipient['pubkey'] ?? '');
2896 $split = (int)($recipient['split'] ?? 0);
2897 if ($pubkey === '' || $split <= 0) continue;
2898
2899 $recipients[] = [
2900 'lightning' => $recipient['lightning'] ?? $recipient['name'] ?? '',
2901 'split' => $split,
2902 'pubkey' => $pubkey,
2903 'customKey' => $recipient['custom_key'] ?? $recipient['customKey'] ?? '',
2904 'customValue'=> $recipient['custom_value'] ?? $recipient['customValue'] ?? '',
2905 'fee' => (isset($recipient['fee']) && $recipient['fee'] === 'true') ? 'true' : 'false',
2906 ];
2907 }
2908 // paralellized (legacy)
2909 } elseif (!empty($EpisodeData['value_pubkey']) && is_array($EpisodeData['value_pubkey'])
2910 && !empty($EpisodeData['value_split']) && is_array($EpisodeData['value_split'])) {
2911
2912 $pubKeys = (array)$EpisodeData['value_pubkey'];
2913 $splits = (array)$EpisodeData['value_split'];
2914 $lightnings = (array)($EpisodeData['value_lightning'] ?? []);
2915 $customKeys = (array)($EpisodeData['value_custom_key'] ?? []);
2916 $customVals = (array)($EpisodeData['value_custom_value'] ?? []);
2917 $fees = (array)($EpisodeData['value_is_fee'] ?? []);
2918
2919 foreach ($pubKeys as $i => $pubkey) {
2920 $pubkey = trim((string)$pubkey);
2921 $split = (int)($splits[$i] ?? 0);
2922 if ($pubkey === '' || $split <= 0) continue;
2923
2924 $recipients[] = [
2925 'lightning' => (string)($lightnings[$i] ?? ''),
2926 'split' => $split,
2927 'pubkey' => $pubkey,
2928 'customKey' => (string)($customKeys[$i] ?? ''),
2929 'customValue' => (string)($customVals[$i] ?? ''),
2930 'fee' => (isset($fees[$i]) && $fees[$i] === 'true') ? 'true' : 'false',
2931 ];
2932 }
2933 }
2934
2935 // output podcast:value block if we have recipients OR vts with remote items
2936 $hasVts = isset($EpisodeData['vts_order']) && !empty($EpisodeData['vts_order']);
2937 if (!empty($recipients) || $hasVts) {
2938 // add blubrry fee recipient when there are other recipients
2939 if (!empty($recipients)) {
2940 $recipients[] = [
2941 'lightning' => 'blubrry@getalby.com',
2942 'split' => 3,
2943 'pubkey' => '03b8a595e4d8e19efa8faa3fbe2524b0d39f7c812415ff831d38a466a4a1bc888a',
2944 'customKey' => '696969',
2945 'customValue' => 'qAHJuqKLmMhTNFualcIj',
2946 'fee' => 'true',
2947 ];
2948 }
2949
2950 echo "\t\t" . '<podcast:value type="lightning" method="keysend" suggested="0.00000005000">' . PHP_EOL;
2951
2952 foreach ($recipients as $recipient) {
2953 $attr = 'type="node" split="' . (int)$recipient['split'] . '" address="' . htmlspecialchars($recipient['pubkey']) . '"';
2954
2955 if (!empty($recipient['lightning'])) $attr .= ' name="' . htmlspecialchars($recipient['lightning']) . '"';
2956 if (!empty($recipient['customKey'])) $attr .= ' customKey="' . htmlspecialchars($recipient['customKey']) . '"';
2957 if (!empty($recipient['customValue'])) $attr .= ' customValue="' . htmlspecialchars($recipient['customValue']) . '"';
2958 if (!empty($recipient['fee']) && $recipient['fee'] === 'true') $attr .= ' fee="true"';
2959
2960 echo "\t\t\t" . "<podcast:valueRecipient $attr/>" . PHP_EOL;
2961 }
2962
2963 $feed_slug = get_query_var('feed');
2964 // if this is a blog feed, we need to access the settings associated to the podcast feed
2965 if (is_category() && $feed_slug == 'feed') {
2966 $feed_slug = 'podcast';
2967 }
2968 if ($hasVts) {
2969 $valueTimeSplits = get_option('vts_'.$feed_slug.'_'.get_the_ID());
2970 if (!is_array($valueTimeSplits))
2971 $valueTimeSplits = [];
2972
2973 foreach ($EpisodeData['vts_order'] as $vts_id) {
2974 if (!isset($valueTimeSplits[$vts_id])) continue;
2975
2976 $timeSplit = $valueTimeSplits[$vts_id];
2977 if (empty($timeSplit['duration'])) continue;
2978
2979 $vtsAttrs = [
2980 'startTime' => $timeSplit['start_time'] ?? 0,
2981 'duration' => $timeSplit['duration']
2982 ];
2983
2984 $recipientType = $timeSplit['recipient'] ?? 0;
2985 if ($recipientType == 0)
2986 $vtsAttrs['remotePercentage'] = $timeSplit['remote_percent'] ?? 0;
2987
2988 $vtsAttrParts = [];
2989 foreach ($vtsAttrs as $key => $value) {
2990 $vtsAttrParts[] = $key.'="'.$value.'"';
2991 }
2992
2993 echo "\t\t\t"."<podcast:valueTimeSplit ".implode(' ', $vtsAttrParts).">\n";
2994
2995 if ($recipientType == 0) {
2996 $remoteItem = $timeSplit['remote_item'] ?? [];
2997
2998 if (!empty($remoteItem['feed_guid'])) {
2999 $attrs = ['feedGuid' => $remoteItem['feed_guid']];
3000
3001 $itemGuid = $remoteItem['item_guid'] ?? '';
3002 if (!empty($itemGuid) && $itemGuid != 'none')
3003 $attrs['itemGuid'] = $itemGuid;
3004
3005 if (!empty($remoteItem['feed_link'])) {
3006 $attrs['feedUrl'] = $remoteItem['feed_link'];
3007 }
3008
3009 if (!empty($remoteItem['medium'])) {
3010 $attrs['medium'] = $remoteItem['medium'];
3011 }
3012
3013 if (!empty($remoteItem['item_title'])) {
3014 $attrs['title'] = $remoteItem['item_title'];
3015 }
3016
3017 $attrParts = [];
3018 foreach ($attrs as $key => $value) {
3019 $attrParts[] = $key.'="'.htmlspecialchars($value).'"';
3020 }
3021
3022 echo "\t\t\t\t"."<podcast:remoteItem ".implode(' ', $attrParts)."/>\n";
3023 }
3024 } else {
3025 $valueRecipients = $timeSplit['value_recipients'] ?? array();
3026
3027 foreach ($valueRecipients as $valueRecipient) {
3028 if (empty($valueRecipient['pubkey'])) continue;
3029
3030 $attrs = [
3031 "type" => "node",
3032 "split" => $valueRecipient['split'] ?? 0,
3033 "address" => $valueRecipient['pubkey'],
3034 ];
3035
3036 if (!empty($valueRecipient['lightning']))
3037 $attrs['name'] = $valueRecipient['lightning'];
3038
3039 if (!empty($valueRecipient['custom_key']))
3040 $attrs["customKey"] = $valueRecipient['custom_key'];
3041
3042 if (!empty($valueRecipient['custom_value']))
3043 $attrs["customValue"] = $valueRecipient['custom_value'];
3044
3045 if (!empty($valueRecipient['value_is_fee']))
3046 $attrs["value_is_fee"] = $valueRecipient['value_is_fee'];
3047
3048
3049 $attrStr = "";
3050 foreach ($attrs as $key => $value) {
3051 $attrStr .= ' '.$key.'="'.$value.'"';
3052 }
3053
3054 echo "\t\t\t\t"."<podcast:valueRecipient $attrStr/>\n";
3055 }
3056 }
3057
3058 echo "\t\t\t"."</podcast:valueTimeSplit>\n";
3059 }
3060 }
3061
3062 echo "\t\t".'</podcast:value>'.PHP_EOL;
3063 }
3064
3065 // RawVoice RSS Tags
3066 if( empty($powerpress_feed['feed_maximizer_on']) )
3067 {
3068 if( !defined('POWERPRESS_RAWVOICE_RSS') || POWERPRESS_RAWVOICE_RSS != false )
3069 {
3070 if( !empty($EpisodeData['podcast_id']) )
3071 echo "\t\t<rawvoice:pid>" . esc_html($EpisodeData['podcast_id']) . "</rawvoice:pid>" . PHP_EOL;
3072 if( !empty($EpisodeData['ishd']) )
3073 echo "\t\t<rawvoice:isHD>yes</rawvoice:isHD>".PHP_EOL;
3074 if( !empty($EpisodeData['image']) )
3075 echo "\t\t<rawvoice:poster url=\"". $EpisodeData['image'] ."\" />".PHP_EOL;
3076 if( !empty($EpisodeData['embed']) )
3077 echo "\t\t<rawvoice:embed>". htmlspecialchars($EpisodeData['embed']) ."</rawvoice:embed>".PHP_EOL;
3078 else if( !empty($powerpress_feed['podcast_embed_in_feed']) && function_exists('powerpress_generate_embed') )
3079 {
3080 $player = powerpressplayer_embedable($EpisodeData['url'], $EpisodeData);
3081 $embed_content = '';
3082
3083 if( $player )
3084 $embed_content = powerpress_generate_embed($player, $EpisodeData);
3085 if( $embed_content )
3086 echo "\t\t<rawvoice:embed>". htmlspecialchars( $embed_content ) ."</rawvoice:embed>".PHP_EOL;
3087 }
3088
3089 if( !empty($EpisodeData['webm_src']) )
3090 {
3091 echo "\t\t<rawvoice:webm src=\"". $EpisodeData['webm_src'] ."\"";
3092 if( $EpisodeData['webm_length'] )
3093 echo " length=\"". $EpisodeData['webm_length'] ."\"";
3094 echo " type=\"video/webm\" />".PHP_EOL;
3095 }
3096
3097 $GeneralSettings = get_option('powerpress_general', array());
3098
3099 require_once(POWERPRESS_ABSPATH .'/powerpress-metamarks.php');
3100 powerpress_metamarks_print_rss2($EpisodeData);
3101 }
3102 }
3103 }
3104
3105 add_filter('rss2_item', 'powerpress_rss2_item');
3106 add_filter('rss2_item_powerpress', 'powerpress_rss2_item');
3107
3108 /*
3109 This filter is only necessary for feeds that are not specifically for podcasting, e.g. a category feed that did not have category podcasting added to it
3110 */
3111 function powerpress_filter_rss_enclosure($content)
3112 {
3113 if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) || isset($GLOBALS['podcast_channel_active']) || defined('PODCASTING_VERSION') ) {
3114 return $content; // Another podcasting plugin is enabled...
3115 }
3116
3117 if( powerpress_is_custom_podcast_feed() && get_query_var('feed') !== 'podcast' && !is_category() && !is_tag() && !is_tax() )
3118 return ''; // We will handle this enclosure in the powerpress_rss2_item() function
3119
3120 $match_count = preg_match('/\surl="([^"]*)"/', $content, $matches); // No URL found, weird
3121 if( count($matches) != 2)
3122 return $content;
3123
3124 // Original Media URL
3125 $OrigURL = $matches[1];
3126
3127 if( substr($OrigURL, 0, 5) != 'http:' && substr($OrigURL, 0, 6) != 'https:' )
3128 return ''; // The URL value is invalid
3129
3130 global $post, $powerpress_rss_enclosure_post_id;
3131 if( empty($powerpress_rss_enclosure_post_id) )
3132 $powerpress_rss_enclosure_post_id = -1;
3133
3134 if( $powerpress_rss_enclosure_post_id == $post->ID )
3135 return ''; // we've already included one enclosure, lets not allow anymore
3136 $powerpress_rss_enclosure_post_id = $post->ID;
3137
3138 $EpisodeData = powerpress_get_enclosure_data($post->ID);
3139
3140 if( false === $EpisodeData )
3141 return '';
3142
3143 // Modified Media URL
3144 $ModifiedURL = powerpress_url_in_feed($EpisodeData['url']); // powerpress_add_redirect_url($OrigURL);
3145
3146 // Check that the content type is a valid one...
3147 $match_count = preg_match('/\stype="([^"]*)"/', $content, $matches);
3148 if( count($matches) > 1 && strstr($matches[1], '/') == false )
3149 {
3150 $ContentType = powerpress_get_contenttype($ModifiedURL);
3151 $content = str_replace("type=\"{$matches[1]}\"", "type=\"$ContentType\"", $content);
3152 }
3153
3154 // Check that the content length is a digit greater that zero
3155 $match_count = preg_match('/\slength="([^"]*)"/', $content, $matches);
3156 if (count($matches) > 1 && (!is_numeric($EpisodeData['size']) || $EpisodeData['size'] <= 0))
3157 {
3158 $content = str_replace("length=\"{$matches[1]}\"", "length=\"5242880\"", $content);
3159 }
3160
3161 // encode htmlspecialchars if necessary
3162 $decoded = htmlspecialchars_decode($ModifiedURL);
3163 if (strlen($decoded) == strlen($EpisodeData['url'])) {
3164 // might need encoded/no risk of double encoding
3165 $ModifiedURL = htmlspecialchars($ModifiedURL);
3166 }
3167
3168 // Replace the original url with the modified one...
3169 if( $OrigURL != $ModifiedURL )
3170 $content = str_replace($OrigURL, $ModifiedURL, $content);
3171
3172 // add proper indentation for rss formatting
3173 return "\t\t" . trim($content) . "\n";
3174 }
3175
3176
3177 add_filter('rss_enclosure', 'powerpress_filter_rss_enclosure', 11);
3178
3179 function powerpress_bloginfo_rss($content, $field = '')
3180 {
3181 $new_value = '';
3182 if( powerpress_is_custom_podcast_feed() )
3183 {
3184 if( is_category() ) {
3185 $Feed = get_option('powerpress_cat_feed_'.get_query_var('cat'), array() );
3186 }
3187 else if( is_tax() || is_tag() ) {
3188 global $powerpress_feed;
3189 if( !empty($powerpress_feed['term_taxonomy_id']) )
3190 $Feed = get_option('powerpress_taxonomy_'.$powerpress_feed['term_taxonomy_id'], array() );
3191 }
3192 else
3193 {
3194 global $powerpress_feed;
3195
3196 if( !empty($powerpress_feed['post_type']) )
3197 {
3198 $feed_slug = get_query_var('feed');
3199 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$powerpress_feed['post_type'], array() );
3200 if( !empty($PostTypeSettingsArray[ $feed_slug ]) )
3201 $Feed = $PostTypeSettingsArray[ $feed_slug ];
3202 }
3203 else
3204 {
3205 $Feed = get_option('powerpress_feed_'.get_query_var('feed'), array() );
3206 if( empty($Feed) && get_query_var('feed') === 'podcast' )
3207 $Feed = get_option('powerpress_feed', array());
3208 }
3209 }
3210
3211 if( !empty($Feed) )
3212 {
3213 switch( $field )
3214 {
3215 case 'description': {
3216 if( !empty($Feed['description']) )
3217 $new_value = $Feed['description'];
3218 else if( is_category() )
3219 {
3220 $category = get_category( get_query_var('cat') );
3221 if( $category->description )
3222 $new_value = $category->description;
3223 }
3224 }; break;
3225 case 'url': {
3226 // If the website URL is set for this podcast then lets use it...
3227 if( !empty($Feed['url']) )
3228 return trim($Feed['url']);
3229
3230 if( is_category() ) {
3231 return get_category_link( get_query_var('cat') );
3232 } else {
3233 $urlTemp = '';
3234 $blogHomepage = get_option('page_for_posts');
3235 if( !empty($blogHomepage) ) {
3236 $urlTemp = get_permalink( $blogHomepage );
3237 }
3238
3239 if( empty($urlTemp) )
3240 $urlTemp = get_bloginfo('url');
3241 if( !empty($urlTemp) )
3242 return $urlTemp;
3243 }
3244 }; break;
3245 case 'name': { // As of wp 4.4+ title is handled by get_the_title_rss completely.
3246 if( !empty($Feed['title']) )
3247 $new_value = $Feed['title'];
3248 }; break;
3249 case 'language': {
3250 // Get the feed language
3251 $lang = '';
3252 if( isset($Feed['rss_language']) && $Feed['rss_language'] != '' )
3253 $lang = $Feed['rss_language'];
3254 if( strlen($lang) == 5 )
3255 $lang = substr($lang,0,3) . strtoupper( substr($lang, 3) ); // Format example: en-US for English, United States
3256 if( !empty($lang) )
3257 return $lang;
3258 }; break;
3259 }
3260 }
3261 }
3262
3263 if( !empty($new_value) )
3264 {
3265 $GeneralSettings = get_option('powerpress_general');
3266 // disable smart typography check
3267 if( empty($GeneralSettings['disable_wptexturize']) )
3268 $new_value = wptexturize($new_value);
3269 $new_value = convert_chars($new_value);
3270 // decode html entities before escaping so numeric entities like &#8212; render as actual chars
3271 if( !empty($GeneralSettings['disable_wptexturize']) )
3272 $new_value = html_entity_decode($new_value, ENT_QUOTES, 'UTF-8');
3273 // convert named HTML entities to numeric for XML compatibility (e.g. &copy; → &#169;)
3274 $new_value = ent2ncr($new_value);
3275 $new_value = esc_html($new_value);
3276 return $new_value;
3277 }
3278
3279 return $content;
3280 }
3281
3282 add_filter('get_bloginfo_rss', 'powerpress_bloginfo_rss', 10, 2);
3283
3284
3285 function powerpress_wp_title_rss($title)
3286 {
3287 if( version_compare($GLOBALS['wp_version'], '4.4', '>=' ) )
3288 {
3289 if( powerpress_is_custom_podcast_feed() )
3290 {
3291 if( is_category() ) {
3292 $Feed = get_option('powerpress_cat_feed_'.get_query_var('cat'), array() );
3293 }
3294 else if( is_tax() || is_tag() ) {
3295 global $powerpress_feed;
3296 if( !empty($powerpress_feed['term_taxonomy_id']) )
3297 $Feed = get_option('powerpress_taxonomy_'.$powerpress_feed['term_taxonomy_id'], array() );
3298 }
3299 else
3300 {
3301 global $powerpress_feed;
3302
3303 if( !empty($powerpress_feed['post_type']) )
3304 {
3305 $feed_slug = get_query_var('feed');
3306 if( !empty($feed_slug) ) {
3307 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$powerpress_feed['post_type'], array() );
3308 if( !empty($PostTypeSettingsArray[ $feed_slug ]) )
3309 $Feed = $PostTypeSettingsArray[ $feed_slug ];
3310 }
3311 }
3312 else
3313 {
3314 $feed_slug = get_query_var('feed');
3315 $Feed = false;
3316 if( !empty($feed_slug) ) {
3317 $Feed = get_option('powerpress_feed_'.get_query_var('feed') );
3318 }
3319 if( empty($Feed) && get_query_var('feed') === 'podcast' )
3320 $Feed = get_option('powerpress_feed');
3321 }
3322 }
3323
3324 if( !empty($Feed) )
3325 {
3326 if( !empty($Feed['title']) )
3327 return esc_html( $Feed['title'] );
3328 }
3329 }
3330 }
3331 else
3332 {
3333 if( powerpress_is_custom_podcast_feed() )
3334 {
3335 if( is_category() )
3336 {
3337 $Feed = get_option('powerpress_cat_feed_'.get_query_var('cat') );
3338 if( $Feed && isset($Feed['title']) && $Feed['title'] != '' )
3339 return ''; // We alrady did a custom title, lets not add the category to it...
3340 }
3341 else
3342 {
3343 return ''; // It is not a category, lets not mess with our beautiful title then
3344 }
3345 }
3346 }
3347
3348 return $title;
3349 }
3350
3351 add_filter('get_wp_title_rss', 'powerpress_wp_title_rss');
3352
3353 function powerpress_the_title_rss($title)
3354 {
3355 $new_title = $title;
3356 $GeneralSettings = get_option('powerpress_general');
3357 // If it is a custom podcast channel...
3358 if( !empty($GeneralSettings['seo_feed_title']) )
3359 {
3360 $feed_slug = 'podcast';
3361 // IF custom post type or channel, use that feed slug...
3362 if( get_query_var('feed') !== 'podcast' && !is_category() && !is_tax() && !is_tag() )
3363 $feed_slug = get_query_var('feed');
3364
3365 // Get the episode specific title...
3366 $EpisodeData = powerpress_get_enclosure_data(get_the_ID(), $feed_slug);
3367 if( !empty($EpisodeData['feed_title']) )
3368 {
3369 $feed_title = ent2ncr( $EpisodeData['feed_title'] );
3370 $feed_title = strip_tags( $feed_title );
3371 $feed_title = esc_html( $feed_title );
3372
3373 //switch( $GeneralSettings['custom_feed_title'] )
3374 switch( $GeneralSettings['seo_feed_title'] )
3375 {
3376 case 1: { // Replaces title
3377 $new_title = $feed_title;
3378 }; break;
3379 case 2: { // Prefixes title
3380 $new_title = $feed_title . ' ' . $title;
3381 }; break;
3382 case 3: { // Postfixes title
3383 $new_title = $title . ' ' . $feed_title;
3384 }; break;
3385 }
3386 }
3387 }
3388
3389 return $new_title;
3390 }
3391
3392 add_filter('the_title_rss', 'powerpress_the_title_rss', 11);
3393
3394
3395 function powerpress_feed_content_type($content_type = '', $feedslug = '')
3396 {
3397 switch( $feedslug )
3398 {
3399 case 'rss':
3400 case 'rss2':
3401 case 'atom':
3402 case 'rdf': {
3403 // Do nothing, let WordPress take care of these
3404 }; break;
3405 case 'podcast': {
3406 // This one is ours!
3407 $content_type = 'application/rss+xml';
3408 }; break;
3409 default: { // Check for the custom podcast feeds
3410 $GeneralSettings = get_option('powerpress_general');
3411 if( !empty($GeneralSettings['custom_feeds'][ $feedslug ]) )
3412 {
3413 $content_type = 'application/rss+xml';
3414 }
3415 else if( !empty($GeneralSettings['posttype_podcasting']) )
3416 {
3417 // We need to look up these settings...
3418 $FeedSlugPostTypesArray = get_option('powerpress_posttype-podcasting');
3419 if( is_array($FeedSlugPostTypesArray) && !empty($FeedSlugPostTypesArray[ $feedslug ]) )
3420 {
3421 $content_type = 'application/rss+xml';
3422 }
3423 }
3424 }
3425 }
3426
3427 return $content_type;
3428 }
3429
3430 add_filter( 'feed_content_type', 'powerpress_feed_content_type', 10, 2 );
3431
3432 function wpse_152316_wp_audio_extensions( $ext )
3433 {
3434 remove_filter( current_filter(), __FUNCTION__ );
3435 $ext[] = '';
3436 return $ext;
3437 }
3438
3439 /**
3440 * Allow unrecognized audio sources hosted on trusted hosts that use query strings on their podcast media.
3441 *
3442 * @see http://wordpress.stackexchange.com/a/152352/26350
3443 */
3444
3445 add_filter( 'wp_audio_shortcode_override',
3446 function( $html, $atts )
3447 {
3448 if (isset($atts['src'])) {
3449 $trusted_hosts_use_qstrings = array('traffic.libsyn.com', 'cdn.simplecast.com', 'buzzsprout.com', 'audioboom.com', 'mc.blubrry.com');
3450 foreach ($trusted_hosts_use_qstrings as $host) {
3451 if (strpos($atts['src'], $host) !== false) {
3452 add_filter('wp_audio_extensions', 'wpse_152316_wp_audio_extensions');
3453 }
3454 }
3455 }
3456 return $html;
3457 }
3458 , PHP_INT_MAX, 2 );
3459
3460 // Following code only works for WP 3.3 or older. WP 3.4+ now uses the get_locale setting, so we have to override directly in the get_bloginfo_rss functoin.
3461 if( version_compare($GLOBALS['wp_version'], '3.4', '<') )
3462 {
3463 function powerpress_rss_language($value)
3464 {
3465 if( powerpress_is_custom_podcast_feed() )
3466 {
3467 global $powerpress_feed;
3468 if( $powerpress_feed && isset($powerpress_feed['rss_language']) && $powerpress_feed['rss_language'] != '' )
3469 $value = $powerpress_feed['rss_language'];
3470 }
3471 return $value;
3472 }
3473
3474 add_filter('option_rss_language', 'powerpress_rss_language');
3475 }
3476
3477 //filter to ensure that guid doesn't come up blank
3478 function powerpress_the_guid($guid) {
3479 global $post;
3480
3481 // Simple case, what is in the DB is better than an empty value
3482 if( empty($guid) && !empty($post->guid) ) {
3483 return $post->guid;
3484 }
3485
3486 if( !empty($post->guid) ) {
3487 if( preg_match('/^https?:\/\//i', $post->guid, $matches) == false ) {
3488 $powerpressGuid = get_post_meta($post->ID, '_powerpress_guid', true);
3489 if( !empty($powerpressGuid) )
3490 return $powerpressGuid;
3491 }
3492 }
3493
3494 return $guid;
3495 }
3496
3497 function powerpress_do_podcast_feed($for_comments=false)
3498 {
3499 global $wp_query, $powerpress_feed;
3500
3501 powerpress_is_podcast_feed(); // Loads the feed settings if not already loaded...
3502
3503 $GeneralSettings = get_option('powerpress_general');
3504 if( isset($GeneralSettings['premium_caps']) && $GeneralSettings['premium_caps'] )
3505 {
3506 $feed_slug = get_query_var('feed');
3507
3508 if( $feed_slug != 'podcast' )
3509 {
3510 $FeedSettings = get_option('powerpress_feed_'.$feed_slug);
3511 if( !empty($FeedSettings['premium']) )
3512 {
3513 require_once( POWERPRESS_ABSPATH.'/powerpress-feed-auth.php');
3514 powerpress_feed_auth( $feed_slug );
3515 }
3516 }
3517 }
3518
3519 // Use the template to gurantee future WordPress behavior
3520 if( defined('POWERPRESS_FEED_TEMPLATE') ) {
3521 load_template( POWERPRESS_FEED_TEMPLATE );
3522 } else {
3523 load_template( POWERPRESS_ABSPATH . '/feed-podcast.php' );
3524 }
3525 }
3526
3527 function powerpress_template_redirect()
3528 {
3529 if( is_feed() && powerpress_is_custom_podcast_feed() )
3530 {
3531 // clean any existing output buffers to rm \n or WS from themes/plugins/wp-config (externally introduced)
3532 // ensures our feed starts with no newlines
3533 while( ob_get_level() > 0 ) {
3534 ob_end_clean();
3535 }
3536
3537 if ( defined('WPSEO_VERSION') && version_compare(WPSEO_VERSION, '7.7', '>=') && class_exists( 'WPSEO_Frontend' ) ) {
3538 $wpseo_frontend = WPSEO_Frontend::get_instance();
3539 if( !empty($wpseo_frontend) ) {
3540 remove_action( 'template_redirect', array( $wpseo_frontend, 'noindex_feed' ) );
3541 }
3542 }
3543
3544 remove_action('template_redirect', 'ol_feed_redirect'); // Remove this action so feedsmith doesn't redirect
3545 global $powerpress_feed;
3546 if( !isset($powerpress_feed['feed_redirect_url']) )
3547 $powerpress_feed['feed_redirect_url'] = '';
3548 $redirect_value = ( !empty($_GET['redirect'])? $_GET['redirect'] : false );
3549 $user_agent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
3550 if( is_array($powerpress_feed) && trim($powerpress_feed['feed_redirect_url']) != '' && !preg_match("/feedburner|feedsqueezer|feedvalidator/i", $user_agent ) && $redirect_value != 'no' )
3551 {
3552 if (function_exists('status_header'))
3553 status_header( 301 );
3554 header("Location: " . trim($powerpress_feed['feed_redirect_url']));
3555 header("HTTP/1.1 301 Moved Permanently");
3556 exit();
3557 }
3558 }
3559 }
3560
3561 add_action('template_redirect', 'powerpress_template_redirect', 0);
3562
3563
3564 function powerpress_rewrite_rules_array($array)
3565 {
3566 global $wp_rewrite;
3567 $settings = get_option('powerpress_general');
3568
3569 $podcast_feeds = array('podcast'=>true);
3570 if( isset($settings['custom_feeds']) && is_array($settings['custom_feeds']) )
3571 $podcast_feeds = array_merge($settings['custom_feeds'], $podcast_feeds );
3572
3573 $merged_slugs = '';
3574 foreach( $podcast_feeds as $feed_slug=> $feed_title )
3575 {
3576 if( $merged_slugs != '' )
3577 $merged_slugs .= '|';
3578 $merged_slugs .= $feed_slug;
3579 }
3580
3581 // $wp_rewrite->index most likely index.php
3582 $new_array[ 'feed/('.$merged_slugs.')/?$' ] = $wp_rewrite->index. '?feed='. $wp_rewrite->preg_index(1);
3583
3584 // If feature is not enabled, use the default permalinks
3585 if( empty($settings['permalink_feeds_only']) )
3586 return array_merge($new_array, $array);
3587
3588 global $wpdb;
3589 reset($podcast_feeds);
3590 foreach( $podcast_feeds as $feed_slug=> $feed_title )
3591 {
3592 $page_name_id = $wpdb->get_var("SELECT ID FROM {$wpdb->posts} WHERE post_name = '".$feed_slug."'");
3593 if( $page_name_id )
3594 {
3595 $new_array[ $feed_slug.'/?$' ] = $wp_rewrite->index. '?pagename='. $feed_slug.'&page_id='.$page_name_id;
3596 unset($podcast_feeds[ $feed_slug ]);
3597 continue;
3598 }
3599
3600 $category = get_category_by_slug($feed_slug);
3601 if( $category )
3602 {
3603 $new_array[ $feed_slug.'/?$' ] = $wp_rewrite->index. '?cat='. $category->term_id; // category_name='. $feed_slug .'&
3604 unset($podcast_feeds[ $feed_slug ]);
3605 }
3606 }
3607
3608 if( count($podcast_feeds) > 0 )
3609 {
3610 reset($podcast_feeds);
3611 $remaining_slugs = '';
3612 foreach( $podcast_feeds as $feed_slug=> $feed_title )
3613 {
3614 if( $remaining_slugs != '' )
3615 $remaining_slugs .= '|';
3616 $remaining_slugs .= $feed_slug;
3617 }
3618
3619 $new_array[ '('.$remaining_slugs.')/?$' ] = $wp_rewrite->index. '?pagename='. $wp_rewrite->preg_index(1);
3620 }
3621
3622 return array_merge($new_array, $array);
3623 }
3624
3625 add_filter('rewrite_rules_array', 'powerpress_rewrite_rules_array');
3626
3627
3628 function powerpress_pre_transient_rewrite_rules($return_rules)
3629 {
3630 global $wp_rewrite;
3631 $GeneralSettings = get_option('powerpress_general');
3632 if( !in_array('podcast', $wp_rewrite->feeds) )
3633 $wp_rewrite->feeds[] = 'podcast';
3634
3635 if( $GeneralSettings && isset($GeneralSettings['custom_feeds']) && is_array($GeneralSettings['custom_feeds']) )
3636 {
3637 foreach( $GeneralSettings['custom_feeds'] as $feed_slug=> $null )
3638 {
3639 if( !in_array($feed_slug, $wp_rewrite->feeds) )
3640 $wp_rewrite->feeds[] = $feed_slug;
3641 }
3642 }
3643
3644 return $return_rules;
3645 }
3646
3647 add_filter('pre_transient_rewrite_rules', 'powerpress_pre_transient_rewrite_rules');
3648
3649 function powerpress_init()
3650 {
3651 // Translation support loaded:
3652 load_plugin_textdomain('powerpress', // domain / keyword name of plugin
3653 POWERPRESS_ABSPATH .'/languages', // Absolute path
3654 basename(POWERPRESS_ABSPATH).'/languages' ); // relative path in plugins folder
3655
3656 /*
3657 ####
3658 # Defines that effect translation defined now:
3659 ####
3660 */
3661 // Set specific play and download labels for your installation of PowerPress
3662 if( !defined('POWERPRESS_LINKS_TEXT') )
3663 define('POWERPRESS_LINKS_TEXT', __('Podcast', 'powerpress') );
3664 if( !defined('POWERPRESS_DURATION_TEXT') )
3665 define('POWERPRESS_DURATION_TEXT', __('Duration', 'powerpress') );
3666 if( !defined('POWERPRESS_PLAY_IN_NEW_WINDOW_TEXT') )
3667 define('POWERPRESS_PLAY_IN_NEW_WINDOW_TEXT', __('Play in new window', 'powerpress') );
3668 if( !defined('POWERPRESS_DOWNLOAD_TEXT') )
3669 define('POWERPRESS_DOWNLOAD_TEXT', __('Download', 'powerpress') );
3670 if( !defined('POWERPRESS_PLAY_TEXT') )
3671 define('POWERPRESS_PLAY_TEXT', __('Play', 'powerpress') );
3672 if( !defined('POWERPRESS_EMBED_TEXT') )
3673 define('POWERPRESS_EMBED_TEXT', __('Embed', 'powerpress') );
3674 if( !defined('POWERPRESS_READ_TEXT') )
3675 define('POWERPRESS_READ_TEXT', __('Read', 'powerpress') );
3676
3677 $GeneralSettings = get_option('powerpress_general');
3678
3679
3680 if( empty($GeneralSettings['disable_appearance']) || $GeneralSettings['disable_appearance'] == false )
3681 {
3682 require_once( POWERPRESS_ABSPATH.'/powerpress-player.php');
3683 powerpressplayer_init($GeneralSettings);
3684 }
3685
3686 // Enable the playlist feature for PowerPress
3687 if( !empty($GeneralSettings['playlist_player']) ) // Either not set or set on
3688 {
3689 require_once(POWERPRESS_ABSPATH.'/powerpress-playlist.php');
3690 }
3691
3692 if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) || isset($GLOBALS['podcast_channel_active']) || defined('PODCASTING_VERSION') )
3693 return false; // Another podcasting plugin is enabled...
3694
3695 // If we are to process podpress data..
3696 if( !empty($GeneralSettings['process_podpress']) )
3697 {
3698 powerpress_podpress_redirect_check();
3699 }
3700
3701 // Add the podcast feeds;
3702 if( !defined('POWERPRESS_NO_PODCAST_FEED') )
3703 {
3704 add_feed('podcast', 'powerpress_do_podcast_feed');
3705 }
3706
3707 if( $GeneralSettings && isset($GeneralSettings['custom_feeds']) && is_array($GeneralSettings['custom_feeds']) )
3708 {
3709 foreach( $GeneralSettings['custom_feeds'] as $feed_slug=> $feed_title )
3710 {
3711 if( $feed_slug != 'podcast' )
3712 add_feed($feed_slug, 'powerpress_do_podcast_feed');
3713 }
3714 }
3715
3716 if( !empty($GeneralSettings['posttype_podcasting']) )
3717 {
3718 // Loop through the posttype podcasting settings and set the feeds for the custom post type slugs...
3719 global $wp_rewrite;
3720
3721
3722 $FeedSlugPostTypesArray = get_option('powerpress_posttype-podcasting'); // Changed field slightly so it does not conflict with a post type "podcasting"
3723 if( $FeedSlugPostTypesArray === false )
3724 {
3725 // Simple one-time fix...
3726 $FeedSlugPostTypesArray = get_option('powerpress_posttype_podcasting');
3727 if( empty($FeedSlugPostTypesArray) )
3728 $FeedSlugPostTypesArray = array();
3729 update_option('powerpress_posttype-podcasting', $FeedSlugPostTypesArray);
3730 if( !array_key_exists('title', $FeedSlugPostTypesArray) ) // AS long as it doesn't have post type specific settings...
3731 delete_option('powerpress_posttype_podcasting');
3732 }
3733
3734 if( empty($FeedSlugPostTypesArray) )
3735 {
3736 $FeedSlugPostTypesArray = array();
3737 }
3738 foreach( $FeedSlugPostTypesArray as $feed_slug=> $FeedSlugPostTypes )
3739 {
3740 if ( !in_array($feed_slug, $wp_rewrite->feeds) ) // we need to add this feed name
3741 {
3742 add_feed($feed_slug, 'powerpress_do_podcast_feed');
3743 foreach( $FeedSlugPostTypes as $post_type_slug=> $title )
3744 {
3745 add_rewrite_rule( '/'. $post_type_slug .'/feed/'. $feed_slug .'/?$', 'index.php?post_type='. $post_type_slug .'&feed='.$feed_slug, 'top' ); // capture the post type feeds
3746 add_rewrite_rule( '/'. $post_type_slug .'/feed/'. $feed_slug .'/?$', 'index.php?post_type='. $post_type_slug .'&feed='.$feed_slug, 'bottom' ); // capture the post type feeds
3747 }
3748 }
3749 }
3750 }
3751
3752 if( defined('GAWP_VERSION') )
3753 {
3754 add_filter('the_content', 'powerpress_yoast_gawp_fix', 120 );
3755 }
3756
3757 if( !empty($GeneralSettings['subscribe_links']) )
3758 {
3759 // 2 Subscribe page shortocde [powerpress_subscribe feedslug="podcast"]
3760 // 3 Subscribe sidebar widget: iTunes, RSS
3761 add_filter('powerpress_player_subscribe_links', 'powerpressplayer_link_subscribe_pre', 1, 3);
3762 add_filter('powerpress_player_subscribe_links', 'powerpressplayer_link_subscribe_post', 1000, 3);
3763 }
3764 wp_register_style(
3765 'powerpress-subscribe-style',
3766 powerpress_get_root_url() . 'css/subscribe.css',
3767 array(),
3768 '20141021',
3769 'all' );
3770
3771 if( !empty($GeneralSettings['rss_emoji']) ) {
3772 if( has_filter('the_content_feed', 'wp_staticize_emoji') ) {
3773 remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); // Remove the emoji images
3774 remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
3775 //add_filter( 'the_content_feed', 'wp_encode_emoji' ); // Convert an emoji to &#x1Fxxx;
3776 //add_filter( 'get_wp_title_rss', 'wp_encode_emoji' );
3777 }
3778 }
3779
3780 if( !defined('POWERPRESS_NO_REMOVE_WP_HEAD') ) {
3781 remove_action('wp_head', 'feed_links', 2);
3782 remove_action('wp_head', 'feed_links_extra', 3);
3783 }
3784
3785 add_filter( 'the_guid', 'powerpress_the_guid', 11 );
3786
3787
3788
3789 if (!isset($GeneralSettings)) {
3790 $GeneralSettings = get_option('powerpress_general');
3791 }
3792
3793 if (!empty($GeneralSettings['powerpress_network'])) {
3794 require_once( POWERPRESS_ABSPATH .'/powerpress-network.php');
3795 if (class_exists('PowerPressNetwork')) {
3796 $GLOBALS['ppn_object'] = new PowerPressNetwork('powerpressadmin_basic');
3797 $GLOBALS['ppn_object']->setDisplay();
3798
3799 add_action('admin_enqueue_scripts', 'powerpress_network_admin_enqueue_scripts');
3800 // frontend styles enqueued conditionally via ShortCode.php
3801 }
3802 }
3803
3804 }
3805
3806 add_action('init', 'powerpress_init', -100); // We need to add the feeds before other plugins start screwing with them
3807
3808 function powerpress_init_block() {
3809 if (function_exists('register_block_type')) {
3810 // register block(s)
3811 register_block_type(__DIR__ . '/blocks/player-block/build', array('render_callback' => function ($attributes, $content, $block) {
3812 $return = '';
3813 $GeneralSettings = get_option('powerpress_general');
3814
3815 // first, dropdown to select feed if necessary
3816 $is_backend = defined('REST_REQUEST') && REST_REQUEST == true && filter_input(INPUT_GET, 'context', FILTER_SANITIZE_SPECIAL_CHARS) == 'edit';
3817 $player_id = esc_attr( $attributes['id'] ?? '' );
3818 $player_feed_slug = esc_attr( $attributes['feed_slug'] ?? '' );
3819 if ($is_backend && !empty($GeneralSettings['custom_feeds']) && !empty($attributes['id'])) {
3820 $return .= "<select id='select-feed-{$player_id}' disabled>";
3821 if (empty($attributes['feed_slug'])) {
3822 $return .= '<option value="" class="pp-block-select">Channel: No selection</option>';
3823 } else {
3824 $return .= '<option value="" class="pp-block-select">Channel: No selection</option>';
3825 }
3826 if ($attributes['feed_slug'] == 'podcast') {
3827 $return .= '<option value="podcast" class="pp-block-select" selected>Channel: Main Feed</option>';
3828 } else {
3829 $return .= '<option value="podcast" class="pp-block-select">Channel: Main Feed</option>';
3830 }
3831 foreach ($GeneralSettings['custom_feeds'] as $slug => $title) {
3832 if (!empty($attributes['feed_slug']) && $attributes['feed_slug'] == $slug) {
3833 $return .= '<option value="' . $slug . '" class="pp-block-select" selected>' . 'Channel: ' . $title . '</option>';
3834 } else {
3835 $return .= '<option value="' . $slug . '" class="pp-block-select">' . 'Channel: ' . $title . '</option>';
3836 }
3837 }
3838 $return .= "</select>";
3839 }
3840
3841 // print shortcode on public side
3842 if (!$is_backend) {
3843 if (!empty($attributes['feed_slug'])) {
3844 return '[powerpress channel="' . $player_feed_slug . '"]';
3845 }
3846 return '';
3847 }
3848
3849 if (!empty($attributes['feed_slug'])) {
3850 // for editor, generate html from the shortcode and send it
3851 $return .= "<div>";
3852 $return .= "<p class='pp-block-error-{$player_id}'></p><div class='pp-block-sample'>";
3853 $return .= do_shortcode('[powerpress sample="1" channel="' . $player_feed_slug . '"]');
3854 $return .= "</div></div>";
3855 }
3856
3857 // randomly, sometimes, when we add the clientId on the frontend, the block does not re-render and just returns empty
3858 if (empty($return)) {
3859 $return = "<p class='alert alert-danger'>" . __("Something went wrong. Please delete this block and restart it.", "powerpress") . "</p>";
3860 }
3861 return $return;
3862 }, 'attributes' => array(
3863 'updated' => array(
3864 'type' => 'boolean',
3865 'default' => false,
3866 ),
3867 'feed_slug' => array(
3868 'type' => 'string',
3869 'default' => empty($GeneralSettings['custom_feeds']) ? 'podcast' : '',
3870 ),
3871 'id' => array(
3872 'type' => 'string',
3873 'default' => '',
3874 ),
3875 )));
3876 }
3877 }
3878
3879 add_action('init', 'powerpress_init_block', 100); // We need to add this AFTER everything initializes
3880
3881
3882 function powerpress_wp_print_styles()
3883 {
3884 $Settings = get_option('powerpress_general');
3885
3886 if( !empty($Settings['audio_player_max_width']) )
3887 {
3888 echo '<style type="text/css">'."\n";
3889 if( is_numeric($Settings['audio_player_max_width']) )
3890 $Settings['audio_player_max_width'] .= 'px';
3891 echo '.powerpress_player .wp-audio-shortcode { max-width: '.$Settings['audio_player_max_width'].'; }'."\n";
3892 echo '</style>'."\n";
3893 }
3894 }
3895
3896 add_action('wp_print_styles', 'powerpress_wp_print_styles');
3897
3898 function powerpress_request($qv)
3899 {
3900 if( !empty($qv['feed']) )
3901 {
3902 $podcast_feed_slug = false;
3903 if( $qv['feed'] == 'podcast' ) {
3904 $GeneralSettings = get_option('powerpress_general');
3905 if( empty($GeneralSettings['posttype_podcasting']) )
3906 $podcast_feed_slug = 'podcast';
3907 } else if( $qv['feed'] == 'rss' || $qv['feed'] == 'rss2' || $qv['feed'] == 'atom' || $qv['feed'] == 'rdf' || $qv['feed'] == 'feed' ) { // 'feed', 'rdf', 'rss', 'rss2', 'atom'
3908 // Skip
3909 } else {
3910 $GeneralSettings = get_option('powerpress_general');
3911 if( empty($GeneralSettings['posttype_podcasting']) && isset($GeneralSettings['custom_feeds']) && is_array($GeneralSettings['custom_feeds']) && !empty($GeneralSettings['custom_feeds'][ $qv['feed'] ] ) )
3912 $podcast_feed_slug = $qv['feed'];
3913
3914
3915 }
3916
3917 if( $podcast_feed_slug )
3918 {
3919 if( !defined('POWERPRESS_POSTTYPE_MIXING') && $qv['feed'] == 'podcast' ) {
3920 $qv['post_type'] = 'post';
3921 } else {
3922 $qv['post_type'] = get_post_types( array('public'=> true, 'capability_type'=>'post') );
3923 if( !empty($qv['post_type']['attachment']) )
3924 unset($qv['post_type']['attachment']);
3925 }
3926
3927 $FeedCustom = get_option('powerpress_feed_'.$podcast_feed_slug); // Get custom feed specific settings
3928 // See if the user set a custom post type only...
3929 if( !empty($FeedCustom) && !empty( $FeedCustom['custom_post_type']) )
3930 $qv['post_type'] = $FeedCustom['custom_post_type'];
3931 }
3932 }
3933 return $qv;
3934 }
3935
3936 add_filter('request', 'powerpress_request');
3937
3938
3939 function powerpress_plugins_loaded()
3940 {
3941 }
3942 add_action('plugins_loaded', 'powerpress_plugins_loaded');
3943
3944
3945 function powerpress_w3tc_can_print_comment($settings)
3946 {
3947 return false;
3948 }
3949
3950 // Disable minifying if W3TC is enabled
3951 function powerpress_w3tc_minify_enable($enable)
3952 {
3953 if( is_feed() )
3954 return false;
3955 return $enable;
3956 }
3957
3958 // Load the general feed settings for feeds handled by powerpress
3959 function powerpress_load_general_feed_settings()
3960 {
3961 global $wp_query;
3962 global $powerpress_feed;
3963
3964 if( $powerpress_feed !== false ) // If it is not false (either NULL or an array) then we already looked these settings up
3965 {
3966 $powerpress_feed = false;
3967
3968 // Get the powerpress settings
3969 $GeneralSettings = get_option('powerpress_general');
3970 if( !isset($GeneralSettings['custom_feeds']['podcast']) )
3971 $GeneralSettings['custom_feeds']['podcast'] = 'Podcast Feed'; // Fixes scenario where the user never configured the custom default podcast feed.
3972 if( empty($GeneralSettings['default_url']) )
3973 $GeneralSettings['default_url'] = '';
3974
3975 if( $GeneralSettings )
3976 {
3977 $FeedSettingsBasic = get_option('powerpress_feed'); // Get overall feed settings
3978 if( is_feed() && defined( 'WPCACHEHOME' ) && empty($GeneralSettings['allow_feed_comments']) )
3979 {
3980 global $wp_super_cache_comments;
3981 $wp_super_cache_comments = 0;
3982 }
3983
3984 if( is_feed() && defined('W3TC') && empty($GeneralSettings['allow_feed_comments']) )
3985 {
3986 add_filter( 'w3tc_can_print_comment', 'powerpress_w3tc_can_print_comment', 10, 1 );
3987 }
3988
3989 if( is_feed() && defined('W3TC') )
3990 {
3991 add_filter( 'w3tc_minify_enable', 'powerpress_w3tc_minify_enable');
3992 }
3993
3994 // If we're in advanced mode and we're dealing with a category feed we're extending, lets work with it...
3995 if( is_category() && isset($GeneralSettings['custom_cat_feeds']) && is_array($GeneralSettings['custom_cat_feeds']) && in_array( get_query_var('cat'), $GeneralSettings['custom_cat_feeds']) )
3996 {
3997 $cat_ID = get_query_var('cat');
3998 $FeedCustom = get_option('powerpress_cat_feed_'.$cat_ID); // Get custom feed specific settings
3999 $Feed = powerpress_merge_empty_feed_settings($FeedCustom, $FeedSettingsBasic);
4000
4001 $powerpress_feed = array();
4002 if( !empty($GeneralSettings['feed_accel']) )
4003 $powerpress_feed['feed_accel'] = true;
4004 $powerpress_feed['is_custom'] = true;
4005 $powerpress_feed['category'] = $cat_ID;
4006 $powerpress_feed['process_podpress'] = !empty($GeneralSettings['process_podpress']); // Category feeds could originate from Podpress
4007 $powerpress_feed['rss_language'] = ''; // default, let WordPress set the language
4008 $powerpress_feed['default_url'] = '';
4009 if( !empty($GeneralSettings['default_url']) )
4010 $powerpress_feed['default_url'] = rtrim($GeneralSettings['default_url'], '/') .'/';
4011 // switching from 'not set' 'yes' 'clean' to 'true' 'false'--for backwards compatibility, 'not set' will now be 'false'
4012 $explicit_array = array("false", "true", "false");
4013 $powerpress_feed['explicit'] = $explicit_array[$Feed['itunes_explicit']];
4014 if( !empty($Feed['itunes_talent_name']) )
4015 $powerpress_feed['itunes_talent_name'] = $Feed['itunes_talent_name'];
4016 else
4017 $powerpress_feed['itunes_talent_name'] = get_wp_title_rss();
4018 $powerpress_feed['enhance_itunes_summary'] = $Feed['enhance_itunes_summary'] ?? 0;
4019 if( !empty($GeneralSettings['seo_itunes']) )
4020 $powerpress_feed['enhance_itunes_summary'] = 1;
4021 if( !empty($GeneralSettings['disable_wptexturize']) )
4022 $powerpress_feed['disable_wptexturize'] = true;
4023 $powerpress_feed['posts_per_rss'] = false;
4024 if( !empty($Feed['posts_per_rss']) && is_numeric($Feed['posts_per_rss']) && $Feed['posts_per_rss'] > 0 )
4025 $powerpress_feed['posts_per_rss'] = $Feed['posts_per_rss'];
4026 $powerpress_feed['feed_redirect_url'] = '';
4027 if( !empty($Feed['feed_redirect_url']) )
4028 $powerpress_feed['feed_redirect_url'] = $Feed['feed_redirect_url'];
4029 if( !empty($Feed['itunes_author_post']) )
4030 $powerpress_feed['itunes_author_post'] = true;
4031 if( !empty($Feed['rss_language']) )
4032 $powerpress_feed['rss_language'] = $Feed['rss_language'];
4033
4034 if( !empty($GeneralSettings['podcast_embed_in_feed']) )
4035 $powerpress_feed['podcast_embed_in_feed'] = true;
4036 if( !empty($Feed['maximize_feed']) )
4037 $powerpress_feed['maximize_feed'] = true;
4038 if( !empty($Feed['unlock_podcast']) )
4039 $powerpress_feed['unlock_podcast'] = true;
4040 if( !empty($Feed['episode_itunes_image']) && !empty($Feed['itunes_image']) )
4041 $powerpress_feed['itunes_image'] = $Feed['itunes_image'];
4042 return;
4043 }
4044 else if( ( defined('POWERPRESS_TAXONOMY_PODCASTING') || !empty($GeneralSettings['taxonomy_podcasting']) ) && ( is_tag() || is_tax() ) )
4045 {
4046 // We need to get the term_id and the tax_id (tt_id)
4047 $term_slug = get_query_var('term');
4048 $taxonomy = get_query_var('taxonomy');
4049
4050 if( empty($term_slug) && empty($taxonomy) ) // Handle situation where tag is the taxonomy we're working with
4051 {
4052 $term_slug = get_query_var('tag');
4053 if( !empty($term_slug) )
4054 $taxonomy = 'post_tag';
4055 }
4056
4057 $term = false;
4058 if( !empty($term_slug) && !empty($taxonomy) )
4059 {
4060 $term = term_exists($term_slug, $taxonomy);
4061 }
4062
4063 if( !empty($term['term_taxonomy_id']) )
4064 {
4065 $FeedCustom = get_option('powerpress_taxonomy_'.$term['term_taxonomy_id'] ); // Get custom feed specific settings
4066 if( $FeedCustom )
4067 {
4068 $Feed = powerpress_merge_empty_feed_settings($FeedCustom, $FeedSettingsBasic);
4069
4070 $powerpress_feed = array();
4071 if( !empty($GeneralSettings['feed_accel']) )
4072 $powerpress_feed['feed_accel'] = true;
4073 $powerpress_feed['is_custom'] = true;
4074 $powerpress_feed['term_taxonomy_id'] = $term['term_taxonomy_id'];
4075 $powerpress_feed['process_podpress'] = false; // Taxonomy feeds will not originate from Podpress
4076 $powerpress_feed['rss_language'] = ''; // default, let WordPress set the language
4077 $powerpress_feed['default_url'] = rtrim($GeneralSettings['default_url'], '/') .'/';
4078 // switching from 'not set' 'yes' 'clean' to 'true' 'false'--for backwards compatibility, 'not set' will now be 'false'
4079 $explicit_array = array("false", "true", "false");
4080 $powerpress_feed['explicit'] = $explicit_array[$Feed['itunes_explicit']];
4081 if( !empty($Feed['itunes_talent_name']) )
4082 $powerpress_feed['itunes_talent_name'] = $Feed['itunes_talent_name'];
4083 else
4084 $powerpress_feed['itunes_talent_name'] = get_wp_title_rss();
4085 $powerpress_feed['enhance_itunes_summary'] = $Feed['enhance_itunes_summary'] ?? 0;
4086 if( !empty($GeneralSettings['seo_itunes']) )
4087 $powerpress_feed['enhance_itunes_summary'] = 1;
4088 if( !empty($GeneralSettings['disable_wptexturize']) )
4089 $powerpress_feed['disable_wptexturize'] = true;
4090 $powerpress_feed['posts_per_rss'] = false;
4091 if( !empty($Feed['posts_per_rss']) && is_numeric($Feed['posts_per_rss']) && $Feed['posts_per_rss'] > 0 )
4092 $powerpress_feed['posts_per_rss'] = $Feed['posts_per_rss'];
4093 if( !empty($Feed['feed_redirect_url']) )
4094 $powerpress_feed['feed_redirect_url'] = $Feed['feed_redirect_url'];
4095 if( !empty($Feed['itunes_author_post']) )
4096 $powerpress_feed['itunes_author_post'] = true;
4097 if( !empty($Feed['rss_language']) )
4098 $powerpress_feed['rss_language'] = $Feed['rss_language'];
4099
4100 if( !empty($GeneralSettings['podcast_embed_in_feed']) )
4101 $powerpress_feed['podcast_embed_in_feed'] = true;
4102 if( !empty($Feed['maximize_feed']) )
4103 $powerpress_feed['maximize_feed'] = true;
4104 if( !empty($Feed['unlock_podcast']) )
4105 $powerpress_feed['unlock_podcast'] = true;
4106 if( !empty($Feed['episode_itunes_image']) && !empty($Feed['itunes_image']) )
4107 $powerpress_feed['itunes_image'] = $Feed['itunes_image'];
4108 return;
4109 }
4110 }
4111 }
4112
4113 $feed_slug = get_query_var('feed');
4114 // Are we dealing with a custom podcast channel or a custom post type podcast feed...
4115 if( !empty($GeneralSettings['posttype_podcasting']) || isset($GeneralSettings['custom_feeds'][ $feed_slug ]) )
4116 {
4117 $Feed = false;
4118 if( !empty($GeneralSettings['posttype_podcasting']) )
4119 {
4120 $post_type = get_query_var('post_type');
4121
4122 if( !empty($post_type) )
4123 {
4124 if ( is_array( $post_type ) ) {
4125 $post_type = reset( $post_type ); // get first element in array
4126 }
4127
4128 // Get the settings for this podcast post type
4129 $PostTypeSettingsArray = get_option('powerpress_posttype_'. $post_type);
4130 if( !empty($PostTypeSettingsArray[ $feed_slug ]) )
4131 {
4132 $FeedCustom = $PostTypeSettingsArray[ $feed_slug ];
4133 $Feed = powerpress_merge_empty_feed_settings($FeedCustom, $FeedSettingsBasic);
4134 $Feed['post_type'] = $post_type;
4135 }
4136 }
4137 }
4138 if( empty($Feed) && isset($GeneralSettings['custom_feeds'][ $feed_slug ]) )
4139 {
4140 $FeedCustom = get_option('powerpress_feed_'.$feed_slug); // Get custom feed specific settings
4141 $Feed = powerpress_merge_empty_feed_settings($FeedCustom, $FeedSettingsBasic, ($feed_slug == 'podcast') );
4142 }
4143
4144 if( $Feed )
4145 {
4146 $powerpress_feed = array();
4147 if( !empty($GeneralSettings['feed_accel']) )
4148 $powerpress_feed['feed_accel'] = true;
4149 $powerpress_feed['is_custom'] = true;
4150 $powerpress_feed['feed-slug'] = $feed_slug;
4151 if( !empty($Feed['post_type']) )
4152 $powerpress_feed['post_type'] = $Feed['post_type'];
4153 $powerpress_feed['process_podpress'] = ($feed_slug=='podcast'? !empty($GeneralSettings['process_podpress']): false); // We don't touch podpress data for custom feeds
4154 $powerpress_feed['rss_language'] = ''; // RSS language should be set by WordPress by default
4155 $powerpress_feed['default_url'] = '';
4156 if( !empty($powerpress_feed['default_url']) )
4157 $powerpress_feed['default_url'] = rtrim($GeneralSettings['default_url'], '/') .'/';
4158 // switching from 'not set' 'yes' 'clean' to 'true' 'false'--for backwards compatibility, 'not set' will now be 'false'
4159 $explicit = ["false", "true", "false"];
4160 $powerpress_feed['explicit'] = 'false';
4161 if( !empty($Feed['itunes_explicit']) )
4162 $powerpress_feed['explicit'] = $explicit[ $Feed['itunes_explicit'] ];
4163 if( !empty($Feed['itunes_talent_name']) )
4164 $powerpress_feed['itunes_talent_name'] = $Feed['itunes_talent_name'];
4165 else
4166 $powerpress_feed['itunes_talent_name'] = get_wp_title_rss();
4167 $powerpress_feed['enhance_itunes_summary'] = $Feed['enhance_itunes_summary'] ?? 0;
4168 if( !empty($GeneralSettings['seo_itunes']) )
4169 $powerpress_feed['enhance_itunes_summary'] = 1;
4170 if( !empty($GeneralSettings['disable_wptexturize']) )
4171 $powerpress_feed['disable_wptexturize'] = true;
4172 $powerpress_feed['posts_per_rss'] = false;
4173 if( !empty($Feed['posts_per_rss']) && is_numeric($Feed['posts_per_rss']) && $Feed['posts_per_rss'] > 0 )
4174 $powerpress_feed['posts_per_rss'] = $Feed['posts_per_rss'];
4175 if( !empty($Feed['feed_redirect_url']) )
4176 $powerpress_feed['feed_redirect_url'] = $Feed['feed_redirect_url'];
4177 if( !empty($Feed['itunes_author_post'] ) )
4178 $powerpress_feed['itunes_author_post'] = true;
4179 if( !empty($Feed['rss_language']) )
4180 $powerpress_feed['rss_language'] = $Feed['rss_language'];
4181 if( !empty($GeneralSettings['podcast_embed_in_feed']) )
4182 $powerpress_feed['podcast_embed_in_feed'] = true;
4183 if( !empty($Feed['maximize_feed']) )
4184 $powerpress_feed['maximize_feed'] = true;
4185 if( !empty($Feed['unlock_podcast']) )
4186 $powerpress_feed['unlock_podcast'] = true;
4187 if( !empty($Feed['episode_itunes_image']) && !empty($Feed['itunes_image']) )
4188 $powerpress_feed['itunes_image'] = $Feed['itunes_image'];
4189 return;
4190 }
4191 }
4192
4193 if ($FeedSettingsBasic === false || !is_array($FeedSettingsBasic)) {
4194 $FeedSettingsBasic = [];
4195 }
4196
4197 if( !isset($FeedSettingsBasic['apply_to']) )
4198 $FeedSettingsBasic['apply_to'] = 1;
4199
4200 // We fell this far,we must be in simple mode or the user never saved customized their custom feed settings
4201 switch( $FeedSettingsBasic['apply_to'] )
4202 {
4203 case 0: // enhance only the podcast feed added by PowerPress, with the logic above this code should never be reached but it is added for readability.
4204 {
4205 if( $feed_slug != 'podcast' )
4206 break;
4207 } // important: no break here!
4208 case 2: // RSS2 Main feed and podcast feed added by PowerPress only
4209 {
4210 if( $feed_slug != 'feed' && $feed_slug != 'rss2' && $feed_slug != 'podcast' )
4211 break; // We're only adding podcasts to the rss2 feed in this situation
4212
4213 if( $wp_query->is_category ) // don't touch the category feeds...
4214 break;
4215
4216 if( $wp_query->is_tag ) // don't touch the tag feeds...
4217 break;
4218
4219 if( $wp_query->is_comment_feed ) // don't touch the comments feeds...
4220 break;
4221 } // important: no break here!
4222 case 1: // All feeds
4223 {
4224 $powerpress_feed = array(); // Only store what's needed for each feed item
4225 if( !empty($GeneralSettings['feed_accel']) )
4226 $powerpress_feed['feed_accel'] = true;
4227 $powerpress_feed['is_custom'] = false; // ($feed_slug == 'podcast'?true:false);
4228 $powerpress_feed['feed-slug'] = $feed_slug;
4229 $powerpress_feed['process_podpress'] = !empty($GeneralSettings['process_podpress']); // We don't touch podpress data for custom feeds
4230 $powerpress_feed['default_url'] = '';
4231 if( !empty($GeneralSettings['default_url']) )
4232 $powerpress_feed['default_url'] = rtrim($GeneralSettings['default_url'], '/') .'/';
4233 // switching from 'not set' 'yes' 'clean' to 'true' 'false'--for backwards compatibility, 'not set' will now be 'false'
4234 $explicit = array("false", "true", "false");
4235 $powerpress_feed['explicit'] = 'false';
4236 if( !empty($FeedSettingsBasic['itunes_explicit']) )
4237 $powerpress_feed['explicit'] = $explicit[$FeedSettingsBasic['itunes_explicit']];
4238 if( !empty($FeedSettingsBasic['itunes_talent_name']) )
4239 $powerpress_feed['itunes_talent_name'] = $FeedSettingsBasic['itunes_talent_name'];
4240 else
4241 $powerpress_feed['itunes_talent_name'] = get_wp_title_rss();
4242 if( !empty($GeneralSettings['disable_wptexturize']) )
4243 $powerpress_feed['disable_wptexturize'] = true;
4244 $powerpress_feed['posts_per_rss'] = false;
4245 if( !empty($FeedSettingsBasic['posts_per_rss']) && is_numeric($FeedSettingsBasic['posts_per_rss']) && $FeedSettingsBasic['posts_per_rss'] > 0 )
4246 $powerpress_feed['posts_per_rss'] = $FeedSettingsBasic['posts_per_rss'];
4247 if( !empty($FeedSettingsBasic['itunes_author_post']) )
4248 $powerpress_feed['itunes_author_post'] = true;
4249 $powerpress_feed['rss_language'] = ''; // Cannot set the language setting in simple mode
4250 if( !empty($GeneralSettings['podcast_embed_in_feed']) )
4251 $powerpress_feed['podcast_embed_in_feed'] = true;
4252 if( !empty($FeedSettingsBasic['episode_itunes_image']) && !empty($FeedSettingsBasic['itunes_image']) )
4253 $powerpress_feed['itunes_image'] = $FeedSettingsBasic['itunes_image'];
4254
4255 }; break;
4256 // All other cases we let fall through
4257 }
4258 }
4259 }
4260 }
4261
4262 // Returns true of the feed should be treated as a podcast feed
4263 function powerpress_is_podcast_feed()
4264 {
4265 if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) || isset($GLOBALS['podcast_channel_active']) || defined('PODCASTING_VERSION') )
4266 return false; // Another podcasting plugin is enabled...
4267
4268 global $powerpress_feed;
4269 if( $powerpress_feed !== false && !is_array($powerpress_feed) )
4270 powerpress_load_general_feed_settings();
4271 if( $powerpress_feed === false )
4272 return false;
4273 return true;
4274 }
4275
4276 // Returns true if the feed is a custom feed added by PowerPress
4277 function powerpress_is_custom_podcast_feed()
4278 {
4279 if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) || isset($GLOBALS['podcast_channel_active']) || defined('PODCASTING_VERSION') )
4280 return false; // Another podcasting plugin is enabled...
4281
4282 global $powerpress_feed;
4283 if( $powerpress_feed !== false && !is_array($powerpress_feed) )
4284 powerpress_load_general_feed_settings();
4285 if( $powerpress_feed === false )
4286 return false;
4287 return $powerpress_feed['is_custom'];
4288 }
4289
4290 function powerpress_posts_fields($cols)
4291 {
4292 if( !is_feed() )
4293 return $cols;
4294
4295 if( is_category() || is_tag() || is_tax() ) {
4296 if( get_query_var('feed') !== 'podcast' )
4297 return $cols;
4298 }
4299
4300 if( powerpress_is_custom_podcast_feed() || get_query_var('feed') === 'podcast' )
4301 {
4302 if( !empty($GLOBALS['powerpress_feed']['feed_accel']) )
4303 {
4304 $feed_slug = get_query_var('feed');
4305 global $wpdb;
4306 $cols .= ", pp_{$wpdb->postmeta}.meta_value AS podcast_meta_value ";
4307 }
4308 }
4309
4310 return $cols;
4311 }
4312 //$fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
4313 add_filter('posts_fields', 'powerpress_posts_fields' );
4314
4315 function powerpress_posts_join($join)
4316 {
4317 if( !is_feed() )
4318 return $join;
4319
4320 if( is_category() || is_tag() || is_tax() ) {
4321 if( get_query_var('feed') !== 'podcast' )
4322 return $join;
4323 }
4324
4325 if( powerpress_is_custom_podcast_feed() || get_query_var('feed') === 'podcast' )
4326 {
4327 global $wpdb;
4328 $join .= " INNER JOIN {$wpdb->postmeta} AS pp_{$wpdb->postmeta} ";
4329 $join .= " ON {$wpdb->posts}.ID = pp_{$wpdb->postmeta}.post_id ";
4330 }
4331
4332 return $join;
4333 }
4334
4335 add_filter('posts_join', 'powerpress_posts_join' );
4336
4337 function powerpress_posts_where($where)
4338 {
4339 if( !is_feed() )
4340 return $where;
4341 if( is_category() || is_tag() || is_tax() ) {
4342 if( get_query_var('feed') !== 'podcast' )
4343 return $where;
4344 }
4345
4346 if( powerpress_is_custom_podcast_feed() || get_query_var('feed') === 'podcast' )
4347 {
4348 global $wpdb, $powerpress_feed;
4349 $where .= " AND (";
4350
4351 if( powerpress_is_custom_podcast_feed() && get_query_var('feed') !== 'podcast' )
4352 $where .= " pp_{$wpdb->postmeta}.meta_key = '_". get_query_var('feed') .":enclosure' AND pp_{$wpdb->postmeta}.meta_value NOT LIKE 'no%' ";
4353 else
4354 $where .= " pp_{$wpdb->postmeta}.meta_key = 'enclosure' AND pp_{$wpdb->postmeta}.meta_value NOT LIKE 'no%' ";
4355
4356 // Include Podpress data if exists...
4357 if( !empty($powerpress_feed['process_podpress']) && get_query_var('feed') === 'podcast' )
4358 $where .= " OR pp_{$wpdb->postmeta}.meta_key = 'podPressMedia' OR pp_{$wpdb->postmeta}.meta_key = '_podPressMedia' ";
4359
4360 $where .= ") ";
4361 }
4362 return $where;
4363 }
4364
4365 add_filter('posts_where', 'powerpress_posts_where' );
4366
4367 // Add the groupby needed for enclosures only
4368 function powerpress_posts_groupby($groupby)
4369 {
4370 if( !is_feed() )
4371 return $groupby;
4372
4373 if( is_category() || is_tag() || is_tax() ) {
4374 if( get_query_var('feed') !== 'podcast' )
4375 return $groupby;
4376 }
4377
4378 if( powerpress_is_custom_podcast_feed() || get_query_var('feed') === 'podcast' )
4379 {
4380 global $wpdb;
4381 $groupby = " {$wpdb->posts}.ID ";
4382 }
4383 return $groupby;
4384 }
4385 add_filter('posts_groupby', 'powerpress_posts_groupby');
4386
4387 function powerpress_post_limits($limits)
4388 {
4389 if( !is_feed() )
4390 return $limits;
4391
4392 if( powerpress_is_custom_podcast_feed() || get_query_var('feed') === 'podcast' )
4393 {
4394 global $powerpress_feed;
4395 if( !empty($powerpress_feed['posts_per_rss']) && preg_match('/^(\d)+$/', trim($powerpress_feed['posts_per_rss'])) )
4396 $limits = "LIMIT 0, {$powerpress_feed['posts_per_rss']}";
4397 }
4398 return $limits;
4399 }
4400 add_filter('post_limits', 'powerpress_post_limits');
4401
4402
4403 function powerpress_do_all_pings()
4404 {
4405 global $wpdb;
4406 $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key = '_encloseme' ");
4407
4408 // Now call the WordPress do_all_pings()...
4409 do_all_pings();
4410 remove_action('do_pings', 'do_all_pings');
4411 }
4412
4413 remove_action('do_pings', 'do_all_pings');
4414 add_action('do_pings', 'powerpress_do_all_pings', 1, 1);
4415
4416 /*
4417 Helper functions:
4418 */
4419 function powerpress_podpress_redirect_check()
4420 {
4421 if( preg_match('/podpress_trac\/([^\/]+)\/([^\/]+)\/([^\/]+)\/(.*)$/', $_SERVER['REQUEST_URI'], $matches) )
4422 {
4423 $post_id = $matches[2];
4424 $mediaNum = $matches[3];
4425 //$filename = $matches[4];
4426 //$method = $matches[1];
4427
4428 if( is_numeric($post_id) && is_numeric($mediaNum))
4429 {
4430 $EpisodeData = powerpress_get_enclosure_data_podpress($post_id, $mediaNum);
4431 if( $EpisodeData && isset($EpisodeData['url']) )
4432 {
4433 if( strpos($EpisodeData['url'], 'http://' ) !== 0 && strpos($EpisodeData['url'], 'https://' ) !== 0 )
4434 {
4435 die('Error occurred obtaining the URL for the requested media file.');
4436 exit;
4437 }
4438
4439 $EnclosureURL = str_replace(' ', '%20', $EpisodeData['url']);
4440 header('Location: '.$EnclosureURL, true, 302);
4441 header('Content-Length: 0');
4442 exit;
4443 }
4444 // Let the WordPress 404 page load as normal
4445 }
4446 }
4447 }
4448
4449 function the_powerpress_content()
4450 {
4451 echo get_the_powerpress_content();
4452 }
4453
4454 /** returns the player and download link HTML for podcast episodes attached to the current post */
4455 function get_the_powerpress_content()
4456 {
4457 global $post;
4458
4459 if( defined('PODPRESS_VERSION') || isset($GLOBALS['podcasting_player_id']) || isset($GLOBALS['podcast_channel_active']) || defined('PODCASTING_VERSION') )
4460 return '';
4461
4462 if( function_exists('post_password_required') )
4463 {
4464 if( post_password_required($post) )
4465 return '';
4466 }
4467
4468 // PowerPress settings:
4469 $GeneralSettings = get_option('powerpress_general');
4470
4471 // No player or links to add to content...
4472 if( !empty($GeneralSettings['disable_appearance']) )
4473 return '';
4474
4475 if( !isset($GeneralSettings['custom_feeds']) )
4476 $GeneralSettings['custom_feeds'] = array('podcast'=>'Default Podcast Feed');
4477
4478 // Re-order so the default podcast episode is the top most...
4479 $Temp = $GeneralSettings['custom_feeds'];
4480 $GeneralSettings['custom_feeds'] = array();
4481 $GeneralSettings['custom_feeds']['podcast'] = 'Default Podcast Feed';
4482
4483 if (is_array($Temp)){
4484 foreach ($Temp as $feed_slug => $feed_title) {
4485 if ($feed_slug == 'podcast')
4486 continue;
4487 $GeneralSettings['custom_feeds'][$feed_slug] = $feed_title;
4488 }
4489 }
4490 // Handle post type feeds....
4491 if( !empty($GeneralSettings['posttype_podcasting']) )
4492 {
4493 $post_type = get_query_var('post_type');
4494 if ( is_array( $post_type ) ) {
4495 $post_type = reset( $post_type ); // get first element in array
4496 }
4497
4498 // Get the feed slugs and titles for this post type
4499 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$post_type);
4500 // Loop through this array of post type settings...
4501 if( !empty($PostTypeSettingsArray) )
4502 {
4503 switch($post_type)
4504 {
4505 case 'post':
4506 case 'page': {
4507 // Do nothing!, we want the default podcast to appear in these post types
4508 }; break;
4509 default: {
4510 if( !empty($post_type) && empty($PostTypeSettingsArray['podcast']) )
4511 unset($GeneralSettings['custom_feeds']['podcast']); // special case, we do not want an accidental podcast episode to appear in a custom post type if the feature is enabled
4512 }; break;
4513 }
4514
4515 if (is_array($PostTypeSettingsArray)) {
4516 foreach ($PostTypeSettingsArray as $feed_slug => $postTypeSettings) {
4517 if (!empty($postTypeSettings['title']))
4518 $GeneralSettings['custom_feeds'][$feed_slug] = $postTypeSettings['title'];
4519 else
4520 $GeneralSettings['custom_feeds'][$feed_slug] = $feed_slug;
4521 }
4522 }
4523 }
4524 }
4525
4526 if( !isset($GeneralSettings['display_player']) )
4527 $GeneralSettings['display_player'] = 1;
4528 if( !isset($GeneralSettings['player_function']) )
4529 $GeneralSettings['player_function'] = 1;
4530 if( !isset($GeneralSettings['podcast_link']) )
4531 $GeneralSettings['podcast_link'] = 1;
4532
4533 // Figure out which players are alerady in the body of the page...
4534 $ExcludePlayers = array();
4535 if( isset($GeneralSettings['disable_player']) )
4536 $ExcludePlayers = $GeneralSettings['disable_player']; // automatically disable the players configured
4537
4538 // LOOP HERE TO DISPLAY EACH MEDIA TYPE
4539 $new_content = '';
4540 foreach( $GeneralSettings['custom_feeds'] as $feed_slug=> $feed_title )
4541 {
4542 // Get the enclosure data
4543 $EpisodeData = powerpress_get_enclosure_data($post->ID, $feed_slug);
4544
4545 if( !$EpisodeData && !empty($GeneralSettings['process_podpress']) && $feed_slug == 'podcast' )
4546 $EpisodeData = powerpress_get_enclosure_data_podpress($post->ID);
4547
4548 if( !$EpisodeData || !$EpisodeData['url'] )
4549 continue;
4550
4551 // Just in case, if there's no URL lets escape!
4552 if( !$EpisodeData['url'] )
4553 continue;
4554
4555 // If the player is not already inserted in the body of the post using the shortcode...
4556 //if( preg_match('/\[powerpress(.*)\]/is', $content) == 0 )
4557 if( !isset($ExcludePlayers[ $feed_slug ]) ) // If the player is not in our exclude list because it's already in the post body somewhere...
4558 {
4559 if( isset($GeneralSettings['premium_caps']) && $GeneralSettings['premium_caps'] && !powerpress_premium_content_authorized($feed_slug) )
4560 {
4561 $new_content .= powerpress_premium_content_message($post->ID, $feed_slug, $EpisodeData);
4562 }
4563 else
4564 {
4565 if( $GeneralSettings['player_function'] != 3 && $GeneralSettings['player_function'] != 0 ) // Play in new window only or disabled
4566 {
4567 do_action('wp_powerpress_player_scripts');
4568 $AddDefaultPlayer = empty($EpisodeData['no_player']);
4569
4570 if( $EpisodeData && !empty($EpisodeData['embed']) )
4571 {
4572 $new_content .= SanitizeEmbed(trim($EpisodeData['embed']));
4573 if( !empty($GeneralSettings['embed_replace_player']) )
4574 $AddDefaultPlayer = false;
4575 }
4576
4577 if( $AddDefaultPlayer )
4578 {
4579 $image = '';
4580 $width = '';
4581 $height = '';
4582 if( isset($EpisodeData['image']) && $EpisodeData['image'] != '' )
4583 $image = $EpisodeData['image'];
4584 if( !empty($EpisodeData['width']) && is_numeric($EpisodeData['width']) )
4585 $width = $EpisodeData['width'];
4586 if( !empty($EpisodeData['height']) && is_numeric($EpisodeData['height']) )
4587 $height = $EpisodeData['height'];
4588
4589 $new_content .= apply_filters('powerpress_player', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData );
4590 }
4591 }
4592
4593 if( !isset($EpisodeData['no_links']) )
4594 {
4595 do_action('wp_powerpress_player_scripts');
4596 $new_content .= apply_filters('powerpress_player_links', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData );
4597 $new_content .= apply_filters('powerpress_player_subscribe_links', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData );
4598 }
4599 }
4600 }
4601 }
4602
4603 return $new_content;
4604 }
4605
4606
4607
4608 // Adds content types that are missing from the default wp_check_filetype function
4609 function powerpress_get_contenttype($file, $use_wp_check_filetype = true)
4610 {
4611 // strip query string and fragment before parsing (pathinfo doesnt handle URLs)
4612 $path = parse_url($file, PHP_URL_PATH) ?: $file;
4613 $parts = pathinfo($path);
4614 if( !empty($parts['extension']) )
4615 {
4616 switch( strtolower($parts['extension']) )
4617 {
4618 // HLS formats
4619 case 'm3u8':
4620 return 'application/vnd.apple.mpegurl'; // Standard MIME type
4621 case 'm3u':
4622 return 'audio/mpegurl'; // Legacy Playlist format
4623 case 'ts':
4624 return 'video/mp2t'; // HLS Transport Stream (MPEG-2)
4625 // Audio formats
4626 case 'mp3': // most common
4627 case 'mpga':
4628 case 'mp2':
4629 case 'mp2a':
4630 case 'm2a':
4631 case 'm3a':
4632 return 'audio/mpeg';
4633 case 'm4a':
4634 return 'audio/x-m4a';
4635 case 'm4b': // Audio book format
4636 return 'audio/m4b';
4637 case 'm4r': // iPhone ringtone format
4638 return 'audio/m4r';
4639 // OGG Internet content types as set forth by rfc5334 (http://tools.ietf.org/html/rfc5334)
4640 case 'opus':
4641 case 'oga':
4642 case 'spx':
4643 return 'audio/ogg';
4644 case 'wma':
4645 return 'audio/x-ms-wma';
4646 case 'wax':
4647 return 'audio/x-ms-wax';
4648 case 'ra':
4649 case 'ram':
4650 return 'audio/x-pn-realaudio';
4651 case 'mp4a':
4652 return 'audio/mp4';
4653 case 'aac':
4654 return 'audio/aac';
4655
4656 // Video formats
4657 case 'm4v':
4658 return 'video/x-m4v';
4659 case 'mpeg':
4660 case 'mpg':
4661 case 'mpe':
4662 case 'm1v':
4663 case 'm2v':
4664 return 'video/mpeg';
4665 case 'mp4':
4666 case 'mp4v':
4667 case 'mpg4':
4668 return 'video/mp4';
4669 case 'asf':
4670 case 'asx':
4671 return 'video/x-ms-asf';
4672 case 'wmx':
4673 return 'video/x-ms-wmx';
4674 case 'avi':
4675 return 'video/x-msvideo';
4676 case 'wmv':
4677 return 'video/x-ms-wmv'; // Check this
4678 case 'flv':
4679 return 'video/x-flv';
4680 case 'mov':
4681 case 'qt':
4682 return 'video/quicktime';
4683 case 'divx':
4684 return 'video/divx';
4685 case '3gp':
4686 return 'video/3gpp';
4687 case 'webm':
4688 return 'video/webm';
4689 case 'ogg': {
4690 if( !defined('POWERPRESS_OGG_VIDEO') )
4691 return 'audio/ogg';
4692 } // Let this fall through as ogg/video
4693 case 'ogv':
4694 return 'video/ogg';
4695
4696 // rarely used
4697 case 'mid':
4698 case 'midi':
4699 return 'audio/midi';
4700 case 'wav':
4701 return 'audio/wav';
4702 case 'aa':
4703 return 'audio/audible';
4704 case 'pdf':
4705 return 'application/pdf';
4706 case 'torrent':
4707 return 'application/x-bittorrent';
4708 case 'swf':
4709 return 'application/x-shockwave-flash';
4710 case 'ogx':
4711 return 'application/ogg';
4712
4713 // Most recently added by Apple:
4714 case 'epub':
4715 return 'document/x-epub';
4716
4717 // Content type for transcript files
4718 case 'srt':
4719 return 'application/srt';
4720 case 'json':
4721 return 'application/json';
4722 case 'vtt':
4723 return 'text/vtt';
4724 case 'html':
4725 return 'text/html';
4726 case 'txt':
4727 return 'text/plain';
4728
4729 default: // Let it fall through
4730 }
4731 }
4732
4733 // Last case let wordpress detect it:
4734 if( $use_wp_check_filetype )
4735 {
4736 $FileType = wp_check_filetype($file);
4737 if( $FileType && isset($FileType['type']) )
4738 return $FileType['type'];
4739 }
4740 return '';
4741 }
4742
4743
4744 function powerpress_itunes_categories($PrefixSubCategories = false)
4745 {
4746 $temp = array();
4747 $temp['01-00'] = 'Arts';
4748 $temp['01-01'] = 'Design';
4749 $temp['01-02'] = 'Fashion & Beauty';
4750 $temp['01-03'] = 'Food';
4751 $temp['01-04'] = 'Literature';
4752 $temp['01-05'] = 'Performing Arts';
4753 $temp['01-06'] = 'Visual Arts';
4754
4755 $temp['02-00'] = 'Business';
4756 $temp['02-01'] = 'Business News';
4757 $temp['02-02'] = 'Careers';
4758 $temp['02-03'] = 'Investing';
4759 $temp['02-04'] = 'Management & Marketing';
4760 $temp['02-05'] = 'Shopping';
4761
4762 $temp['03-00'] = 'Comedy';
4763
4764 $temp['04-00'] = 'Education';
4765 $temp['04-01'] = 'Education Technology';
4766 $temp['04-02'] = 'Higher Education';
4767 $temp['04-03'] = 'K-12';
4768 $temp['04-04'] = 'Language Courses';
4769 $temp['04-05'] = 'Training';
4770
4771 $temp['05-00'] = 'Games & Hobbies';
4772 $temp['05-01'] = 'Automotive';
4773 $temp['05-02'] = 'Aviation';
4774 $temp['05-03'] = 'Hobbies';
4775 $temp['05-04'] = 'Other Games';
4776 $temp['05-05'] = 'Video Games';
4777
4778 $temp['06-00'] = 'Government & Organizations';
4779 $temp['06-01'] = 'Local';
4780 $temp['06-02'] = 'National';
4781 $temp['06-03'] = 'Non-Profit';
4782 $temp['06-04'] = 'Regional';
4783
4784 $temp['07-00'] = 'Health';
4785 $temp['07-01'] = 'Alternative Health';
4786 $temp['07-02'] = 'Fitness & Nutrition';
4787 $temp['07-03'] = 'Self-Help';
4788 $temp['07-04'] = 'Sexuality';
4789
4790 $temp['08-00'] = 'Kids & Family';
4791
4792 $temp['09-00'] = 'Music';
4793
4794 $temp['10-00'] = 'News & Politics';
4795
4796 $temp['11-00'] = 'Religion & Spirituality';
4797 $temp['11-01'] = 'Buddhism';
4798 $temp['11-02'] = 'Christianity';
4799 $temp['11-03'] = 'Hinduism';
4800 $temp['11-04'] = 'Islam';
4801 $temp['11-05'] = 'Judaism';
4802 $temp['11-06'] = 'Other';
4803 $temp['11-07'] = 'Spirituality';
4804
4805 $temp['12-00'] = 'Science & Medicine';
4806 $temp['12-01'] = 'Medicine';
4807 $temp['12-02'] = 'Natural Sciences';
4808 $temp['12-03'] = 'Social Sciences';
4809
4810 $temp['13-00'] = 'Society & Culture';
4811 $temp['13-01'] = 'History';
4812 $temp['13-02'] = 'Personal Journals';
4813 $temp['13-03'] = 'Philosophy';
4814 $temp['13-04'] = 'Places & Travel';
4815
4816 $temp['14-00'] = 'Sports & Recreation';
4817 $temp['14-01'] = 'Amateur';
4818 $temp['14-02'] = 'College & High School';
4819 $temp['14-03'] = 'Outdoor';
4820 $temp['14-04'] = 'Professional';
4821
4822 $temp['15-00'] = 'Technology';
4823 $temp['15-01'] = 'Gadgets';
4824 $temp['15-02'] = 'Tech News';
4825 $temp['15-03'] = 'Podcasting';
4826 $temp['15-04'] = 'Software How-To';
4827
4828 $temp['16-00'] = 'TV & Film';
4829
4830 if( $PrefixSubCategories )
4831 {
4832 foreach( $temp as $key=> $val )
4833 {
4834 $parts = explode('-', $key);
4835 $cat = $parts[0];
4836 $subcat = $parts[1];
4837
4838 if( $subcat != '00' )
4839 $temp[$key] = $temp[$cat.'-00'].' > '.$val;
4840 }
4841 reset($temp);
4842 }
4843
4844 return $temp;
4845 }
4846
4847 /**
4848 * Categories for 2019+ Apple Podcast directory
4849 */
4850 function powerpress_apple_categories($PrefixSubCategories = false) {
4851 $temp = array();
4852 $temp['01-00'] = 'Arts';
4853 $temp['01-01'] = 'Books';
4854 $temp['01-02'] = 'Design';
4855 $temp['01-03'] = 'Fashion & Beauty';
4856 $temp['01-04'] = 'Food';
4857 $temp['01-05'] = 'Performing Arts';
4858 $temp['01-06'] = 'Visual Arts';
4859
4860 $temp['02-00'] = 'Business';
4861 $temp['02-01'] = 'Careers';
4862 $temp['02-02'] = 'Entrepreneurship';
4863 $temp['02-03'] = 'Investing';
4864 $temp['02-04'] = 'Management';
4865 $temp['02-05'] = 'Marketing';
4866 $temp['02-06'] = 'Non-Profit';
4867
4868 $temp['03-00'] = 'Comedy';
4869 $temp['03-01'] = 'Comedy Interviews';
4870 $temp['03-02'] = 'Improv';
4871 $temp['03-03'] = 'Stand-Up';
4872
4873 $temp['04-00'] = 'Education';
4874 $temp['04-01'] = 'Courses';
4875 $temp['04-02'] = 'How To';
4876 $temp['04-03'] = 'Language Learning';
4877 $temp['04-04'] = 'Self-Improvement';
4878
4879 $temp['05-00'] = 'Fiction';
4880 $temp['05-01'] = 'Comedy Fiction';
4881 $temp['05-02'] = 'Drama';
4882 $temp['05-03'] = 'Science Fiction';
4883
4884 $temp['06-00'] = 'Government';
4885
4886 $temp['07-00'] = 'Health & Fitness';
4887 $temp['07-01'] = 'Alternative Health';
4888 $temp['07-02'] = 'Fitness';
4889 $temp['07-03'] = 'Medicine';
4890 $temp['07-04'] = 'Mental Health';
4891 $temp['07-05'] = 'Nutrition';
4892 $temp['07-06'] = 'Sexuality';
4893
4894 $temp['08-00'] = 'History';
4895
4896 $temp['09-00'] = 'Kids & Family';
4897 $temp['09-01'] = 'Education for Kids';
4898 $temp['09-02'] = 'Parenting';
4899 $temp['09-03'] = 'Pets & Animals';
4900 $temp['09-04'] = 'Stories for Kids';
4901
4902 $temp['10-00'] = 'Leisure';
4903 $temp['10-01'] = 'Animation & Manga';
4904 $temp['10-02'] = 'Automotive';
4905 $temp['10-03'] = 'Aviation';
4906 $temp['10-04'] = 'Crafts';
4907 $temp['10-05'] = 'Games';
4908 $temp['10-06'] = 'Hobbies';
4909 $temp['10-07'] = 'Home & Garden';
4910 $temp['10-08'] = 'Video Games';
4911
4912 $temp['11-00'] = 'Music';
4913 $temp['11-01'] = 'Music Commentary';
4914 $temp['11-02'] = 'Music History';
4915 $temp['11-03'] = 'Music Interviews';
4916
4917 $temp['12-00'] = 'News';
4918 $temp['12-01'] = 'Business News';
4919 $temp['12-02'] = 'Daily News';
4920 $temp['12-03'] = 'Entertainment News';
4921 $temp['12-04'] = 'News Commentary';
4922 $temp['12-05'] = 'Politics';
4923 $temp['12-06'] = 'Sports News';
4924 $temp['12-07'] = 'Tech News';
4925
4926 $temp['13-00'] = 'Religion & Spirituality';
4927 $temp['13-01'] = 'Buddhism';
4928 $temp['13-02'] = 'Christianity';
4929 $temp['13-03'] = 'Hinduism';
4930 $temp['13-04'] = 'Islam';
4931 $temp['13-05'] = 'Judaism';
4932 $temp['13-06'] = 'Religion';
4933 $temp['13-07'] = 'Spirituality';
4934
4935 $temp['14-00'] = 'Science';
4936 $temp['14-01'] = 'Astronomy';
4937 $temp['14-02'] = 'Chemistry';
4938 $temp['14-03'] = 'Earth Sciences';
4939 $temp['14-04'] = 'Life Sciences';
4940 $temp['14-05'] = 'Mathematics';
4941 $temp['14-06'] = 'Natural Sciences';
4942 $temp['14-07'] = 'Nature';
4943 $temp['14-08'] = 'Physics';
4944 $temp['14-09'] = 'Social Sciences';
4945
4946 $temp['15-00'] = 'Society & Culture';
4947 $temp['15-01'] = 'Documentary';
4948 $temp['15-02'] = 'Personal Journals';
4949 $temp['15-03'] = 'Philosophy';
4950 $temp['15-04'] = 'Places & Travel';
4951 $temp['15-06'] = 'Relationships';
4952
4953 $temp['16-00'] = 'Sports';
4954 $temp['16-01'] = 'Baseball';
4955 $temp['16-02'] = 'Basketball';
4956 $temp['16-03'] = 'Cricket';
4957 $temp['16-04'] = 'Fantasy Sports';
4958 $temp['16-05'] = 'Football';
4959 $temp['16-06'] = 'Golf';
4960 $temp['16-07'] = 'Hockey';
4961 $temp['16-08'] = 'Rugby';
4962 $temp['16-09'] = 'Running';
4963 $temp['16-10'] = 'Soccer';
4964 $temp['16-11'] = 'Swimming';
4965 $temp['16-12'] = 'Tennis';
4966 $temp['16-13'] = 'Volleyball';
4967 $temp['16-15'] = 'Wilderness';
4968 $temp['16-16'] = 'Wrestling';
4969
4970 $temp['17-00'] = 'Technology';
4971
4972 $temp['18-00'] = 'True Crime';
4973
4974 $temp['19-00'] = 'TV & Film';
4975 $temp['19-01'] = 'After Shows';
4976 $temp['19-02'] = 'Film History';
4977 $temp['19-03'] = 'Film Interviews';
4978 $temp['19-04'] = 'Film Reviews';
4979 $temp['19-05'] = 'TV Reviews';
4980
4981 if( $PrefixSubCategories )
4982 {
4983 foreach( $temp as $key=> $val )
4984 {
4985 $parts = explode('-', $key);
4986 $cat = $parts[0];
4987 $subcat = $parts[1];
4988
4989 if( $subcat != '00' )
4990 $temp[$key] = $temp[$cat.'-00'].' > '.$val;
4991 }
4992 reset($temp);
4993 }
4994
4995 return $temp;
4996 }
4997
4998 function powerpress_googleplay_categories()
4999 {
5000 $temp = array();
5001 $temp['01-00'] = 'Arts';
5002 $temp['02-00'] = 'Business';
5003 $temp['03-00'] = 'Comedy';
5004 $temp['04-00'] = 'Education';
5005 $temp['05-00'] = 'Games & Hobbies';
5006 $temp['06-00'] = 'Government & Organizations';
5007 $temp['07-00'] = 'Health';
5008 $temp['08-00'] = 'Kids & Family';
5009 $temp['09-00'] = 'Music';
5010 $temp['10-00'] = 'News & Politics';
5011 $temp['11-00'] = 'Religion & Spirituality';
5012 $temp['12-00'] = 'Science & Medicine';
5013 $temp['13-00'] = 'Society & Culture';
5014 $temp['14-00'] = 'Sports & Recreation';
5015 $temp['15-00'] = 'Technology';
5016 $temp['16-00'] = 'TV & Film';
5017
5018 return $temp;
5019 }
5020
5021 function powerpress_get_root_url()
5022 {
5023 /*
5024 // OLD CODE:
5025 $powerpress_dirname = basename( POWERPRESS_ABSPATH );
5026 return WP_PLUGIN_URL . '/'. $powerpress_dirname .'/';
5027 */
5028 $local_path = __FILE__;
5029 if( DIRECTORY_SEPARATOR == '\\' ) { // Win32 fix
5030 $local_path = basename(dirname(__FILE__)) .'/'. basename(__FILE__);
5031 }
5032 $plugin_url = plugins_url('', $local_path);
5033 return $plugin_url . '/';
5034 }
5035
5036 /**
5037 * blubrry publish url derivation
5038 *
5039 * @return string publish url with trailing slash.
5040 */
5041 function powerpress_get_publish_url() {
5042 $origin_array = explode('.', POWERPRESS_BLUBRRY_API_URL);
5043 $origin_array[0] = str_replace('api', 'publish', $origin_array[0]);
5044 return rtrim(implode('.', $origin_array), '/') . '/';
5045 }
5046
5047 function powerpress_get_the_exerpt($for_summary = false, $no_filters = false, $post_id = false)
5048 {
5049 if( $no_filters ) {
5050 if( $post_id > 0 ) {
5051 $post = get_post($post_id);
5052 $subtitle = $post->post_excerpt;
5053 if ( $subtitle == '') {
5054
5055 $subtitle = $post->post_content;
5056 $shortcodesTemp = $GLOBALS['shortcode_tags'];
5057 $GLOBALS['shortcode_tags']['skipto'] = 'powerpress_shortcode_skipto';
5058 $subtitle = do_shortcode($subtitle);
5059 $GLOBALS['shortcode_tags'] = $shortcodesTemp;
5060
5061 $subtitle = strip_shortcodes( $subtitle );
5062 $subtitle = str_replace(']]>', ']]&gt;', $subtitle);
5063 $subtitle = strip_tags($subtitle);
5064 }
5065 }
5066 else if( is_object($GLOBALS['post']) )
5067 {
5068 $subtitle = $GLOBALS['post']->post_excerpt;
5069 if ( $subtitle == '') {
5070
5071 $subtitle = $GLOBALS['post']->post_content;
5072
5073 $shortcodesTemp = $GLOBALS['shortcode_tags'];
5074 $GLOBALS['shortcode_tags']['skipto'] = 'powerpress_shortcode_skipto';
5075 $subtitle = do_shortcode($subtitle);
5076 $GLOBALS['shortcode_tags'] = $shortcodesTemp;
5077
5078 $subtitle = strip_shortcodes( $subtitle );
5079 $subtitle = str_replace(']]>', ']]&gt;', $subtitle);
5080 $subtitle = strip_tags($subtitle);
5081 }
5082 }
5083 } else {
5084 $subtitle = get_the_excerpt();
5085 }
5086
5087 $subtitle = trim( strip_tags( $subtitle ) );
5088 if( !empty($subtitle) )
5089 return $subtitle;
5090 return powerpress_get_the_content( $for_summary, $no_filters );
5091 }
5092
5093 function powerpress_get_the_content($for_summary = true, $no_filters = false, $no_strip_tags = false) {
5094 if( $no_filters ) {
5095 global $post;
5096 $content_no_html = $post->post_content;
5097
5098 $shortcodesTemp = $GLOBALS['shortcode_tags'];
5099 $GLOBALS['shortcode_tags']['skipto'] = 'powerpress_shortcode_skipto';
5100 $content_no_html = do_shortcode($content_no_html);
5101 $GLOBALS['shortcode_tags'] = $shortcodesTemp;
5102
5103 //$content_no_html = strip_shortcodes( $content_no_html );
5104 $content_no_html = str_replace(']]>', ']]&gt;', $content_no_html);
5105 $content_no_html = wp_staticize_emoji( _oembed_filter_feed_content( $content_no_html ) );
5106 } else {
5107 $content_no_html = get_the_content();
5108 }
5109
5110 $content_no_html = strip_shortcodes( $content_no_html );
5111 if( $no_strip_tags )
5112 return $content_no_html;
5113
5114 if( $for_summary ) {
5115 return trim( strip_tags($content_no_html, '<a><p><br><ul><li>') );
5116 }
5117 return trim( strip_tags($content_no_html) );
5118 }
5119
5120
5121
5122 function powerpress_url_in_feed($url) {
5123 if( defined('POWERPRESS_FEEDS_FORCE_HTTP') && is_feed() ) {
5124 if( preg_match('/^https:\/\/(.*)$/', $url, $matches) ) {
5125 return 'http://'.$matches[1];
5126 }
5127 }
5128 else if( defined('POWERPRESS_FEEDS_FORCE_HTTPS') && is_feed() ) {
5129 if( preg_match('/^http:\/\/(.*)$/', $url, $matches) ) {
5130 return 'https://'.$matches[1];
5131 }
5132 }
5133 return $url;
5134 }
5135
5136 function powerpress_format_itunes_value($value, $tag, $cdata=false)
5137 {
5138 if( $cdata ) {
5139 $value = str_replace(']]>', ']]&gt;', $value);
5140 return powerpress_trim_value($value, $tag);
5141 }
5142
5143 if( !defined('POWERPRESS_DISABLE_ITUNES_UTF8') || POWERPRESS_DISABLE_ITUNES_UTF8 == false ) // If not defined or it is false
5144 {
5145 global $wpdb;
5146 switch( $wpdb->charset )
5147 {
5148 case 'utf8': break;
5149 case 'utf8mb3': break;
5150 case 'utf8mb4': break;
5151 default: {
5152
5153 // preg_match fails when it encounters invalid UTF8 in $string
5154 if ( 1 !== @preg_match( '/^./us', $value ) ) {
5155 $encoding_detected = mb_detect_encoding($value, ['UTF-8', 'ISO-8859-1', 'Windows-1252', 'ASCII'], true);
5156 if ($encoding_detected !== false) {
5157 $value = mb_convert_encoding($value, 'UTF-8', $encoding_detected);
5158 } else {
5159 $value = mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
5160 }
5161 // legacy
5162 // $value = utf8_encode($value); <-- utf8_encode deprecated function
5163 }
5164 }
5165 }
5166 }
5167
5168 // Code added to solve issue with KimiliFlashEmbed plugin and also remove the shortcode for the WP Audio Player
5169 // 99.9% of the time this code will not be necessary
5170 $value = preg_replace("/\[(kml_(flash|swf)embed|audio\:)\b(.*?)(?:(\/))?(\]|$)/isu", '', $value);
5171 $value = @html_entity_decode($value, ENT_COMPAT, 'UTF-8'); // Remove any additional entities such as &nbsp;
5172 $value = preg_replace( '/&amp;/ui' , '&', $value); // Precaution in case it didn't get removed from function above.
5173
5174 return esc_html( powerpress_trim_value($value, $tag) );
5175 }
5176
5177
5178 function powerpress_trim_value(string $value, string $tag)
5179 {
5180 // anon fallback funtions
5181 $strlen = function_exists('mb_strlen')
5182 ? function($val) { return mb_strlen($val); }
5183 : function($val) { return strlen($val); };
5184
5185 $strrpos = function_exists('mb_strrpos')
5186 ? function($val, $search) { return mb_strrpos($val, $search); }
5187 : function($val, $search) { return strrpos($val, $search); };
5188
5189 $substr = function_exists('mb_substr')
5190 ? function($val, $start, $len) { return mb_substr($val, $start, $len); }
5191 : function($val, $start, $len) { return substr($val, $start, $len); };
5192
5193 $value = trim($value); // First we need to trim the string
5194 $length = $strlen($value);
5195 $trim_at = false;
5196 $remove_new_lines = false;
5197
5198 // Assign trim_at, remove 3 additional chars for ellipses '...'
5199 switch($tag)
5200 {
5201 case 'description':
5202 if( $length > 10000 )
5203 $trim_at = 9997;
5204 break;
5205
5206 case 'episode_no_display':
5207 if ( $length > 32 )
5208 $trim_at = 29;
5209 break;
5210
5211 case 'credit_name':
5212 case 'address':
5213 case 'soundbite_title':
5214 case 'donate_label':
5215 case 'copyright':
5216 case 'trailer':
5217 case 'tag_purpose':
5218 $remove_new_lines = true;
5219 if ( $length > 128 )
5220 $trim_at = 125;
5221 break;
5222
5223 case 'tag_content':
5224 if ( $length > 4000 )
5225 $trim_at = 3997;
5226 break;
5227
5228 case 'author':
5229 case 'name':
5230 default:
5231 $remove_new_lines = true;
5232 if( $length > 255 )
5233 $trim_at = 252;
5234 }
5235
5236 if( $trim_at ) {
5237 // Start trimming
5238 $value = $substr($value, 0, $trim_at);
5239
5240 if( $trim_at >= 125 ) {
5241 // find last punctuation, mark for clean cut
5242 $clean_cut = max(
5243 $strrpos($value, '.') ?: 0,
5244 $strrpos($value, ',') ?: 0,
5245 $strrpos($value, '!') ?: 0,
5246 $strrpos($value, '?') ?: 0,
5247 $strrpos($value, "\n") ?: 0
5248 );
5249
5250 if ( $clean_cut > ($trim_at - 50) ) {
5251 $value = $substr($value, 0, $clean_cut + 1);
5252 $value .= '...';
5253 }
5254 }
5255 }
5256
5257 if( $remove_new_lines )
5258 $value = str_replace( array("\r\n\r\n", "\n", "\r", "\t","- "), array(' - ',' ', '', ' ', ''), $value );
5259
5260 return $value;
5261 }
5262
5263 function powerpress_add_redirect_url($MediaURL, $EpisodeData = false) // $channel = 'podcast')
5264 {
5265 if( preg_match('/^https?:\/\//i', $MediaURL) == 0 )
5266 return $MediaURL; // If the user is hosting media not via http (e.g. ftp) then we can't handle the redirect
5267
5268 // don't add redirects to youtube urls (handles youtube links already saved in wpdb)
5269 if(isYoutubeURL($MediaURL)) return $MediaURL;
5270
5271 if( !is_array($EpisodeData) )
5272 {
5273 $feed_slug = '';
5274 if( is_string($EpisodeData) && !empty($EpisodeData) ) {
5275 $feed_slug = $EpisodeData;
5276 }
5277
5278 $EpisodeData = array();
5279 if( !empty($feed_slug) )
5280 $EpisodeData['feed'] = $EpisodeData;
5281 }
5282
5283 if( empty($EpisodeData['feed']) )
5284 $EpisodeData['feed'] = 'podcast';
5285
5286 $NewURL = apply_filters( 'powerpress_redirect_url', $MediaURL, $EpisodeData );
5287
5288 $URLScheme = ( (preg_match('/^https:\/\//i', $NewURL) != 0 ) ? 'https://':'http://');
5289
5290 $GeneralSettings = get_option('powerpress_general');
5291 $redirects = array('redirect0'=>'', 'redirect1'=>'', 'redirect2'=>'', 'redirect3'=>'');
5292 if( !empty($GeneralSettings['redirect1']) )
5293 $redirects['redirect1'] = $GeneralSettings['redirect1'];
5294 if( !empty($GeneralSettings['redirect2']) )
5295 $redirects['redirect2'] = $GeneralSettings['redirect2'];
5296 if( !empty($GeneralSettings['redirect3']) )
5297 $redirects['redirect3'] = $GeneralSettings['redirect3'];
5298
5299 if( !empty($GeneralSettings['cat_casting']) ) { // If category podcasting...
5300
5301 if( !empty($EpisodeData['category']) ) {
5302
5303 $FeedCatSettings = get_option('powerpress_cat_feed_'.$EpisodeData['category'] );
5304 if( !empty($FeedCatSettings['redirect']) ) {
5305 $redirects['redirect0'] = $FeedCatSettings['redirect'];
5306 $redirects['redirect1'] = '';
5307 $redirects['redirect2'] = '';
5308 $redirects['redirect3'] = '';
5309 }
5310 if( !empty($FeedCatSettings['redirect2']) ) {
5311 $redirects['redirect1'] = $FeedCatSettings['redirect2'];
5312 }
5313 } else { // Use the old way
5314
5315 if( is_category() ) { // Special case where we want to track the category separately
5316 $FeedCatSettings = get_option('powerpress_cat_feed_'.get_query_var('cat') );
5317 if( $FeedCatSettings && !empty($FeedCatSettings['redirect']) ) {
5318 $redirects['redirect0'] = $FeedCatSettings['redirect'];
5319 $redirects['redirect1'] = '';
5320 $redirects['redirect2'] = '';
5321 $redirects['redirect3'] = '';
5322 if( !empty($FeedCatSettings['redirect2']) ) {
5323 $redirects['redirect1'] = $FeedCatSettings['redirect2'];
5324 }
5325 }
5326 } else if( is_single() ) {
5327 $categories = wp_get_post_categories( get_the_ID() );
5328 if( count($categories) == 1 ) { // See if only one category is associated with this post
5329 foreach( $categories as $null=> $cat_id ) {
5330 break;
5331 }
5332 $FeedCatSettings = get_option('powerpress_cat_feed_'.$cat_id );
5333 if( $FeedCatSettings && !empty($FeedCatSettings['redirect']) ) {
5334 $redirects['redirect0'] = $FeedCatSettings['redirect'];
5335 $redirects['redirect1'] = '';
5336 $redirects['redirect2'] = '';
5337 $redirects['redirect3'] = '';
5338 if( !empty($FeedCatSettings['redirect2']) ) {
5339 $redirects['redirect1'] = $FeedCatSettings['redirect2'];
5340 }
5341 }
5342 }
5343 }
5344 }
5345 }
5346
5347 //custom_feeds
5348 if( !empty($GeneralSettings['channels']) ) {
5349
5350 $FeedSettings = get_option('powerpress_feed_'. $EpisodeData['feed']);
5351 if( !empty($FeedSettings['redirect']) )
5352 {
5353 // Override the redirect
5354 $redirects['redirect0'] = $FeedSettings['redirect'];
5355 $redirects['redirect1'] = '';
5356 $redirects['redirect2'] = '';
5357 $redirects['redirect3'] = '';
5358 }
5359 if( !empty($FeedSettings['redirect2']) ) {
5360 $redirects['redirect1'] = $FeedSettings['redirect2'];
5361 }
5362 }
5363
5364 if( !empty($GeneralSettings['posttype_podcasting']) ) // Post Type Podcasting
5365 {
5366 $post_type = get_post_type();
5367 switch($post_type) {
5368 case 'post':
5369 case 'page': {
5370 // Do nothing!, we want the default podcast and channels to appear in these post types
5371 }; break;
5372 default: {
5373 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$post_type);
5374
5375 // We found a post type statsitics tracking
5376 if( !empty($PostTypeSettingsArray[ $EpisodeData['feed'] ]['redirect']) )
5377 {
5378 $redirects['redirect0'] = $PostTypeSettingsArray[ $EpisodeData['feed'] ]['redirect'];
5379 $redirects['redirect1'] = '';
5380 $redirects['redirect2'] = '';
5381 $redirects['redirect3'] = '';
5382 }
5383 if( !empty($PostTypeSettingsArray[ $EpisodeData['feed'] ]['redirect2']) ) {
5384 $redirects['redirect1'] = $PostTypeSettingsArray[ $EpisodeData['feed'] ]['redirect2'];
5385 }
5386 };
5387 }
5388 }
5389
5390 if( version_compare($GLOBALS['wp_version'], '4.5', '>=' ) )
5391 {
5392 if( !empty($GeneralSettings['taxonomy_podcasting']) ) // Taxonomy Podcasting
5393 {
5394 $PowerPressTaxonomies = get_option('powerpress_taxonomy_podcasting');
5395 if( !empty($PowerPressTaxonomies) )
5396 {
5397 foreach ($PowerPressTaxonomies as $key => $value) {
5398 $ttid_found = $key;
5399
5400 $TaxonomySettings = get_option('powerpress_taxonomy_'.$ttid_found);
5401 // Found it???
5402 if( !empty($TaxonomySettings['redirect']) )
5403 {
5404 $redirects['redirect0'] = $TaxonomySettings['redirect'];
5405 $redirects['redirect1'] = '';
5406 $redirects['redirect2'] = '';
5407 $redirects['redirect3'] = '';
5408 break;
5409 }
5410 }
5411 }
5412 }
5413 }
5414
5415 // Allow other apps to update the redirects
5416 $redirects = apply_filters('powerpress_redirects', $redirects, $EpisodeData);
5417
5418 for( $x = 3; $x >= 0; $x-- )
5419 {
5420 $key = sprintf('redirect%d', $x);
5421 if( !empty($redirects[ $key ]) )
5422 {
5423 if( preg_match('/^https?:\/\/(.*)$/', trim($redirects[ $key ]) , $matches ) == 0 )
5424 continue;
5425
5426 // skip adding redirect to enclosure URL if its charable
5427 if (is_chartable_url($redirects[$key])) {
5428 continue;
5429 }
5430
5431 $redirectClean = $matches[1];
5432 if( substr($redirectClean, -1, 1) != '/' ) // Rediercts need to end with a slash /.
5433 $redirectClean .= '/';
5434
5435 if( !empty($redirectClean) )
5436 {
5437 if( strpos($redirectClean, '/') == 0 ) // Not a valid redirect URL
5438 continue;
5439
5440 if( !strstr($NewURL, $redirectClean) ) // If the redirect is not already added...
5441 $NewURL = $URLScheme. $redirectClean . str_replace($URLScheme, '', $NewURL);
5442 }
5443 }
5444 }
5445
5446 return $NewURL;
5447 }
5448
5449 if (!function_exists('is_chartable_url')) {
5450 function is_chartable_url($redirectUrl)
5451 {
5452 if (strpos($redirectUrl, 'chrt.fm') !== false || strpos($redirectUrl, 'chtbl.com') !== false) {
5453 return true;
5454 } else {
5455 return false;
5456 }
5457 }
5458 }
5459
5460 function powerpress_add_flag_to_redirect_url($MediaURL, $Flag)
5461 {
5462 // First strip any previous flags...
5463 return $MediaURL;
5464 }
5465
5466 /*
5467 Code contributed from upekshapriya on the Blubrry Forums
5468 */
5469 function powerpress_byte_size($ppbytes)
5470 {
5471 $ppbytes = intval($ppbytes);
5472 if( empty($ppbytes) )
5473 return '';
5474 $ppsize = intval($ppbytes) / 1024;
5475 if($ppsize < 1024)
5476 {
5477 $ppsize = number_format($ppsize, 1);
5478 $ppsize .= 'KB';
5479 }
5480 else
5481 {
5482 if($ppsize / 1024 < 1024)
5483 {
5484 $ppsize = number_format($ppsize / 1024, 1);
5485 $ppsize .= 'MB';
5486 }
5487 else if ($ppsize / 1024 / 1024 < 1024)
5488 {
5489 $ppsize = number_format($ppsize / 1024 / 1024, 1);
5490 $ppsize .= 'GB';
5491 }
5492 }
5493 return $ppsize;
5494 }
5495
5496 // Merges settings from feed settings page to empty custom feed settings
5497 function powerpress_merge_empty_feed_settings($CustomFeedSettings, $FeedSettings, $DefaultPodcastFeed = false)
5498 {
5499 unset($FeedSettings['apply_to']);
5500 // Remove settings from main $FeedSettings that should not be copied to custom feed.
5501 if( !$DefaultPodcastFeed )
5502 {
5503 unset($FeedSettings['itunes_new_feed_url']);
5504 unset($FeedSettings['feed_redirect_url']);
5505 unset($FeedSettings['itunes_complete']);
5506 unset($FeedSettings['itunes_block']);
5507 unset($FeedSettings['maximize_feed']);
5508 unset($FeedSettings['live_item']);
5509 }
5510
5511 if( !$CustomFeedSettings )
5512 return $FeedSettings; // If the $CustomFeedSettings is false
5513
5514 if (is_array($CustomFeedSettings)) {
5515 foreach ($CustomFeedSettings as $key => $value) {
5516 if ($value !== '' || !isset($FeedSettings[$key]))
5517 $FeedSettings[$key] = $value;
5518 }
5519 }
5520
5521 return $FeedSettings;
5522 }
5523
5524 function powerpress_readable_duration($duration, $include_hour=false)
5525 {
5526 $seconds = 0;
5527 $parts = explode(':', $duration);
5528 // phpstan: explode returns strings, type safety
5529 if( count($parts) == 3 )
5530 $seconds = (int)$parts[2] + ((int)$parts[1]*60) + ((int)$parts[0]*60*60);
5531 else if ( count($parts) == 2 )
5532 $seconds = (int)$parts[1] + ((int)$parts[0]*60);
5533 else
5534 $seconds = (int)$parts[0];
5535
5536 $hours = 0;
5537 $minutes = 0;
5538 if( $seconds >= (60*60) )
5539 {
5540 $hours = floor( $seconds /(60*60) );
5541 $seconds -= (60*60*$hours);
5542 }
5543 if( $seconds >= (60) )
5544 {
5545 $minutes = floor( $seconds /(60) );
5546 $seconds -= (60*$minutes);
5547 }
5548
5549 if( $hours || $include_hour ) // X:XX:XX (readable)
5550 return sprintf('%d:%02d:%02d', $hours, $minutes, $seconds);
5551
5552 return sprintf('%d:%02d', $minutes, $seconds); // X:XX or 0:XX (readable)
5553 }
5554
5555 // Duratoin in form of seconds (parses hh:mm:ss)
5556 function powerpress_raw_duration($duration)
5557 {
5558 $duration = trim($duration);
5559 $Parts = explode(':',$duration);
5560 if( empty($Parts) )
5561 return $duration;
5562
5563 if( count($Parts) == 3 )
5564 return ((intval($Parts[0])*60*60) + (intval($Parts[1])*60) + intval($Parts[2]));
5565 else if( count($Parts) == 2 )
5566 return ((intval($Parts[0])*60) + intval($Parts[1]));
5567 //else if( count($Parts) == 1 )
5568 // return ($Parts[0]);
5569
5570 // We never found any colons, so we assume duration is seconds
5571 return $duration;
5572 }
5573
5574 // For grabbing data from Podpress data stored serialized, the strings for some values can sometimes get corrupted, so we fix it...
5575
5576 function powerpress_repair_serialize($string)
5577 {
5578 // allowed_classes => false prevents php object injection via crafted serialized data
5579 if( @unserialize($string, ['allowed_classes' => false]) )
5580 return $string; // Nothing to repair...
5581
5582 $string = preg_replace_callback('/(s:(\d+):"([^"]*)")/',
5583 'powerpress_repair_serialize_callback',
5584 $string);
5585
5586 if( substr($string, 0, 2) == 's:' ) // Sometimes the serialized data is double serialized, so we need to re-serialize the outside string
5587 {
5588 $string = preg_replace_callback('/(s:(\d+):"(.*)"(;))$/',
5589 'powerpress_repair_serialize_callback',
5590 $string);
5591 }
5592
5593 return $string;
5594 }
5595
5596 function powerpress_repair_serialize_callback($matches)
5597 {
5598 if( strlen($matches[3]) == $matches[2] )
5599 return $matches[0];
5600 return sprintf('s:%d:"%s"', strlen($matches[3]), $matches[3]) . (!empty($matches[4])?';':'');
5601 }
5602
5603 function powerpress_base64_encode($value)
5604 {
5605 return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
5606 }
5607 /*
5608 powerpress_get_post_meta()
5609 Safe function to retrieve corrupted PodPress data from the database
5610 @post_id - post id to retrieve post meta for
5611 @key - key to retrieve post meta for
5612 */
5613 function powerpress_get_post_meta($post_id, $key)
5614 {
5615 $pp_meta_cache = wp_cache_get($post_id, 'post_meta');
5616 if ( !$pp_meta_cache ) {
5617 update_postmeta_cache($post_id);
5618 $pp_meta_cache = wp_cache_get($post_id, 'post_meta');
5619 }
5620
5621 $meta = false;
5622 if ( isset($pp_meta_cache[$key]) )
5623 $meta = $pp_meta_cache[$key][0];
5624
5625 if ( is_serialized( $meta ) ) // Logic used up but not including WordPress 2.8, new logic doesn't make sure if unserialized failed or not
5626 {
5627 // allowed_classes => false prevents php object injection via crafted serialized data
5628 if ( false !== ( $gm = @unserialize( $meta, ['allowed_classes' => false] ) ) )
5629 return $meta;
5630 }
5631
5632 return $meta;
5633 }
5634
5635 function powerpress_get_enclosure($post_id, $feed_slug = 'podcast')
5636 {
5637 $Data = powerpress_get_enclosure_data($post_id, $feed_slug);
5638 if( $Data )
5639 return $Data['url'];
5640 return false;
5641 }
5642
5643 function powerpress_get_enclosure_data($post_id, $feed_slug = 'podcast', $raw_data = false, $add_redirect=true)
5644 {
5645 global $post;
5646 if( false != $raw_data )
5647 $MetaData = $raw_data;
5648 else
5649 {
5650 if( !empty($post->podcast_meta_value) && $post->ID == $post_id) // See if we got the meta data from the initial query...
5651 {
5652 // Make sure this is not serialized data from PodPress...
5653 $partsTest = explode("\n", $post->podcast_meta_value, 4);
5654 if( count($partsTest) > 2 ) {
5655 $PodcastData = powerpress_get_enclosure_data($post_id, $feed_slug, $post->podcast_meta_value, true);
5656 return $PodcastData;
5657 }
5658 }
5659
5660 if( 'podcast' == $feed_slug || '' == $feed_slug )
5661 $MetaData = get_post_meta($post_id, 'enclosure', true);
5662 else
5663 $MetaData = get_post_meta($post_id, '_'. $feed_slug .':enclosure', true);
5664 }
5665 if( empty($MetaData) )
5666 return false;
5667
5668 $MetaParts = explode("\n", $MetaData, 4);
5669
5670 $Serialized = false;
5671 $Data = array();
5672 $Data['id'] = $post_id;
5673 $Data['feed'] = $feed_slug;
5674 $Data['url'] = '';
5675 $Data['duration'] = '';
5676 $Data['size'] = '';
5677 $Data['type'] = '';
5678 $Data['width'] = '';
5679 $Data['height'] = '';
5680
5681 if( count($MetaParts) > 0 )
5682 $Data['url'] = trim($MetaParts[0]);
5683 if( count($MetaParts) > 1 )
5684 $Data['size'] = trim($MetaParts[1]);
5685 if( count($MetaParts) > 2 )
5686 $Data['type'] = trim($MetaParts[2]);
5687 if( count($MetaParts) > 3 )
5688 $Serialized = $MetaParts[3];
5689
5690 if ($MetaParts[0] == 'no') {
5691 return false;
5692 }
5693
5694 if( $Serialized )
5695 {
5696 // allowed_classes => false prevents php object injection via crafted serialized data
5697 $ExtraData = @unserialize($Serialized, ['allowed_classes' => false]);
5698 if( $ExtraData && is_array($ExtraData) )
5699 {
5700 foreach( $ExtraData as $key=> $value ) {
5701
5702 // Make sure specific fields are not overwritten...
5703 switch( $key ) {
5704 case 'id':
5705 case 'feed':
5706 case 'url':
5707 case 'size':
5708 case 'type': break;
5709 default: $Data[ $key ] = $value;
5710 }
5711 }
5712
5713 if( isset($Data['length']) ) // Setting from the "Podcasting" plugin...
5714 $Data['duration'] = powerpress_readable_duration($Data['length'], true);
5715
5716 if( !empty($Data['webm_src']) )
5717 {
5718 $Data['webm_src'] = trim($Data['webm_src']);
5719 }
5720
5721
5722 if( strpos($MetaParts[0], 'http://') !== 0 && !empty($Data['hosting']) ) // if the URL is not set (just file name) and we're a hosting customer...
5723 {
5724 $post_status = get_post_status($post_id);
5725 switch( $post_status )
5726 {
5727 case 'pending':
5728 case 'draft':
5729 case 'auto-draft': {
5730 // Determine if audio or video, then set the demo episode here...
5731 $Data['url'] = 'http://media.blubrry.com/blubrry/content.blubrry.com/blubrry/preview.mp3'; // audio
5732 if( strstr($Data['type'], 'video') )
5733 $Data['url'] = 'http://media.blubrry.com/blubrry/content.blubrry.com/blubrry/preview.mp4'; // video
5734 }; break;
5735 }
5736 }
5737 }
5738 }
5739
5740 // If the URL is using Blubrry hosting, then lets pump it up to https...
5741 if( is_ssl() && preg_match('/^http:\/\/(.*\/content\.blubrry\.com\/.*)$/i', $Data['url'], $matches) )
5742 {
5743 $Data['url'] = 'https://'. $matches[1];
5744 }
5745
5746 // Check that the content type is a valid one...
5747 if( strstr($Data['type'], '/') == false )
5748 $Data['type'] = powerpress_get_contenttype($Data['url']);
5749
5750 // Do redirect filter here...
5751 if( $add_redirect && !empty($Data['url']) )
5752 $Data['url'] = powerpress_add_redirect_url( $Data['url'], $Data );
5753
5754 if( $add_redirect && !empty($Data['webm_src']) )
5755 $Data['webm_src'] = powerpress_add_redirect_url( $Data['webm_src'], $Data );
5756
5757 return apply_filters('powerpress_get_enclosure_data', $Data);
5758 }
5759
5760 function powerpress_get_enclosure_data_podpress($post_id, $mediaNum = 0, $include_premium = false)
5761 {
5762 $podPressMedia = powerpress_get_post_meta($post_id, 'podPressMedia');
5763 if( !$podPressMedia )
5764 $podPressMedia = powerpress_get_post_meta($post_id, '_podPressMedia'); // handles latest verions of PodPress
5765 if( $podPressMedia )
5766 {
5767
5768 if( !is_array($podPressMedia) )
5769 {
5770 // Sometimes the stored data gets messed up, we can fix it here:
5771 $podPressMedia = powerpress_repair_serialize($podPressMedia);
5772 // allowed_classes => false prevents php object injection via crafted serialized data
5773 $podPressMedia = @unserialize($podPressMedia, ['allowed_classes' => false]);
5774 }
5775
5776 // Do it a second time in case it is double serialized
5777 if( !is_array($podPressMedia) )
5778 {
5779 // Sometimes the stored data gets messed up, we can fix it here:
5780 $podPressMedia = powerpress_repair_serialize($podPressMedia);
5781 // allowed_classes => false prevents php object injection via crafted serialized data
5782 $podPressMedia = @unserialize($podPressMedia, ['allowed_classes' => false]);
5783 }
5784
5785 if( is_array($podPressMedia) && isset($podPressMedia[$mediaNum]) && isset($podPressMedia[$mediaNum]['URI']) )
5786 {
5787 if( $include_premium == false && isset($podPressMedia[$mediaNum]['premium_only']) && ($podPressMedia[$mediaNum]['premium_only'] == 'on' || $podPressMedia[$mediaNum]['premium_only'] == true) )
5788 return false;
5789
5790 $Data = array();
5791 $Data['id'] = $post_id;
5792 $Data['feed'] = 'podcast';
5793 $Data['duration'] = 0;
5794 $Data['url'] = '';
5795 $Data['size'] = 0;
5796 $Data['type'] = '';
5797 $Data['width'] = '';
5798 $Data['height'] = '';
5799
5800 $Data['url'] = $podPressMedia[$mediaNum]['URI'];
5801 if( isset($podPressMedia[$mediaNum]['size']) )
5802 $Data['size'] = $podPressMedia[$mediaNum]['size'];
5803 if( isset($PodPressSettings[$mediaNum]['duration']) )
5804 $Data['duration'] = $podPressMedia[$mediaNum]['duration'];
5805 if( isset($PodPressSettings[$mediaNum]['previewImage']) )
5806 $Data['image'] = $podPressMedia[$mediaNum]['previewImage'];
5807
5808 if( strpos($Data['url'], 'http://' ) !== 0 && strpos($Data['url'], 'https://' ) !== 0 )
5809 {
5810 $PodPressSettings = get_option('podPress_config');
5811 if( $PodPressSettings && isset($PodPressSettings['mediaWebPath']) )
5812 $Data['url'] = rtrim($PodPressSettings['mediaWebPath'], '/') . '/' . ltrim($Data['url'], '/');
5813 unset($PodPressSettings);
5814 }
5815
5816 if( strpos($Data['url'], 'http://' ) !== 0 && strpos($Data['url'], 'https://' ) !== 0 )
5817 {
5818 $Settings = get_option('powerpress_general');
5819 if( $Settings && isset($Settings['default_url']) )
5820 $Data['url'] = rtrim($Settings['default_url'], '/') . '/' . ltrim($Data['url'], '/');
5821 }
5822
5823 if( strpos($Data['url'], 'http://' ) !== 0 && strpos($Data['url'], 'https://' ) !== 0 )
5824 return false;
5825
5826 $Data['type'] = powerpress_get_contenttype($Data['url']); // Detect the content type
5827 $Data['url'] = powerpress_add_redirect_url($Data['url'], $Data); // Add redirects to Media URL
5828
5829 return apply_filters('powerpress_get_enclosure_data', $Data);
5830 }
5831 }
5832 return false;
5833 }
5834
5835 function powerpress_get_apple_id($url, $strict=false)
5836 {
5837 if( $strict )
5838 {
5839 $results = preg_match('/apple\.com\/.*\/id(\d+)/i', $url, $matches);
5840 if( !$results )
5841 $results = preg_match('/apple\.com\/.*id\=(\d+)/i', $url, $matches);
5842 if( $results )
5843 return $matches[1];
5844 return 0;
5845 }
5846 $results = preg_match('/\/id(\d+)/i', $url, $matches);
5847 if( !$results )
5848 $results = preg_match('/id\=(\d+)/i', $url, $matches);
5849 if( $results )
5850 return $matches[1];
5851 return 0;
5852 }
5853
5854
5855 function the_powerpress_all_players($slug = false, $no_link=false)
5856 {
5857 echo get_the_powerpress_all_players($slug, $no_link);
5858 }
5859
5860 function get_the_powerpress_all_players($slug = false, $no_link=false)
5861 {
5862 $return = '';
5863 //Use this function to insert the Powerpress player anywhere in the page.
5864 //Made by Nicolas Bouliane (http://nicolasbouliane.com/)
5865
5866 /*We're going to use the Loop to retrieve the latest post with the 'enclosure' custom key set
5867 //then interpret it and manually launch powerpressplayer_build with the URL contained within
5868 //that data.*/
5869
5870 //Let's reset the Loop to make sure we look through all posts
5871 rewind_posts();
5872
5873 // Get the list of podcast channel slug names...
5874 $GeneralSettings = get_option('powerpress_general');
5875
5876 // No player or links to add to content...
5877 if( !empty($GeneralSettings['disable_appearance']) )
5878 return $return;
5879
5880 $ChannelSlugs = array('podcast');
5881 if( $slug == false )
5882 {
5883 if( isset($GeneralSettings['custom_feeds']['podcast']) )
5884 $ChannelSlugs = array(); // Reset the array so it is added from the list in specified order
5885 foreach( $GeneralSettings['custom_feeds'] as $feed_slug=> $null )
5886 $ChannelSlugs[] = $feed_slug;
5887 }
5888 else if( is_array($slug) )
5889 {
5890 $ChannelSlugs = $slug;
5891 }
5892 else
5893 {
5894 $ChannelSlugs = array($slug);
5895 }
5896
5897 // Loop through the posts
5898 while( have_posts() )
5899 {
5900 the_post();
5901
5902 foreach( $ChannelSlugs as $null=> $feed_slug )
5903 {
5904 // Do we follow the global settings to disable a player?
5905 if( isset($GeneralSettings['disable_player']) && isset($GeneralSettings['disable_player'][$feed_slug]) && $slug == false )
5906 continue;
5907
5908 $EpisodeData = powerpress_get_enclosure_data(get_the_ID(), $feed_slug);
5909 if( !$EpisodeData && !empty($GeneralSettings['process_podpress']) && $feed_slug == 'podcast' )
5910 $EpisodeData = powerpress_get_enclosure_data_podpress(get_the_ID());
5911
5912 if( !$EpisodeData )
5913 continue;
5914
5915 $AddDefaultPlayer = true;
5916 if( !empty($EpisodeData['embed']) )
5917 {
5918 $return .= SanitizeEmbed($EpisodeData['embed']);
5919 if( !empty($GeneralSettings['embed_replace_player']) )
5920 $AddDefaultPlayer = false;
5921 }
5922
5923 if( isset($GeneralSettings['premium_caps']) && $GeneralSettings['premium_caps'] && !powerpress_premium_content_authorized($feed_slug) )
5924 {
5925 $return .= powerpress_premium_content_message(get_the_ID(), $feed_slug, $EpisodeData);
5926 continue;
5927 }
5928
5929 if( !isset($EpisodeData['no_player']) && $AddDefaultPlayer )
5930 {
5931 do_action('wp_powerpress_player_scripts');
5932 $return .= apply_filters('powerpress_player', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData );
5933 }
5934 if( !isset($EpisodeData['no_links']) && $no_link == false )
5935 {
5936 do_action('wp_powerpress_player_scripts');
5937 $return .= apply_filters('powerpress_player_links', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData );
5938 $return .= apply_filters('powerpress_player_subscribe_links', '', powerpress_add_flag_to_redirect_url($EpisodeData['url'], 'p'), $EpisodeData );
5939 }
5940 }
5941 reset($ChannelSlugs);
5942 }
5943
5944 return $return;
5945 }
5946
5947 function powerpress_premium_content_authorized_filter($default, $feed_slug)
5948 {
5949 if( $feed_slug != 'podcast' )
5950 {
5951 $FeedSettings = get_option('powerpress_feed_'. $feed_slug);
5952 if( isset($FeedSettings['premium']) && $FeedSettings['premium'] != '' )
5953 return current_user_can($FeedSettings['premium']);
5954 }
5955
5956 $post_type = get_query_var('post_type');
5957 if ( is_array( $post_type ) ) {
5958 $post_type = reset( $post_type ); // get first element in array
5959 }
5960
5961 if( $post_type != 'post' )
5962 {
5963 $GeneralSettings = get_option('powerpress_general');
5964 if( !empty($GeneralSettings['posttype_podcasting']) ) // Custom Post Types
5965 {
5966 // Get the feed slugs and titles for this post type
5967 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$post_type);
5968 if( !empty($PostTypeSettingsArray[$feed_slug]['premium']) )
5969 return current_user_can($PostTypeSettingsArray[$feed_slug]['premium']);
5970 }
5971 }
5972
5973 return $default;
5974 }
5975 add_filter('powerpress_premium_content_authorized', 'powerpress_premium_content_authorized_filter', 10, 2);
5976
5977 function powerpress_premium_content_authorized($feed_slug)
5978 {
5979 return apply_filters('powerpress_premium_content_authorized', true, $feed_slug );
5980 }
5981
5982 function powerpress_premium_content_message($post_id, $feed_slug, $EpisodeData = false)
5983 {
5984 if( !$EpisodeData && $post_id )
5985 $EpisodeData = powerpress_get_enclosure_data($post_id, $feed_slug);
5986
5987 if( !$EpisodeData )
5988 return '';
5989 $FeedSettings = get_option('powerpress_feed_'.$feed_slug);
5990 $post_type = get_query_var('post_type');
5991 if ( is_array( $post_type ) ) {
5992 $post_type = reset( $post_type ); // get first element in array
5993 }
5994
5995 if( $post_type != 'post' )
5996 {
5997 $GeneralSettings = get_option('powerpress_general');
5998 if( !empty($GeneralSettings['posttype_podcasting']) ) // Custom Post Types
5999 {
6000 // Get the feed slugs and titles for this post type
6001 $PostTypeSettingsArray = get_option('powerpress_posttype_'.$post_type);
6002 if( !empty($PostTypeSettingsArray[$feed_slug]['premium']) )
6003 {
6004 $FeedSettings = $PostTypeSettingsArray[$feed_slug];
6005 }
6006 }
6007 }
6008
6009 $extension = 'unknown';
6010 $parts = pathinfo($EpisodeData['url']);
6011 if( $parts && isset($parts['extension']) )
6012 $extension = strtolower($parts['extension']);
6013
6014 if( isset($FeedSettings['premium_label']) && $FeedSettings['premium_label'] != '' ) // User has a custom label
6015 return '<p class="powerpress_links powerpress_links_'. $extension .'">'. $FeedSettings['premium_label'] . '</p>'.PHP_EOL_WEB;
6016
6017 return '<p class="powerpress_links powerpress_links_'. $extension .'">'. htmlspecialchars($FeedSettings['title']) .': <a href="'. get_bloginfo('url') .'/wp-login.php" title="Protected Content">(Protected Content)</a></p>'.PHP_EOL_WEB;
6018 }
6019
6020 function powerpress_is_mobile_client()
6021 {
6022 _deprecated_function( __FUNCTION__, '7.0' );
6023 return false;
6024 }
6025
6026 function powerpress_get_api_array()
6027 {
6028 $return = array();
6029 if( strstr(POWERPRESS_BLUBRRY_API_URL, 'http://api.blubrry.com') == false ) // If not the default
6030 {
6031 $return = explode(';', POWERPRESS_BLUBRRY_API_URL);
6032 }
6033 else
6034 {
6035 $return[] = 'https://api.blubrry.com/'; // Use secure URL first when possible
6036 $return[] = 'https://api.blubrry.net/';
6037 }
6038
6039 return $return;
6040 }
6041
6042
6043 function powerpress_in_wp_head()
6044 {
6045 $e = new Exception();
6046 $trace = $e->getTrace();
6047
6048 if( !empty($trace) ) {
6049 foreach( $trace as $index=> $call ) {
6050 if( isset($call['function']) ) {
6051 // Which calls should we not add the player and links...
6052 switch( $call['function'] ) {
6053 case 'wp_head': return true; break;
6054 }
6055 }
6056 }
6057 }
6058 return false;
6059 }
6060
6061 function powerpress_in_custom_post_widget()
6062 {
6063 if( !class_exists('custom_post_widget') )
6064 return false;
6065
6066 $e = new Exception();
6067 $trace = $e->getTrace();
6068
6069 if( !empty($trace) ) {
6070
6071 foreach( $trace as $index=> $call ) {
6072 if( isset($call['function']) ) {
6073 // Which calls should we not add the player and links...
6074 switch( $call['function'] ) {
6075 case 'custom_post_widget_shortcode': return true; break;
6076 }
6077 }
6078 }
6079 }
6080 return false;
6081 }
6082
6083 function powerpress_admin_migration_notice() {
6084 $QueuedResults = get_option('powerpress_migrate_queued');
6085 $Status = get_option('powerpress_migrate_status');
6086 $completed = false;
6087
6088 // we have successfully migrated all media, or there is no more media to migrate
6089 if (count($QueuedResults) == $Status['completed'] || ($Status['queued'] == 0 && $Status['downloading'] == 0)) {
6090 $completed = true;
6091 }
6092
6093 $alert_link = $root_url = ( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://' ) ) . htmlspecialchars($_SERVER['HTTP_HOST']) . "/wp-admin/admin.php?page=powerpress/powerpressadmin_migrate.php";
6094 $alert_class = 'powerpress-notice notice is-dismissible ';
6095 if ($completed) {
6096 $alert_class .= ' notice-success ';
6097 $alert_message = 'Your migration has completed. ';
6098 $alert_link = $root_url . "&action=powerpress-migrate-media&migrate_step=3";;
6099 $alert_link_message = " to update your episodes.";
6100 } else {
6101 $alert_class .= ' notice-info ';
6102 $alert_message = 'Your migration is in progress. ';
6103 $alert_link = $root_url . "&action=powerpress-migrate-media&refresh_migrate_status=1";
6104 $alert_link_message = " to check the status of your migration.";
6105 }
6106
6107
6108 $html = "<p class='alertMessage'>$alert_message<a href='$alert_link'>Click here</a>$alert_link_message</p>"
6109 . '<p>&nbsp; <a style="float:right;" href="#" class="notice-dismiss-link"></a></p>' . PHP_EOL;
6110 powerpress_page_message_add_notice($html, 'inline');
6111 }
6112
6113 // rvMigrateMedia::isYoutubeURL
6114 function isYoutubeURL($url)
6115 {
6116 $host = parse_url($url, PHP_URL_HOST);
6117 if (empty($host)) {
6118 return false;
6119 }
6120
6121 $youtubeHostnames = [
6122 'www.youtube.com',
6123 'youtube.com',
6124 'm.youtube.com',
6125 'www.youtube-nocookie.com',
6126 'music.youtube.com',
6127 ];
6128
6129 if (in_array($host, $youtubeHostnames)) {
6130 return true;
6131 }
6132
6133 // see https://gist.github.com/afeld/1254889 for regex details
6134 $youtube_regexp = "/^https?:\/\/(?:www\.)?(?:youtube.com|youtu.be)\/(?:watch\?(?=.*v=([\w\-]+))(?:\S+)?|([\w\-]+))$/i";
6135
6136 if (preg_match($youtube_regexp, $url)) {
6137 return true;
6138 }
6139
6140 return false;
6141 }
6142
6143 function getRemoteFileSize($url, $userAgent = 'PowerPress')
6144 {
6145 $cUrl = curl_init();
6146 curl_setopt($cUrl, CURLOPT_USERAGENT, $userAgent);
6147 curl_setopt($cUrl, CURLOPT_URL, $url);
6148 curl_setopt($cUrl, CURLOPT_FOLLOWLOCATION, 1); // Handles location: refreshes
6149 curl_setopt($cUrl, CURLOPT_MAXREDIRS, 12); // Max 12
6150 curl_setopt($cUrl, CURLOPT_HEADER, 1);
6151 curl_setopt($cUrl, CURLOPT_TIMEOUT, (45)); // trnasfer timeout (45 seconds)
6152 curl_setopt($cUrl, CURLOPT_CONNECTTIMEOUT, 15); // Connect time out (15 seconds)
6153 curl_setopt($cUrl, CURLOPT_ENCODING, 'gzip,deflate'); // Added to support compression
6154 curl_setopt($cUrl, CURLOPT_SSL_VERIFYHOST, 2);
6155 curl_setopt($cUrl, CURLOPT_SSL_VERIFYPEER, true);
6156 curl_setopt($cUrl, CURLOPT_CAINFO, dirname(__FILE__) . '/certificates/ca-bundle.crt');
6157 curl_setopt($cUrl, CURLOPT_RETURNTRANSFER, true);
6158 curl_setopt($cUrl, CURLOPT_NOBODY, true); // convert to a HEAD request
6159
6160 $contentLength = 0;
6161 $pageContent = curl_exec($cUrl);
6162 $length = curl_getinfo($cUrl, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
6163 if (!empty($length)) {
6164 $contentLength = intval($length);
6165 } else {
6166 $lines = explode("\n", $pageContent);
6167 foreach ($lines as $rowNumber => $line) {
6168
6169 if (preg_match('/^content-length: (.*)$/i', $line, $matches)) {
6170 $contentLength = $matches[1];
6171 }
6172 }
6173 }
6174
6175 if (version_compare(PHP_VERSION, '8.0', '<')) {
6176 curl_close($cUrl);
6177 } else {
6178 unset($cUrl);
6179 }
6180 return $contentLength;
6181 }
6182
6183 /**
6184 * Generate Select options for common countries, value is tied to country code
6185 */
6186 function powerpress_print_select_options_country($selectedOption = '')
6187 {
6188 $countries = [
6189 'United States' => 'US', 'Afghanistan' => 'AF', 'Albania' => 'AL', 'Algeria' => 'DZ', 'American Samoa' => 'AS', 'Andorra' => 'AD', 'Angola' => 'AO',
6190 'Anguilla' => 'AI', 'Antarctica' => 'AQ', 'Antigua and Barbuda' => 'AG', 'Argentina' => 'AR', 'Armenia' => 'AM', 'Aruba' => 'AW', 'Australia' => 'AU',
6191 'Austria' => 'AT', 'Azerbaijan' => 'AZ', 'Bahamas' => 'BS', 'Bahrain' => 'BH', 'Bangladesh' => 'BD', 'Barbados' => 'BB', 'Belarus' => 'BY', 'Belgium' => 'BE',
6192 'Belize' => 'BZ', 'Benin' => 'BJ', 'Bermuda' => 'BM', 'Bhutan' => 'BT', 'Bolivia (Plurinational State of)' => 'BO', 'Bosnia and Herzegovina' => 'BA', 'Botswana' => 'BW',
6193 'Bouvet Island' => 'BV', 'Brazil' => 'BR', 'British Indian Ocean Territory' => 'IO', 'Brunei Darussalam' => 'BN', 'Bulgaria' => 'BG',
6194 'Burkina Faso' => 'BF', 'Burundi' => 'BI', 'Cabo Verde' => 'CV', 'Cambodia' => 'KH', 'Cameroon' => 'CM', 'Canada' => 'CA', 'Caribbean Netherlands' => 'BQ',
6195 'Cayman Islands' => 'KY', 'Central African Republic' => 'CF', 'Chad' => 'TD', 'Chile' => 'CL', 'China' => 'CN', 'Christmas Island' => 'CX', 'Cocos (Keeling) Islands' => 'CC',
6196 'Colombia' => 'CO', 'Comoros' => 'KM', 'Congo' => 'CG', 'Congo, Democratic Republic of the' => 'CD', 'Cook Islands' => 'CK', 'Costa Rica' => 'CR', 'Croatia' => 'HR', 'Cuba' => 'CU',
6197 'Cyprus' => 'CY', 'Czech Republic' => 'CZ', 'Denmark' => 'DK', 'Djibouti' => 'DJ', 'Dominica' => 'DM', 'Dominican Republic' => 'DO', 'Ecuador' => 'EC', 'Egypt' => 'EG',
6198 'El Salvador' => 'SV', 'Equatorial Guinea' => 'GQ', 'Eritrea' => 'ER', 'Estonia' => 'EE', 'Eswatini (Swaziland)' => 'SZ', 'Ethiopia' => 'ET', 'Falkland Islands (Malvinas)' => 'FK',
6199 'Faroe Islands' => 'FO', 'Fiji' => 'FJ', 'Finland' => 'FI', 'France' => 'FR', 'French Guiana' => 'GF', 'French Polynesia' => 'PF', 'French Southern Territories' => 'TF',
6200 'Gabon' => 'GA', 'Gambia' => 'GM', 'Georgia' => 'GE', 'Germany' => 'DE', 'Ghana' => 'GH', 'Gibraltar' => 'GI', 'Greece' => 'GR', 'Greenland' => 'GL',
6201 'Grenada' => 'GD', 'Guadeloupe' => 'GP', 'Guam' => 'GU', 'Guatemala' => 'GT', 'Guernsey' => 'GG', 'Guinea' => 'GN', 'Guinea-Bissau' => 'GW', 'Guyana' => 'GY', 'Haiti' => 'HT',
6202 'Heard Island and Mcdonald Islands' => 'HM', 'Honduras' => 'HN', 'Hong Kong' => 'HK', 'Hungary' => 'HU', 'Iceland' => 'IS', 'India' => 'IN', 'Indonesia' => 'ID', 'Iran' => 'IR', 'Iraq' => 'IQ',
6203 'Ireland' => 'IE', 'Isle of Man' => 'IM', 'Italy' => 'IT', 'Jamaica' => 'JM', 'Japan' => 'JP', 'Jersey' => 'JE', 'Jordan' => 'JO', 'Kazakhstan' => 'KZ',
6204 'Kenya' => 'KE', 'Kiribati' => 'KI', 'Korea, North' => 'KP', 'Korea, South' => 'KR', 'Kosovo' => 'XK', 'Kuwait' => 'KW', 'Kyrgyzstan' => 'KG', 'Lao People\'s Democratic Republic' => 'LA', 'Latvia' => 'LV', 'Lebanon' => 'LB',
6205 'Lesotho' => 'LS', 'Liberia' => 'LR', 'Libya' => 'LY', 'Liechtenstein' => 'LI', 'Lithuania' => 'LT', 'Luxembourg' => 'LU', 'Macao' => 'MO', 'Macedonia North' => 'MK', 'Madagascar' => 'MG',
6206 'Malawi' => 'MW', 'Malaysia' => 'MY', 'Maldives' => 'MV', 'Mali' => 'ML', 'Malta' => 'MT', 'Marshall Islands' => 'MH', 'Martinique' => 'MQ', 'Mauritania' => 'MR', 'Mauritius' => 'MU', 'Mayotte' => 'YT',
6207 'Mexico' => 'MX', 'Micronesia' => 'FM', 'Moldova' => 'MD', 'Monaco' => 'MC', 'Mongolia' => 'MN', 'Montenegro' => 'ME', 'Montserrat' => 'MS', 'Morocco' => 'MA', 'Mozambique' => 'MZ',
6208 'Myanmar (Burma)' => 'MM', 'Namibia' => 'NA', 'Nauru' => 'NR', 'Nepal' => 'NP', 'Netherlands' => 'NL', 'Netherlands Antilles' => 'AN', 'New Caledonia' => 'NC', 'New Zealand' => 'NZ', 'Nicaragua' => 'NI', 'Niger' => 'NE',
6209 'Nigeria' => 'NG', 'Niue' => 'NU', 'Norfolk Island' => 'NF', 'Northern Mariana Islands' => 'MP', 'Norway' => 'NO', 'Oman' => 'OM', 'Pakistan' => 'PK', 'Palau' => 'PW', 'Palestine' => 'PS', 'Panama' => 'PA', 'Papua New Guinea' => 'PG',
6210 'Paraguay' => 'PY', 'Peru' => 'PE', 'Philippines' => 'PH', 'Pitcairn Islands' => 'PN', 'Poland' => 'PL', 'Portugal' => 'PT', 'Puerto Rico' => 'PR', 'Qatar' => 'QA', 'Reunion' => 'RE', 'Romania' => 'RO',
6211 'Russian Federation' => 'RU', 'Rwanda' => 'RW', 'Saint Barthélemy' => 'BL', 'Saint Helena' => 'SH', 'Saint Kitts and Nevis' => 'KN',
6212 'Saint Lucia' => 'LC', 'Saint Martin' => 'MF', 'Saint Pierre and Miquelon' => 'PM', 'Saint Vincent and the Grenadines' => 'VC', 'Samoa' => 'WS', 'San Marino' => 'SM', 'Sao Tome and Principe' => 'ST',
6213 'Saudi Arabia' => 'SA', 'Senegal' => 'SN', 'Serbia' => 'RS', 'Serbia and Montenegro' => 'CS', 'Seychelles' => 'SC', 'Sierra Leone' => 'SL',
6214 'Singapore' => 'SG', 'Sint Maarten' => 'SX', 'Slovakia' => 'SK', 'Slovenia' => 'SI', 'Solomon Islands' => 'SB', 'Somalia' => 'SO', 'South Africa' => 'ZA',
6215 'South Georgia and the South Sandwich Islands' => 'GS', 'South Sudan' => 'SS', 'Spain' => 'ES', 'Sri Lanka' => 'LK', 'Sudan' => 'SD', 'Suriname' => 'SR', 'Svalbard and Jan Mayen' => 'SJ', 'Sweden' => 'SE', 'Switzerland' => 'CH', 'Syria' => 'SY',
6216 'Taiwan' => 'TW', 'Tajikistan' => 'TJ', 'Tanzania' => 'TZ', 'Thailand' => 'TH', 'Timor-Leste' => 'TL', 'Togo' => 'TG', 'Tokelau' => 'TK', 'Tonga' => 'TO',
6217 'Trinidad and Tobago' => 'TT', 'Tunisia' => 'TN', 'Turkmenistan' => 'TM', 'Turks and Caicos Islands' => 'TC', 'Tuvalu' => 'TV', 'U.S. Outlying Islands' => 'UM', 'Uganda' => 'UG', 'Ukraine' => 'UA', 'United Arab Emirates' => 'AE',
6218 'United Kingdom' => 'GB', 'Uruguay' => 'UY', 'Uzbekistan' => 'UZ', 'Vanuatu' => 'VU', 'Vatican City Holy See' => 'VA', 'Venezuela' => 'VE', 'Vietnam' => 'VN', 'Virgin Islands, British' => 'VG', 'Virgin Islands, U.S' => 'VI', 'Wallis and Futuna' => 'WF', 'Western Sahara' => 'EH', 'Yemen' => 'YE', 'Zambia' => 'ZM', 'Zimbabwe' => 'ZW',
6219 ];
6220 foreach ($countries as $countryName => $countryCode) {
6221 if ($countryCode == $selectedOption) {
6222 echo '<option selected value="'.$countryCode.'">'.__($countryName, "powerpress").'</option>';
6223 } else {
6224 echo '<option value="'.$countryCode.'">'.__($countryName, "powerpress").'</option>';
6225 }
6226 }
6227 }
6228
6229
6230 // Language List -> Moved from powerpressadmin.php
6231 function powerpress_languages()
6232 {
6233 // List copied from PodPress:
6234 $langs = array();
6235 $langs['en-US'] = __('English (United States)', 'powerpress');
6236
6237 $langs['af'] = __('Afrikaans', 'powerpress');
6238 $langs['sq'] = __('Albanian', 'powerpress');
6239 $langs['ar'] = __('Arabic', 'powerpress');
6240 $langs['ar-SA'] = __('Arabic (Saudi Arabia)', 'powerpress');
6241 $langs['ar-EG'] = __('Arabic (Egypt)', 'powerpress');
6242 $langs['ar-DZ'] = __('Arabic (Algeria)', 'powerpress');
6243 $langs['ar-TN'] = __('Arabic (Tunisia)', 'powerpress');
6244 $langs['ar-YE'] = __('Arabic (Yemen)', 'powerpress');
6245 $langs['ar-JO'] = __('Arabic (Jordan)', 'powerpress');
6246 $langs['ar-KW'] = __('Arabic (Kuwait)', 'powerpress');
6247 $langs['ar-BH'] = __('Arabic (Bahrain)', 'powerpress');
6248 $langs['eu'] = __('Basque', 'powerpress');
6249 $langs['be'] = __('Belarusian', 'powerpress');
6250 $langs['bg'] = __('Bulgarian', 'powerpress');
6251 $langs['ca'] = __('Catalan', 'powerpress');
6252 $langs['zh'] = __('Chinese', 'powerpress');
6253 $langs['zh-CN'] = __('Chinese (Simplified)', 'powerpress');
6254 $langs['zh-TW'] = __('Chinese (Traditional)', 'powerpress');
6255 $langs['hr'] = __('Croatian', 'powerpress');
6256 $langs['cs'] = __('Czech', 'powerpress');
6257 $langs['cr'] = __('Cree', 'powerpress');
6258 $langs['da'] = __('Danish', 'powerpress');
6259 $langs['nl'] = __('Dutch', 'powerpress');
6260 $langs['nl-BE'] = __('Dutch (Belgium)', 'powerpress');
6261 $langs['nl-NL'] = __('Dutch (Netherlands)', 'powerpress');
6262 $langs['en'] = __('English', 'powerpress');
6263 $langs['en-AU'] = __('English (Australia)', 'powerpress');
6264 $langs['en-BZ'] = __('English (Belize)', 'powerpress');
6265 $langs['en-CA'] = __('English (Canada)', 'powerpress');
6266 $langs['en-IE'] = __('English (Ireland)', 'powerpress');
6267 $langs['en-JM'] = __('English (Jamaica)', 'powerpress');
6268 $langs['en-NZ'] = __('English (New Zealand)', 'powerpress');
6269 $langs['en-PH'] = __('English (Phillipines)', 'powerpress');
6270 $langs['en-ZA'] = __('English (South Africa)', 'powerpress');
6271 $langs['en-TT'] = __('English (Trinidad)', 'powerpress');
6272 $langs['en-GB'] = __('English (United Kingdom)', 'powerpress');
6273 $langs['en-ZE'] = __('English (Zimbabwe)', 'powerpress');
6274 $langs['et'] = __('Estonian', 'powerpress');
6275 $langs['fo'] = __('Faeroese', 'powerpress');
6276 $langs['fi'] = __('Finnish', 'powerpress');
6277 $langs['fr'] = __('French', 'powerpress');
6278 $langs['fr-BE'] = __('French (Belgium)', 'powerpress');
6279 $langs['fr-CA'] = __('French (Canada)', 'powerpress');
6280 $langs['fr-FD'] = __('French (France)', 'powerpress');
6281 $langs['fr-LU'] = __('French (Luxembourg)', 'powerpress');
6282 $langs['fr-MC'] = __('French (Monaco)', 'powerpress');
6283 $langs['fr-CH'] = __('French (Switzerland)', 'powerpress');
6284 $langs['gl'] = __('Galician', 'powerpress');
6285 $langs['gd'] = __('Gaelic', 'powerpress');
6286 $langs['de'] = __('German', 'powerpress');
6287 $langs['de-AT'] = __('German (Austria)', 'powerpress');
6288 $langs['de-DE'] = __('German (Germany)', 'powerpress');
6289 $langs['de-LI'] = __('German (Liechtenstein)', 'powerpress');
6290 $langs['de-LU'] = __('German (Luxembourg)', 'powerpress');
6291 $langs['de-CH'] = __('German (Switzerland)', 'powerpress');
6292 $langs['el'] = __('Greek', 'powerpress');
6293 $langs['haw'] = __('Hawaiian', 'powerpress');
6294 $langs['he'] = __('Hebrew', 'powerpress');
6295 $langs['hu'] = __('Hungarian', 'powerpress');
6296 $langs['is'] = __('Icelandic', 'powerpress');
6297 $langs['id'] = __('Indonesian', 'powerpress');
6298 $langs['ga'] = __('Irish', 'powerpress');
6299 $langs['it'] = __('Italian', 'powerpress');
6300 $langs['hi'] = __('Hindi', 'powerpress');
6301 $langs['it-IT'] = __('Italian (Italy)', 'powerpress');
6302 $langs['it-CH'] = __('Italian (Switzerland)', 'powerpress');
6303 $langs['ja'] = __('Japanese', 'powerpress');
6304 $langs['ko'] = __('Korean', 'powerpress');
6305 $langs['mk'] = __('Macedonian', 'powerpress');
6306 $langs['no'] = __('Norwegian', 'powerpress');
6307 $langs['pa'] = __('Punjabi', 'powerpress');
6308 $langs['pl'] = __('Polish', 'powerpress');
6309 $langs['pt'] = __('Portuguese', 'powerpress');
6310 $langs['pt-BR'] = __('Portuguese (Brazil)', 'powerpress');
6311 $langs['pt-PT'] = __('Portuguese (Portugal)', 'powerpress');
6312 $langs['ro'] = __('Romanian', 'powerpress');
6313 $langs['ro-MO'] = __('Romanian (Moldova)', 'powerpress');
6314 $langs['ro-RO'] = __('Romanian (Romania)', 'powerpress');
6315 $langs['ru'] = __('Russian', 'powerpress');
6316 $langs['ru-MO'] = __('Russian (Moldova)', 'powerpress');
6317 $langs['ru-RU'] = __('Russian (Russia)', 'powerpress');
6318 $langs['sr'] = __('Serbian', 'powerpress');
6319 $langs['sk'] = __('Slovak', 'powerpress');
6320 $langs['sl'] = __('Slovenian', 'powerpress');
6321 $langs['es'] = __('Spanish', 'powerpress');
6322 $langs['es-AR'] = __('Spanish (Argentina)', 'powerpress');
6323 $langs['es-BO'] = __('Spanish (Bolivia)', 'powerpress');
6324 $langs['es-CL'] = __('Spanish (Chile)', 'powerpress');
6325 $langs['es-CO'] = __('Spanish (Colombia)', 'powerpress');
6326 $langs['es-CR'] = __('Spanish (Costa Rica)', 'powerpress');
6327 $langs['es-DO'] = __('Spanish (Dominican Republic)', 'powerpress');
6328 $langs['es-EC'] = __('Spanish (Ecuador)', 'powerpress');
6329 $langs['es-SV'] = __('Spanish (El Salvador)', 'powerpress');
6330 $langs['es-GT'] = __('Spanish (Guatemala)', 'powerpress');
6331 $langs['es-HN'] = __('Spanish (Honduras)', 'powerpress');
6332 $langs['es-MX'] = __('Spanish (Mexico)', 'powerpress');
6333 $langs['es-NI'] = __('Spanish (Nicaragua)', 'powerpress');
6334 $langs['es-PA'] = __('Spanish (Panama)', 'powerpress');
6335 $langs['es-PY'] = __('Spanish (Paraguay)', 'powerpress');
6336 $langs['es-PE'] = __('Spanish (Peru)', 'powerpress');
6337 $langs['es-PR'] = __('Spanish (Puerto Rico)', 'powerpress');
6338 $langs['es-ES'] = __('Spanish (Spain)', 'powerpress');
6339 $langs['es-UY'] = __('Spanish (Uruguay)', 'powerpress');
6340 $langs['es-VE'] = __('Spanish (Venezuela)', 'powerpress');
6341 $langs['sv'] = __('Swedish', 'powerpress');
6342 $langs['sv-FI'] = __('Swedish (Finland)', 'powerpress');
6343 $langs['sv-SE'] = __('Swedish (Sweden)', 'powerpress');
6344 $langs['sw'] = __('Swahili', 'powerpress');
6345 $langs['ta'] = __('Tamil', 'powerpress');
6346 $langs['th'] = __('Thai', 'powerpress');
6347 $langs['bo'] = __('Tibetan', 'powerpress');
6348 $langs['tr'] = __('Turkish', 'powerpress');
6349 $langs['uk'] = __('Ukranian', 'powerpress');
6350 $langs['ve'] = __('Venda', 'powerpress');
6351 $langs['vi'] = __('Vietnamese', 'powerpress');
6352 $langs['zu'] = __('Zulu', 'powerpress');
6353 $langs['fa'] = __('Persian', 'powerpress');
6354 $langs['fa-AF'] = __('Persian (Afghanistan)', 'powerpress');
6355 $langs['cmn'] = __('Chinese (Mandarin)', 'powerpress');
6356 $langs['lt'] = __('Lithuanian', 'powerpress');
6357 $langs['lv'] = __('Latvian', 'powerpress');
6358 $langs['ms'] = __('Malay', 'powerpress');
6359 $langs['te'] = __('Telugu', 'powerpress');
6360
6361 return $langs;
6362 }
6363
6364 function powerpress_valid_language($language)
6365 {
6366 $language = trim((string) $language);
6367 if ($language === '') {
6368 return '';
6369 }
6370
6371 $langs = powerpress_languages();
6372 if (isset($langs[$language])) {
6373 return $language;
6374 }
6375
6376 foreach (array_keys($langs) as $code) {
6377 if (strcasecmp($code, $language) === 0) {
6378 return $code;
6379 }
6380 }
6381
6382 return '';
6383 }
6384
6385 /**
6386 * Generates Select options for common Language codes with geographic distinctions
6387 */
6388 function powerpress_print_select_options_lang_codes($selectedOption = '')
6389 {
6390 $lang_code = powerpress_languages();
6391
6392 $options = '';
6393 foreach ($lang_code as $code => $name) {
6394 $selected = ($code === $selectedOption) ? ' selected' : '';
6395 $options .= '<option value="' . esc_attr($code) . '"' . $selected . '>' . esc_html($name) . '</option>';
6396 }
6397 return $options;
6398 }
6399
6400 /**
6401 * Generate Select Options for Roles
6402 */
6403 function powerpress_print_select_options_roles($selectedOption = 'Guest') {
6404 $options = [
6405 "Director", "Assistant Director", "Executive Producer", "Senior Producer", "Producer",
6406 "Associate Producer", "Development Producer", "Creative Director", "Host", "Co-Host",
6407 "Guest Host", "Guest", "Voice Actor", "Narrator", "Announcer", "Reporter", "Author",
6408 "Editorial Director", "Co-Writer", "Writer", "Songwriter", "Guest Writer", "Story Editor",
6409 "Managing Editor", "Script Editor", "Script Coordinator", "Researcher", "Editor", "Fact Checker",
6410 "Translator", "Transcriber", "Logger", "Studio Coordinator", "Technical Director", "Technical Manager",
6411 "Audio Engineer", "Remote Recording Engineer", "Post Production Engineer", "Audio Editor", "Sound Designer",
6412 "Foley Artist", "Composer", "Theme Music", "Music Production", "Music Contributor", "Production Coordinator",
6413 "Booking Coordinator", "Production Assistant", "Content Manager", "Marketing Manager", "Sales Representative",
6414 "Sales Manager", "Graphic Designer", "Cover Art Designer", "Social Media Manager", "Consultant", "Intern",
6415 "Camera Operator", "Lighting Designer", "Camera Grip", "Assistant Camera", "Editor", "Assistant Editor"
6416 ];
6417
6418 foreach ($options as $option) {
6419 if ($option == $selectedOption) {
6420 echo '<option selected value="' . $option . '">' . __($option, "powerpress").'</option>';
6421 } else {
6422 echo '<option value="' . $option . '">' . __($option, "powerpress") . '</option>';
6423 }
6424 }
6425 }
6426
6427 /**
6428 * Conver raw seconds to string in the form of HH:MM:SS
6429 */
6430 function powerpress_seconds_to_hms($secs)
6431 {
6432 $secs = (int)$secs;
6433 $h = floor($secs / 3600);
6434 $m = floor(($secs % 3600) / 60);
6435 $s = $secs % 60;
6436 return sprintf('%02d:%02d:%02d', $h, $m, $s);
6437 }
6438
6439 // =========================
6440 // TEMPLATE RENDERING HELPER
6441 // =========================
6442
6443 function powerpress_render_template($config) {
6444 $type = $config['type'];
6445 $FeedSlug = $config['FeedSlug'];
6446 $DataSource = $config['Data'];
6447 $namePrefix = $config['NamePrefix'];
6448 $section_data = [];
6449
6450 $templates = [
6451 'location' => 'location.php',
6452 'copyright' => 'copyright.php',
6453 'credit' => 'credit.php',
6454 'v4v' => 'v4v.php',
6455 'soundbites' => 'soundbite.php',
6456 'social_interact' => 'social-interact.php',
6457 'donate' => 'donate.php',
6458 'txt_tag' => 'txt-tag.php',
6459 'alternate_enclosure'=> 'alt-enclosure.php',
6460 'content_link' => 'content-link.php',
6461 'update_frequency' => 'update-frequency.php',
6462 ];
6463
6464 if (!isset($templates[$type])) {
6465 throw new InvalidArgumentException("Unknown section type: {$type}");
6466 }
6467
6468 include(POWERPRESS_ABSPATH . "/views/pci/{$templates[$type]}");
6469 }
6470
6471 // =====================================
6472 // UPDATE FREQUENCY NORMALIZATION HELPER
6473 // =====================================
6474
6475 /** normalize legacy save pattern for feed output */
6476 function powerpress_normalize_update_frequency($value, $week_indices = null, $month_interval = null) {
6477 if (is_array($value)) return $value;
6478 if (empty($value)) return null;
6479
6480 $freq_map = [1 => 'DAILY', 2 => 'WEEKLY', 3 => 'MONTHLY'];
6481 $value = (int) $value;
6482 if (!isset($freq_map[$value])) return null;
6483
6484 $result = ['freq' => $freq_map[$value]];
6485
6486 if ($value === 2 && $week_indices !== null && trim((string)$week_indices) !== '') {
6487 $code_lookup = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
6488 $codes = [];
6489 foreach (explode(',', $week_indices) as $idx) {
6490 $idx = (int) trim($idx);
6491 if (isset($code_lookup[$idx]))
6492 $codes[] = $code_lookup[$idx];
6493 }
6494 if (!empty($codes))
6495 $result['byday'] = implode(',', $codes);
6496 }
6497
6498 if ($value === 3 && !empty($month_interval)) {
6499 $result['interval'] = (int) $month_interval;
6500 }
6501
6502 return $result;
6503 }
6504
6505 require_once(POWERPRESS_ABSPATH.'/powerpress-hosting.class.php');
6506
6507 // Are we in the admin?
6508 if( is_admin() )
6509 {
6510 require_once(POWERPRESS_ABSPATH.'/powerpressadmin.php');
6511 register_activation_hook( __FILE__, 'powerpress_admin_activate' );
6512 }
6513
6514 if( defined('POWERPRESS_SUBSCRIBE') && POWERPRESS_SUBSCRIBE )
6515 {
6516 require_once(POWERPRESS_ABSPATH.'/powerpress-subscribe.php');
6517 }
6518
6519 // For testing purposes in development
6520 if( defined('POWERPRESS_NEW_CODE') && POWERPRESS_NEW_CODE && file_exists(POWERPRESS_ABSPATH.'/powerpress-new-code.php') )
6521 {
6522 require_once(POWERPRESS_ABSPATH.'/powerpress-new-code.php');
6523 }
6524
6525 if( defined('POWERPRESS_PREMIUM_GROUPS_PLUGIN') ) {
6526
6527 function powerpress_pre_get_posts($query) {
6528 if( $query->is_feed() && powerpress_is_custom_podcast_feed() && method_exists('Groups_Post_Access', 'posts_where') )
6529 {
6530 $feed_slug = get_query_var('feed');
6531
6532 if( $feed_slug != 'podcast' )
6533 {
6534 $FeedSettings = get_option('powerpress_feed_'.$feed_slug);
6535 if( !empty($FeedSettings['premium']) )
6536 {
6537 if( has_filter('posts_where', 'Groups_Post_Access::posts_where') )
6538 {
6539 remove_filter('posts_where', 'Groups_Post_Access::posts_where');
6540 }
6541 }
6542 }
6543 }
6544 }
6545 add_filter('pre_get_posts', 'powerpress_pre_get_posts');
6546 }
6547
6548 /**
6549 * enqueue css and js assets with standard powerpress settings.
6550 *
6551 * simplifies registration and enqueueing with:
6552 * - auto-suffix (.min for production, none for debug)
6553 * - auto-dependency on powerpress-variables.css for styles
6554 * - es module support via 'module' => true
6555 *
6556 * example:
6557 * powerpress_enqueue_assets([
6558 * 'powerpress-stats' => [
6559 * 'path' => 'css/components/stats-widget',
6560 * ],
6561 * 'powerpress-chart' => [
6562 * 'type' => 'script',
6563 * 'path' => '3rdparty/chart',
6564 * 'no_suffix' => true,
6565 * ],
6566 * 'powerpress-stats-widget' => [
6567 * 'type' => 'script',
6568 * 'path' => 'js/modules/program-card/StatsWidget',
6569 * 'deps' => ['powerpress-chart'],
6570 * 'module' => true,
6571 * ],
6572 * ]);
6573 *
6574 * @param array $assets associative array of handle => config pairs.
6575 * Config keys:
6576 * - type: 'style' or 'script' (default: 'style')
6577 * - path: local path relative to plugin root (without extension or suffix)
6578 * - url: external url (alternative to path)
6579 * - deps: array of dependencies (default: [])
6580 * - version: version string (default: POWERPRESS_VERSION)
6581 *
6582 * - footer: load in footer (scripts only, default: true)
6583 * - strategy: 'defer' or 'async' (scripts only, default: 'defer')
6584 * - module: add type="module" attribute (scripts only, default: false)
6585 *
6586 * - no_suffix: skip .min suffix even in production (default: false)
6587 */
6588 function powerpress_enqueue_assets(array $assets): void {
6589 if (empty($assets)) {
6590 return;
6591 }
6592
6593 // 1) SETUP
6594 $debug = defined('WP_DEBUG') && WP_DEBUG;
6595 $suffix = $debug ? '' : '.min';
6596 $base_url = powerpress_get_root_url();
6597 $warnings = [];
6598 $module_handles = [];
6599
6600 // 2) AUTO-REGISTER VARIABLES.CSS
6601 if (!wp_style_is('powerpress-variables', 'registered')) {
6602 wp_register_style('powerpress-variables', "{$base_url}css/variables{$suffix}.css", [], POWERPRESS_VERSION);
6603 }
6604
6605 // 3) PROCESS EACH ASSET
6606 foreach ($assets as $handle => $config) {
6607 if (!is_string($handle) || !is_array($config)) {
6608 $warnings[] = 'Invalid handle or config: ' . print_r($handle, true);
6609 continue;
6610 }
6611
6612 $type = $config['type'] ?? 'style';
6613 if ($type !== 'style' && $type !== 'script') {
6614 $warnings[] = "Invalid type for '{$handle}': must be 'style' or 'script'";
6615 continue;
6616 }
6617
6618 $deps = $config['deps'] ?? [];
6619 $version = $config['version'] ?? POWERPRESS_VERSION;
6620
6621 // resolve url from external url or local path
6622 if (!empty($config['url'])) {
6623 $url = $config['url'];
6624 } elseif (!empty($config['path'])) {
6625 $ext = ($type === 'script') ? '.js' : '.css';
6626 $file_suffix = empty($config['no_suffix']) ? $suffix : '';
6627 $url = $base_url . $config['path'] . $file_suffix . $ext;
6628 } else {
6629 $warnings[] = "Missing path or url for '{$handle}'";
6630 continue;
6631 }
6632
6633 // register and enqueue
6634 if ($type === 'script') {
6635 if (!in_array('wp-i18n', $deps, true)) {
6636 $deps[] = 'wp-i18n';
6637 }
6638 $args = [
6639 'in_footer' => $config['footer'] ?? true,
6640 'strategy' => $config['strategy'] ?? 'defer',
6641 ];
6642 wp_register_script($handle, $url, $deps, $version, $args);
6643 wp_enqueue_script($handle);
6644
6645 // enable js translations via wp.i18n
6646 wp_set_script_translations($handle, 'powerpress');
6647
6648 if (!empty($config['module'])) {
6649 $module_handles[] = $handle;
6650 }
6651 } else {
6652 // styles auto depend on variables.css
6653 if ($handle !== 'powerpress-variables' && !in_array('powerpress-variables', $deps)) {
6654 $deps[] = 'powerpress-variables';
6655 }
6656 wp_register_style($handle, $url, $deps, $version);
6657 wp_enqueue_style($handle);
6658 }
6659 }
6660
6661 // 4) ADD MODULE SUPPORT VIA SCRIPT TAG FILTER
6662 if (!empty($module_handles)) {
6663 add_filter('script_loader_tag', function($tag, $handle) use ($module_handles) {
6664 if (in_array($handle, $module_handles)) {
6665 return str_replace('<script ', '<script type="module" ', $tag);
6666 }
6667 return $tag;
6668 }, 10, 2);
6669 }
6670
6671 // 5) LOG WARNINGS IN DEBUG MODE
6672 if ($debug && !empty($warnings)) {
6673 $log_warnings = function() use ($warnings) {
6674 echo '<script>console.warn("PowerPress enqueue_assets:", ' . wp_json_encode($warnings) . ');</script>';
6675 };
6676 add_action('admin_footer', $log_warnings);
6677 add_action('wp_footer', $log_warnings);
6678 }
6679 }
6680
6681 // ===================
6682 // NETWORK ASSET SETUP
6683 // ===================
6684 function powerpress_network_admin_enqueue_scripts() {
6685 if (empty($_GET['page']) || $_GET['page'] !== 'network-plugin') return;
6686
6687 if (is_admin()) {
6688 // admin styles + js for network pages
6689 powerpress_enqueue_assets([
6690 'powerpress-admin-css' => ['path' => 'css/admin'],
6691 'ppn-admin' => ['path' => 'css/ppn-admin'],
6692 'powerpress-bootstrap-grid' => ['path' => 'css/bootstrap-grid'],
6693 'powerpress-network-js' => ['type' => 'script', 'path' => 'js/network', 'module' => true, 'deps' => ['wp-i18n']],
6694 'material-icons-outlined' => ['type' => 'style', 'url' => 'https://fonts.googleapis.com/icon?family=Material+Icons+Outlined'],
6695 'roboto-font' => ['type' => 'style', 'url' => 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700&display=swap'],
6696 ]);
6697 }
6698 }
6699
6700
6701 // eof
6702