public function was_deactivated()
 {
     $feeds = \Podlove\Model\Feed::all("WHERE `bitlove` = '1'");
     foreach ($feeds as $feed) {
         delete_transient("podlove_bitlove_feed_url_" . $feed->id);
     }
 }
 public function handle_model_change($model)
 {
     $tainting_classes = array(Model\Episode::name(), Model\Feed::name(), Model\Podcast::name(), Model\Template::name(), Model\TemplateAssignment::name());
     $tainting_classes = apply_filters('podlove_cache_tainting_classes', $tainting_classes);
     if (in_array($model::name(), $tainting_classes)) {
         $this->taint();
     }
 }
 /**
  * Ping hub for every feed.
  *
  * @todo do it in a wp cron for more faster UX
  * @todo subscribe url or redirect=no url?
  */
 public function announce_feed_changes($post_ID, $post)
 {
     if (get_post_type($post) !== 'podcast') {
         return;
     }
     foreach (Model\Feed::all() as $feed) {
         $this->send_ping($feed->get_subscribe_url());
     }
 }
function podlove_jetpack_remove_rss_icon()
{
    if (!method_exists('Jetpack_Site_Icon', 'init')) {
        return;
    }
    if (!($feed_slug = get_query_var('feed'))) {
        return;
    }
    if (!($feed = \Podlove\Model\Feed::find_one_by_slug($feed_slug))) {
        return;
    }
    remove_action('rss2_head', [Jetpack_Site_Icon::init(), 'rss2_icon']);
}
Beispiel #5
0
    public function __construct($feed_slug)
    {
        add_action('atom_ns', function () {
            echo 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"';
        });
        add_filter('feed_link', function ($output, $feed) use($feed_slug) {
            return get_bloginfo('url') . '/feed/' . $feed_slug . '/';
        }, 10, 2);
        $podcast = Model\Podcast::get_instance();
        $feed = Model\Feed::find_one_by_slug($feed_slug);
        $episode_asset = $feed->episode_asset();
        $file_type = $episode_asset->file_type();
        add_filter('podlove_feed_enclosure', function ($enclosure, $enclosure_url, $enclosure_file_size, $mime_type) {
            return sprintf('<link rel="enclosure" href="%s" length="%s" type="%s"/>', $enclosure_url, $enclosure_file_size, $mime_type);
        }, 10, 4);
        mute_feed_title();
        override_feed_title($feed);
        override_feed_language($feed);
        override_feed_head('atom_head', $podcast, $feed, $file_type);
        override_feed_entry('atom_entry', $podcast, $feed, $file_type);
        add_action('atom_head', function () use($podcast, $feed, $file_type) {
            ?>
			<link rel="self" type="application/atom+xml" title="<?php 
            echo $feed->title_for_discovery();
            ?>
" href="<?php 
            echo $feed->get_subscribe_url();
            ?>
" />
			<?php 
            $feeds = Model\Feed::all();
            foreach ($feeds as $other_feed) {
                if ($other_feed->id !== $feed->id) {
                    ?>
					<link rel="alternate" type="application/atom+xml" title="<?php 
                    echo $other_feed->title_for_discovery();
                    ?>
" href="<?php 
                    echo $other_feed->get_subscribe_url();
                    ?>
" />
					<?php 
                }
            }
        }, 9);
        $this->do_feed($feed);
    }
Beispiel #6
0
 public function __construct($feed_slug)
 {
     add_action('rss2_ns', function () {
         echo 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"';
     });
     $podcast = Model\Podcast::get_instance();
     $feed = Model\Feed::find_one_by_slug($feed_slug);
     $episode_asset = $feed->episode_asset();
     $file_type = $episode_asset->file_type();
     add_filter('podlove_feed_enclosure', function ($enclosure, $enclosure_url, $enclosure_file_size, $mime_type) {
         return sprintf('<enclosure url="%s" length="%s" type="%s" />', $enclosure_url, $enclosure_file_size, $mime_type);
     }, 10, 4);
     mute_feed_title();
     override_feed_title($feed);
     override_feed_language($feed);
     override_feed_head('rss2_head', $podcast, $feed, $file_type);
     override_feed_entry('rss2_item', $podcast, $feed, $file_type);
     $this->do_feed($feed);
 }
