Example #1
0
/**
 * Podcast Landing Page URL
 * 
 * @todo  move to Model\Podcast->get_landing_page_url()
 * 
 * @return string
 */
function get_landing_page_url()
{
    $landing_page = \Podlove\get_setting('website', 'landing_page');
    switch ($landing_page) {
        case 'homepage':
            return home_url();
            break;
        case 'archive':
            if ('on' == \Podlove\get_setting('website', 'episode_archive')) {
                $archive_slug = trim(\Podlove\get_setting('website', 'episode_archive_slug'), '/');
                $blog_prefix = \Podlove\get_blog_prefix();
                $blog_prefix = $blog_prefix ? trim($blog_prefix, '/') . '/' : '';
                return trailingslashit(get_option('home') . $blog_prefix) . $archive_slug;
            }
            break;
        default:
            if (is_numeric($landing_page)) {
                if ($link = get_permalink($landing_page)) {
                    return $link;
                }
            }
            break;
    }
    // always default to home page
    return home_url();
}
Example #2
0
 public function __construct()
 {
     $labels = array('name' => __('Episodes', 'podlove'), 'singular_name' => __('Episode', 'podlove'), 'add_new' => __('Add New', 'podlove'), 'add_new_item' => __('Add New Episode', 'podlove'), 'edit_item' => __('Edit Episode', 'podlove'), 'new_item' => __('New Episode', 'podlove'), 'all_items' => __('All Episodes', 'podlove'), 'view_item' => __('View Episode', 'podlove'), 'search_items' => __('Search Episodes', 'podlove'), 'not_found' => __('No episodes found', 'podlove'), 'not_found_in_trash' => __('No episodes found in Trash', 'podlove'), 'parent_item_colon' => '', 'menu_name' => __('Episodes', 'podlove'));
     $slug = trim(\Podlove\get_setting('custom_episode_slug'));
     $args = array('labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 5, 'query_var' => true, 'rewrite' => true, 'capability_type' => 'post', 'has_archive' => true, 'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'custom-fields', 'trackbacks'), 'register_meta_box_cb' => '\\Podlove\\Podcast_Post_Meta_Box::add_meta_box', 'menu_icon' => PLUGIN_URL . '/images/episodes-icon-16x16.png', 'rewrite' => array('slug' => strlen($slug) ? $slug : 'podcast', 'with_front' => false));
     if (strlen($slug) === 0) {
         \Podlove\Episode_Routing::init();
     }
     new \Podlove\Podcast_Post_Meta_Box();
     $args = apply_filters('podlove_post_type_args', $args);
     register_post_type('podcast', $args);
     add_action('admin_menu', array($this, 'create_menu'));
     add_filter('default_content', array($this, 'set_default_episode_content'), 20, 2);
     add_action('after_delete_post', array($this, 'delete_trashed_episodes'));
     if (is_admin()) {
         add_action('podlove_list_shows', array($this, 'list_shows'));
         add_action('podlove_list_formats', array($this, 'list_formats'));
         wp_register_script('podlove_admin_episode', \Podlove\PLUGIN_URL . '/js/admin/episode.js', array('jquery'), '1.1');
         wp_register_script('podlove_admin_dashboard_validation', \Podlove\PLUGIN_URL . '/js/admin/dashboard_validation.js', array('jquery'), '1.1');
         wp_register_script('podlove_admin_episode_asset_settings', \Podlove\PLUGIN_URL . '/js/admin/episode_asset_settings.js', array('jquery'), '1.1');
         wp_register_script('podlove_admin', \Podlove\PLUGIN_URL . '/js/admin.js', array('jquery', 'podlove_admin_episode', 'podlove_admin_dashboard_validation', 'podlove_admin_episode_asset_settings'), '1.0');
         wp_enqueue_script('podlove_admin');
     }
     add_filter('request', array($this, 'add_post_type_to_feeds'));
     add_filter('get_the_excerpt', array($this, 'default_excerpt_to_episode_summary'));
     \Podlove\Feeds\init();
 }
function podlove_handle_media_file_tracking(\Podlove\Model\MediaFile $media_file)
{
    if (\Podlove\get_setting('tracking', 'mode') !== "ptm_analytics") {
        return;
    }
    if (strtoupper($_SERVER['REQUEST_METHOD']) === 'HEAD') {
        return;
    }
    $intent = new Model\DownloadIntent();
    $intent->media_file_id = $media_file->id;
    $intent->accessed_at = date('Y-m-d H:i:s');
    $ptm_source = trim(podlove_get_query_var('ptm_source'));
    $ptm_context = trim(podlove_get_query_var('ptm_context'));
    if ($ptm_source) {
        $intent->source = $ptm_source;
    }
    if ($ptm_context) {
        $intent->context = $ptm_context;
    }
    // set user agent
    $ua_string = trim($_SERVER['HTTP_USER_AGENT']);
    if ($agent = Model\UserAgent::find_or_create_by_uastring($ua_string)) {
        $intent->user_agent_id = $agent->id;
    }
    // save HTTP range header
    // @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 for spec
    if (isset($_SERVER['HTTP_RANGE'])) {
        $intent->httprange = $_SERVER['HTTP_RANGE'];
    }
    // get ip, but don't store it
    $ip_string = $_SERVER['REMOTE_ADDR'];
    try {
        $ip = IP\Address::factory($_SERVER['REMOTE_ADDR']);
        if (method_exists($ip, 'as_IPv6_address')) {
            $ip = $ip->as_IPv6_address();
        }
        $ip_string = $ip->format(IP\Address::FORMAT_COMPACT);
    } catch (\InvalidArgumentException $e) {
        \Podlove\Log::get()->addWarning('Could not use IP "' . $_SERVER['REMOTE_ADDR'] . '"' . $e->getMessage());
    }
    // Generate a hash from IP address and UserAgent so we can identify
    // identical requests without storing an IP address.
    if (function_exists('openssl_digest')) {
        $intent->request_id = openssl_digest($ip_string . $ua_string, 'sha256');
    } else {
        $intent->request_id = sha1($ip_string . $ua_string);
    }
    $intent = $intent->add_geo_data($ip_string);
    $intent->save();
}
/**
 * Handle configured redirects
 */
function podlove_handle_user_redirects()
{
    global $wpdb, $wp_query;
    if (is_admin()) {
        return;
    }
    // check for global redirects
    $parsed_request = parse_url($_SERVER['REQUEST_URI']);
    $parsed_request_url = $parsed_request['path'];
    if (isset($parsed_request['query'])) {
        $parsed_request_url .= "?" . $parsed_request['query'];
    }
    $redirects = \Podlove\get_setting('redirects', 'podlove_setting_redirect');
    foreach ($redirects as $index => $redirect) {
        if (!isset($redirect['active'])) {
            continue;
        }
        if (!strlen(trim($redirect['from'])) || !strlen(trim($redirect['to']))) {
            continue;
        }
        $parsed_url = parse_url($redirect['from']);
        $parsed_redirect_url = $parsed_url['path'];
        if (isset($parsed_url['query'])) {
            $parsed_redirect_url .= "?" . $parsed_url['query'];
        }
        if (untrailingslashit($parsed_redirect_url) === untrailingslashit($parsed_request_url)) {
            if ($redirect['code']) {
                $http_code = (int) $redirect['code'];
            } else {
                $http_code = 301;
                // default to permanent
            }
            // fallback for HTTP/1.0 clients
            if ($http_code == 307 && $_SERVER['SERVER_PROTOCOL'] == "HTTP/1.0") {
                $http_code = 302;
            }
            // increment redirection counter
            $redirects[$index]['count'] += 1;
            \Podlove\save_setting('redirects', 'podlove_setting_redirect', $redirects);
            // redirect
            status_header($http_code);
            $wp_query->is_404 = false;
            wp_redirect($redirect['to'], $http_code);
            exit;
        }
    }
}
function handle_feed_proxy_redirects()
{
    if (!($feed_slug = get_query_var('feed'))) {
        return;
    }
    $paged = get_query_var('paged') ? get_query_var('paged') : 1;
    $is_debug_view = false;
    if (\Podlove\get_setting('website', 'feeds_skip_redirect') == 'on' && filter_input(INPUT_GET, 'redirect') == 'no') {
        $should_redirect = false;
        $is_debug_view = true;
    } elseif (preg_match("/feedburner|feedsqueezer|feedvalidator|feedpress/i", filter_input(INPUT_SERVER, 'HTTP_USER_AGENT'))) {
        $should_redirect = false;
    } else {
        $should_redirect = true;
    }
    $is_feed_page = $paged > 1;
    if (!($feed = Model\Feed::find_one_by_slug($feed_slug))) {
        return;
    }
    /**
     * Before we redirect to a proxy or deliver the feed, ensure that the canonical
     * feed URL was accessed.
     */
    if (!$is_debug_view && get_option('permalink_structure') != '') {
        $feed_url = $feed->get_subscribe_url();
        $request_url = "http" . (isset($_SERVER['HTTPS']) ? 's' : '') . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        $url = parse_url($request_url);
        if (!\Podlove\PHP\ends_with($url['path'], '/') && \Podlove\PHP\ends_with($feed_url, '/') || \Podlove\PHP\ends_with($url['path'], '/') && !\Podlove\PHP\ends_with($feed_url, '/')) {
            if ($is_feed_page) {
                $feed_url = add_query_arg(['paged' => $paged], $feed_url);
            }
            wp_redirect($feed_url, 301);
            exit;
        }
    }
    // most HTTP/1.0 client's don't understand 307, so we fall back to 302
    $http_status_code = $_SERVER['SERVER_PROTOCOL'] == "HTTP/1.0" ? 302 : $feed->redirect_http_status;
    if (!$is_feed_page && strlen($feed->redirect_url) > 0 && $should_redirect && $http_status_code > 0) {
        header(sprintf("Location: %s", $feed->redirect_url), TRUE, $http_status_code);
        exit;
    } else {
        // don't redirect; prepare feed
        status_header(200);
        RSS::prepare_feed($feed->slug);
    }
}
 public function __construct($handle)
 {
     if (\Podlove\get_setting('tracking', 'mode') !== "ptm_analytics") {
         return;
     }
     self::$pagehook = add_submenu_page($handle, __('Analytics', 'podlove'), __('Analytics', 'podlove'), 'podlove_read_analytics', 'podlove_analytics', array($this, 'page'));
     $this->init_page_documentation(self::$pagehook);
     // add_action( 'admin_init', array( $this, 'process_form' ) );
     add_action('admin_init', array($this, 'scripts_and_styles'));
     if (isset($_GET['action']) && $_GET['action'] == 'show') {
         add_action('load-' . self::$pagehook, function () {
             add_action('add_meta_boxes_' . \Podlove\Settings\Analytics::$pagehook, function () {
                 add_meta_box(\Podlove\Settings\Analytics::$pagehook . '_release_downloads_chart', __('Downloads over Time', 'podlove'), '\\Podlove\\Settings\\Analytics::chart', \Podlove\Settings\Analytics::$pagehook, 'normal');
                 add_meta_box(\Podlove\Settings\Analytics::$pagehook . '_numbers', __('Download Numbers', 'podlove'), '\\Podlove\\Settings\\Analytics::numbers', \Podlove\Settings\Analytics::$pagehook, 'normal');
             });
             do_action('add_meta_boxes_' . \Podlove\Settings\Analytics::$pagehook);
             wp_enqueue_script('postbox');
         });
     }
     add_filter('screen_settings', [$this, 'screen_settings'], 10, 2);
 }
 private static function follow_url($url)
 {
     if (!function_exists('curl_exec')) {
         return false;
     }
     $curl = curl_init();
     $curl_version = curl_version();
     curl_setopt($curl, CURLOPT_URL, $url);
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     // make curl_exec() return the result
     curl_setopt($curl, CURLOPT_HEADER, true);
     // header only
     curl_setopt($curl, CURLOPT_NOBODY, true);
     // return no body; HTTP request method: HEAD
     curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, \Podlove\get_setting('website', 'ssl_verify_peer') == 'on');
     // Don't check SSL certificate in order to be able to use self signed certificates
     curl_setopt($curl, CURLOPT_FAILONERROR, true);
     curl_setopt($curl, CURLOPT_TIMEOUT, 3);
     // HEAD requests shouldn't take > 2 seconds
     curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
     // follow redirects
     curl_setopt($curl, CURLOPT_MAXREDIRS, 0);
     // maximum number of redirects
     curl_setopt($curl, CURLOPT_USERAGENT, \Podlove\Http\Curl::user_agent());
     $response = curl_exec($curl);
     $response_header = curl_getinfo($curl);
     $error = curl_error($curl);
     curl_close($curl);
     if (isset($response_header['redirect_url']) && $response_header['redirect_url']) {
         return $response_header['redirect_url'];
     } else {
         return false;
     }
     // return array(
     // 	'header'   => $response_header,
     // 	'response' => $response,
     // 	'error'    => $error
     // );
 }
