PluginProbe
PowerPress Podcasting plugin by Blubrry / 11.17.9
PowerPress Podcasting plugin by Blubrry v11.17.9
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.9, at powerpress.php

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