function override_feed_item_limit($limits)
{
    global $wp_query;
    if (!is_feed()) {
        return $limits;
    }
    if (!($feed = \Podlove\Model\Feed::find_one_by_slug(get_query_var('feed_slug')))) {
        return $limits;
    }
    $custom_limit = (int) $feed->limit_items;
    if ($custom_limit > 0) {
        return "LIMIT {$custom_limit}";
    } elseif ($custom_limit == 0) {
        return $limits;
        // WordPress default
    } else {
        return '';
        // no limit
    }
}
 private function reset_migration()
 {
     delete_option('podlove_module_migration');
     delete_option('podlove_migration');
     delete_option('podlove_migration_validation_cache');
     delete_option('podlove_asset_assignment');
     delete_option('podlove_migrated_posts_cache');
     $args = array('post_type' => 'podcast', 'posts_per_page' => -1);
     $query = new \WP_Query($args);
     while ($query->have_posts()) {
         $query->the_post();
         wp_delete_post(get_the_ID());
     }
     wp_reset_postdata();
     foreach (Model\EpisodeAsset::all() as $asset) {
         $asset->delete();
     }
     foreach (Model\Feed::all() as $feed) {
         $feed->delete();
     }
 }
 public function prepare_items()
 {
     // number of items per page
     $per_page = get_user_meta(get_current_user_id(), 'podlove_feeds_per_page', true);
     if (empty($per_page)) {
         $per_page = 10;
     }
     // define column headers
     $this->_column_headers = $this->get_column_info();
     // retrieve data
     $data = \Podlove\Model\Feed::all('ORDER BY position ASC');
     // get current page
     $current_page = $this->get_pagenum();
     // get total items
     $total_items = count($data);
     // extrage page for current page only
     $data = array_slice($data, ($current_page - 1) * $per_page, $per_page);
     // add items to table
     $this->items = $data;
     // register pagination options & calculations
     $this->set_pagination_args(array('total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil($total_items / $per_page)));
 }
Beispiel #10
0
 public function prepare_items()
 {
     // number of items per page
     $per_page = 10;
     // define column headers
     $columns = $this->get_columns();
     $hidden = array();
     $sortable = $this->get_sortable_columns();
     $this->_column_headers = array($columns, $hidden, $sortable);
     // retrieve data
     $data = \Podlove\Model\Feed::all();
     // get current page
     $current_page = $this->get_pagenum();
     // get total items
     $total_items = count($data);
     // extrage page for current page only
     $data = array_slice($data, ($current_page - 1) * $per_page, $per_page);
     // add items to table
     $this->items = $data;
     // register pagination options & calculations
     $this->set_pagination_args(array('total_items' => $total_items, 'per_page' => $per_page, 'total_pages' => ceil($total_items / $per_page)));
 }
 public function ajax_validate_feed()
 {
     $feed_id = $_REQUEST['feed_id'];
     $redirect = $_REQUEST['redirect'] == '0' ? FALSE : TRUE;
     $feed = \Podlove\Model\Feed::find_by_id($feed_id);
     // Delete feed source transient
     $errors_and_warnings = \Podlove\Modules\FeedValidation\Model\FeedValidator::getValidationErrorsandWarnings($feed->id, $redirect);
     // renew transients
     set_transient('podlove_dashboard_feed_validation_' . $feed->id, \Podlove\Modules\FeedValidation\Model\FeedValidator::getValidationIcon($feed->id, $redirect), 3600 * 24);
     set_transient('podlove_dashboard_feed_information_' . $feed->id, \Podlove\Modules\FeedValidation\Model\FeedValidator::getInformation($feed->id, $redirect), 3600 * 24);
     if ($redirect === TRUE) {
         set_transient('podlove_dashboard_feed_r_validation_' . $feed->id, \Podlove\Modules\FeedValidation\Model\FeedValidator::getValidationIcon($feed->id, $redirect), 3600 * 24);
         set_transient('podlove_dashboard_feed_r_information_' . $feed->id, \Podlove\Modules\FeedValidation\Model\FeedValidator::getInformation($feed->id, $redirect), 3600 * 24);
     }
     \Podlove\AJAX\Ajax::respond_with_json(array('validation_icon' => \Podlove\Modules\FeedValidation\Model\FeedValidator::getValidationIcon($feed->id, $redirect)));
 }
Beispiel #12
0
/**
 * 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 2:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `chapters` TEXT AFTER `cover_art`', \Podlove\Model\Release::table_name());
            $wpdb->query($sql);
            break;
        case 3:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `format` VARCHAR(255) AFTER `slug`', \Podlove\Model\Feed::table_name());
            $wpdb->query($sql);
            break;
        case 4:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `title` VARCHAR(255) AFTER `id`', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            break;
        case 5:
            \Podlove\Modules\Base::activate('podlove_web_player');
            break;
        case 6:
            // title column is "int" for some people. this migration fixes that
            $sql = sprintf('SHOW COLUMNS FROM `wp_podlove_medialocation` WHERE Field = "title"', \Podlove\Model\EpisodeAsset::table_name());
            $row = $wpdb->get_row($sql);
            if (strtolower(substr($row->Type, 0, 3)) === 'int') {
                $wpdb->query(sprintf('UPDATE `%s` SET title = NULL', \Podlove\Model\EpisodeAsset::table_name()));
                $wpdb->query(sprintf('ALTER TABLE `%s` MODIFY COLUMN `title` VARCHAR(255)', \Podlove\Model\EpisodeAsset::table_name()));
            }
            break;
        case 7:
            // move language from feed to show
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `language` VARCHAR(255) AFTER `summary`', \Podlove\Model\Show::table_name());
            $wpdb->query($sql);
            $sql = sprintf('ALTER TABLE `%s` DROP COLUMN `language`', \Podlove\Model\Feed::table_name());
            $wpdb->query($sql);
            break;
        case 8:
            $sql = sprintf('ALTER TABLE `%s` ADD COLUMN `supports_cover_art` INT', \Podlove\Model\Show::table_name());
            $wpdb->query($sql);
            break;
        case 9:
            // huge architecture migration
            // assume first show will be blueprint for the podcast
            $show = $wpdb->get_row(sprintf('SELECT * FROM %s LIMIT 1', $wpdb->prefix . 'podlove_show'), ARRAY_A);
            $show_id = $show['id'];
            // On my local machine the migration runs twice.
            // This is a quick fix. caveat: someone who has no show defined
            // will need to uninstall the plugin. That seems acceptable.
            if (!$show_id) {
                return;
            }
            // all releases of this show will be converted to episodes
            $releases = $wpdb->get_results(sprintf('
					SELECT
						E.post_id, R.episode_id, R.active, R.enable, R.slug, R.duration, R.cover_art, R.chapters
					FROM 
						%s R
						INNER JOIN %s E ON R.episode_id = E.id
					WHERE
						R.show_id = "%s"
					', $wpdb->prefix . 'podlove_release', $wpdb->prefix . 'podlove_episode', $show_id), ARRAY_A);
            // write show settings to podcast
            $podcast = \Podlove\Model\Podcast::get_instance();
            foreach ($show as $key => $value) {
                $podcast->{$key} = $value;
            }
            $podcast->save();
            // rebuild show table
            \Podlove\Model\Show::destroy();
            \Podlove\Model\Show::build();
            // rebuild episodes table
            \Podlove\Model\Episode::destroy();
            \Podlove\Model\Episode::build();
            foreach ($releases as $release) {
                $episode = new \Podlove\Model\Episode();
                foreach ($release as $key => $value) {
                    if (!in_array($key, array('episode_id'))) {
                        $episode->{$key} = $value;
                    }
                }
                $episode->save();
            }
            // clean feed table
            $sql = sprintf('DELETE FROM `%s` WHERE `show_id` != "%s"', \Podlove\Model\Feed::table_name(), $show_id);
            $wpdb->query($sql);
            $sql = sprintf('ALTER TABLE `%s` DROP COLUMN `show_id`', \Podlove\Model\Feed::table_name());
            $wpdb->query($sql);
            // fix mediafile table
            $sql = sprintf('ALTER TABLE `%s` CHANGE `release_id` `episode_id` INT', \Podlove\Model\MediaFile::table_name());
            $wpdb->query($sql);
            // remove suffix
            $sql = sprintf('ALTER TABLE `%s` DROP COLUMN `suffix`', \Podlove\Model\EpisodeAsset::table_name());
            $wpdb->query($sql);
            // add more default formats
            $default_formats = array(array('name' => 'PDF Document', 'type' => 'ebook', 'mime_type' => 'application/pdf', 'extension' => 'pdf'), array('name' => 'ePub Document', 'type' => 'ebook', 'mime_type' => 'application/epub+zip', 'extension' => 'epub'), array('name' => 'PNG Image', 'type' => 'image', 'mime_type' => 'image/png', 'extension' => 'png'), array('name' => 'JPEG Image', 'type' => 'image', 'mime_type' => 'image/jpeg', 'extension' => 'jpg'));
            foreach ($default_formats as $format) {
                $f = new Model\FileType();
                foreach ($format as $key => $value) {
                    $f->{$key} = $value;
                }
                $f->save();
            }
            // update assistant
            $assistant = \Podlove\Modules\EpisodeAssistant\Episode_Assistant::instance();
            $template = $assistant->get_module_option('title_template');
            $template = str_replace('%show_slug%', '%podcast_slug%', $template);
            $assistant->update_module_option('title_template', $template);
            // update media locations
            $media_locations = \Podlove\Model\EpisodeAsset::all();
            foreach ($media_locations as $media_location) {
                $media_location->url_template = str_replace('%suffix%', '', $media_location->url_template);
                $media_location->save();
            }
            break;
        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;
    }
}
Beispiel #13
0
/**
 * Adds feed discover links to WordPress head.
 *
 * @todo find a better place for this function
 */
function add_feed_discoverability()
{
    if (is_admin()) {
        return;
    }
    $feeds = \Podlove\Model\Feed::find_all_by_discoverable(1);
    foreach ($feeds as $feed) {
        echo '<link rel="alternate" type="' . $feed->get_content_type() . '" title="' . esc_attr($feed->title_for_discovery()) . '" href="' . $feed->get_subscribe_url() . "\" />\n";
    }
}
 public static function logValidation($feedid, $errors_and_warnings, $redirected = FALSE)
 {
     $feed = \Podlove\Model\Feed::find_by_id($feedid);
     $feed_subscribe_url = $redirected === FALSE ? $feed->get_subscribe_url() : $feed->redirect_url;
     if ($redirected === TRUE) {
         $redirected = ' (Redirected)';
     }
     foreach ($errors_and_warnings['warnings'] as $warning_key => $warning) {
         \Podlove\Log::get()->addInfo('Warning: ' . $warning['text'] . ', line ' . $warning['line'] . ' in Feed <a href="' . $feed_subscribe_url . '">' . $feed->name . $redirected . '</a>.');
     }
     foreach ($errors_and_warnings['errors'] as $error_key => $error) {
         \Podlove\Log::get()->addError('Error: ' . $error['text'] . ', line ' . $error['line'] . ' in Feed <a href="' . $feed_subscribe_url . '">' . $feed->name . $redirected . '</a>.');
     }
 }
Beispiel #15
0
 private function edit_template()
 {
     $feed = \Podlove\Model\Feed::find_by_id($_REQUEST['feed']);
     echo '<h3>' . sprintf(__('Edit Feed: %s', 'podlove'), $feed->name) . '</h3>';
     $this->form_template($feed, 'save');
 }
 function feed_head_contributors()
 {
     global $wp_query;
     $feed = \Podlove\Model\Feed::find_one_by_slug($wp_query->query_vars['feed']);
     if (!$feed) {
         return;
     }
     $contributor_xml = $this->prepare_contributions_for_feed(\Podlove\Modules\Contributors\Model\ShowContribution::all(), $feed);
     echo apply_filters('podlove_feed_head_contributors', $contributor_xml);
 }
    public function template()
    {
        // load already migrated posts
        $migrated_posts_cache = get_option('podlove_migrated_posts_cache', array());
        // then begin to migrate
        $migration_settings = get_option('podlove_migration', array());
        // Basic Podcast Settings
        $podcast = Model\Podcast::get();
        $podcast->title = $migration_settings['podcast']['title'];
        $podcast->subtitle = $migration_settings['podcast']['subtitle'];
        $podcast->summary = $migration_settings['podcast']['summary'];
        $podcast->media_file_base_uri = \Podlove\Modules\Migration\get_media_file_base_url();
        // harvest low hanging podPress fruits
        if ($podPress_config = get_option('podPress_config')) {
            if (isset($podPress_config['iTunes']['image']) && !$podcast->cover_image) {
                $podcast->cover_image = $podPress_config['iTunes']['image'];
            }
        }
        // harvest low hanging PowerPress fruits
        if ($powerPress_config = get_option('powerpress_feed')) {
            if (isset($powerPress_config['itunes_image']) && !$podcast->cover_image) {
                $podcast->cover_image = $powerPress_config['itunes_image'];
            }
            if (isset($powerPress_config['itunes_cat_1']) && !$podcast->category_1) {
                $podcast->category_1 = $powerPress_config['itunes_cat_1'];
            }
            if (isset($powerPress_config['itunes_cat_2']) && !$podcast->category_2) {
                $podcast->category_2 = $powerPress_config['itunes_cat_2'];
            }
            if (isset($powerPress_config['itunes_cat_3']) && !$podcast->category_3) {
                $podcast->category_3 = $powerPress_config['itunes_cat_3'];
            }
        }
        $podcast->save();
        // Create Template
        $template = Model\Template::find_one_by_title('default');
        if (!$template) {
            $template = new Model\Template();
            $template->title = 'default';
            $template->content = <<<EOT
{{ episode.player }}
[podlove-episode-downloads]
EOT;
            $template->save();
        }
        $template_assignment = Model\TemplateAssignment::get_instance();
        if ($template_assignment->has_property($migration_settings['cleanup']['template'])) {
            $template_assignment->{$migration_settings}['cleanup']['template'] = $template->id;
            $template_assignment->save();
        }
        // Create Assets
        $assets = array();
        foreach ($migration_settings['file_types'] as $file_type_id => $_) {
            $file_type = Model\FileType::find_one_by_id($file_type_id);
            $is_image = in_array($file_type->extension, array('png', 'jpg', 'jpeg', 'gif'));
            $asset = Model\EpisodeAsset::find_one_by_file_type_id($file_type_id);
            if (!$asset) {
                $asset = new Model\EpisodeAsset();
                $asset->title = $file_type->name;
                $asset->file_type_id = $file_type_id;
                $asset->downloadable = !$is_image;
                $asset->save();
            }
            $assets[] = $asset;
            if ($is_image) {
                $asset_assignments = get_option('podlove_asset_assignment', array());
                if (!$asset_assignments['image']) {
                    $asset_assignments['image'] = $asset->id;
                    update_option('podlove_asset_assignment', $asset_assignments);
                }
            }
            // create feeds
            if (stripos($file_type->mime_type, 'audio') !== false) {
                $feed = Model\Feed::find_one_by_episode_asset_id($asset->id);
                if (!$feed) {
                    $feed = new Model\Feed();
                    $feed->episode_asset_id = $asset->id;
                    $feed->name = $file_type->name;
                    $feed->title = $file_type->name;
                    $feed->slug = $file_type->extension;
                    $feed->format = 'rss';
                    $feed->enable = true;
                    $feed->discoverable = true;
                    $feed->limit_items = -1;
                    $feed->save();
                }
            }
            // set web player settings
            $webplayer_formats = get_option('podlove_webplayer_formats', array());
            if (!isset($webplayer_formats['audio'])) {
                $webplayer_formats['audio'] = array();
            }
            if (stripos($file_type->mime_type, 'audio/mpeg') !== false) {
                $webplayer_formats['audio']['mp3'] = $asset->id;
            } elseif (stripos($file_type->mime_type, 'audio/mp4') !== false) {
                $webplayer_formats['audio']['mp4'] = $asset->id;
            } elseif (stripos($file_type->mime_type, 'audio/ogg') !== false) {
                $webplayer_formats['audio']['ogg'] = $asset->id;
            } elseif (stripos($file_type->mime_type, 'audio/opus') !== false) {
                $webplayer_formats['audio']['opus'] = $asset->id;
            }
            update_option('podlove_webplayer_formats', $webplayer_formats);
        }
        // flush rules after migration
        set_transient('podlove_needs_to_flush_rewrite_rules', true);
        ?>
		
		<form action="" method="POST">
			<input type="submit" name="prev" class="btn" value="<?php 
        echo __('Back', 'podlove');
        ?>
">
			<input type="submit" name="next" id="continue_to_finish_button" class="btn btn-primary disabled pull-right" value="<?php 
        echo __('Continue to last step', 'podlove');
        ?>
">
		</form>

		<div class="row-fluid">
			<div class="span12">
				<h3 id="migration-header">Migrating <small></small></h3>
			</div>
		</div>

		<div class="progress progress-striped active" id="migration_progress">
			<div class="bar" style="width:0%"></div>
		</div>

		<p>
			<input type="button" id="start_migration_button" class="btn btn-primary" value="<?php 
        echo __('Start Migration', 'podlove');
        ?>
">
			Starting the migration creates the actual episodes one by one.			
		</p>

		<table class="table table-condensed" id="posts_to_migrate">
			<thead>
				<tr>
					<th>Status</th>
					<th>Episode</th>
				</tr>
			</thead>
			<tbody>
				<?php 
        $migrated_post_ids = array_keys($migrated_posts_cache);
        ?>
				<?php 
        foreach ($migration_settings['episodes'] as $post_id => $_) {
            ?>
					<?php 
            $done = in_array($post_id, $migrated_post_ids);
            ?>
					<tr data-post-id="<?php 
            echo $post_id;
            ?>
" <?php 
            echo $done ? 'class="done"' : '';
            ?>
>
						<td class="status">
							<span class="waiting" <?php 
            echo !$done ? '' : 'style="display:none"';
            ?>
>not yet migrated</span>
							<span class="migrating" style="display:none"><i class="podlove-icon-spinner rotate"></i></span>
							<span class="done" <?php 
            echo $done ? '' : 'style="display:none"';
            ?>
><i class="podlove-icon-ok"></i></span>
						</td>
						<td class="episode">
							<?php 
            if ($done) {
                ?>
								<a href="<?php 
                echo get_edit_post_link($migrated_posts_cache[$post_id]);
                ?>
" target="_blank">
									<?php 
                echo get_the_title($post_id);
                ?>
								</a>
							<?php 
            } else {
                ?>
								<?php 
                echo get_the_title($post_id);
                ?>
							<?php 
            }
            ?>
						</td>
					</tr>
				<?php 
        }
        ?>
			</tbody>
		</table>

		<script type="text/javascript">
		jQuery(function($) {
			var posts_to_migrate = $("#posts_to_migrate tbody tr").length;

			$("#continue_to_finish_button").hide();

			function update_migration_progress_bar() {
				var posts_done = $("#posts_to_migrate tbody tr.done").length;
				progress = Math.round(posts_done / posts_to_migrate * 100)
				$("#migration_progress .bar")
					.css("width", progress + "%")
					.html(posts_done + " / " + posts_to_migrate);

				if ( progress == 100 ) {
					$("#migration_progress")
						.removeClass("active")
						.addClass("progress-success")
						.find(".bar").html("Done! Whoop whoop!");

					$("#migration-header small").html('');
					$("#start_migration_button").addClass("disabled");
					$("#continue_to_finish_button").removeClass("disabled").show();
				}
			};

			function podlove_migrate_one_post() {
				$("#posts_to_migrate tbody tr:not(.done):first").each(function() {
					var post_id = $(this).data("post-id")
					    that = $(this),
					    episode_title = $(".episode", that).html();

					$("#migration-header small").html(episode_title);

					var data = {
						action: 'podlove-migrate-post',
						post_id: post_id
					};

					$.ajax({
						url: ajaxurl,
						data: data,
						dataType: 'json',
						beforeSend: function(jqXHR, settings) {
							$(".waiting, .done", that).hide();
							$(".migrating", that).show();
						},
						success: function(result) {
							var episode_url = result.url;

							$(".waiting, .migrating", that).hide();
							$(".done", that).show();
							that.addClass("done");

							// add link
							$(".episode", that).html('<a href="' + episode_url + '" target="_blank">' + episode_title + '</a>')

							// update progress bar
							update_migration_progress_bar();

							// continue
							podlove_migrate_one_post();
						}
					});
				});
			}

			$("#start_migration_button").on("click", function(){
				if (!$(this).hasClass("disabled")) {
					$(this).addClass("disabled");
					podlove_migrate_one_post();
				}
			});
			update_migration_progress_bar();
		});
		</script>
		<?php 
    }
 /**
  * Feed list, ready for subscribe button.
  * 
  * @return array list of prepared feed data-objects
  */
 private function feeds()
 {
     return TemplateCache::get_instance()->cache_for('podlove_subscribe_button_feeds', function () {
         $feeds = array_map(function ($feed) {
             $file_type = $feed->episode_asset()->file_type();
             return ['type' => $file_type->type, 'format' => self::feed_format($file_type->extension), 'url' => $feed->get_subscribe_url(), 'variant' => 'high'];
         }, $this->discoverable_feeds());
         $itunes_feed = Feed::find_one_by_where('itunes_feed_id > 0');
         if ($itunes_feed) {
             // @todo verify the url is valid
             $feeds[] = ['type' => 'itunes-url', 'url' => 'https://itunes.apple.com/podcast/id' . $itunes_feed->itunes_feed_id];
         }
         return $feeds;
     });
 }
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);
}
 public function update_feed_position()
 {
     $feed_id = (int) $_REQUEST['feed_id'];
     $position = (double) $_REQUEST['position'];
     Model\Feed::find_by_id($feed_id)->update_attributes(array('position' => $position));
     die;
 }