Example #8
0
        add_filter('podlove_subscribe_url', 'domain_mapping_post_content', 20);
    }
});
/**
 * This helps to get your blog tidy.
 * It's all about "Settings > Reading > Front page displays"
 *
 * Default: Check "Your latest posts" and we won't change anything.
 * However, if you check "A static page", we assume you'd like to separate
 * blog and podcast by moving your blog away and the podcast directory to "/".
 * That's what we do here.
 *
 * It's magic. Okay, I should probably document this publicly at some point.
 */
add_filter('pre_get_posts', function ($wp_query) {
    if (is_home() && $wp_query->is_main_query() && \Podlove\get_setting('merge_episodes') === 'on') {
        $wp_query->set('post_type', array('post', 'podcast'));
        return $wp_query;
    }
    if (get_option('show_on_front') === 'posts') {
        return $wp_query;
    }
    if ($wp_query->get('page_id') == get_option('page_on_front')) {
        $wp_query->set('post_type', array('podcast'));
        // fix conditional functions
        $wp_query->set('page_id', '');
        $wp_query->is_page = 0;
        $wp_query->is_singular = 0;
    }
    return $wp_query;
});
    public function init()
    {
        // always flush rewrite rules here for custom_episode_slug setting
        if ($this->is_active()) {
            set_transient('podlove_needs_to_flush_rewrite_rules', true);
        }
        add_settings_section('podlove_settings_general', __('', 'podlove'), function () {
            echo '<h3>' . __('Website Settings', 'podlove') . '</h3>';
        }, Settings::$pagehook);
        add_settings_section('podlove_settings_files', __('', 'podlove'), function () {
            echo '<h3>' . __('Files & Downloads', 'podlove') . '</h3>';
        }, Settings::$pagehook);
        add_settings_section('podlove_settings_feeds', __('', 'podlove'), function () {
            echo '<h3>' . __('Feeds', 'podlove') . '</h3>';
        }, Settings::$pagehook);
        add_settings_field('podlove_setting_merge_episodes', sprintf('<label for="merge_episodes">%s</label>', __('Combine blog & podcast', 'podlove')), function () {
            ?>
				<input name="podlove_website[merge_episodes]" id="merge_episodes" type="checkbox" <?php 
            checked(\Podlove\get_setting('website', 'merge_episodes'), 'on');
            ?>
>
				<?php 
            echo __('Include episode posts on the front page and in the blog feed', 'podlove');
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_hide_wp_feed_discovery', sprintf('<label for="hide_wp_feed_discovery">%s</label>', __('Hide blog feeds', 'podlove')), function () {
            ?>
				<input name="podlove_website[hide_wp_feed_discovery]" id="hide_wp_feed_discovery" type="checkbox" <?php 
            checked(\Podlove\get_setting('website', 'hide_wp_feed_discovery'), 'on');
            ?>
>
				<?php 
            echo __('Hide default WordPress feeds for blog and comments (no auto-discovery).', 'podlove');
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_custom_episode_slug', sprintf('<label for="custom_episode_slug">%s</label>', __('Permalink structure for episodes', 'podlove')), function () {
            $use_post_permastruct = \Podlove\get_setting('website', 'use_post_permastruct');
            $custom_episode_slug = \Podlove\get_setting('website', 'custom_episode_slug');
            if ($blog_prefix = \Podlove\get_blog_prefix()) {
                $custom_episode_slug = preg_replace('|^/?blog|', '', $custom_episode_slug);
            }
            ?>
				<input name="podlove_website[use_post_permastruct]" id="use_post_permastruct" type="checkbox" <?php 
            checked($use_post_permastruct, 'on');
            ?>
> <?php 
            _e('Use the same permalink structure as posts', 'podlove');
            ?>
				<div id="custom_podcast_permastruct"<?php 
            if ($use_post_permastruct) {
                echo ' style="display:none;"';
            }
            ?>
>
					<code><?php 
            echo get_option('home');
            ?>
</code>
					<input name="podlove_website[custom_episode_slug]" id="custom_episode_slug" type="text" value="<?php 
            echo $custom_episode_slug;
            ?>
">
					<p><span class="description">
						<?php 
            echo __('
							Placeholders: %podcast% (post name slug), %post_id%, %year%, %monthnum%, %day%, %hour%, %minute%, %second%, %category%, %author%<br>
							Example schemes: <code>/%podcast%</code>, <code>/episode/%podcast%</code>, <code>/%year%/%monthnum%/%podcast%</code>', 'podlove');
            ?>
					</span></p>
				</div>
				
				<script type="text/javascript">
				jQuery(function($) {
					$(document).ready(function() {

						function handle_permastruct_settings() {
							if ( $("#use_post_permastruct").is( ':checked' ) ) {
								$("#custom_podcast_permastruct").slideUp();
							} else {
								$("#custom_podcast_permastruct").slideDown();
							}
						}

						$("#use_post_permastruct").on("click", function(e) {
							handle_permastruct_settings();
						});

						handle_permastruct_settings();
					});
				});
				</script>

				<style type="text/css">
				#custom_podcast_permastruct {
					margin-top: 10px;
				}
				</style>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_episode_archive', sprintf('<label for="episode_archive">%s</label>', __('Episode pages', 'podlove')), function () {
            $enable_episode_archive = \Podlove\get_setting('website', 'episode_archive');
            $episode_archive_slug = \Podlove\get_setting('website', 'episode_archive_slug');
            if ($blog_prefix = \Podlove\get_blog_prefix()) {
                $episode_archive_slug = preg_replace('|^/?blog|', '', $episode_archive_slug);
            }
            ?>
				<input name="podlove_website[episode_archive]" id="episode_archive" type="checkbox" <?php 
            checked($enable_episode_archive, 'on');
            ?>
> <?php 
            _e('Enable episode pages: a complete, paginated list of episodes, sorted by publishing date.', 'podlove');
            ?>
				<div id="episode_archive_slug_edit"<?php 
            if (!$enable_episode_archive) {
                echo ' style="display:none;"';
            }
            ?>
>
					<code><?php 
            echo get_option('home') . $blog_prefix;
            ?>
</code>
					<input class="podlove-check-input" name="podlove_website[episode_archive_slug]" id="episode_archive_slug" type="text" value="<?php 
            echo $episode_archive_slug;
            ?>
">
				</div>
				
				<script type="text/javascript">
				jQuery(function($) {
					$(document).ready(function() {
						$("#episode_archive").on("click", function(e) {
							if ( $(this).is( ':checked' ) ) {
								$("#episode_archive_slug_edit").slideDown();
							} else {
								$("#episode_archive_slug_edit").slideUp();
							}
						});
					});
				});
				</script>

				<style type="text/css">
				#episode_archive_slug_edit {
					margin-top: 10px;
				}
				</style>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_landing_page', sprintf('<label for="landing_page">%s</label>', __('Podcast landing page', 'podlove')), function () {
            $landing_page = \Podlove\get_setting('website', 'landing_page');
            $landing_page_options = array(array('value' => 'homepage', 'text' => __('Front page', 'podlove')), array('value' => 'archive', 'text' => __('Episode pages', 'podlove')), array('text' => '––––––––––', 'disabled' => true));
            $pages_query = new \WP_Query(array('post_type' => 'page', 'nopaging' => true));
            if ($pages_query->have_posts()) {
                while ($pages_query->have_posts()) {
                    $pages_query->the_post();
                    $landing_page_options[] = array('value' => get_the_ID(), 'text' => get_the_title());
                }
            }
            wp_reset_postdata();
            ?>
				<select name="podlove_website[landing_page]" id="landing_page">
					<?php 
            foreach ($landing_page_options as $option) {
                ?>
						<option
							<?php 
                if (isset($option['value'])) {
                    ?>
								value="<?php 
                    echo $option['value'];
                    ?>
"
								<?php 
                    if ($landing_page == $option['value']) {
                        ?>
 selected<?php 
                    }
                    ?>
							<?php 
                }
                ?>
							<?php 
                if (isset($option['disabled']) && $option['disabled']) {
                    ?>
 disabled<?php 
                }
                ?>
						>
							<?php 
                echo $option['text'];
                ?>
						</option>
					<?php 
            }
            ?>
				</select>

				<script type="text/javascript">
				jQuery(function($) {
					$(document).ready(function() {
						var maybe_toggle_episode_archive_option = function() {
							var $archive = $("#episode_archive"),
								$archive_option = $("#landing_page option:eq(1)"),
								$home_option = $("#landing_page option:eq(0)");

							if ($archive.is(':checked')) {
								$archive_option.attr('disabled', false);
							} else {
								$archive_option.attr('disabled', 'disabled');
								// if it was selected before, unselect it
								if ($archive_option.attr('selected') == 'selected') {
									$archive_option.attr('selected', false);
									$home_option.attr('selected', 'selected');
								}
							}

						};

						$("#episode_archive").on("click", function(e) {
							maybe_toggle_episode_archive_option();
						});

						maybe_toggle_episode_archive_option();
					});
				});
				</script>
				<?php 
            echo __('This defines the landing page to your podcast. It is the site that the your podcast feeds link to.', 'podlove');
            ?>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_url_template', sprintf('<label for="url_template">%s</label>', __('Episode Asset URL Template.', 'podlove')), function () {
            ?>
				<input name="podlove_website[url_template]" id="url_template" type="text" value="<?php 
            echo \Podlove\get_setting('website', 'url_template');
            ?>
" class="large-text podlove-check-input">
				<p>
					<span class="description">
						<?php 
            echo __('Is used to generate URLs. You probably don\'t want to change this.', 'podlove');
            ?>
					</span>
				</p>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_files');
        add_settings_field('podlove_setting_ssl_verify_peer', sprintf('<label for="ssl_verify_peer">%s</label>', __('Check for Assets with SSL-peer-verification.', 'podlove')), function () {
            ?>
				<input name="podlove_website[ssl_verify_peer]" id="ssl_verify_peer" type="checkbox" <?php 
            checked(\Podlove\get_setting('website', 'ssl_verify_peer'), 'on');
            ?>
>
				<?php 
            echo __('If you provide your assets via https with a self-signed or not verifiable SSL-certificate, podlove should display your assets as non exiting. You might solve this by deactivating the ssl peer verification for asset checking. (Detailed: This sets "CURLOPT_SSL_VERIFYPEER" to FALSE.)', 'podlove');
            ?>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_files');
        add_settings_field('podlove_setting_feeds_skip_redirect', sprintf('<label for="feeds_skip_redirect">%s</label>', __('Allow to skip feed redirects', 'podlove')), function () {
            ?>
				<input name="podlove_website[feeds_skip_redirect]" id="feeds_skip_redirect" type="checkbox" <?php 
            checked(\Podlove\get_setting('website', 'feeds_skip_redirect'), 'on');
            ?>
>
				<?php 
            echo __('If you need to debug you feeds while using a feed proxy, add <code>?redirect=no</code> to the feed URL to skip the redirect.', 'podlove');
            ?>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_feeds');
        register_setting(Settings::$pagehook, 'podlove_website', function ($options) {
            /**
             * handle checkboxes
             */
            $checkboxes = array('merge_episodes', 'hide_wp_feed_discovery', 'use_post_permastruct', 'episode_archive', 'ssl_verify_peer', 'feeds_skip_redirect');
            foreach ($checkboxes as $checkbox_key) {
                if (!isset($options[$checkbox_key])) {
                    $options[$checkbox_key] = 'off';
                }
            }
            /**
             * handle permastructs
             */
            $prefix = $blog_prefix = '';
            $iis7_permalinks = iis7_supports_permalinks();
            if (!got_mod_rewrite() && !$iis7_permalinks) {
                $prefix = '/index.php';
            }
            if (is_multisite() && !is_subdomain_install() && is_main_site()) {
                $blog_prefix = '';
            }
            // Episode permastruct
            if (array_key_exists('custom_episode_slug', $options)) {
                $options['custom_episode_slug'] = preg_replace('#/+#', '/', '/' . str_replace('#', '', $options['custom_episode_slug']));
                if ($prefix && $blog_prefix) {
                    $options['custom_episode_slug'] = $prefix . preg_replace('#^/?index\\.php#', '', $options['custom_episode_slug']);
                } else {
                    $options['custom_episode_slug'] = $blog_prefix . $options['custom_episode_slug'];
                }
            }
            // Archive slug
            if (array_key_exists('episode_archive_slug', $options)) {
                $options['episode_archive_slug'] = preg_replace('#/+#', '/', '/' . str_replace('#', '', $options['episode_archive_slug']));
                if ($prefix && $blog_prefix) {
                    $options['episode_archive_slug'] = $prefix . preg_replace('#^/?index\\.php#', '', $options['episode_archive_slug']);
                } else {
                    $options['episode_archive_slug'] = $blog_prefix . $options['episode_archive_slug'];
                }
            }
            return $options;
        });
    }
 * Adds feed discover links to WordPress head.
 */
function podlove_add_feed_discoverability()
{
    if (is_admin()) {
        return;
    }
    if (!function_exists('\\Podlove\\Feeds\\prepare_for_feed')) {
        require_once \Podlove\PLUGIN_DIR . 'lib/feeds/base.php';
    }
    $cache = \Podlove\Cache\TemplateCache::get_instance();
    echo $cache->cache_for('feed_discoverability', function () {
        $feeds = \Podlove\Model\Podcast::get()->feeds();
        $html = '';
        foreach ($feeds as $feed) {
            if ($feed->discoverable) {
                $html .= '<link rel="alternate" type="' . $feed->get_content_type() . '" title="' . \Podlove\Feeds\prepare_for_feed($feed->title_for_discovery()) . '" href="' . $feed->get_subscribe_url() . "\" />\n";
            }
        }
        return $html;
    });
}
add_action('init', function () {
    // priority 2 so they are placed below the WordPress default discovery links
    add_action('wp_head', 'podlove_add_feed_discoverability', 2);
    // hide WordPress default link discovery
    if (\Podlove\get_setting('website', 'hide_wp_feed_discovery') === 'on') {
        remove_action('wp_head', 'feed_links', 2);
        remove_action('wp_head', 'feed_links_extra', 3);
    }
});
 /**
  * Get a podcast setting.
  *
  * Valid namespaces / names:
  *
  *  ```
  *  website
  *  	merge_episodes
  *  	hide_wp_feed_discovery
  *  	use_post_permastruct
  *  	custom_episode_slug
  *  	episode_archive
  *  	episode_archive_slug
  *  	url_template
  *  	ssl_verify_peer
  *  metadata
  *  	enable_episode_recording_date
  *  	enable_episode_explicit
  *  	enable_episode_license
  *  redirects
  *  	podlove_setting_redirect
  *  tracking
  *  	mode
  *  ```
  *
  * @accessor
  */
 public function setting($namespace, $name)
 {
     return \Podlove\get_setting($namespace, $name);
 }
/**
 * Replace placeholders in permalinks with the correct values
 */
function podlove_generate_custom_post_link($post_link, $id, $leavename = false, $sample = false)
{
    // Get post
    $post = get_post($id);
    // Generate urls only for podcast episodes
    if ('podcast' != $post->post_type) {
        return $post_link;
    }
    // Draft or pending?
    $draft_or_pending = isset($post->post_status) && in_array($post->post_status, array('draft', 'pending', 'auto-draft'));
    // Sample
    if ($sample && true == $leavename) {
        $post->post_name = "%pagename%";
    }
    // Get permastruct
    $permastruct = \Podlove\get_setting('website', 'custom_episode_slug');
    if (podlove_and_wordpress_permastructs_are_equal()) {
        $permastruct = str_replace('%postname%', '%podcast%', get_option('permalink_structure'));
    }
    // Only post_name in URL
    if ("/%podcast%" == untrailingslashit($permastruct) && (!$draft_or_pending || $sample)) {
        return home_url(user_trailingslashit($post->post_name));
    }
    // Generate post link
    if (!$draft_or_pending || $sample) {
        $post_link = home_url(user_trailingslashit($permastruct));
    }
    // Replace simple placeholders
    $unixtime = strtotime($post->post_date);
    $post_link = str_replace('%year%', date('Y', $unixtime), $post_link);
    $post_link = str_replace('%monthnum%', date('m', $unixtime), $post_link);
    $post_link = str_replace('%day%', date('d', $unixtime), $post_link);
    $post_link = str_replace('%hour%', date('H', $unixtime), $post_link);
    $post_link = str_replace('%minute%', date('i', $unixtime), $post_link);
    $post_link = str_replace('%second%', date('s', $unixtime), $post_link);
    $post_link = str_replace('%post_id%', $post->ID, $post_link);
    $post_link = str_replace('%podcast%', $post->post_name, $post_link);
    // category and author replacement copied from WordPress core
    if (false !== strpos($permastruct, '%category%')) {
        $cats = get_the_category($post->ID);
        if ($cats) {
            usort($cats, '_usort_terms_by_ID');
            // order by ID
            $category_object = apply_filters('post_link_category', $cats[0], $cats, $post);
            $category_object = get_term($category_object, 'category');
            $category = $category_object->slug;
            if ($parent = $category_object->parent) {
                $category = get_category_parents($parent, false, '/', true) . $category;
            }
        }
        if (empty($category)) {
            $default_category = get_category(get_option('default_category'));
            $category = is_wp_error($default_category) ? '' : $default_category->slug;
        }
        $post_link = str_replace('%category%', $category, $post_link);
    }
    if (false !== strpos($permastruct, '%author%')) {
        $authordata = get_userdata($post->post_author);
        $post_link = str_replace('%author%', $authordata->user_nicename, $post_link);
    }
    return $post_link;
}
 * Handle "Merge Episodes" setting
 */
/**
 * Checking "merge_episodes" allows to see episodes on the front page.
 */
add_action('pre_get_posts', function ($wp_query) {
    if (\Podlove\get_setting('website', 'merge_episodes') !== 'on') {
        return;
    }
    if (is_home() && $wp_query->is_main_query() && !isset($wp_query->query_vars["post_type"])) {
        $wp_query->set('post_type', array_merge(array('post', 'podcast'), (array) $wp_query->get('post_type')));
    }
});
/**
 * Checking "merge_episodes" also includes episodes in main feed
 */
add_filter('request', function ($query_var) {
    if (!isset($query_var['feed'])) {
        return $query_var;
    }
    if (\Podlove\get_setting('website', 'merge_episodes') !== 'on') {
        return $query_var;
    }
    $extend = array('post' => 'post', 'podcast' => 'podcast');
    if (empty($query_var['post_type']) || !is_array($query_var['post_type'])) {
        $query_var['post_type'] = $extend;
    } else {
        $query_var['post_type'] = array_merge($query_var['post_type'], $extend);
    }
    return $query_var;
});
function override_feed_entry($hook, $podcast, $feed, $format)
{
    add_action($hook, function () use($podcast, $feed, $format) {
        global $post;
        $cache = \Podlove\Cache\TemplateCache::get_instance();
        echo $cache->cache_for('feed_item_' . $feed->slug . '_' . $post->ID, function () use($podcast, $feed, $format, $post) {
            $xml = "";
            $episode = Model\Episode::find_one_by_post_id($post->ID);
            $asset = $feed->episode_asset();
            $file = Model\MediaFile::find_by_episode_id_and_episode_asset_id($episode->id, $asset->id);
            $asset_assignment = Model\AssetAssignment::get_instance();
            if (!$file) {
                return;
            }
            $enclosure_file_size = $file->size;
            $cover_art_url = "";
            if ($cover_art = $episode->cover_art()) {
                $cover_art_url = $cover_art->url();
            }
            if (isset($_REQUEST['tracking']) && $_REQUEST['tracking'] == 'no') {
                $enclosure_url = $episode->enclosure_url($feed->episode_asset(), null, null);
            } else {
                $enclosure_url = $episode->enclosure_url($feed->episode_asset(), "feed", $feed->slug);
            }
            $deep_link = Model\Feed::get_link_tag(array('prefix' => 'atom', 'rel' => 'http://podlove.org/deep-link', 'type' => '', 'title' => '', 'href' => get_permalink() . "#"));
            $xml .= apply_filters('podlove_deep_link', $deep_link, $feed);
            $xml .= apply_filters('podlove_feed_enclosure', '', $enclosure_url, $enclosure_file_size, $format->mime_type, $file);
            $duration = sprintf('<itunes:duration>%s</itunes:duration>', $episode->get_duration('HH:MM:SS'));
            $xml .= apply_filters('podlove_feed_itunes_duration', $duration);
            $author = apply_filters('podlove_feed_content', $podcast->author_name);
            $author = sprintf('<itunes:author>%s</itunes:author>', $author);
            $xml .= apply_filters('podlove_feed_itunes_author', $author);
            $subtitle = apply_filters('podlove_feed_content', \Podlove\PHP\escape_shortcodes(strip_tags($episode->subtitle)));
            $subtitle = sprintf('<itunes:subtitle>%s</itunes:subtitle>', $subtitle);
            $xml .= apply_filters('podlove_feed_itunes_subtitle', $subtitle);
            $summary = apply_filters('podlove_feed_content', \Podlove\PHP\escape_shortcodes(strip_tags($episode->summary)));
            $summary = sprintf('<itunes:summary>%s</itunes:summary>', $summary);
            $xml .= apply_filters('podlove_feed_itunes_summary', $summary);
            if (\Podlove\get_setting('metadata', 'enable_episode_explicit')) {
                $itunes_explicit = apply_filters('podlove_feed_content', $episode->explicit_text());
                $itunes_explicit = sprintf('<itunes:explicit>%s</itunes:explicit>', $itunes_explicit);
                $xml .= apply_filters('podlove_feed_itunes_explicit', $itunes_explicit);
            }
            if ($cover_art_url) {
                $cover_art = sprintf('<itunes:image href="%s" />', $cover_art_url);
            } else {
                $cover_art = '';
            }
            $xml .= apply_filters('podlove_feed_itunes_image', $cover_art);
            if ($feed->embed_content_encoded) {
                add_filter('the_content_feed', function ($content, $feed_type) {
                    return preg_replace('#<style(.*?)>(.*?)</style>#is', '', $content);
                }, 10, 2);
                $content_encoded = '<content:encoded><![CDATA[' . get_the_content_feed('rss2') . ']]></content:encoded>';
                $xml .= apply_filters('podlove_feed_content_encoded', $content_encoded);
            }
            ob_start();
            do_action('podlove_append_to_feed_entry', $podcast, $episode, $feed, $format);
            $xml .= ob_get_contents();
            ob_end_clean();
            return $xml;
        }, 15 * MINUTE_IN_SECONDS);
    }, 11);
}
Example #15
0
 public function __construct()
 {
     $errors =& $this->errors;
     $notices =& $this->notices;
     $this->fields = array('site' => array('title' => 'Website', 'callback' => function () {
         return get_site_url();
     }), 'php_version' => array('title' => 'PHP Version', 'callback' => function () {
         return phpversion();
     }), 'wp_version' => array('title' => 'WordPress Version', 'callback' => function () {
         return get_bloginfo('version');
     }), 'podlove_version' => array('title' => 'Publisher Version', 'callback' => function () {
         return \Podlove\get_plugin_header('Version');
     }), 'player_version' => array('title' => 'Web Player Version', 'callback' => function () {
         if (!defined('PODLOVEWEBPLAYER_DIR')) {
             return 'no web player found';
         }
         $pwp_file = PODLOVEWEBPLAYER_DIR . 'podlove-web-player.php';
         if (!is_readable($pwp_file)) {
             return 'not readable';
         }
         $plugin_data = \get_plugin_data($pwp_file);
         return $plugin_data['Version'];
     }), 'twig_version' => array('title' => 'Twig Version', 'callback' => function () {
         return \Twig_Environment::VERSION;
     }), 'open_basedir' => array('callback' => function () use(&$notices) {
         $open_basedir = trim(ini_get('open_basedir'));
         if ($open_basedir != '.') {
             $notices[] = 'The PHP setting "open_basedir" is not empty. This is incompatible with curl, a library required by Podlove Publisher. We have a workaround in place but it is preferred to fix the issue. Please ask your hoster to unset "open_basedir".';
         }
         if ($open_basedir) {
             return $open_basedir;
         } else {
             return 'ok';
         }
     }), 'curl' => array('title' => 'curl Version', 'callback' => function () use(&$errors) {
         $module_loaded = in_array('curl', get_loaded_extensions());
         $function_disabled = stripos(ini_get('disable_functions'), 'curl_exec') !== false;
         $out = '';
         if ($module_loaded) {
             $curl = curl_version();
             $out .= $curl['version'];
         } else {
             $out .= 'EXTENSION MISSING';
             $errors[] = 'curl extension is not loaded';
         }
         if ($function_disabled) {
             $out .= ' | curl_exec is disabled';
             $errors[] = 'curl_exec is disabled';
         }
         return $out;
     }), 'iconv' => array('callback' => function () use(&$errors) {
         $iconv_available = function_exists('iconv');
         if (!$iconv_available) {
             $errors[] = 'You need to install/activate php5-iconv';
         }
         return $iconv_available ? "available" : "MISSING";
     }), 'simplexml' => array('callback' => function () use(&$errors) {
         if (!($simplexml = in_array('SimpleXML', get_loaded_extensions()))) {
             $errors[] = 'You need to install/activate the PHP SimpleXML module';
         }
         return $simplexml ? 'ok' : 'missing!';
     }), 'max_execution_time' => array('callback' => function () {
         return ini_get('max_execution_time');
     }), 'upload_max_filesize' => array('callback' => function () {
         return ini_get('upload_max_filesize');
     }), 'memory_limit' => array('callback' => function () {
         return ini_get('memory_limit');
     }), 'disable_classes' => array('callback' => function () {
         return ini_get('disable_classes');
     }), 'disable_functions' => array('callback' => function () {
         return ini_get('disable_functions');
     }), 'permalinks' => array('callback' => function () use(&$errors) {
         $permalinks = \get_option('permalink_structure');
         if (!$permalinks) {
             $errors[] = sprintf(__('You are using the default WordPress permalink structure. This may cause problems with some podcast clients. Go to %s and set it to anything but default (for example "Post name").', 'podlove'), admin_url('options-permalink.php'));
             return __("\"non-pretty\" Permalinks: Please change permalink structure", 'podlove');
         }
         return "ok ({$permalinks})";
     }), 'podlove_permalinks' => array('callback' => function () use(&$errors) {
         if (\Podlove\get_setting('website', 'use_post_permastruct') == 'on') {
             return 'ok';
         }
         if (stristr(\Podlove\get_setting('website', 'custom_episode_slug'), '%podcast%') === FALSE) {
             $website_options = get_option('podlove_website');
             $website_options['use_post_permastruct'] = 'on';
             update_option('podlove_website', $website_options);
         }
         return 'ok';
     }), 'podcast_settings' => array('callback' => function () use(&$errors) {
         $out = '';
         $podcast = Model\Podcast::get();
         if (!$podcast->title) {
             $error = __('Your podcast needs a title.', 'podlove');
             $errors[] = $error;
             $out .= $error;
         }
         if (!$podcast->media_file_base_uri) {
             $error = __('Your podcast needs an upload location for file storage.', 'podlove');
             $errors[] = $error;
             $out .= $error;
         }
         if (!$out) {
             $out = "ok";
         }
         return $out;
     }), 'web_player' => array('callback' => function () use(&$errors) {
         foreach (get_option('podlove_webplayer_formats', array()) as $_ => $media_types) {
             foreach ($media_types as $extension => $asset_id) {
                 if ($asset_id) {
                     return "ok";
                 }
             }
         }
         $error = __('You need to assign at least one asset to the web player.', 'podlove');
         $errors[] = $error;
         return $error;
     }), 'podlove_cache' => array('callback' => function () {
         return \Podlove\Cache\TemplateCache::is_enabled() ? 'on' : 'off';
     }), 'assets' => array('callback' => function () {
         $assets = array();
         foreach (\Podlove\Model\EpisodeAsset::all() as $asset) {
             $file_type = $asset->file_type();
             $assets[] = array('extension' => $file_type->extension, 'mime_type' => $file_type->mime_type);
         }
         return "\n\t" . implode("\n\t", array_map(function ($asset) {
             return str_pad($asset['extension'], 7) . $asset['mime_type'];
         }, $assets));
     }));
     $this->fields = apply_filters('podlove_system_report_fields', $this->fields);
     $this->run();
 }
Example #16
0
    public function __construct($handle)
    {
        Settings::$pagehook = add_submenu_page($handle, 'Settings', 'Settings', 'administrator', 'podlove_settings_settings_handle', array($this, 'page'));
        add_settings_section('podlove_settings_general', __('General Settings', 'podlove'), function () {
            /* section head html */
        }, Settings::$pagehook);
        add_settings_field('podlove_setting_merge_episodes', sprintf('<label for="merge_episodes">%s</label>', __('Display episodes on front page together with blog posts', 'podlove')), function () {
            ?>
				<input name="podlove[merge_episodes]" id="merge_episodes" type="checkbox" <?php 
            checked(\Podlove\get_setting('merge_episodes'), 'on');
            ?>
>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_hide_wp_feed_discovery', sprintf('<label for="hide_wp_feed_discovery">%s</label>', __('Hide default WordPress Feeds for blog and comments (no auto-discovery).', 'podlove')), function () {
            ?>
				<input name="podlove[hide_wp_feed_discovery]" id="hide_wp_feed_discovery" type="checkbox" <?php 
            checked(\Podlove\get_setting('hide_wp_feed_discovery'), 'on');
            ?>
>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_field('podlove_setting_custom_episode_slug', sprintf('<label for="custom_episode_slug">%s</label>', __('URL segment prefix for podcast episode posts. Leave empty to remove the prefix.', 'podlove')), function () {
            ?>
				<input name="podlove[custom_episode_slug]" id="custom_episode_slug" type="text" value="<?php 
            echo \Podlove\get_setting('custom_episode_slug');
            ?>
">
				<p>
					<span class="description"><?php 
            echo __('Must be a suitable URL part: lowercase characters, numbers and hyphens.', 'podlove');
            ?>
</span>
				</p>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_general');
        add_settings_section('podlove_settings_modules', __('Modules', 'podlove'), function () {
            /* section head html */
        }, Settings::$pagehook);
        $modules = \Podlove\Modules\Base::get_all_module_names();
        foreach ($modules as $module_name) {
            $class = \Podlove\Modules\Base::get_class_by_module_name($module_name);
            if (!class_exists($class)) {
                continue;
            }
            $module = $class::instance();
            $module_options = $module->get_registered_options();
            if ($module_options) {
                register_setting(Settings::$pagehook, $module->get_module_options_name());
            }
            add_settings_field('podlove_setting_module_' . $module_name, sprintf('<label for="' . $module_name . '">%s</label>', $module->get_module_name()), function () use($module, $module_name, $module_options) {
                ?>
					<label for="<?php 
                echo $module_name;
                ?>
">
						<input name="podlove_active_modules[<?php 
                echo $module_name;
                ?>
]" id="<?php 
                echo $module_name;
                ?>
" type="checkbox" <?php 
                checked(\Podlove\Modules\Base::is_active($module_name), true);
                ?>
>
						<?php 
                echo $module->get_module_description();
                ?>
					</label>
					<?php 
                if ($module_options) {
                    ?>
<h4><?php 
                    echo __('Settings');
                    ?>
</h4><?php 
                    // prepare settings object because form framework expects an object
                    $settings_object = new \stdClass();
                    foreach ($module_options as $key => $value) {
                        $settings_object->{$key} = $module->get_module_option($key);
                    }
                    \Podlove\Form\build_for($settings_object, array('context' => $module->get_module_options_name(), 'submit_button' => false), function ($form) use($module_options) {
                        $wrapper = new \Podlove\Form\Input\TableWrapper($form);
                        foreach ($module_options as $module_option_name => $args) {
                            call_user_func_array(array($wrapper, $args['input_type']), array($module_option_name, $args['args']));
                        }
                    });
                }
            }, Settings::$pagehook, 'podlove_settings_modules');
        }
        register_setting(Settings::$pagehook, 'podlove');
        register_setting(Settings::$pagehook, 'podlove_active_modules');
    }
 public function get_url_template()
 {
     return $this->with_blog_scope(function () {
         return \Podlove\get_setting('website', 'url_template');
     });
 }
/**
 * Find and run migration for given version number.
 *
 * @todo  move migrations into separate files
 * 
 * @param  int $version
 */
function run_migrations_for_version($version)
{
    global $wpdb;
    switch ($version) {
        case 10:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `summary` TEXT', \Podlove\Model\Episode::table_name());
            $wpdb->query($sql);
            break;
        case 11:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `downloadable` INT', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            break;
        case 12:
            $sql = sprintf('UPDATE `%s` SET `downloadable` = 1', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            break;
        case 13:
            $opus = array('name' => 'Opus Audio', 'type' => 'audio', 'mime_type' => 'audio/opus', 'extension' => 'opus');
            $f = new \Podlove\Model\FileType();
            foreach ($opus as $key => $value) {
                $f->{$key} = $value;
            }
            $f->save();
            break;
        case 14:
            $sql = sprintf('ALTER TABLE `%s` RENAME TO `%s`', $wpdb->prefix . 'podlove_medialocation', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            break;
        case 15:
            $sql = sprintf('ALTER TABLE `%s` CHANGE `media_location_id` `episode_asset_id` INT', \Podlove\Model\MediaFile::table_name());
            $wpdb->query($sql);
            break;
        case 16:
            $sql = sprintf('ALTER TABLE `%s` CHANGE `media_location_id` `episode_asset_id` INT', \Podlove\Model\Feed::table_name());
            $wpdb->query($sql);
            break;
        case 17:
            $sql = sprintf('ALTER TABLE `%s` RENAME TO `%s`', $wpdb->prefix . 'podlove_mediaformat', \Podlove\Model\FileType::table_name());
            $wpdb->query($sql);
            break;
        case 18:
            $sql = sprintf('ALTER TABLE `%s` CHANGE `media_format_id` `file_type_id` INT', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            break;
        case 19:
            \Podlove\Model\Template::build();
            break;
        case 20:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `suffix` VARCHAR(255)', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            $sql = sprintf('ALTER TABLE `%s` DROP COLUMN `url_template`', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            break;
        case 21:
            $podcast = Model\Podcast::get();
            $podcast->url_template = '%media_file_base_url%%episode_slug%%suffix%.%format_extension%';
            $podcast->save();
            break;
        case 22:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `redirect_http_status` INT AFTER `redirect_url`', Model\Feed::table_name());
            $wpdb->query($sql);
            break;
        case 23:
            $sql = sprintf('ALTER TABLE `%s` DROP COLUMN `show_description`', Model\Feed::table_name());
            $wpdb->query($sql);
            break;
        case 24:
            $podcast = Model\Podcast::get();
            update_option('podlove_asset_assignment', array('image' => $podcast->supports_cover_art, 'chapters' => $podcast->chapter_file));
            break;
        case 25:
            // rename meta podlove_guid to _podlove_guid
            $episodes = Model\Episode::all();
            foreach ($episodes as $episode) {
                $post = get_post($episode->post_id);
                // skip revisions
                if ($post->post_status == 'inherit') {
                    continue;
                }
                $guid = get_post_meta($episode->post_id, 'podlove_guid', true);
                if (!$guid) {
                    $guid = $post->guid;
                }
                delete_post_meta($episode->post_id, 'podlove_guid');
                update_post_meta($episode->post_id, '_podlove_guid', $guid);
            }
            break;
        case 26:
            $wpdb->query(sprintf('ALTER TABLE `%s` MODIFY COLUMN `subtitle` TEXT', Model\Episode::table_name()));
            break;
        case 27:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `record_date` DATETIME AFTER `chapters`', Model\Episode::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `publication_date` DATETIME AFTER `record_date`', Model\Episode::table_name()));
            break;
        case 28:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `position` FLOAT AFTER `downloadable`', Model\EpisodeAsset::table_name()));
            $wpdb->query(sprintf('UPDATE `%s` SET position = id', Model\EpisodeAsset::table_name()));
            break;
        case 29:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `embed_content_encoded` INT AFTER `limit_items`', Model\Feed::table_name()));
            break;
        case 30:
            $wpdb->query(sprintf('ALTER TABLE `%s` MODIFY `autoinsert` VARCHAR(255)', Model\Template::table_name()));
            break;
        case 32:
            flush_rewrite_rules();
            break;
        case 33:
            $apd = array('name' => 'Auphonic Production Description', 'type' => 'metadata', 'mime_type' => 'application/json', 'extension' => 'json');
            $f = new \Podlove\Model\FileType();
            foreach ($apd as $key => $value) {
                $f->{$key} = $value;
            }
            $f->save();
            break;
        case 34:
            $options = get_option('podlove', array());
            if (!array_key_exists('episode_archive', $options)) {
                $options['episode_archive'] = 'on';
            }
            if (!array_key_exists('episode_archive_slug', $options)) {
                $options['episode_archive_slug'] = '/podcast/';
            }
            if (!array_key_exists('use_post_permastruct', $options)) {
                $options['use_post_permastruct'] = 'off';
            }
            if (!array_key_exists('custom_episode_slug', $options)) {
                $options['custom_episode_slug'] = '/podcast/%podcast%/';
            } else {
                $options['custom_episode_slug'] = preg_replace('#/+#', '/', '/' . str_replace('#', '', $options['custom_episode_slug']));
            }
            update_option('podlove', $options);
            break;
        case 35:
            Model\Feed::build_indices();
            Model\FileType::build_indices();
            Model\EpisodeAsset::build_indices();
            Model\MediaFile::build_indices();
            Model\Episode::build_indices();
            Model\Template::build_indices();
            break;
        case 36:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `etag` VARCHAR(255)', Model\MediaFile::table_name()));
            break;
        case 37:
            \Podlove\Modules\Base::activate('asset_validation');
            break;
        case 38:
            \Podlove\Modules\Base::activate('logging');
            break;
        case 39:
            // migrate previous template autoinsert settings
            $assignments = Model\TemplateAssignment::get_instance();
            $results = $wpdb->get_results(sprintf('SELECT * FROM `%s`', Model\Template::table_name()));
            foreach ($results as $template) {
                if ($template->autoinsert == 'beginning') {
                    $assignments->top = $template->id;
                } elseif ($template->autoinsert == 'end') {
                    $assignments->bottom = $template->id;
                }
            }
            $assignments->save();
            // remove template autoinsert column
            $sql = sprintf('ALTER TABLE `%s` DROP COLUMN `autoinsert`', \Podlove\Model\Template::table_name());
            $wpdb->query($sql);
            break;
        case 40:
            $wpdb->query(sprintf('UPDATE `%s` SET position = id WHERE position IS NULL', Model\EpisodeAsset::table_name()));
            break;
        case 41:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `position` FLOAT AFTER `slug`', Model\Feed::table_name()));
            $wpdb->query(sprintf('UPDATE `%s` SET position = id', Model\Feed::table_name()));
            break;
        case 42:
            $wpdb->query('DELETE FROM `' . $wpdb->options . '` WHERE option_name LIKE "%podlove_chapters_string_%"');
            break;
        case 43:
            $podlove_options = get_option('podlove', array());
            $podlove_website = array('merge_episodes' => isset($podlove_options['merge_episodes']) ? $podlove_options['merge_episodes'] : false, 'hide_wp_feed_discovery' => isset($podlove_options['hide_wp_feed_discovery']) ? $podlove_options['hide_wp_feed_discovery'] : false, 'use_post_permastruct' => isset($podlove_options['use_post_permastruct']) ? $podlove_options['use_post_permastruct'] : false, 'custom_episode_slug' => isset($podlove_options['custom_episode_slug']) ? $podlove_options['custom_episode_slug'] : '/episode/%podcast%', 'episode_archive' => isset($podlove_options['episode_archive']) ? $podlove_options['episode_archive'] : false, 'episode_archive_slug' => isset($podlove_options['episode_archive_slug']) ? $podlove_options['episode_archive_slug'] : '/podcast/', 'url_template' => isset($podlove_options['url_template']) ? $podlove_options['url_template'] : '%media_file_base_url%%episode_slug%%suffix%.%format_extension%');
            $podlove_metadata = array('enable_episode_record_date' => isset($podlove_options['enable_episode_record_date']) ? $podlove_options['enable_episode_record_date'] : false, 'enable_episode_publication_date' => isset($podlove_options['enable_episode_publication_date']) ? $podlove_options['enable_episode_publication_date'] : false);
            $podlove_redirects = array('podlove_setting_redirect' => isset($podlove_options['podlove_setting_redirect']) ? $podlove_options['podlove_setting_redirect'] : array());
            add_option('podlove_website', $podlove_website);
            add_option('podlove_metadata', $podlove_metadata);
            add_option('podlove_redirects', $podlove_redirects);
            break;
        case 44:
            $wpdb->query('DELETE FROM `' . $wpdb->postmeta . '` WHERE meta_key = "last_validated_at"');
            break;
        case 45:
            delete_transient('podlove_auphonic_user');
            delete_transient('podlove_auphonic_presets');
            break;
        case 46:
            if (\Podlove\Modules\Base::is_active('contributors')) {
                // manually trigger activation if the old module was active
                $module = \Podlove\Modules\Contributors\Contributors::instance();
                $module->was_activated('contributors');
                // then, migrate existing contributors
                // register old taxonomy so it can be queried
                $args = array('hierarchical' => false, 'labels' => array(), 'show_ui' => true, 'show_tagcloud' => true, 'query_var' => true, 'rewrite' => array('slug' => 'contributor'));
                register_taxonomy('podlove-contributors', 'podcast', $args);
                $contributor_settings = get_option('podlove_contributors', array());
                $contributors = get_terms('podlove-contributors', array('hide_empty' => 0));
                if ($contributors && !is_wp_error($contributors) && \Podlove\Modules\Contributors\Model\Contributor::count() == 0) {
                    foreach ($contributors as $contributor) {
                        // create new contributor
                        $new = new \Podlove\Modules\Contributors\Model\Contributor();
                        $new->publicname = $contributor->name;
                        $new->realname = $contributor->name;
                        $new->slug = $contributor->slug;
                        $new->showpublic = true;
                        if (isset($contributor_settings[$contributor->term_id]['contributor_email'])) {
                            $email = $contributor_settings[$contributor->term_id]['contributor_email'];
                            if ($email) {
                                $new->privateemail = $email;
                                $new->avatar = $email;
                            }
                        }
                        $new->save();
                        // create contributions
                        $query = new \WP_Query(array('posts_per_page' => -1, 'post_type' => 'podcast', 'tax_query' => array(array('taxonomy' => 'podlove-contributors', 'field' => 'slug', 'terms' => $contributor->slug))));
                        while ($query->have_posts()) {
                            $post = $query->next_post();
                            $contribution = new \Podlove\Modules\Contributors\Model\EpisodeContribution();
                            $contribution->contributor_id = $new->id;
                            $contribution->episode_id = Model\Episode::find_one_by_post_id($post->ID)->id;
                            $contribution->save();
                        }
                    }
                }
            }
            break;
        case 47:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `protected` TINYINT(1) NULL', \Podlove\Model\Feed::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `protection_type` TINYINT(1)', \Podlove\Model\Feed::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `protection_user` VARCHAR(60)', \Podlove\Model\Feed::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `protection_password` VARCHAR(64)', \Podlove\Model\Feed::table_name()));
            break;
        case 48:
            $podcast = Model\Podcast::get();
            $podcast->limit_items = '-1';
            $podcast->save();
            break;
        case 49:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `explicit` TINYINT', Model\Episode::table_name()));
            break;
        case 50:
            $podcast = Model\Podcast::get();
            $podcast->license_type = 'other';
            $podcast->save();
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `license_type` VARCHAR(255) AFTER `publication_date`', Model\Episode::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `license_name` TEXT AFTER `license_type`', Model\Episode::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `license_url` TEXT AFTER `license_name`', Model\Episode::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `license_cc_allow_modifications` TEXT AFTER `license_url`', Model\Episode::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `license_cc_allow_commercial_use` TEXT AFTER `license_cc_allow_modifications`', Model\Episode::table_name()));
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `license_cc_license_jurisdiction` TEXT AFTER `license_cc_allow_commercial_use`', Model\Episode::table_name()));
            break;
        case 51:
            if (\Podlove\Modules\Base::is_active('contributors')) {
                \Podlove\Modules\Contributors\Model\ContributorGroup::build();
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `group_id` VARCHAR(255) AFTER `role_id`', \Podlove\Modules\Contributors\Model\EpisodeContribution::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `group_id` VARCHAR(255) AFTER `role_id`', \Podlove\Modules\Contributors\Model\ShowContribution::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `paypal` VARCHAR(255) AFTER `flattr`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `bitcoin` VARCHAR(255) AFTER `paypal`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `litecoin` VARCHAR(255) AFTER `bitcoin`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` DROP COLUMN `permanentcontributor`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` DROP COLUMN `role`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
            }
            break;
        case 52:
            if (\Podlove\Modules\Base::is_active('contributors')) {
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `jobtitle` VARCHAR(255) AFTER `department`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
            }
            break;
        case 53:
            // set all Episode as published (fix for ADN Module)
            $episodes = Model\Episode::all();
            foreach ($episodes as $episode) {
                $post = get_post($episode->post_id);
                if ($post->post_status == 'publish') {
                    update_post_meta($episode->post_id, '_podlove_episode_was_published', true);
                }
            }
            break;
        case 54:
            if (\Podlove\Modules\Base::is_active('contributors')) {
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `googleplus` TEXT AFTER `ADN`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` CHANGE COLUMN `showpublic` `visibility` TINYINT(1)', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
            }
            break;
        case 55:
            if (\Podlove\Modules\Base::is_active('contributors')) {
                \Podlove\Modules\Contributors\Model\DefaultContribution::build();
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `comment` TEXT AFTER `position`', \Podlove\Modules\Contributors\Model\EpisodeContribution::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `comment` TEXT AFTER `position`', \Podlove\Modules\Contributors\Model\ShowContribution::table_name()));
            }
            break;
        case 56:
            // migrate Podcast Contributors to Default Contributors
            if (\Podlove\Modules\Base::is_active('contributors')) {
                $podcast_contributors = \Podlove\Modules\Contributors\Model\ShowContribution::all();
                foreach ($podcast_contributors as $podcast_contributor_key => $podcast_contributor) {
                    $new = new \Podlove\Modules\Contributors\Model\DefaultContribution();
                    $new->contributor_id = $podcast_contributor->contributor_id;
                    $new->group_id = $podcast_contributor->group_id;
                    $new->role_id = $podcast_contributor->role_id;
                    $new->position = $podcast_contributor->positon;
                    $new->save();
                }
            }
            break;
        case 57:
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `append_name_to_podcast_title` TINYINT(1) NULL AFTER `embed_content_encoded`', \Podlove\Model\Feed::table_name()));
            break;
        case 58:
            // if contributors module is active, activate social module
            if (\Podlove\Modules\Base::is_active('contributors')) {
                \Podlove\Modules\Base::activate('social');
            }
            break;
        case 59:
            if (\Podlove\Modules\Base::is_active('bitlove')) {
                $wpdb->query(sprintf("ALTER TABLE `%s` ADD COLUMN `bitlove` TINYINT(1) DEFAULT '0'", \Podlove\Model\Feed::table_name()));
            }
            break;
        case 60:
            \Podlove\Modules\Base::activate('oembed');
            \Podlove\Modules\Base::activate('feed_validation');
            break;
        case 61:
            $wpdb->query(sprintf('ALTER TABLE `%s` DROP COLUMN `publication_date`', Model\Episode::table_name()));
            break;
        case 62:
            // rename column
            $wpdb->query(sprintf('ALTER TABLE `%s` CHANGE COLUMN `record_date` `recording_date` DATETIME', Model\Episode::table_name()));
            // update settings
            $meta = get_option('podlove_metadata');
            if (isset($meta['enable_episode_publication_date'])) {
                unset($meta['enable_episode_publication_date']);
            }
            if (isset($meta['enable_episode_record_date'])) {
                $meta['enable_episode_recording_date'] = $meta['enable_episode_record_date'];
                unset($meta['enable_episode_record_date']);
            }
            update_option('podlove_metadata', $meta);
            break;
        case 63:
            if (\Podlove\Modules\Base::is_active('social')) {
                $tumblr_service = \Podlove\Modules\Social\Model\Service::find_one_by_property('title', 'Tumblr');
                $tumblr_service->url_scheme = 'http://%account-placeholder%.tumblr.com/';
                $tumblr_service->save();
            }
            break;
        case 64:
            if (\Podlove\Modules\Base::is_active('social')) {
                $services = array(array('title' => '500px', 'type' => 'social', 'description' => '500px Account', 'logo' => '500px-128.png', 'url_scheme' => 'https://500px.com/%account-placeholder%'), array('title' => 'Last.fm', 'type' => 'social', 'description' => 'Last.fm Account', 'logo' => 'lastfm-128.png', 'url_scheme' => 'https://www.lastfm.de/user/%account-placeholder%'), array('title' => 'OpenStreetMap', 'type' => 'social', 'description' => 'OpenStreetMap Account', 'logo' => 'openstreetmap-128.png', 'url_scheme' => 'https://www.openstreetmap.org/user/%account-placeholder%'), array('title' => 'Soup', 'type' => 'social', 'description' => 'Soup Account', 'logo' => 'soup-128.png', 'url_scheme' => 'http://%account-placeholder%.soup.io'));
                foreach ($services as $service_key => $service) {
                    $c = new \Podlove\Modules\Social\Model\Service();
                    $c->title = $service['title'];
                    $c->type = $service['type'];
                    $c->description = $service['description'];
                    $c->logo = $service['logo'];
                    $c->url_scheme = $service['url_scheme'];
                    $c->save();
                }
            }
            break;
        case 65:
            if (\Podlove\Modules\Base::is_active('social')) {
                $flattr_service = \Podlove\Modules\Social\Model\Service::find_one_by_where("`title` = 'Flattr' AND `type` = 'donation'");
                if ($flattr_service) {
                    $contributor_flattr_donations_accounts = \Podlove\Modules\Social\Model\ContributorService::find_all_by_property('service_id', $flattr_service->id);
                    foreach ($contributor_flattr_donations_accounts as $contributor_flattr_donations_account) {
                        $contributor = \Podlove\Modules\Contributors\Model\Contributor::find_by_id($contributor_flattr_donations_account->contributor_id);
                        if ($contributor && is_null($contributor->flattr)) {
                            $contributor->flattr = $contributor_flattr_donations_account->value;
                            $contributor->save();
                        }
                        $contributor_flattr_donations_account->delete();
                    }
                    $flattr_service->delete();
                }
            }
            break;
        case 66:
            // Temporary add license_type and CC license fields to episode model
            \Podlove\Model\Episode::property('license_type', 'VARCHAR(255)');
            \Podlove\Model\Episode::property('license_cc_allow_modifications', 'VARCHAR(255)');
            \Podlove\Model\Episode::property('license_cc_allow_commercial_use', 'VARCHAR(255)');
            \Podlove\Model\Episode::property('license_cc_license_jurisdiction', 'VARCHAR(255)');
            $podcast = \Podlove\Model\Podcast::get();
            $episodes = \Podlove\Model\Episode::all();
            // Migration for Podcast
            if ($podcast->license_type == 'cc' && $podcast->license_cc_allow_commercial_use !== '' && $podcast->license_cc_allow_modifications !== '' && $podcast->license_cc_license_jurisdiction !== '') {
                $license = array('version' => '3.0', 'commercial_use' => $podcast->license_cc_allow_commercial_use, 'modification' => $podcast->license_cc_allow_modifications, 'jurisdiction' => $podcast->license_cc_license_jurisdiction);
                $podcast->license_url = \Podlove\Model\License::get_url_from_license($license);
                $podcast->license_name = \Podlove\Model\License::get_name_from_license($license);
                $podcast->save();
            }
            // Migration for Episodes
            foreach ($episodes as $episode) {
                if ($episode->license_type == 'other' || $episode->license_cc_allow_commercial_use == '' || $episode->license_cc_allow_modifications == '' || $episode->license_cc_license_jurisdiction == '') {
                    continue;
                }
                $license = array('version' => '3.0', 'commercial_use' => $episode->license_cc_allow_commercial_use, 'modification' => $episode->license_cc_allow_modifications, 'jurisdiction' => $episode->license_cc_license_jurisdiction);
                $episode->license_url = \Podlove\Model\License::get_url_from_license($license);
                $episode->license_name = \Podlove\Model\License::get_name_from_license($license);
                $episode->save();
            }
            break;
        case 67:
            if (\Podlove\Modules\Base::is_active('social')) {
                $instagram_service = \Podlove\Modules\Social\Model\Service::find_one_by_where("`title` = 'Instagram' AND `type` = 'social'");
                if ($instagram_service) {
                    $instagram_service->url_scheme = 'https://instagram.com/%account-placeholder%';
                    $instagram_service->save();
                }
            }
            break;
        case 68:
            // Do that ADN module fix again, as we forgot to mark all episodes as published if the ADN module is activated
            $episodes = Model\Episode::all();
            foreach ($episodes as $episode) {
                $post = get_post($episode->post_id);
                if ($post->post_status == 'publish' && !get_post_meta($episode->post_id, '_podlove_episode_was_published', true)) {
                    update_post_meta($episode->post_id, '_podlove_episode_was_published', true);
                }
            }
            break;
        case 69:
            if (\Podlove\Modules\Base::is_active('app_dot_net')) {
                $adn = \Podlove\Modules\AppDotNet\App_Dot_Net::instance();
                if ($adn->get_module_option('adn_auth_key')) {
                    $adn->update_module_option('adn_automatic_announcement', 'on');
                }
            }
            break;
        case 70:
            \Podlove\Model\DownloadIntent::build();
            \Podlove\Model\UserAgent::build();
            break;
        case 71:
            // update for everyone, so even those with inactive service tables get updated
            $wpdb->query(sprintf('ALTER TABLE `%s` CHANGE COLUMN `type` `category` VARCHAR(255)', \Podlove\Modules\Social\Model\Service::table_name()));
            $wpdb->query(sprintf("ALTER TABLE `%s` ADD COLUMN `type` VARCHAR(255) AFTER `category`", \Podlove\Modules\Social\Model\Service::table_name()));
            $services = \Podlove\Modules\Social\Model\Service::all();
            foreach ($services as $service) {
                $service->type = strtolower($service->title);
                $service->save();
            }
            break;
        case 72:
            if (\Podlove\Modules\Base::is_active('social')) {
                $services = array(array('title' => 'Vimeo', 'type' => 'vimeo', 'category' => 'social', 'description' => 'Vimeo Account', 'logo' => 'vimeo-128.png', 'url_scheme' => 'http://vimeo.com/%account-placeholder%'), array('title' => 'about.me', 'type' => 'about.me', 'category' => 'social', 'description' => 'about.me Account', 'logo' => 'aboutme-128.png', 'url_scheme' => 'http://about.me/%account-placeholder%'), array('title' => 'Gittip', 'type' => 'gittip', 'category' => 'donation', 'description' => 'Gittip Account', 'logo' => 'gittip-128.png', 'url_scheme' => 'https://www.gittip.com/%account-placeholder%'));
                foreach ($services as $service_key => $service) {
                    $c = new \Podlove\Modules\Social\Model\Service();
                    $c->title = $service['title'];
                    $c->type = $service['type'];
                    $c->category = $service['category'];
                    $c->description = $service['description'];
                    $c->logo = $service['logo'];
                    $c->url_scheme = $service['url_scheme'];
                    $c->save();
                }
            }
            break;
        case 73:
            if (\Podlove\Modules\Base::is_active('social')) {
                $jabber_service = \Podlove\Modules\Social\Model\Service::find_one_by_where("`type` = 'jabber' AND `category` = 'social'");
                if ($jabber_service) {
                    $jabber_service->url_scheme = 'jabber:%account-placeholder%';
                    $jabber_service->save();
                }
            }
            break;
        case 74:
            Model\GeoArea::build();
            Model\GeoAreaName::build();
            \Podlove\Geo_Ip::register_updater_cron();
            break;
        case 75:
            $tracking = get_option('podlove_tracking');
            $tracking['mode'] = 0;
            update_option('podlove_tracking', $tracking);
            break;
        case 76:
            set_transient('podlove_needs_to_flush_rewrite_rules', true);
            break;
        case 77:
            // delete empty user agents
            $userAgentTable = Model\UserAgent::table_name();
            $downloadIntentTable = Model\DownloadIntent::table_name();
            $sql = "SELECT\n\t\t\t\tdi.id\n\t\t\tFROM\n\t\t\t\t{$downloadIntentTable} di\n\t\t\t\tJOIN {$userAgentTable} ua ON ua.id = di.user_agent_id\n\t\t\tWHERE\n\t\t\t\tua.user_agent IS NULL";
            $ids = $wpdb->get_col($sql);
            if (is_array($ids) && count($ids)) {
                $sql = "UPDATE {$downloadIntentTable} SET user_agent_id = NULL WHERE id IN (" . implode(",", $ids) . ")";
                $wpdb->query($sql);
                $sql = "DELETE FROM {$userAgentTable} WHERE user_agent IS NULL";
                $wpdb->query($sql);
            }
            break;
        case 78:
            if (\Podlove\Modules\Base::is_active('social')) {
                $c = new \Podlove\Modules\Social\Model\Service();
                $c->title = 'Auphonic Credits';
                $c->category = 'donation';
                $c->type = 'auphonic credits';
                $c->description = 'Auphonic Account';
                $c->logo = 'auphonic-128.png';
                $c->url_scheme = 'https://auphonic.com/donate_credits?user=%account-placeholder%';
                $c->save();
            }
            break;
        case 79:
            set_transient('podlove_needs_to_flush_rewrite_rules', true);
            $cache = \Podlove\Cache\TemplateCache::get_instance();
            $cache->setup_purge();
            break;
        case 80:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `httprange` VARCHAR(255)', \Podlove\Model\DownloadIntent::table_name());
            $wpdb->query($sql);
            break;
        case 81:
            // remove all caches with old namespace
            $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE \"_transient_podlove_cache%\"");
            break;
        case 82:
            // set all redirect entries to active
            $redirect_settings = \Podlove\get_setting('redirects', 'podlove_setting_redirect');
            foreach ($redirect_settings as $index => $data) {
                $redirect_settings[$index]['active'] = 'active';
            }
            update_option('podlove_redirects', array('podlove_setting_redirect' => $redirect_settings));
            break;
        case 83:
            \Podlove\Model\DownloadIntentClean::build();
            $alterations = array('ALTER TABLE `%s` ADD COLUMN `bot` TINYINT', 'ALTER TABLE `%s` ADD COLUMN `client_name` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `client_version` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `client_type` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `os_name` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `os_version` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `device_brand` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `device_model` VARCHAR(255)');
            foreach ($alterations as $sql) {
                $wpdb->query(sprintf($sql, Model\UserAgent::table_name()));
            }
            Model\UserAgent::reparse_all();
            break;
        case 84:
            delete_option('podlove_tpl_cache_keys');
            break;
        case 85:
            add_option('podlove_tracking_delete_head_requests', 1);
            break;
        case 86:
            if (\Podlove\Modules\Base::is_active('social')) {
                $c = new \Podlove\Modules\Social\Model\Service();
                $c->title = 'Foursquare';
                $c->category = 'social';
                $c->type = 'foursquare';
                $c->description = 'Foursquare Account';
                $c->logo = 'foursquare-128.png';
                $c->url_scheme = 'https://foursquare.com/%account-placeholder%';
                $c->save();
                $services = array(array('title' => 'ResearchGate', 'name' => 'researchgate', 'category' => 'social', 'description' => 'ResearchGate URL', 'logo' => 'researchgate-128.png', 'url_scheme' => '%account-placeholder%'), array('title' => 'ORCiD', 'name' => 'orcid', 'category' => 'social', 'description' => 'ORCiD', 'logo' => 'orcid-128.png', 'url_scheme' => 'https://orcid.org/%account-placeholder%'), array('title' => 'Scopus', 'name' => 'scous', 'category' => 'social', 'description' => 'Scopus Author ID', 'logo' => 'scopus-128.png', 'url_scheme' => 'https://www.scopus.com/authid/detail.url?authorId=%account-placeholder%'));
                foreach ($services as $service_key => $service) {
                    $c = new \Podlove\Modules\Social\Model\Service();
                    $c->title = $service['title'];
                    $c->category = $service['category'];
                    $c->type = $service['name'];
                    $c->description = $service['description'];
                    $c->logo = $service['logo'];
                    $c->url_scheme = $service['url_scheme'];
                    $c->save();
                }
            }
            break;
        case 87:
            if (\Podlove\Modules\Base::is_active('app_dot_net')) {
                $adn = \Podlove\Modules\AppDotNet\App_Dot_Net::instance();
                if ($adn->get_module_option('adn_auth_key')) {
                    $adn->update_module_option('adn_poster_image_fallback', 'on');
                }
            }
            break;
        case 88:
            $service = new \Podlove\Modules\Social\Model\Service();
            $service->title = 'Email';
            $service->category = 'social';
            $service->type = 'email';
            $service->description = 'Email';
            $service->logo = 'email-128.png';
            $service->url_scheme = 'mailto:%account-placeholder%';
            $service->save();
            break;
        case 89:
            $email_service = \Podlove\Modules\Social\Model\Service::find_one_by_type('email');
            foreach (\Podlove\Modules\Contributors\Model\Contributor::all() as $contributor) {
                if (!$contributor->publicemail) {
                    continue;
                }
                $contributor_service = new \Podlove\Modules\Social\Model\ContributorService();
                $contributor_service->contributor_id = $contributor->id;
                $contributor_service->service_id = $email_service->id;
                $contributor_service->value = $contributor->publicemail;
                $contributor_service->save();
            }
            break;
        case 90:
            \Podlove\Modules\Base::activate('subscribe_button');
            break;
        case 91:
            $c = new \Podlove\Modules\Social\Model\Service();
            $c->title = 'Miiverse';
            $c->category = 'social';
            $c->type = 'miiverse';
            $c->description = 'Miiverse Account';
            $c->logo = 'miiverse-128.png';
            $c->url_scheme = 'https://miiverse.nintendo.net/users/%account-placeholder%';
            $c->save();
            break;
        case 92:
            $c = new \Podlove\Modules\Social\Model\Service();
            $c->title = 'Prezi';
            $c->category = 'social';
            $c->type = 'prezi';
            $c->description = 'Prezis';
            $c->logo = 'prezi-128.png';
            $c->url_scheme = 'http://prezi.com/user/%account-placeholder%';
            $c->save();
            break;
        case 93:
            // podlove_init_user_agent_refresh();
            // do nothing instead, because see 94 below
            break;
        case 94:
            // this is a duplicate of migration 83 but it looks like that didn't work.
            Model\DownloadIntentClean::build();
            $alterations = array('ALTER TABLE `%s` ADD COLUMN `bot` TINYINT', 'ALTER TABLE `%s` ADD COLUMN `client_name` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `client_version` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `client_type` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `os_name` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `os_version` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `device_brand` VARCHAR(255)', 'ALTER TABLE `%s` ADD COLUMN `device_model` VARCHAR(255)');
            foreach ($alterations as $sql) {
                $wpdb->query(sprintf($sql, Model\UserAgent::table_name()));
            }
            podlove_init_user_agent_refresh();
            // manually trigger intent cron after user agents are parsed
            // parameter to make sure WP does not skip it due to 10 minute rule
            wp_schedule_single_event(time() + 120, 'podlove_cleanup_download_intents', ['really' => true]);
            // manually trigger average cron after intents are calculated
            wp_schedule_single_event(time() + 240, 'recalculate_episode_download_average', ['really' => true]);
            break;
        case 95:
            // add missing flattr column
            $wpdb->query(sprintf('ALTER TABLE `%s` ADD COLUMN `flattr` VARCHAR(255) AFTER `avatar`', \Podlove\Modules\Contributors\Model\Contributor::table_name()));
            break;
        case 96:
            \Podlove\DeleteHeadRequests::init();
            break;
        case 97:
            // recalculate all downloads average data
            $wpdb->query(sprintf('DELETE FROM `%s` WHERE `meta_key` LIKE "_podlove_eda%%"', $wpdb->postmeta));
            break;
        case 98:
            delete_transient('podlove_dashboard_stats_contributors');
            break;
        case 99:
            // Activate network module for migrating users.
            // Core modules are automatically activated for _new_ setups and
            // whenever modules change. Since this can't be guaranteed for
            // existing setups, it must be triggered manually.
            \Podlove\Modules\Networks\Networks::instance()->was_activated();
            break;
        case 101:
            // add patreon
            if (\Podlove\Modules\Social\Model\Service::table_exists()) {
                \Podlove\Modules\Social\RepairSocial::fix_missing_services();
            }
            break;
        case 102:
            // update logos
            if (\Podlove\Modules\Social\Model\Service::table_exists()) {
                \Podlove\Modules\Social\Social::update_existing_services();
            }
            break;
        case 103:
            $assignment = get_option('podlove_template_assignment', []);
            if ($assignment['top'] && is_numeric($assignment['top'])) {
                $assignment['top'] = Model\Template::find_by_id($assignment['top'])->title;
            }
            if ($assignment['bottom'] && is_numeric($assignment['bottom'])) {
                $assignment['bottom'] = Model\Template::find_by_id($assignment['bottom'])->title;
            }
            update_option('podlove_template_assignment', $assignment);
            break;
        case 104:
            \Podlove\unschedule_events(\Podlove\Cache\TemplateCache::CRON_PURGE_HOOK);
            break;
        case 105:
            // activate flattr plugin
            \Podlove\Modules\Base::activate('flattr');
            // migrate flattr data
            $podcast = Model\Podcast::get();
            $settings = get_option('podlove_flattr', []);
            $settings['account'] = $podcast->flattr;
            $settings['contributor_shortcode_default'] = 'yes';
            update_option('podlove_flattr', $settings);
            break;
        case 106:
            podlove_init_user_agent_refresh();
            break;
        case 107:
            // skipped
            break;
        case 108:
            podlove_init_user_agent_refresh();
            break;
        case 109:
            \podlove_init_capabilities();
            break;
        case 110:
            if (\Podlove\Modules\Social\Model\Service::table_exists()) {
                \Podlove\Modules\Social\Social::update_existing_services();
                \Podlove\Modules\Social\Social::build_missing_services();
            }
            break;
        case 111:
            if (\Podlove\Modules\Social\Model\Service::table_exists()) {
                \Podlove\Modules\Social\Social::update_existing_services();
                \Podlove\Modules\Social\Social::build_missing_services();
            }
            break;
    }
}
 /**
  * @todo  use \Podlove\Http\Curl
  * 
  * @return array	
  */
 public static function curl_get_header_for_url($url, $etag = NULL)
 {
     if (!function_exists('curl_exec')) {
         return [];
     }
     $curl = curl_init();
     if (\Podlove\Http\Curl::curl_can_follow_redirects()) {
         curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
         // follow redirects
         curl_setopt($curl, CURLOPT_MAXREDIRS, 5);
         // maximum number of redirects
     } else {
         $url = \Podlove\Http\Curl::resolve_redirects($url, 5);
     }
     curl_setopt($curl, CURLOPT_URL, $url);
     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     // make curl_exec() return the result
     curl_setopt($curl, CURLOPT_HEADER, true);
     // header only
     curl_setopt($curl, CURLOPT_NOBODY, true);
     // return no body; HTTP request method: HEAD
     curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, \Podlove\get_setting('website', 'ssl_verify_peer') == 'on');
     // Don't check SSL certificate in order to be able to use self signed certificates
     curl_setopt($curl, CURLOPT_FAILONERROR, true);
     curl_setopt($curl, CURLOPT_TIMEOUT, 3);
     // HEAD requests shouldn't take > 2 seconds
     if ($etag) {
         curl_setopt($curl, CURLOPT_HTTPHEADER, array('If-None-Match: "' . $etag . '"'));
     }
     curl_setopt($curl, CURLOPT_USERAGENT, \Podlove\Http\Curl::user_agent());
     $response = curl_exec($curl);
     $response_header = curl_getinfo($curl);
     $error = curl_error($curl);
     curl_close($curl);
     return array('header' => $response_header, 'response' => $response, 'error' => $error);
 }
<?php

add_filter('podlove_episode_form_data', function ($form_data) {
    if (!\Podlove\get_setting('metadata', 'enable_episode_explicit')) {
        return $form_data;
    }
    $form_data[] = array('type' => 'select', 'key' => 'explicit', 'options' => array('label' => __('Explicit Content?', 'podlove'), 'type' => 'checkbox', 'html' => array('style' => 'width: 200px;'), 'default' => '-1', 'options' => array(0 => 'no', 1 => 'yes', 2 => 'clean')), 'position' => 770);
    return $form_data;
});
add_filter('podlove_episode_data_filter', function ($filter) {
    return array_merge($filter, ['explicit' => FILTER_SANITIZE_STRING]);
});
    public function init()
    {
        add_settings_section('podlove_settings_redirects', __('', 'podlove'), function () {
            echo '<h3>' . __('Redirects', 'podlove') . '</h3>';
        }, Settings::$pagehook);
        add_settings_field('podlove_setting_redirect', '', function () {
            $redirect_settings = \Podlove\get_setting('redirects', 'podlove_setting_redirect');
            if (!is_array($redirect_settings)) {
                $redirect_settings = array();
            } else {
                // avoids array-index-based glitches
                $redirect_settings = array_values($redirect_settings);
            }
            ?>

				<table id="podlove-redirects" class="podlove_alternating" border="0" cellspacing="0">
					<thead>
						<tr>
							<th style="width: 55px"><?php 
            echo __('Active', 'podlove');
            ?>
</th>
							<th><?php 
            echo __('From URL', 'podlove');
            ?>
</th>
							<th><?php 
            echo __('To URL', 'podlove');
            ?>
</th>
							<th><?php 
            echo __('Redirect Method', 'podlove');
            ?>
</th>
							<th class="count">
								<?php 
            echo __('Redirects', 'podlove');
            ?>
							</th>
							<th class="delete"></th>
							<th class="move"></th>
						</tr>
					</thead>
					<tbody id="podlove-redirects-table-body" style="min-height: 50px;">
						<tr style="display: none;">
							<td><em><?php 
            echo __('No redirects were added yet.', 'podlove');
            ?>
</em></td>
						</tr>
					</tbody>
				</table>

				<script type="text/template" id="redirect-row-template">
				<tr data-index="{{index}}">
					<td>
						<input type="checkbox" name="podlove_redirects[podlove_setting_redirect][{{index}}][active]" value="active">
					</td>
					<td>
						<input type="text" class="podlove-check-input" id="podlove_redirects_podlove_setting_redirect_{{index}}_from" name="podlove_redirects[podlove_setting_redirect][{{index}}][from]" value="{{redirect-from}}"><span class="podlove-input-status" data-podlove-input-status-for="podlove_redirects_podlove_setting_redirect_{{index}}_from"></span>
					</td>
					<td>
						<input type="text" class="podlove-check-input" id="podlove_redirects_podlove_setting_redirect_{{index}}_to" name="podlove_redirects[podlove_setting_redirect][{{index}}][to]" value="{{redirect-to}}"><span class="podlove-input-status" data-podlove-input-status-for="podlove_redirects_podlove_setting_redirect_{{index}}_to"></span>
					</td>
					<td>
						<select name="podlove_redirects[podlove_setting_redirect][{{index}}][code]">
							<option value="307"><?php 
            echo __('Temporary Redirect (HTTP Status 307)', 'podlove');
            ?>
</option>
							<option value="301"><?php 
            echo __('Permanent Redirect (HTTP Status 301)', 'podlove');
            ?>
</option>
						</select>
					</td>
					<td class="count">
						<span data-podlove-input-status-for="podlove_redirects_podlove_setting_redirect_{{index}}_count">{{count}}</span>
						<input type="hidden" name="podlove_redirects[podlove_setting_redirect][{{index}}][count]" value="{{count}}">
					</td>
					<td class="delete">
						<a href="#" class="button delete"><?php 
            echo __('delete', 'podlove');
            ?>
</a>
					</td>
					<td class="move column-move"><i class="reorder-handle podlove-icon-reorder"></i></td>
				</tr>
				</script>

				<script type="text/javascript">
				(function($) {

					var existing_redirects = <?php 
            echo json_encode(array_values($redirect_settings));
            ?>
;
					var template_id = "#redirect-row-template";
					var container_id = "#podlove-redirects";

					function add_row(index, data) {
						var row = $(template_id).html();

						row = row.replace(/\{\{index\}\}/g, index);
						row = row.replace(/\{\{redirect-from\}\}/g, data.from ? data.from : "");
						row = row.replace(/\{\{redirect-to\}\}/g, data.to ? data.to : "");
						row = row.replace(/\{\{count\}\}/g, data.count ? data.count : "0");

						$row = $(row);
						$row.find("select option[value=\"" + data.code + "\"]").prop("selected", true);

						if (data.active) {
							$row.find("input[type=\"checkbox\"]").prop("checked", true);
						}

						$("tbody", container_id).append($row);

						$row.find("input[type=text]:first").focus();
						clean_up_input();
					}

					$(document).ready(function() {

						$.each(existing_redirects, function(index, entry) {
							add_row(index, entry);
						});

						$("#podlove_add_new_rule").on("click", function () {
							add_row($("tbody tr", container_id).length, {active: "active"});
						});

						$(container_id).on("click", "td.delete a", function(e) {
							e.preventDefault();
							$(this).closest("tr").remove();
							return false;
						});

						$("tbody", container_id).sortable({
							handle: ".reorder-handle",
							helper: function(e, tr) {
							    var $originals = tr.children();
							    var $helper = tr.clone();
							    $helper.children().each(function(index) {
							    	// Set helper cell sizes to match the original sizes
							    	$(this).width($originals.eq(index).width());
							    });
							    return $helper.css({
							    	background: '#EAEAEA'
							    });
							},
							update: function() { }
						});

					});
				}(jQuery));
				</script>

				<p>
					<a href="#" id="podlove_add_new_rule" class="button"><?php 
            echo __('Add new rule');
            ?>
</a>
				</p>
				<p class="description">
					<?php 
            echo __('Create custom permanent redirects. URLs can be absolute like <code>http://example.com/feed</code> or relative to the website like <code>/feed</code>.', 'podlove');
            ?>
				</p>

				<style type="text/css">
				#podlove-redirects th.count,
				#podlove-redirects td.count,
				#podlove-redirects th.delete,
				#podlove-redirects td.delete,
				#podlove-redirects th.move,
				#podlove-redirects td.move {
					width: 50px;
					text-align: right;
				}

				#podlove-redirects td input[type="text"] {
					width: 100%;
				}

				.form-table > tbody > tr > th {
					display: none;
				}
				</style>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_redirects');
        register_setting(Settings::$pagehook, 'podlove_redirects');
    }
<?php

add_filter('podlove_episode_form_data', function ($form_data) {
    if (!\Podlove\get_setting('metadata', 'enable_episode_recording_date')) {
        return $form_data;
    }
    $form_data[] = array('type' => 'string', 'key' => 'recording_date', 'options' => array('label' => __('Recording Date', 'podlove'), 'description' => '', 'html' => array('class' => 'regular-text podlove-check-input')), 'position' => 750);
    return $form_data;
});
add_filter('podlove_episode_data_filter', function ($filter) {
    return array_merge($filter, ['recording_date' => FILTER_SANITIZE_STRING]);
});
<?php

use Podlove\Model;
if (\Podlove\get_setting('metadata', 'enable_episode_license')) {
    add_action('podlove_episode_meta_box_end', 'podlove_episode_license_add_js');
    add_filter('podlove_episode_form_data', 'podlove_episode_license_extend_form', 10, 2);
}
function podlove_episode_license_add_js()
{
    $episode = Model\Episode::find_or_create_by_post_id(get_the_ID());
    ?>
	<script type="text/javascript">
	PODLOVE.License({
		plugin_url: "<?php 
    echo \Podlove\PLUGIN_URL;
    ?>
",

		locales:  JSON.parse('<?php 
    echo json_encode(\Podlove\License\locales_cc());
    ?>
'),
		versions: JSON.parse('<?php 
    echo json_encode(\Podlove\License\version_per_country_cc());
    ?>
'),
		license:  JSON.parse('<?php 
    echo json_encode(\Podlove\Model\License::get_license_from_url($episode->license_url));
    ?>
'),
    public function init()
    {
        add_settings_section('podlove_settings_episode', __('', 'podlove'), function () {
            echo '<h3>' . __('Download Tracking & Analytics Settings', 'podlove') . '</h3>';
            ?>
				<style type="text/css">
				.form-table .aligned-radio { display: table; margin-bottom: 10px; }
				.form-table .aligned-radio .row { display: table-row; }
				.form-table .aligned-radio .row > div { display: table-cell; }
				</style>
				<?php 
        }, Settings::$pagehook);
        add_settings_field('podlove_setting_tracking', sprintf('<label for="mode">%s</label>', __('Tracking Mode', 'podlove')), function () {
            ?>
				<label class="aligned-radio">
					<div class="row">
						<div>
							<input name="podlove_tracking[mode]" type="radio" value="0" <?php 
            checked(\Podlove\get_setting('tracking', 'mode'), 0);
            ?>
 />
						</div>
						<div>
							<?php 
            echo sprintf('<div><strong>%s</strong><br>%s</div>', __('No Tracking', 'podlove'), __('Original file URLs are presented to users and clients. No download-data is tracked.', 'podlove'));
            ?>
						</div>
					</div>
				</label>
				
				<label class="aligned-radio">
					<div class="row">
						<div>
							<input name="podlove_tracking[mode]" type="radio" value="ptm" <?php 
            checked(\Podlove\get_setting('tracking', 'mode'), 'ptm');
            ?>
 />
						</div>
						<div>
							<?php 
            echo sprintf('<div><strong>%s</strong><br>%s</div>', __('Tracking URL Parameters', 'podlove'), __('Original file URLs are extended by tracking parameters before presenting them to users and clients. 
									This is useful if you are using your server log files for download analytics. 
									No download-data is tracked.', 'podlove'));
            ?>
						</div>
					</div>
				</label>

				<label class="aligned-radio">
					<div class="row">
						<div>
							<input name="podlove_tracking[mode]" type="radio" value="ptm_analytics" <?php 
            checked(\Podlove\get_setting('tracking', 'mode'), 'ptm_analytics');
            ?>
 />
						</div>
						<div>
							<?php 
            echo sprintf('<div><strong>%s</strong><br>%s</div>', __('Tracking URL Parameters &amp; Analytics', 'podlove'), __('Instead of the original file URLs, users and clients see a link that points to the Publisher. 
									The Publisher logs the download intent and redirects the user to the original file. 
									That way the Publisher is able to generate download statistics. ', 'podlove'));
            ?>
						</div>
					</div>
				</label>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_episode');
        add_settings_field('podlove_status_location_database', sprintf('<label for="mode">%s</label>', __('Geolocation Lookup', 'podlove')), function () {
            $file = \Podlove\Geo_Ip::get_upload_file_path();
            \Podlove\Geo_Ip::register_updater_cron();
            ?>
				<?php 
            if (file_exists($file)) {
                ?>
					<p>
						<?php 
                echo __("Geolocation database", "podlove");
                ?>
:
						<code><?php 
                echo $file;
                ?>
</code>
					</p>
					<p>
						<?php 
                echo __("Last modified", "podlove");
                ?>
: 
						<?php 
                echo date(get_option('date_format') . ' ' . get_option('time_format'), filemtime($file));
                ?>
					</p>
					<p>
						<?php 
                echo sprintf(__("The database is updated automatically once a month. Next scheduled update: %s", "podlove"), date(get_option('date_format') . ' ' . get_option('time_format'), wp_next_scheduled('podlove_geoip_db_update')));
                ?>
					</p>
					<p>
						<button name="update_geo_database" class="button button-primary" value="1"><?php 
                echo __("Update Now", "podlove");
                ?>
</button>
					</p>
				<?php 
            } else {
                ?>
					<p>
						<?php 
                echo __("You need to download a geolocation-database for lookups to work.", "podlove");
                ?>
					</p>
					<p>
						<button name="update_geo_database" class="button button-primary" value="1"><?php 
                echo __("Download Now", "podlove");
                ?>
</button>
					</p>
				<?php 
            }
            ?>
				<p>
					<!-- This snippet must be included, as stated here: http://dev.maxmind.com/geoip/geoip2/geolite2/ -->
					<em>
						This product includes GeoLite2 data created by MaxMind, available from
						<a href="http://www.maxmind.com">http://www.maxmind.com</a>.
					</em>
				</p>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_episode');
        add_settings_field('podlove_debug_tracking', sprintf('<label for="mode">%s</label>', __('Debug Tracking', 'podlove')), function () {
            if (!\get_option('permalink_structure')) {
                ?>
					<div class="error">
						<p>
							<b><?php 
                echo __('Please Change Permalink Structure', 'podlove');
                ?>
</b>
							<?php 
                echo sprintf(__('You are using the default WordPress permalink structure. 
								This may cause problems with some podcast clients when you activate tracking.
								Go to %s and set it to anything but default (for example "Post name") before activating Tracking.', 'podlove'), '<a href="' . admin_url('options-permalink.php') . '">' . __('Permalink Settings') . '</a>');
                ?>
						</p>
					</div>
					<?php 
            }
            $media_file = Model\MediaFile::find_example();
            if (!$media_file) {
                return;
            }
            $episode = $media_file->episode();
            if (!$episode) {
                return;
            }
            $public_url = $media_file->get_public_file_url("debug");
            $actual_url = $media_file->get_file_url();
            ?>
				<h4>Example Episode</h4>
				<p>
					<?php 
            echo $episode->full_title();
            ?>
				</p>
				<h4>Media File</h4>
				<p>
					<h5>Actual Location</h5>
					<code><?php 
            echo $actual_url;
            ?>
</code>
				</p>
				<p>
					<h5>Public URL</h5>
					<code><?php 
            echo $public_url;
            ?>
</code>
				</p>
				<p>
					<h5>Validations</h5>
					<ul>
						<li>
							<!-- check rewrite rules -->
							<?php 
            if (\Podlove\Tracking\Debug::rewrites_exist()) {
                ?>
								✔ Rewrite Rules Exist
							<?php 
            } else {
                ?>
								✘ <strong>Rewrite Rules Missing</strong>
								<!-- todo: repair button -->
							<?php 
            }
            ?>
						</li>
						<li>
							<?php 
            if (\Podlove\Tracking\Debug::url_resolves_correctly($public_url, $actual_url)) {
                ?>
								✔ URL resolves correctly
							<?php 
            } else {
                ?>
								✘ <strong>URL does not resolve correctly</strong>
							<?php 
            }
            ?>
						</li>
						<li>
							<!-- check http/https consistency -->
							<?php 
            if (\Podlove\Tracking\Debug::is_consistent_https_chain($public_url, $actual_url)) {
                ?>
								✔ Consistent protocol chain
							<?php 
            } else {
                ?>
								✘ <strong>Protocol chain is inconsistent</strong>: Your site uses SSL but the files are not served with SSL.
								Many clients will not allow to download episodes. To fix this, serve files via SSL or deactivate tracking.
							<?php 
            }
            ?>
						</li>
					<!-- todo: check regularly and spit user in his face if it blows up -->
					</ul>
				</p>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_episode');
        register_setting(Settings::$pagehook, 'podlove_tracking', function ($args) {
            if (isset($_REQUEST['update_geo_database'])) {
                \Podlove\Geo_Ip::update_database();
            }
            \Podlove\Cache\TemplateCache::get_instance()->setup_purge();
            return $args;
        });
    }
    public function init()
    {
        add_settings_section('podlove_settings_episode', __('', 'podlove'), function () {
            echo '<h3>' . __('Episode Metadata Settings', 'podlove') . '</h3>';
        }, Settings::$pagehook);
        add_settings_field('podlove_setting_episode_recording_date', sprintf('<label for="enable_episode_recording_date">%s</label>', __('Enable recording date field.', 'podlove')), function () {
            ?>
				<label>
					<input name="podlove_metadata[enable_episode_recording_date]" id="enable_episode_recording_date" type="radio" value="1" <?php 
            checked(\Podlove\get_setting('metadata', 'enable_episode_recording_date'), 1);
            ?>
 /> <?php 
            echo __('enable', 'podlove');
            ?>
				</label>
				<label>
					<input name="podlove_metadata[enable_episode_recording_date]" id="enable_episode_recording_date" type="radio" value="0" <?php 
            checked(\Podlove\get_setting('metadata', 'enable_episode_recording_date'), 0);
            ?>
 /> <?php 
            echo __('disable', 'podlove');
            ?>
				</label>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_episode');
        add_settings_field('podlove_setting_episode_explicit', sprintf('<label for="enable_episode_explicit">%s</label>', __('Enable explicit content field.', 'podlove')), function () {
            ?>
				<label>
					<input name="podlove_metadata[enable_episode_explicit]" id="enable_episode_explicit" type="radio" value="1" <?php 
            checked(\Podlove\get_setting('metadata', 'enable_episode_explicit'), 1);
            ?>
 /> <?php 
            echo __('enable', 'podlove');
            ?>
				</label>
				<label>
					<input name="podlove_metadata[enable_episode_explicit]" id="enable_episode_explicit" type="radio" value="0" <?php 
            checked(\Podlove\get_setting('metadata', 'enable_episode_explicit'), 0);
            ?>
 /> <?php 
            echo __('disable', 'podlove');
            ?>
				</label>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_episode');
        add_settings_field('podlove_setting_episode_license', sprintf('<label for="enable_episode_license">%s</label>', __('Enable license field.', 'podlove')), function () {
            ?>
				<label>
					<input name="podlove_metadata[enable_episode_license]" id="enable_episode_license" type="radio" value="1" <?php 
            checked(\Podlove\get_setting('metadata', 'enable_episode_license'), 1);
            ?>
 /> <?php 
            echo __('enable', 'podlove');
            ?>
				</label>
				<label>
					<input name="podlove_metadata[enable_episode_license]" id="enable_episode_license" type="radio" value="0" <?php 
            checked(\Podlove\get_setting('metadata', 'enable_episode_license'), 0);
            ?>
 /> <?php 
            echo __('disable', 'podlove');
            ?>
				</label>
				<?php 
        }, Settings::$pagehook, 'podlove_settings_episode');
        register_setting(Settings::$pagehook, 'podlove_metadata');
    }