Skip to content

Instantly share code, notes, and snippets.

@JiveDig
Created July 30, 2024 20:02
Show Gist options
  • Save JiveDig/f6ea19a80b569da390c6294c544bb6df to your computer and use it in GitHub Desktop.
Save JiveDig/f6ea19a80b569da390c6294c544bb6df to your computer and use it in GitHub Desktop.
Custom WP feed for MSN News
<?php
// Prevent direct file access.
defined( 'ABSPATH' ) || die;
/**
* Add MSN News feed.
* Flush permalinks after adding this feed.
*
* @return void
*/
// add_action( 'after_setup_theme', function() {
// if ( ! class_exists( 'Mai_MSN_Feed' ) ) {
// return;
// }
//
// new Mai_MSN_Feed(
// [
// 'slug' => 'msn-news', // The feed name.
// 'credits' => true, // Show "This post originally appeared on" credits.
// 'query_args' => [
// 'posts_per_page' => 36,
// 'meta_query' => [
// [
// 'relation' => 'OR',
// [
// 'key' => 'tvn_source_url',
// 'compare' => 'NOT EXISTS',
// ],
// [
// 'key' => 'tvn_source_url',
// 'value' => '',
// 'compare' => '=',
// ],
// ],
// ],
// ],
// ]
// );
// });
/**
* Custom MSN RSS feed class.
*
* @version 1.0.0
*
* @link (specs) https://helpcenter.microsoftstart.com/kb/feed-specifications
* @link (old validator) https://feeds.msn.com/evaluation
* @link (new validator) https://www.msn.com/en-us/partnerhub/management/feed/list
*/
class Mai_MSN_Feed {
protected $args;
/**
* Construct the class.
*/
function __construct( $args ) {
$this->args = wp_parse_args( $args,
[
'slug' => 'msn-news', // The feed name.
'credits' => true, // Add credits to the feed.
'duration' => 'hourly', // Default 'hourly'. Accepts 'hourly', 'daily', 'weekly', 'monthly', 'yearly'.
'frequency' => 1, // Default '1'. The frequency of RSS updates within the update period.
'ttl' => 15, // Time to live in minutes.
]
);
$this->args['query_args'] = wp_parse_args( $this->args['query_args'],
[
'post_type' => 'post',
'post_status' => 'publish',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
]
);
// Run hooks.
$this->hooks();
}
/**
* Add hooks.
*
* @since 1.0.0
*
* @return void
*/
function hooks() {
add_feed( $this->args['slug'], [ $this, 'render_feed' ] );
}
/**
* Custom RSS feed callback.
*
* @since 1.0.0
*
* @return void
*/
function render_feed() {
/**
* Feed header.
*/
header( 'Content-Type: ' . feed_content_type( 'rss-http' ) . '; charset=' . get_option( 'blog_charset' ), true );
/**
* Start RSS feed.
*/
echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?' . '>';
?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
>
<channel>
<title><?php bloginfo_rss( 'name' ); ?></title>
<link><?php bloginfo_rss( 'url' ); ?></link>
<description><?php bloginfo_rss( 'description' ); ?></description>
<lastBuildDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_lastpostmodified( 'GMT' ), false ); ?></lastBuildDate>
<language><?php bloginfo_rss( 'language' ); ?></language>
<copyright><?php echo get_bloginfo( 'name' ); ?></copyright>
<ttl><?php echo $this->args['ttl']; ?></ttl>
<sy:updatePeriod><?php echo apply_filters( 'rss_update_period', $this->args['duration'] ); ?></sy:updatePeriod>
<sy:updateFrequency><?php echo apply_filters( 'rss_update_frequency', $this->args['frequency'] ); ?></sy:updateFrequency>
<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
<?php do_action( 'rss2_head' ); ?>
<?php
$query = new WP_Query( $this->args['query_args'] );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) : $query->the_post();
?>
<item>
<title><?php the_title_rss(); ?></title>
<link><?php the_permalink_rss(); ?></link>
<guid isPermaLink="false"><?php the_guid(); ?></guid>
<dc:creator><?php the_author(); ?></dc:creator>
<pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true, get_the_ID() ), false ); ?></pubDate>
<dcterms:modified><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_modified_time( 'Y-m-d H:i:s', true, get_the_ID() ), false ); ?></dcterms:modified>
<?php
echo $this->get_categories();
echo $this->get_thumbnail();
?>
<description><![CDATA[
<?php
ob_start();
the_excerpt_rss();
$excerpt = ob_get_clean();
echo $this->get_content( $excerpt );
echo $this->get_credits();
?>
]]></description>
<content:encoded><![CDATA[
<?php
echo $this->get_content( get_the_content_feed( 'msn-news' ) );
echo $this->get_credits();
?>
]]></content:encoded>
</item>
<?php
endwhile;
}
wp_reset_postdata();
?>
</channel>
</rss>
<?php
}
/**
* Get categories for the feed.
* Comma-separated list of categories.
*
* @since 1.0.0
*
* @return string
*/
function get_categories() {
$categories = [];
$cats = get_the_terms( get_the_ID(), 'category' );
// Add categories.
if ( $cats && ! is_wp_error( $cats ) ) {
foreach ( $cats as $cat ) {
$categories[] = $cat->name;
}
}
return $categories ? sprintf( '<category>%s</category>', implode( ',', $categories ) ) : '';
}
/**
* Get the post thumbnail for the feed.
*
* @since 1.0.0
*
* @return string
*/
function get_thumbnail() {
$thumbnail = '';
$image_id = get_post_thumbnail_id();
$image = $image_id ? image_get_intermediate_size( $image_id, 'landscape-md' ) : '';
$thumbnail = $image ? sprintf( '<p><img src="%s" class="type:primaryImage" /></p>', $image['url'] ) : '';
return $thumbnail;
}
/**
* Handles content cleanup for MSN feeds.
*
* @since 1.0.0
*
* @param string $content The HTML content.
* @param string $feed The feed name.
*
* @return string
*/
function get_content( $content ) {
if ( ! $content || ! function_exists( 'mai_get_dom_document' ) || ! function_exists( 'mai_get_dom_html' ) ) {
return $content;
}
// Set up DOMDocument.
$dom = mai_get_dom_document( $content );
$xpath = new DOMXPath( $dom );
// Get all paragraphs.
$elements = $xpath->query( '/p' );
// If we have paragraphs.
if ( $elements->length ) {
// Loop through each paragraph.
foreach ( $elements as $node ) {
// Loop through each child node.
foreach ( $node->childNodes as $child ) {
// Skip if not a link.
if ( 'a' !== $child->nodeName ) {
continue;
}
// Replace link with span.
$new = $dom->createElement( 'span', $child->textContent );
$new->setAttribute( 'class', 'link-removed' );
$child->parentNode->replaceChild( $new, $child );
}
}
}
// Get figure/div wrappers to remove from twitter embeds.
$elements = $xpath->query( '//figure[contains(concat(" ", normalize-space(@class), " "), " wp-block-embed-twitter ")]' );
// If we have elements.
if ( $elements->length ) {
// Loop through each element.
foreach ( $elements as $node ) {
// Get the blockquote.
$blockquote = $node->getElementsByTagName( 'blockquote' )->item(0);
// If we have a blockquote, replace the node with it.
if ( $blockquote ) {
$node->parentNode->replaceChild( $blockquote, $node );
}
}
}
// Get the HTML content.
$content = mai_get_dom_html( $dom );
return $content;
}
/**
* Get the credits for the feed.
*
* @since 1.0.0
*
* @return string
*/
function get_credits() {
// Bail if credits are disabled.
if ( ! $this->args['credits'] ) {
return '';
}
return sprintf( '<p>The post <a href="%s">%s</a> originally published on <a href="%s">%s</a>.</p>', get_permalink(), get_the_title(), home_url(), get_bloginfo( 'name' ) );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment