Featured Image For Courses
Currently, we already have thumbnail & featured image for Space.
But for course, we only have thumbnail option, no featured image option here.
I think we can also have Featured Image option also for Course, that will be displayed at the top of single course page, just like the space banner displayed at the top.
That will make the single course page better.
Thankyou!
We are already on the process, the work is in the initial phase.
Tawsif Ahmed RiyadΒ perfect and thankyou
Tawsif Ahmed RiyadΒ brilliant! ... finally! ... thanks for the update!
I build a snippet to use until itβs official. You can see it used here:
/**
* Course Cover Hero
*
* Injects the course cover_photo as a full-bleed hero banner above
* the course intro β identical in style to FC's space cover images.
*
* Route: /course/{slug}/lessons
* Target: .fcom_course_intro (prepends the hero above it)
*
* KILL SWITCH: set MN_FLAG_COURSE_COVER_HERO to false in MN_Config
* when FluentCommunity ships this natively. Zero residue β snippet
* stays inactive, no DOM changes.
*
* Depends on: MN_FLAG_COURSE_COVER_HERO constant (MN_Config)
*/
if ( ! defined( 'ABSPATH' ) ) exit;
// ββ Kill switch β flip to false in MN_Config when FC ships natively ββ
if ( defined( 'MN_FLAG_COURSE_COVER_HERO' ) && ! MN_FLAG_COURSE_COVER_HERO ) {
return;
}
// ββ AJAX: return cover_photo URL for a course slug ββββββββββββββββββββ
add_action( 'wp_ajax_mn_get_course_cover', function () {
// No nonce needed β public read, no state change
global $wpdb;
$slug = sanitize_text_field( $_POST['slug'] ?? '' );
if ( ! $slug ) wp_send_json_error( 'Missing slug.' );
$row = $wpdb->get_row( $wpdb->prepare(
"SELECT cover_photo FROM {$wpdb->prefix}fcom_spaces
WHERE slug = %s AND type = 'course' LIMIT 1",
$slug
) );
if ( ! $row || empty( $row->cover_photo ) ) {
wp_send_json_error( 'No cover.' );
}
wp_send_json_success( [ 'cover' => esc_url( $row->cover_photo ) ] );
} );
add_action( 'wp_ajax_nopriv_mn_get_course_cover', function () {
global $wpdb;
$slug = sanitize_text_field( $_POST['slug'] ?? '' );
if ( ! $slug ) wp_send_json_error( 'Missing slug.' );
$row = $wpdb->get_row( $wpdb->prepare(
"SELECT cover_photo FROM {$wpdb->prefix}fcom_spaces
WHERE slug = %s AND type = 'course' LIMIT 1",
$slug
) );
if ( ! $row || empty( $row->cover_photo ) ) {
wp_send_json_error( 'No cover.' );
}
wp_send_json_success( [ 'cover' => esc_url( $row->cover_photo ) ] );
} );
// ββ Frontend ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
add_action( 'fluent_community/portal_footer', function () {
$ajax = admin_url( 'admin-ajax.php' );
?>
<style>
/* ββ Hero wrapper β mirrors .object_header / .object_cover ββ */
#mn-course-hero {
position: relative;
width: 100%;
/* 16:5 ratio matches FC space banners on desktop */
aspect-ratio: 16 / 5;
overflow: hidden;
background: var(--fcom-secondary-bg, #f0f0f1);
/* Sits flush above .fcom_course_intro which has its own padding */
margin-bottom: 0;
}
#mn-course-hero img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center center;
}
/* Subtle bottom fade so the hero blends into the page bg */
#mn-course-hero::after {
content: '';
position: absolute;
inset: auto 0 0 0;
height: 40%;
background: linear-gradient(
to bottom,
transparent 0%,
var(--fcom-primary-bg, #fff) 100%
);
pointer-events: none;
}
/* Skeleton shimmer while loading */
#mn-course-hero.mn-hero-loading {
background: linear-gradient(
90deg,
var(--fcom-secondary-bg, #eee) 25%,
var(--fcom-active-bg, #e0e0e0) 50%,
var(--fcom-secondary-bg, #eee) 75%
);
background-size: 200% 100%;
animation: mn-hero-shimmer 1.4s infinite;
}
@keyframes mn-hero-shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* Mobile β taller ratio on narrow screens, matches FC space cover behaviour */
<a data-user_name="media" class="fcom_mention fcom_route" href="https://community.wpmanageninja.com/portal/u/media/">Media Rocketeer</a> (max-width: 640px) {
#mn-course-hero {
aspect-ratio: 16 / 7;
}
/* FC removes the gradient fade on mobile since it's shorter */
#mn-course-hero::after {
height: 30%;
}
}
</style>
<script>
(function () {
var AJAX_URL = '<?php echo esc_js( $ajax ); ?>';
var injected = false;
var lastPath = location.pathname;
var lastSlug = '';
// ββ Route guard βββββββββββββββββββββββββββββββββββββββββββ
function getCourseSlug() {
// Matches /course/{slug}/lessons AND /course/{slug}/lessons/
var m = location.pathname.match( /\/course\/([^/?#\/]+)\/lessons/ );
return m ? m[1] : null;
}
// ββ Build hero element ββββββββββββββββββββββββββββββββββββ
function buildHero() {
var el = document.createElement( 'div' );
el.id = 'mn-course-hero';
el.className = 'mn-hero-loading';
return el;
}
// ββ Find the insertion anchor βββββββββββββββββββββββββββββ
// FC renders .fcom_course_intro inside a content wrapper.
// We prepend the hero as the first sibling before it.
function getAnchor() {
return document.querySelector( '.fcom_course_intro' )
|| document.querySelector( '.fcom_content.fcom_course_content' )
|| document.querySelector( '.fcom_course_wrap' );
}
// ββ Fetch cover and hydrate βββββββββββββββββββββββββββββββ
function fetchAndHydrate( slug, heroEl ) {
var body = new URLSearchParams( {
action : 'mn_get_course_cover',
slug : slug,
} );
fetch( AJAX_URL, {
method : 'POST',
credentials : 'same-origin',
body : body,
} )
.then( function (r) { return r.json(); } )
.then( function (res) {
heroEl.classList.remove( 'mn-hero-loading' );
if ( ! res.success ) {
// No cover photo β remove the placeholder entirely
heroEl.remove();
return;
}
var img = document.createElement( 'img' );
img.src = res.data.cover;
img.alt = '';
img.loading = 'lazy';
// If image fails to load, remove the hero rather than showing a broken slot
img.onerror = function () { heroEl.remove(); };
heroEl.appendChild( img );
} )
.catch( function () {
heroEl.remove();
} );
}
// ββ Inject ββββββββββββββββββββββββββββββββββββββββββββββββ
function tryInject() {
if ( document.getElementById( 'mn-course-hero' ) ) {
injected = true;
return;
}
var slug = getCourseSlug();
if ( ! slug ) return;
var anchor = getAnchor();
if ( ! anchor ) return;
var hero = buildHero();
// Insert immediately before .fcom_course_intro (or its parent)
anchor.parentNode.insertBefore( hero, anchor );
fetchAndHydrate( slug, hero );
lastSlug = slug;
injected = true;
}
// ββ SPA-aware interval loop βββββββββββββββββββββββββββββββ
setInterval( function () {
var currentPath = location.pathname;
if ( currentPath !== lastPath ) {
lastPath = currentPath;
injected = false;
lastSlug = '';
var old = document.getElementById( 'mn-course-hero' );
if ( old ) old.remove();
}
if ( ! injected && getCourseSlug() ) {
tryInject();
}
}, 800 );
})();
</script>
<?php
} );
Jacob WindhamΒ thanks a lot!!!
Jacob WindhamΒ This is cool. I'm going to try it out. :)