/** * Insert HTML meta tags into site head. */ public function insert_twitter_card_metadata() { $post_id = get_the_ID(); if (!$post_id) { return; } $episode = \Podlove\Model\Episode::find_one_by_post_id($post_id); if (!$episode) { return; } $podcast = Model\Podcast::get(); $cover_art_url = $episode->cover_art_with_fallback()->url(); // define meta tags $data = array(array('name' => 'twitter:card', 'content' => 'summary'), array('name' => 'twitter:url', 'content' => get_permalink()), array('name' => 'twitter:title', 'content' => get_the_title()), array('name' => 'twitter:description', 'content' => $episode->description())); if ($site = $this->get_module_option('site')) { $data[] = array('name' => 'twitter:site', 'content' => $site); } if ($creator = $this->get_module_option('creator')) { $data[] = array('name' => 'twitter:creator', 'content' => $creator); } if ($cover_art_url) { $data[] = array('name' => 'twitter:image', 'content' => $cover_art_url); } // print meta tags $dom = new DomDocumentFragment(); foreach ($data as $meta_element) { $element = $dom->createElement('meta'); foreach ($meta_element as $attribute => $value) { $element->setAttribute($attribute, $value); } $dom->appendChild($element); } echo $dom; }
function override_feed_language($feed) { add_filter('pre_option_rss_language', function ($language) use($feed) { $podcast = Model\Podcast::get(); return apply_filters('podlove_feed_language', $podcast->language ? $podcast->language : $language); }); }
public function register_page() { $podcast = \Podlove\Model\Podcast::get(); $form_attributes = array('context' => 'podlove_flattr', 'action' => $this->get_url()); ?> <p> <?php echo __('This Flattr account will be associated with your Podcast. Flattr donations for e.g. new episodes will be linked with this account.', 'podlove'); ?> </p> <style type="text/css"> /* add linebreak after each radio button+label */ input[type="radio"] + label::after { content: " "; display: block; } </style> <?php \Podlove\Form\build_for((object) Flattr::get_setting(), $form_attributes, function ($form) { $wrapper = new \Podlove\Form\Input\TableWrapper($form); $podcast = $form->object; $wrapper->string('account', ['label' => __('Flattr Account', 'podlove'), 'html' => ['class' => 'regular-text required podlove-check-input']]); if (\Podlove\Modules\Base::is_active('contributors')) { $wrapper->radio('contributor_shortcode_default', ['label' => __('Default Parameter in Contributors Shortcodes', 'podlove'), 'description' => '<br>' . __('You can override this setting individually by passing along the <code>flattr="yes"</code> or <code>flattr="no"</code> parameter to the shortcodes.', 'podlove'), 'options' => ['yes' => 'yes, show Flattr buttons by default', 'no' => 'no, do not show Flattr buttons by default'], 'default' => 'no']); } }); }
public function register_page() { $podcast = \Podlove\Model\Podcast::get(); $form_attributes = array('context' => 'podlove_podcast', 'action' => $this->get_url()); ?> <p> <?php echo __('You may provide additional information about your podcast that may or may not be used by podcast directories like iTunes.', 'podlove'); ?> </p> <?php \Podlove\Form\build_for($podcast, $form_attributes, function ($form) { $wrapper = new \Podlove\Form\Input\TableWrapper($form); $podcast = $form->object; $wrapper->string('author_name', array('label' => __('Author Name', 'podlove'), 'description' => __('Publicly displayed in Podcast directories.', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input'))); $wrapper->string('publisher_name', array('label' => __('Publisher Name', 'podlove'), 'description' => __('', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input'))); $wrapper->string('publisher_url', array('label' => __('Publisher URL', 'podlove'), 'description' => __('', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input', 'data-podlove-input-type' => 'url'))); $wrapper->string('owner_name', array('label' => __('Owner Name', 'podlove'), 'description' => __('Used by iTunes and other Podcast directories to contact you.', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input'))); $wrapper->string('owner_email', array('label' => __('Owner Email', 'podlove'), 'description' => __('Used by iTunes and other Podcast directories to contact you.', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input', 'data-podlove-input-type' => 'email'))); $wrapper->string('keywords', array('label' => __('Keywords', 'podlove'), 'description' => __('List of keywords. Separate with commas.', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input'))); $wrapper->select('category_1', array('label' => __('iTunes Categories', 'podlove'), 'description' => '', 'type' => 'select', 'options' => \Podlove\Itunes\categories())); $wrapper->select('category_2', array('label' => '', 'description' => '', 'type' => 'select', 'options' => \Podlove\Itunes\categories())); $wrapper->select('category_3', array('label' => '', 'description' => '<br>' . __('For placement within the older, text-based browse system, podcast feeds may list up to 3 category/subcategory pairs. (For example, "Music" counts as 1, as does "Business > Careers.") For placement within the newer browse system based on Category links, however, and for placement within the Top Podcasts and Top Episodes lists that appear in the right column of most podcast pages, only the first category listed in the feed is used.') . ' (<a href="http://www.apple.com/itunes/podcasts/specs.html#category" target="_blank">http://www.apple.com/itunes/podcasts/specs.html#category</a>)', 'options' => \Podlove\Itunes\categories())); $wrapper->select('explicit', array('label' => __('Explicit Content?', 'podlove'), 'description' => __('', 'podlove'), 'type' => 'checkbox', 'options' => array(0 => 'no', 1 => 'yes', 2 => 'clean'))); $wrapper->checkbox('complete', array('label' => __('Podcast complete?', 'podlove'), 'description' => __('Shows that this Podcast is finished and no further episodes will be added.', 'podlove'), 'default' => false)); }); }
/** * Insert HTML meta tags into site head. * * @todo caching * @todo let user choose what's in og:description: subtitle, excerpt, ... * @todo handle multiple releases per episode */ public function insert_open_graph_metadata() { $post_id = get_the_ID(); if (!$post_id) { return; } $episode = \Podlove\Model\Episode::find_one_by_post_id($post_id); if (!$episode) { return; } $podcast = Model\Podcast::get_instance(); // determine image $cover_art_url = $episode->get_cover_art(); if (!$cover_art_url) { $cover_art_url = $podcast->cover_image; } ?> <meta property="og:type" content="website" /> <meta property="og:site_name" content="<?php echo $episode->full_title(); ?> " /> <meta property="og:title" content="<?php the_title(); ?> " /> <?php if ($cover_art_url) { ?> <meta property="og:image" content="<?php echo $cover_art_url; ?> " /> <?php } ?> <meta property="og:url" content="<?php the_permalink(); ?> " /> <?php $media_files = $episode->media_files(); ?> <?php foreach ($media_files as $media_file) { ?> <meta property="og:audio" content="<?php echo $media_file->get_file_url(); ?> " /> <meta property="og:audio:type" content="<?php echo $media_file->episode_asset()->file_type()->mime_type; ?> " /> <?php } ?> <?php }
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(); } }
/** * Insert HTML meta tags into site head. * * @todo caching * @todo let user choose what's in og:description: subtitle, excerpt, ... * @todo handle multiple releases per episode */ public static function get_open_graph_metadata() { $post_id = get_the_ID(); if (!$post_id) { return; } $episode = \Podlove\Model\Episode::find_one_by_post_id($post_id); if (!$episode) { return; } $podcast = Model\Podcast::get(); $cover_art_url = $episode->cover_art_with_fallback()->url(); // determine featured image (thumbnail) $thumbnail = NULL; if (has_post_thumbnail()) { $post_thumbnail_id = get_post_thumbnail_id($post_id); $thumbnailInfo = wp_get_attachment_image_src($post_thumbnail_id); if (is_array($thumbnailInfo)) { list($thumbnail, $width, $height) = $thumbnailInfo; } } $description = NULL; if ($episode->summary && $episode->subtitle) { $description = $episode->subtitle . "\n" . $episode->summary; } elseif ($episode->summary) { $description = $episode->summary; } elseif ($episode->subtitle) { $description = $episode->subtitle; } // define meta tags $data = array(array('property' => 'og:type', 'content' => 'website'), array('property' => 'og:site_name', 'content' => $podcast->title ? $podcast->title : get_the_title()), array('property' => 'og:title', 'content' => get_the_title()), array('property' => 'og:url', 'content' => get_permalink())); if ($description) { $data[] = array('property' => 'og:description', 'content' => $description); } if ($cover_art_url) { $data[] = array('property' => 'og:image', 'content' => $cover_art_url); } if (isset($thumbnail)) { $data[] = array('property' => 'og:image', 'content' => $thumbnail); } foreach ($episode->media_files() as $media_file) { $mime_type = $media_file->episode_asset()->file_type()->mime_type; if (stripos($mime_type, 'audio') !== false) { $data[] = array('property' => 'og:audio', 'content' => $media_file->get_file_url()); $data[] = array('property' => 'og:audio:type', 'content' => $mime_type); } } // print meta tags $dom = new DomDocumentFragment(); foreach ($data as $meta_element) { $element = $dom->createElement('meta'); foreach ($meta_element as $attribute => $value) { $element->setAttribute($attribute, $value); } $dom->appendChild($element); } return $dom; }
public function widget($args, $instance) { $podcast = \Podlove\Model\Podcast::get(); echo $args['before_widget']; if (!empty($instance['title'])) { echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; } echo $podcast->get_license_html(); echo $args['after_widget']; }
public function widget($args, $instance) { $podcast = \Podlove\Model\Podcast::get(); echo $args['before_widget']; if (!empty($instance['title'])) { echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; } echo do_shortcode('[podlove-template template="' . $instance['template'] . '" autop="' . ($instance['autop'] ? 'yes' : 'no') . '"]'); echo $args['after_widget']; }
/** * Apply Twig to given template * * @param string $html File path or HTML string. * @param array $vars optional variables for Twig context * @return string rendered template string */ public static function apply_to_html($html, $vars = array()) { // file loader for internal use $file_loader = new \Twig_Loader_Filesystem(); $file_loader->addPath(implode(DIRECTORY_SEPARATOR, array(\Podlove\PLUGIN_DIR, 'templates')), 'core'); // other modules can register their own template directories/namespaces $file_loader = apply_filters('podlove_twig_file_loader', $file_loader); // database loader for user templates $db_loader = new TwigLoaderPodloveDatabase(); $loaders = array($file_loader, $db_loader); $loaders = apply_filters('podlove_twig_loaders', $loaders); $loader = new \Twig_Loader_Chain($loaders); $twig = new \Twig_Environment($loader, array('autoescape' => false)); $twig->addExtension(new \Twig_Extensions_Extension_I18n()); $twig->addExtension(new \Twig_Extensions_Extension_Date()); $formatBytesFilter = new \Twig_SimpleFilter('formatBytes', function ($string) { return \Podlove\format_bytes($string, 0); }); $padLeftFilter = new \Twig_SimpleFilter('padLeft', function ($string, $padChar, $length) { while (strlen($string) < $length) { $string = $padChar . $string; } return $string; }); $twig->addFilter($formatBytesFilter); $twig->addFilter($padLeftFilter); // add functions foreach (self::$template_tags as $tag) { $func = new \Twig_SimpleFunction($tag, function () use($tag) { return $tag(); }); $twig->addFunction($func); } $context = ['option' => $vars]; // add podcast to global context $context = array_merge($context, ['podcast' => new Podcast(Model\Podcast::get())]); // Apply filters to twig templates $context = apply_filters('podlove_templates_global_context', $context); // add podcast to global context if we are in an episode if ($episode = Model\Episode::find_one_by_property('post_id', get_the_ID())) { $context = array_merge($context, array('episode' => new Episode($episode))); } try { return $twig->render($html, $context); } catch (\Twig_Error $e) { $message = $e->getRawMessage(); $line = $e->getTemplateLine(); $template = $e->getTemplateFile(); \Podlove\Log::get()->addError($message, ['type' => 'twig', 'line' => $line, 'template' => $template]); } return ""; }
function page() { ?> <div class="wrap"> <div id="icon-options-general" class="icon32"></div> <h2><?php echo __('Podcast Settings'); ?> </h2> <form method="post" action="options.php"> <?php settings_fields(Podcast::$pagehook); ?> <?php $podcast = \Podlove\Model\Podcast::get_instance(); $form_attributes = array('context' => 'podlove_podcast', 'form' => false); \Podlove\Form\build_for($podcast, $form_attributes, function ($form) { $wrapper = new \Podlove\Form\Input\TableWrapper($form); $podcast = $form->object; $wrapper->string('title', array('label' => __('Title', 'podlove'), 'description' => __('', 'podlove'), 'html' => array('class' => 'regular-text required'))); $wrapper->string('subtitle', array('label' => __('Subtitle', 'podlove'), 'description' => __('The subtitle is used by iTunes.', 'podlove'), 'html' => array('class' => 'regular-text'))); $wrapper->text('summary', array('label' => __('Summary', 'podlove'), 'description' => __('A couple of sentences describing the podcast.', 'podlove'), 'html' => array('rows' => 5, 'cols' => 40))); $wrapper->string('slug', array('label' => __('Mnemonic', 'podlove'), 'description' => __('The abbreviation for your podcast. Commonly the initials of the title.', 'podlove'), 'html' => array('class' => 'regular-text required'))); $wrapper->image('cover_image', array('label' => __('Cover Art URL', 'podlove'), 'description' => __('JPEG or PNG. At least 1400 x 1400 pixels.', 'podlove'), 'html' => array('class' => 'regular-text'), 'image_width' => 300, 'image_height' => 300)); $wrapper->string('author_name', array('label' => __('Author Name', 'podlove'), 'description' => __('Publicly displayed in Podcast directories.', 'podlove'), 'html' => array('class' => 'regular-text'))); $wrapper->string('owner_name', array('label' => __('Owner Name', 'podlove'), 'description' => __('Used by iTunes and other Podcast directories to contact you.', 'podlove'), 'html' => array('class' => 'regular-text'))); $wrapper->string('owner_email', array('label' => __('Owner Email', 'podlove'), 'description' => __('Used by iTunes and other Podcast directories to contact you.', 'podlove'), 'html' => array('class' => 'regular-text'))); $wrapper->string('keywords', array('label' => __('Keywords', 'podlove'), 'description' => __('List of keywords. Separate with commas.', 'podlove'), 'html' => array('class' => 'regular-text'))); $wrapper->select('category_1', array('label' => __('iTunes Categories', 'podlove'), 'description' => '', 'type' => 'select', 'options' => \Podlove\Itunes\categories())); $wrapper->select('category_2', array('label' => '', 'description' => '', 'type' => 'select', 'options' => \Podlove\Itunes\categories())); $wrapper->select('category_3', array('label' => '', 'description' => '<br>' . __('For placement within the older, text-based browse system, podcast feeds may list up to 3 category/subcategory pairs. (For example, "Music" counts as 1, as does "Business > Careers.") For placement within the newer browse system based on Category links, however, and for placement within the Top Podcasts and Top Episodes lists that appear in the right column of most podcast pages, only the first category listed in the feed is used.') . ' (<a href="http://www.apple.com/itunes/podcasts/specs.html#category" target="_blank">http://www.apple.com/itunes/podcasts/specs.html#category</a>)', 'options' => \Podlove\Itunes\categories())); $wrapper->select('language', array('label' => __('Language', 'podlove'), 'description' => __('', 'podlove'), 'default' => get_bloginfo('language'), 'options' => \Podlove\Locale\locales())); $wrapper->select('explicit', array('label' => __('Explicit Content?', 'podlove'), 'description' => __('', 'podlove'), 'type' => 'checkbox', 'options' => array(0 => 'no', 1 => 'yes', 2 => 'clean'))); $wrapper->string('media_file_base_uri', array('label' => __('Media File Base URL', 'podlove'), 'description' => __('Example: http://cdn.example.com/pod/', 'podlove'), 'html' => array('class' => 'regular-text required'))); $artwork_options = array('0' => __('None', 'podlove'), 'manual' => __('Manual Entry', 'podlove')); $episode_assets = Model\EpisodeAsset::all(); foreach ($episode_assets as $episode_asset) { $file_type = $episode_asset->file_type(); if ($file_type && $file_type->type === 'image') { $artwork_options[$episode_asset->id] = sprintf(__('Media File: %s', 'podlove'), $episode_asset->title); } } $wrapper->select('supports_cover_art', array('label' => __('Episode Artwork Media File', 'podlove'), 'options' => $artwork_options)); }); ?> </form> </div> <?php }
public function get_current_episode($post_id) { if (get_post_status($post_id) !== 'publish' || get_post_type($post_id) !== 'podcast') { // If post is not public, 404 will be replied status_header(404); return; } $episode = \Podlove\Model\Episode::find_one_by_post_id($post_id); $podcast = \Podlove\Model\Podcast::get(); $permalink = get_permalink($post_id); $player_width = "560px"; $player_height = "140px"; return array('version' => '1.0', 'type' => 'rich', 'width' => $player_width, 'height' => $player_height, 'title' => $episode->full_title(), 'url' => get_permalink($post_id), 'author_name' => $podcast->full_title(), 'author_url' => site_url(), 'thumbnail_url' => $episode->cover_art_with_fallback()->url(), 'html' => '<iframe width="' . $player_width . '" height="' . $player_height . '" src="' . $permalink . (strpos($permalink, '?') === FALSE ? "?" : "&") . 'standalonePlayer"></iframe>'); }
/** * Fetch all Pocasts in the current list */ public function podcasts() { $podcasts = json_decode($this->podcasts); $podcast_objects = array(); foreach ($podcasts as $podcast) { switch ($podcast->type) { default: case 'wplist': $podcast_objects[] = Podcast::get($podcast->podcast); break; } } return $podcast_objects; }
public function getHtml() { if ($this->type == 'cc') { return "\n\t\t\t<div class=\"podlove_cc_license\">\n\t\t\t\t<img src=\"" . $this->getPictureUrl() . "\" alt=\"License\" />\n\t\t\t\t<p>\n\t\t\t\t\tThis work is licensed under a <a rel=\"license\" href=\"" . $this->getUrl() . "\">" . $this->getName() . "</a>.\n\t\t\t\t</p>\n\t\t\t</div>"; } if ($this->type == 'other') { return "\n\t\t\t<div class=\"podlove_license\">\n\t\t\t\t<p>\n\t\t\t\t\t" . sprintf(__('This work is licensed under the %s license.', 'podlove'), '<a rel="license" href="' . $this->url . '">' . $this->name . '</a>') . "\n\t\t\t\t</p>\n\t\t\t</div>"; } // episodes fall back to podcast licenses if ($this->scope == 'episode') { return Podcast::get()->get_license_html(); } // ... otherwise, a license is missing return "\n\t\t<div class=\"podlove_license\">\n\t\t\t\t<p style='color: red;'>\n\t\t\t\t\t" . __('This work is (not yet) licensed, as no license was chosen.', 'podlove') . "\n\t\t\t\t</p>\n\t\t</div>"; }
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); }
/** * Fetch all podcasts for Publisher blogs, ordered */ public static function podcasts($sortby = "title", $sort = 'ASC') { $podcast_blog_ids = Network::podcast_blog_ids(); if (empty($podcast_blog_ids)) { return []; } foreach ($podcast_blog_ids as $blog_id) { $podcasts[$blog_id] = Podcast::get($blog_id); } uasort($podcasts, function ($a, $b) use($sortby, $sort) { return strnatcmp($a->{$sortby}, $b->{$sortby}); }); if ($sort == 'DESC') { krsort($podcasts); } return $podcasts; }
public function register_page() { $podcast = \Podlove\Model\Podcast::get(); $form_attributes = array('context' => 'podlove_podcast', 'action' => $this->get_url(), 'is_table' => false); ?> <p> <?php echo sprintf(__('These are the current social media acccount of your podcast. Display this list using the shortcode %s', 'podlove'), '<code>[podlove-podcast-social-media-list]</code>'); ?> </p> <?php \Podlove\Form\build_for($podcast, $form_attributes, function ($form) { $wrapper = new \Podlove\Form\Input\DivWrapper($form); $podcast = $form->object; $wrapper->callback('services', array('callback' => array(__CLASS__, 'podcast_form_extension_form'))); }); }
public function column_limit($feed) { // FIXME: Feeds verschwinden beim Speichern!!! $podlove_feed_limit = \Podlove\Model\Podcast::get()->limit_items; switch ($feed->limit_items) { case '0': return get_option('posts_per_rss') . ' (WordPress default)'; break; case '-1': return 'unlimited'; break; case '-2': return ($podlove_feed_limit == '-1' ? 'unlimited' : ($podlove_feed_limit == '0' ? get_option('posts_per_rss') . ' (WordPress default)' : $podlove_feed_limit)) . ' (global default)'; break; default: return $feed->limit_items; break; } }
public function register_page() { $podcast = \Podlove\Model\Podcast::get(); $form_attributes = array('context' => 'podlove_podcast', 'action' => $this->get_url()); ?> <p> <?php echo __('The Podlove Publisher expects all your media files to be in the same <strong>Upload Location</strong>. It should be a publicly readable directory containing all media files. You should not create a separate directory for each episode.', 'podlove'); ?> </p> <?php \Podlove\Form\build_for($podcast, $form_attributes, function ($form) { $wrapper = new \Podlove\Form\Input\TableWrapper($form); $podcast = $form->object; $wrapper->string('media_file_base_uri', array('label' => __('Upload Location', 'podlove'), 'description' => __('Example: http://cdn.example.com/pod/', 'podlove'), 'html' => array('class' => 'regular-text required podlove-check-input', 'data-podlove-input-type' => 'url'))); }); }
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); }
/** * 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; }); }
private static function get_styles() { ob_start(); $podcast_ids = self::podcast_ids(); $podcast_ids = array_filter($podcast_ids, function ($id) { return Podcast::get($id)->has_cover_art(); }); $blavatar_classes = implode(", ", array_map(function ($id) { return "#wp-admin-bar-blog-{$id} .blavatar"; }, $podcast_ids)); $blavatar_before_classes = implode(", ", array_map(function ($id) { return "#wpadminbar .quicklinks li#wp-admin-bar-blog-{$id} .blavatar:before"; }, $podcast_ids)); $cover_styles = implode("\n", array_map(function ($id) { return "#wp-admin-bar-blog-{$id} .blavatar { background-image: url(" . Podcast::get($id)->cover_art()->setWidth(40)->url() . "); }"; }, $podcast_ids)); echo $cover_styles; echo $blavatar_classes; ?> { background-size: 100% 100%; margin-right: 5px; width: 20px; height: 20px; position: relative; top: 4px; left: -3px; } <?php echo $blavatar_before_classes; ?> { content: none; } <?php $html = ob_get_contents(); ob_end_clean(); return $html; }
public static function content() { global $wpdb; $sql = "\n\t\tSELECT\n\t\t\tp.post_status,\n\t\t\tmf.episode_id,\n\t\t\tmf.episode_asset_id,\n\t\t\tmf.size,\n\t\t\tmf.id media_file_id\n\t\tFROM\n\t\t\t`" . Model\MediaFile::table_name() . "` mf\n\t\t\tJOIN `" . Model\Episode::table_name() . "` e ON e.id = mf.`episode_id`\n\t\t\tJOIN `" . $wpdb->posts . "` p ON e.`post_id` = p.`ID`\n\t\tWHERE\n\t\t\tp.`post_type` = 'podcast'\n\t\t\tAND p.post_status in ('private', 'draft', 'publish', 'pending', 'future')\n\t\t"; $rows = $wpdb->get_results($sql, ARRAY_A); $media_files = []; foreach ($rows as $row) { if (!isset($media_files[$row['episode_id']])) { $media_files[$row['episode_id']] = ['post_status' => $row["post_status"]]; } $media_files[$row['episode_id']][$row['episode_asset_id']] = ['size' => $row['size'], 'media_file_id' => $row['media_file_id']]; } $podcast = Model\Podcast::get(); $episodes = $podcast->episodes(); $assets = Model\EpisodeAsset::all(); $header = [__('Episode', 'podlove')]; foreach ($assets as $asset) { $header[] = $asset->title; } $header[] = __('Status', 'podlove'); \Podlove\load_template('settings/dashboard/file_validation', ['episodes' => $episodes, 'assets' => $assets, 'media_files' => $media_files, 'header' => $header]); }
public function register_page() { $podcast = \Podlove\Model\Podcast::get(); $form_attributes = array('context' => 'podlove_podcast', 'action' => $this->get_url()); ?> <p> <?php echo __('These are the three most important fields describing your podcast. <strong>Title</strong> is the title of the podcast that is the primary field to be used to represent the podcast in directories, lists and other uses. The <strong>subtitle</strong> is an extension to the title. The subtitle is meant to clarify what the podcast is about. While a title can be anything, a subtitle should be more descriptive in what the content actually wants to convey and what the most important information is, you want everybody want to know about the offering. A <strong>summary</strong> is a much more precise and elaborate description of the podcast\'s content. While title and subtitle are rather concise, a summary is meant to consist of one or more sentences that form a paragraph or more.', 'podlove'); ?> </p> <?php \Podlove\Form\build_for($podcast, $form_attributes, function ($form) { $wrapper = new \Podlove\Form\Input\TableWrapper($form); $podcast = $form->object; $wrapper->string('title', array('label' => __('Title', 'podlove'), 'html' => array('class' => 'regular-text required podlove-check-input'))); $wrapper->string('subtitle', array('label' => __('Subtitle', 'podlove'), 'description' => __('Extension to the title. Clarify what the podcast is about.', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input'))); $wrapper->text('summary', array('label' => __('Summary', 'podlove'), 'description' => __('Elaborate description of the podcast\'s content.', 'podlove'), 'html' => array('rows' => 3, 'cols' => 40, 'class' => 'autogrow podlove-check-input'))); $wrapper->upload('cover_image', array('label' => __('Cover Art URL', 'podlove'), 'description' => __('JPEG or PNG. At least 1400 x 1400 pixels.', 'podlove'), 'html' => array('class' => 'regular-text podlove-check-input', 'data-podlove-input-type' => 'url'), 'media_button_text' => __("Use for Podcast Cover Art", 'podlove'))); $wrapper->select('language', array('label' => __('Language', 'podlove'), 'description' => __('', 'podlove'), 'default' => get_bloginfo('language'), 'options' => \Podlove\Locale\locales())); }); }
/** * Fetch form data for EpisodeAssets multiselect. * * @param \Podlove\Model\Episode $episode * @return array */ public static function episode_assets_form($episode) { $episode_assets = Model\EpisodeAsset::all(); // field to generate option list $asset_options = array(); // values for option list $asset_values = array(); foreach ($episode_assets as $asset) { if (!($file_type = $asset->file_type())) { continue; } // get formats configured for this show $asset_options[$asset->id] = $asset->title; // find out which formats are active $asset_values[$asset->id] = NULL !== Model\MediaFile::find_by_episode_id_and_episode_asset_id($episode->id, $asset->id); } // FIXME: empty checkbox -> no file id // solution: when one checks the box, an AJAX request has to create and validate the file $episode_assets_form = array('label' => __('Media Files', 'podlove'), 'description' => '', 'options' => $asset_options, 'default' => true, 'multi_values' => $asset_values, 'before' => function () { ?> <table class='media_file_table' border="0" cellspacing="0"> <tr> <th><?php echo __('Enable', 'podlove'); ?> </th> <th><?php echo __('Asset', 'podlove'); ?> </th> <th><?php echo __('Asset File Name', 'podlove'); ?> </th> <th><?php echo __('Filesize', 'podlove'); ?> </th> <th><?php echo __('Status', 'podlove'); ?> </th> <th></th> </tr> <?php }, 'after' => function () { ?> </table> <p> <span class="description"> <?php echo __('Media File Base URL', 'podlove') . ': ' . \Podlove\Model\Podcast::get()->media_file_base_uri; ?> </span> </p> <?php }, 'around_each' => function ($callback) { ?> <tr class="media_file_row"> <td class="enable"> </td> <td class="asset"> <?php call_user_func($callback); ?> </td> <td class="url"></td> <td class="size"></td> <td class="status"></td> <td class="update"></td> </tr> <?php }, 'multiselect_callback' => function ($asset_id) use($episode) { $asset = \Podlove\Model\EpisodeAsset::find_by_id($asset_id); $format = $asset->file_type(); $file = \Podlove\Model\MediaFile::find_by_episode_id_and_episode_asset_id($episode->id, $asset->id); $size = is_object($file) ? (int) $file->size : 0; if ($size === 1) { $size = "unknown"; } $attributes = array('data-template' => \Podlove\Model\Podcast::get()->get_url_template(), 'data-size' => $size, 'data-episode-asset-id' => $asset->id, 'data-episode-id' => $episode->id, 'data-file-url' => is_object($file) ? $file->get_file_url() : ''); if ($file) { $attributes['data-id'] = $file->id; } $out = ''; foreach ($attributes as $key => $value) { $out .= sprintf('%s="%s" ', $key, $value); } return $out; }); if (empty($asset_options)) { $episode_assets_form['description'] = sprintf('<span style="color: red">%s</span>', __('You need to configure assets for this show. No assets, no fun.', 'podlove')) . ' ' . sprintf('<a href="%s">%s</a>', admin_url('admin.php?page=podlove_episode_assets_settings_handle'), __('Configure Assets', 'podlove')); } return $episode_assets_form; }
/** * Render player * * @param string $context (optional) player context for tracking. Set to NULL for auto-detection. Default: NULL * @return string */ public function render($context = NULL) { if (count($this->player_format_assignments) == 0) { return ''; } // build main audio/video tag $xml = new \SimpleXMLElement('<' . $this->get_media_tag() . '/>'); $xml->addAttribute('id', $this->get_html_id()); $xml->addAttribute('controls', 'controls'); $xml->addAttribute('preload', 'none'); $width = strtolower(trim($this->get_webplayer_setting($this->get_media_tag(), 'width'))); $height = strtolower(trim($this->get_webplayer_setting($this->get_media_tag(), 'height'))); if ($this->is_video) { $xml->addAttribute('poster', $this->episode->cover_art_with_fallback()->url()); $xml->addAttribute('width', $width); $xml->addAttribute('height', $height); } else { $xml->addAttribute('style', sprintf('width: %s; height: %s', empty($width) || $width == 'auto' ? '100%' : $width . 'px', empty($height) ? '30px' : $height)); } // get all relevant info about media files $media_files = array(); foreach ($this->files as $file) { $asset = $file->episode_asset(); $mime = $asset->file_type()->mime_type; $media_files[$mime] = array('file' => $file, 'mime_type' => $mime, 'url' => $file->get_file_url(), 'publicUrl' => $file->get_public_file_url("webplayer", is_null($context) ? $this->get_tracking_context() : $context), 'assetTitle' => $asset->title()); } if (!count($media_files)) { return ""; } // sort files bases on mime type so preferred get output first $sorted_files = array(); $preferred_order = array('audio/mp4', 'audio/aac', 'audio/opus', 'audio/ogg', 'audio/vorbis'); foreach ($preferred_order as $order_key) { if (isset($media_files[$order_key]) && $media_files[$order_key]) { $sorted_files[] = $media_files[$order_key]; unset($media_files[$order_key]); } } foreach ($media_files as $file) { $sorted_files[] = $file; } // add all sources $flash_fallback_func = function (&$xml) { }; foreach ($sorted_files as $file) { $mime_type = $file['mime_type']; $source = $xml->addChild('source'); $source->addAttribute('src', $file['publicUrl']); $source->addAttribute('type', $mime_type); if ($mime_type == 'audio/mpeg') { $flash_fallback_func = function (&$xml) use($file) { $flash_fallback = $xml->addChild('object'); $flash_fallback->addAttribute('type', 'application/x-shockwave-flash'); $flash_fallback->addAttribute('data', plugins_url('player/podlove-web-player/static/', __FILE__) . 'flashmediaelement.swf'); $params = array(array('name' => 'movie', 'value' => plugins_url('player/podlove-web-player/static/', __FILE__) . 'flashmediaelement.swf'), array('name' => 'flashvars', 'value' => 'controls=true&file=' . $file['url'])); foreach ($params as $param) { $p = $flash_fallback->addChild('param'); $p->addAttribute('name', $param['name']); $p->addAttribute('value', $param['value']); } }; } } // add flash fallback after all <source>s $flash_fallback_func($xml); // prettify and prepare to render $xml_string = $xml->asXML(); $xml_string = $this->format_xml($xml_string); $xml_string = $this->remove_xml_header($xml_string); // get podcast object $podcast = Podcast::get(); if ($this->episode->license_name && $this->episode->license_url) { $license_name = $this->episode->license_name; $license_url = $this->episode->license_url; } else { $license_name = $podcast->license_name; $license_url = $podcast->license_url; } // set JavaScript options $truthy = array(true, 'true', 'on', 1, "1"); $init_options = array('pluginPath' => plugins_url('player/podlove-web-player/static/', __FILE__), 'alwaysShowHours' => true, 'alwaysShowControls' => true, 'timecontrolsVisible' => false, 'summaryVisible' => false, 'hidetimebutton' => in_array($this->get_webplayer_setting('buttons_time'), $truthy, true), 'hidedownloadbutton' => in_array($this->get_webplayer_setting('buttons_download'), $truthy, true), 'hidesharebutton' => in_array($this->get_webplayer_setting('buttons_share'), $truthy, true), 'sharewholeepisode' => in_array($this->get_webplayer_setting('buttons_sharemode'), $truthy, true), 'loop' => false, 'chapterlinks' => 'all', 'permalink' => get_permalink($this->post->ID), 'title' => get_the_title($this->post->ID), 'subtitle' => wptexturize(convert_chars(trim($this->episode->subtitle))), 'summary' => nl2br(wptexturize(convert_chars(trim($this->episode->summary)))), 'publicationDate' => mysql2date("c", $this->post->post_date), 'poster' => $this->episode->cover_art_with_fallback()->setWidth(200)->url(), 'showTitle' => $podcast->title, 'showSubtitle' => $podcast->subtitle, 'showSummary' => $podcast->summary, 'showPoster' => $podcast->cover_art()->setWidth(200)->url(), 'show' => array('title' => $podcast->title, 'subtitle' => $podcast->subtitle, 'summary' => $podcast->summary, 'poster' => $podcast->cover_art()->setWidth(200)->url(), 'url' => \Podlove\get_landing_page_url()), 'license' => array('name' => $license_name, 'url' => $license_url), 'downloads' => array_map(function ($mf) { return array('assetTitle' => $mf['assetTitle'], 'downloadUrl' => $mf['publicUrl'], 'url' => $mf['url']); }, array_values($sorted_files)), 'duration' => $this->episode->get_duration(), 'chaptersVisible' => in_array(\Podlove\get_webplayer_setting('chaptersVisible'), $truthy, true), 'features' => array("current", "progress", "duration", "tracks", "fullscreen", "volume")); if ($chapters = $this->episode->get_chapters('json')) { $init_options['chapters'] = json_decode($chapters); } $xml_string .= "\n" . "\n<script>\n" . "jQuery('#" . $this->get_html_id() . "').podlovewebplayer(" . json_encode($init_options) . ");" . "\n</script>\n"; return $xml_string; }
public static function button($args) { return (new Button(Model\Podcast::get()))->render($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 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; } }
public function cover_art() { return $this->with_blog_scope(function () { $podcast = Podcast::get(); $asset_assignment = AssetAssignment::get_instance(); if (!$asset_assignment->image) { return false; } if ($asset_assignment->image == 'manual') { $cover_art = trim($this->cover_art); if (empty($cover_art)) { return false; } else { return new Image($cover_art, $this->title()); } } $cover_art_file_id = $asset_assignment->image; if (!($asset = EpisodeAsset::find_one_by_id($cover_art_file_id))) { return false; } if (!($file = MediaFile::find_by_episode_id_and_episode_asset_id($this->id, $asset->id))) { return false; } return $file->size > 0 ? new Image($file->get_file_url(), $this->title()) : false; }); }
public function podcast() { $podcast = Model\Podcast::get(); $podcast_data = array(); foreach ($podcast->property_names() as $property) { $podcast_data[$property] = $podcast->{$property}; } self::respond_with_json($podcast_data); }