Beispiel #21
0
    });
    add_filter('query_vars', function ($qv) {
        $qv[] = 'show_slug';
        $qv[] = 'feed_slug';
        return $qv;
    });
}
// Hooks:
// parse_query => query vars available
// wp => query_posts done
add_action('wp', function () {
    global $wp_query;
    if (!($feed_slug = get_query_var('feed_slug'))) {
        return;
    }
    if (!($feed = \Podlove\Model\Feed::find_one_by_slug($feed_slug))) {
        return;
    }
    $is_feedburner_bot = preg_match("/feedburner|feedsqueezer/i", $_SERVER['HTTP_USER_AGENT']);
    $is_manual_redirect = !isset($_REQUEST['redirect']) || $_REQUEST['redirect'] != "no";
    if (strlen($feed->redirect_url) > 0 && $is_manual_redirect && !$is_feedburner_bot) {
        header(sprintf("Location: %s", $feed->redirect_url), TRUE, 302);
        exit;
    } else {
        // make sure is_feed() returns true
        add_filter('the_content', function ($content) {
            global $wp_query;
            $wp_query->is_feed = true;
            return $content;
        });
        if ($feed->format === "rss") {
 public static function prepare_feed($feed_slug)
 {
     global $wp_query;
     add_action('rss2_ns', function () {
         echo 'xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" ';
         echo 'xmlns:psc="http://podlove.org/simple-chapters" ';
         echo 'xmlns:content="http://purl.org/rss/1.0/modules/content/" ';
         echo 'xmlns:fh="http://purl.org/syndication/history/1.0" ';
     });
     $podcast = Model\Podcast::get();
     if (!($feed = Model\Feed::find_one_by_slug($feed_slug))) {
         self::wp_404();
     }
     if (!($episode_asset = $feed->episode_asset())) {
         self::wp_404();
     }
     $file_type = $episode_asset->file_type();
     add_filter('podlove_feed_enclosure', function ($enclosure, $enclosure_url, $enclosure_file_size, $mime_type, $media_file) {
         if ($enclosure_file_size < 0) {
             $enclosure_file_size = 0;
         }
         $dom = new \Podlove\DomDocumentFragment();
         $element = $dom->createElement('enclosure');
         $attributes = ['url' => $enclosure_url, 'length' => $enclosure_file_size, 'type' => $mime_type];
         $attributes = apply_filters('podlove_feed_enclosure_attributes', $attributes, $media_file);
         foreach ($attributes as $k => $v) {
             $element->setAttribute($k, $v);
         }
         $dom->appendChild($element);
         return (string) $dom;
     }, 10, 5);
     override_feed_title($feed);
     override_feed_description($feed);
     override_feed_language($feed);
     override_feed_head('rss2_head', $podcast, $feed, $file_type);
     override_feed_entry('rss2_item', $podcast, $feed, $file_type);
     add_action('rss2_item', function () {
         if (apply_filters('podlove_feed_show_summary', true)) {
             echo "<description><![CDATA[";
             \Podlove\Feeds\the_description();
             echo "]]></description>";
         }
     }, 9);
     add_action('rss2_head', function () use($podcast, $feed) {
         global $wp_query;
         $current_page = get_query_var('paged') ? get_query_var('paged') : 1;
         $feed_url_for_page = function ($page) use($feed) {
             $url = $feed->get_subscribe_url();
             if ($page > 0) {
                 $url .= '?paged=' . $page;
             }
             return $url;
         };
         if ($current_page < $wp_query->max_num_pages) {
             echo "\n\t" . sprintf('<atom:link rel="next" href="%s" />', $feed_url_for_page($current_page + 1));
         }
         if ($current_page > 2) {
             echo "\n\t" . sprintf('<atom:link rel="prev" href="%s" />', $feed_url_for_page($current_page - 1));
         } elseif ($current_page == 2) {
             echo "\n\t" . sprintf('<atom:link rel="prev" href="%s" />', $feed_url_for_page(0));
         }
         echo "\n\t" . sprintf('<atom:link rel="first" href="%s" />', $feed_url_for_page(0));
         if ($wp_query->max_num_pages > 1) {
             echo "\n\t" . sprintf('<atom:link rel="last" href="%s" />', $feed_url_for_page($wp_query->max_num_pages));
         }
         if ($podcast->language) {
             echo "\n\t" . '<language>' . $podcast->language . '</language>';
         }
         do_action('podlove_rss2_head', $feed);
     }, 9);
     $posts_per_page = $feed->limit_items == 0 ? get_option('posts_per_rss') : $feed->limit_items;
     if ($posts_per_page == Model\Feed::ITEMS_GLOBAL_LIMIT) {
         $posts_per_page = $podcast->limit_items;
     }
     // now override the option so WP core functions accessing the option get the "correct" value
     add_filter('pre_option_posts_per_rss', function ($_) use($posts_per_page) {
         return $posts_per_page;
     });
     $args = array('post_type' => 'podcast', 'post__in' => $feed->post_ids(), 'posts_per_page' => $posts_per_page);
     # The theme "getnoticed" globally overrides post_types in pre_get_posts.
     # Fix: hook in after the theme and override it again.
     # It's not bad practice because I *really* only want episodes in this feed.
     add_action('pre_get_posts', function ($query) {
         $query->set('post_type', 'podcast');
     }, 20);
     /**
      * In feeds, WordPress ignores the 'posts_per_page' parameter 
      * and overrides it with the 'posts_per_rss' option. So we need to
      * override that option.
      */
     add_filter('post_limits', function ($limits) use($feed) {
         $page = get_query_var('paged') ? (int) get_query_var('paged') : 1;
         $max = $feed->get_post_limit_sql();
         $start = $max * ($page - 1);
         if ($max > 0) {
             return 'LIMIT ' . $start . ', ' . $max;
         } else {
             return '';
         }
     });
     $args = array_merge($wp_query->query_vars, $args);
     query_posts($args);
 }
/**
 * 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;
    }
}
 public function render_link()
 {
     echo Model\Feed::get_link_tag(array('prefix' => 'atom', 'rel' => 'http://podlove.org/simple-chapters', 'type' => '', 'title' => '', 'href' => get_permalink() . "?chapters_format=psc"));
 }