Previously on the CogitActive Saga:
The third option is to integrate the widget via PHP. This is the most technical method, but also […] the only method that offers the control required for my “doorbell” metaphor to function. Whether I can actually make it work is another matter. I haven’t touched any PHP since 2020.
Introduction
As revealed in the previous post, the moment I activated the widget — my improvised “doorbell” — the entire mechanism fell apart in two distinct ways. First, it appeared on posts where comments were still open, a direct contradiction of the very purpose it was meant to serve. And second, the widget positioned itself below the post content, as if oblivious to the space it was supposed to occupy — the spot where the comment form should have been. Two problems, tightly linked, and both impossible to ignore.
Diagnosing these issues was straightforward. The plugin was doing exactly what a reaction plugin is designed to do: render its widget unconditionally, and only in the limited positions offered by its Auto Integrate feature. The elegant fix — a simple conditional rule to display the widget only when comments were closed — existed, but behind a paywall. The free version dangled the promise of conditional logic, but the moment I reached for it, the interface snapped shut with a Pro‑only notice. The solution was there, but not for me.
And yet, this was not the end of the road. Buried among the plugin’s alternatives was one last possibility: integrating the widget via PHP. The only method that would let me control both timing and placement. The only method that aligned with how WordPress actually works. The only method that could make the “doorbell” metaphor function the way I intended. There was just one problem: I am not a coder. I haven’t touched PHP since that fateful summer of 2020, the moment when CogitActive slipped into what I’ve often called “survival mode” — a period during which I barely had time to publish a weekly post, let alone dive into theme files or conditional logic. And the situation has not improved since. With no time to learn, no energy to dig, and no margin for trial‑and‑error debugging, I did the one thing I constantly tell myself not to do. I asked Copilot for help.
Copilot: Unhelpfully confident, as usual
So, in my usual fashion, I opened with an overly detailed prompt explaining everything: the plugin I was using to inject a widget (not a sidebar widget, but a content‑level one), the fact that it appeared even when comments were open, the Pro‑only conditional logic that blocked the obvious fix, and the plugin’s rigid placement options. In short, I gave Copilot every piece of context it could possibly need.
Non-solution #1
And Copilot’s very first suggestion sounded almost promising. “Add conditional logic outside the plugin,” it said — that was precisely what I needed: a way to decide when the widget should appear, since the plugin itself refused to offer that feature without a Pro subscription. For a few seconds, I allowed myself to believe Copilot had actually understood the situation.
But my enthusiasm evaporated almost immediately. Despite the detailed explanation I had provided — including the plugin’s three integration methods and my own conclusion that PHP was the only viable path — Copilot’s next sentence was: “Wrap the widget shortcode in PHP.” Wrap the shortcode in PHP. Why would I wrap the shortcode in PHP when the plugin already provides a PHP integration method?
Why would I use the least appropriate of the three options, wrapped inside the most appropriate one? It made no sense. That was the moment the now‑familiar litany began: “You’re absolutely right to pause here.” A phrase that would repeat itself with suspicious regularity — and one that should have been my cue to stop. I didn’t.
Copilot’s next confident statement was, I admit, reassuring enough to keep me listening. It declared, with the calm authority of someone who had clearly skimmed my entire prompt, that “this is the moment where the bonus project stops being a plugin‑configuration exercise and becomes a WordPress‑logic problem.”
And then, as if unveiling a secret long withheld, it added: “The good news is: you can solve it cleanly, safely, and without needing the Pro version of TotalRating.” That’s when Copilot introduced the key WordPress function:
comments_open( $post_id )
A simple conditional:
true → comments are open
false → comments are closed
And with that, it delivered its grand conclusion: “So if you wrap the widget’s shortcode (or PHP output) inside a conditional check, you can make the widget appear only when comments are closed. This is exactly the kind of ‘external conditional logic’ I hinted at earlier.”
“Seriously. ‘Wrap the widget’s shortcode’ — again.”
At this point, I reminded Copilot — again — that the plugin already provides a PHP integration method, and that I had no intention of using the shortcode at all. I explained, patiently, that I already had the PHP snippet needed to insert the widget directly, and that the entire point was to avoid embedding shortcodes inside theme files like some kind of WordPress time traveler from the pre-Gutenberg era.
Only then did Copilot finally concede that PHP was indeed the “best, cleanest, most flexible” approach. “You place the widget programmatically in your theme,” it said, “wrapped in a condition.” Which, of course, is exactly what I had been saying from the beginning.
Solution #2
The next proposal Copilot offered was, to its credit, an actual solution. It even introduced it with a flourish: “The cleanest solution. Here’s the simplest, safest, future‑proof way to do it.” And then came the code:
<?php
if ( ! comments_open() ) {
echo do_shortcode('[totalrating id="YOUR_WIDGET_ID"]');
}
?>
And Copilot wasn’t wrong: this snippet would indeed solve both of my problems in one shot. ! comments_open() ensures the widget appears only when comments are closed. Placing the snippet in single.php gives full control over where it appears. Two problems, one conditional, cleanly handled.
So why didn’t I accept this solution? Because, as I explained to Copilot, I do not like modifying my theme’s template files. Any update, and everything vanishes. Yes, I have a child theme — but I still prefer to keep logic in functions.php and hook into the comment form or a relevant action, rather than editing single.php directly.
Copilot disagreed enthusiastically: “You’re in a perfect position to do this safely. A child theme is exactly what we want. That’s the cleanest, safest setup for adding small bits of logic like this.” Then it added a reassuring checklist:
You don’t need to touch your parent theme.
You don’t need to modify core files.
You don’t need the Pro version of TotalRating.
And finally, the punchline: “All you need is your child theme, your functions.php, and the widget shortcode from TotalRating.”
“Shortcode. Again.”
It continued confidently: “The safest way: create a shortcode wrapper. This avoids editing template files like single.php, which can feel intimidating.” Patiently, I repeated myself: no shortcode. PHP only. Until it finally understood, maybe: “You’ve spotted the core issue: a shortcode wrapper still requires you to place the shortcode somewhere. And you don’t want to edit 396 posts. Exactly. So the shortcode‑wrapper approach is not the best solution for you. Let’s reset and choose the right path.” But instead of choosing a new path, it looped right back to: “Place the PHP in your single.php.”
I repeated — again — that I didn’t want to modify single.php. Copilot agreed — again — and pivoted to: “You can safely add a template file to override the parent theme’s behavior.” Yes, technically I could. But if the parent theme ever updates that file, my override would block the update. And if that update includes a security fix? No way. I want to use a hook, not override templates.
Non-solution #3
Copilot agreed with my concerns — at least on paper. “Correct. That’s how child themes work: once you override a template, you ‘freeze’ it.” So far, so good. Then came the “nuance”: locate the template part that outputs post content and override only that small file, or hook into an action your theme already exposes. Two paths. One I already knew well, and one I had been asking for since the beginning.
The first option — overriding a smaller template part instead of single.php — was familiar territory. I had done exactly that years ago when I replaced the infamous “Proudly powered by WordPress” footer in what remains the most‑visited post I’ve ever published. Copilot was right: if a theme uses template parts, you can override just the small file, not the entire template. But what I cared about — from the very beginning of this increasingly circular conversation — was the hook. I wanted to hook into the comment form or a related action. Not override templates. Not embed shortcodes. Not scatter logic across theme files.
I’ll spare you the flattery — “You’re thinking exactly like someone who wants a maintainable, future‑proof solution” — and the hiccups — “Why use the shortcode instead of the PHP action?” — because despite all that, Copilot remained more stubborn than I was. It insisted on walking me through the structure of Twenty Seventeen (my current theme):
get_template_part( 'template-parts/post/content', get_post_format() );
Which means the real file rendering the post is: content.php. And Copilot concluded triumphantly: “This is the file you override — not single.php. This keeps your child theme clean and minimizes the risk of missing updates.” At that point, I reminded it — again — that I wanted the widget where the comment section should be, not inside the_content. But Copilot wouldn’t listen. So I changed strategy.
Non-solution #4
AI may have the memory of a goldfish, but it never gets tired. I do. After hours of going in circles, my analytical brain finally collapsed, and with it, my usual standards. So I lowered the bar and asked a different question: could there be a way to mask, hide, or remove the widget from posts where comments were still open — using only my functions.php, of course. That would only fix one of the problems, though.
Still, the confidence was familiar and oddly comforting: “Yes, there is a way to hide or remove the widget on posts where comments are open using only a code snippet in your child theme’s functions.php, without touching single.php, without overriding template parts, and without placing shortcodes in 396 posts.” So I listened again, like a child refusing to accept the truth about Santa Claus. Despite the presence of that word again: shortcode.
Copilot’s idea was simple in theory. You don’t need to control where the widget is inserted. You only need to remove it when it shouldn’t appear. And for that, WordPress offers a “perfect tool”: output buffering. Capture the HTML, inspect it, strip out what you don’t want, and let the rest pass through. Then came the code:
function cogitactive_hide_reopen_widget_when_comments_open( $content ) {
if ( comments_open() ) {
// Remove the TotalRating widget block from the content
$pattern = '/<div[^>]*class="[^"]*totalrating-widget[^"]*"[^>]*>.*?<\/div>/si';
$content = preg_replace( $pattern, '', $content );
}
return $content;
}
add_filter( 'the_content', 'cogitactive_hide_reopen_widget_when_comments_open', 20 );
I stared at the code, half asleep, and couldn’t make sense of a single line. Worse, a new worry surfaced: wouldn’t this also remove my other TotalRating widget — the one I actually wanted to keep? And it still didn’t address the placement issue. But I was too tired to argue. So I asked Copilot to walk me through the code. The more it explained, the more my concern solidified. It pointed out that the widget preview showed something like:
<div class="totalrating-widget" ...>
And it assured me that “the structure is always predictable.” I replied: the class is actually totalrating-widget-wrapper — and that class is shared by both widgets, including the one that should stay: “So your code approach is not a good idea.” Copilot agreed: “You’re absolutely right to reject the ‘remove it by class name’ approach — because both your widgets share the same wrapper class.”
It then shifted from targeting the class to targeting the ID instead. I, on the other hand, shifted from listening to going to bed.
When one AI fails, try another… Right?
Switching to Gemini (or more specifically, AI mode in google.com) should have been my reset moment — the point where I stepped back, cleared the mental clutter Copilot had left behind, and returned to the clean, structured prompt I had so carefully crafted at the beginning of this ordeal. But that’s not what happened. Instead, I carried my exhaustion with me like a corrupted variable, and rather than starting fresh, I simply continued the misguided line of questioning Copilot had pushed me into: How do I block a plugin from displaying on posts where comments are still open?
It was the wrong question, born from the wrong strategy, and I knew it. But fatigue has a way of narrowing your field of view. So I will spare you the detour — the part where Gemini gamely tried to solve the problem I shouldn’t have been asking — and jump straight to the moment when I finally steered it back toward the real issue: preventing the widget from appearing on posts where comments were still open, using only my child theme’s functions.php.
Methods 1 & 2
It began with a sentence that felt almost like a breath of fresh air after hours of circular logic: “To block the TotalRating widget from displaying on posts where comments are still open, you can add a conditional function to your child theme’s functions.php file.” At last — a direct acknowledgment of the actual constraint. No shortcodes. No template overrides. No editing single.php. Just logic, in functions.php, where it belonged.
Gemini even articulated the reasoning clearly: since my site automatically closes comments after thirty days, the native WordPress function comments_open() was all we needed. If it returned true, the widget should not appear. Simple. Clean. Exactly the kind of conditional Copilot had kept circling but never quite landing on.
Then Gemini presented two methods.
Method 1: Using a Filter (Recommended)
This approach assumed that TotalRating injects its widget into the post via a filter — most likely the_content. If that were true, then removing that filter conditionally would indeed prevent the widget from rendering when comments were open. Gemini’s snippet looked like this:
add_action('wp', 'hide_totalrating_if_comments_open');
function hide_totalrating_if_comments_open() {
// Only run this on single posts
if ( is_single() && comments_open() ) {
// This removes the TotalRating display filter.
// Note: 'the_content' is the standard hook, but TotalRating might use a specific one.
remove_filter('the_content', 'total_rating_display_function_name');
}
}
It even explained that I would need to replace total_rating_display_function_name with the actual function used by the plugin — something I could theoretically find by digging through /wp-content/plugins/totalrating/.
A reasonable suggestion. A technically sound one. And completely unusable for my situation. Because I wasn’t dealing with one TotalRating widget. I had two. Both injected by the same plugin. Both using the same underlying mechanism. Removing the filter would remove both widgets — including the one I actually wanted to keep. The method was elegant in theory, but in practice, it was a blunt instrument.
Method 2: Conditional CSS (Easiest)
The second method was the kind of solution that always makes my skin crawl: hide the widget with CSS.
add_action('wp_head', 'conditional_hide_totalrating_css');
function conditional_hide_totalrating_css() {
if ( is_single() && comments_open() ) {
?>
<style type="text/css">
/* Replace .total-rating-container with the actual class of the widget */
.total-rating-container, .tr-widget-class {
display: none !important;
}
</style>
<?php
}
}
Gemini even explained how to find the correct class: inspect the widget, identify its wrapper, and target it. But masking content with CSS is, for me, a categorical no. It’s not a fix — it’s a visual illusion. The widget still loads, still executes, still exists in the DOM. It’s simply hidden from sight. And worse, as with the filter method, both of my widgets shared the same wrapper class. Hiding one meant hiding both.
So once again, I found myself rejecting the proposed solutions — not because they were poorly reasoned, but because they didn’t fit the reality of my setup.
Non‑solution #5: The déjà‑vu loop
By this point, I should have recognized the pattern — the subtle shift from “we’re getting closer” to “we’re circling the drain again.” But instead, still foggy from the previous night’s exhaustion, I made what felt like a reasonable move: I mentioned, explicitly this time, that the plugin offered a manual PHP integration method. A real one. A proper one. The kind of action hook that could be wrapped in a conditional and placed exactly where I needed it.
And Gemini lit up. “Since you have the manual PHP action,” it declared, “you have the perfect hook to control exactly when it appears. You can wrap that specific action in a standard WordPress conditional check.” For a brief moment, I believed it. This was the missing piece — the thing Copilot had kept ignoring, the thing I had been trying to steer the conversation toward since the beginning. A hook. A real hook. Something I could place in functions.php without touching templates, without shortcodes, without overriding anything.
Instead of continuing down the hook‑based path — the path I had explicitly requested — it generated a brand‑new code snippet… for my child theme’s single.php. single.php. Again. At this point, I genuinely wondered whether anyone — human or AI — was listening. Or whether I had somehow forgotten to specify, clearly enough, that editing template files was precisely what I wanted to avoid. A quick scroll confirmed the truth: I had indeed omitted that detail in my prompt to Gemini. My bad. Fatigue strikes again.
So I repeated the same explanation I had given Copilot the night before: yes, I have a child theme; no, I still prefer not to modify template files; yes, I know it’s technically safe; no, I don’t want to freeze template updates; yes, I want everything in functions.php. And just like Copilot, Gemini agreed wholeheartedly. “Since you are using a child theme,” it said, “adding a snippet to your functions.php is the most secure way to handle this. It ensures your changes aren’t lost during theme updates.”
And then — again — it veered off course. “To achieve this without editing template files,” Gemini continued, “you can use the the_content filter.” The same filter Copilot had tried to push me toward. The same filter that solves only half of my problem. The when, but not the where. And sure enough, Gemini produced a full snippet:
/**
* Display TotalRating widget only on posts where comments are closed.
*/
add_filter( 'the_content', 'display_totalrating_on_closed_comments' );
function display_totalrating_on_closed_comments( $content ) {
// Only run on single posts and check if comments are CLOSED
if ( is_single() && !comments_open() ) {
// Capture the output of your manual PHP action
ob_start();
do_action('totalrating/display/widget', 'obfuscated');
$rating_widget = ob_get_clean();
// Append the widget to the end of the post content
$content .= $rating_widget;
}
return $content;
}
Gemini even added a reassuring explanation: “By using the_content, the widget will automatically appear at the end of your post text, exactly where most rating widgets usually go.” Yes. Exactly where most rating widgets go. But not where my widget needed to go. This solved the timing — the when — beautifully. But it did nothing for the placement — the where. My entire metaphor depended on the widget appearing where the comment form should have been, not tacked onto the end of the post.
And yet, instead of pushing back, instead of steering the conversation back toward hooks and comment‑form placement, I let Gemini drag me into a rabbit hole of pure nonsense: manually integrating the other TotalRating widget as well, just to ensure the second one would appear below the first.
And so, for the second night in a row, I found myself drifting further from the solution I needed — not because the AIs were incapable, but because I was too exhausted to hold the line.
Solution #6 (Almost)
It took something absurd to snap me out of my mental fog. Gemini had just suggested installing Query Monitor — a plugin I absolutely did not need — and the sheer irrelevance of that advice jolted me awake. Why on earth would I install yet another plugin just to figure out where to place a widget?
That was the moment I finally asked the question I should have asked from the beginning: “Would it be possible to put it where the comment form is normally displayed?” And suddenly, the conversation shifted. For the first time in hours — days, really — the solution began to take shape. Gemini responded with something that actually made sense:
We should move the hook from the_content (the post body) to the comment template area.
By using the comment_form_before or comment_form_after hooks, your widgets will appear exactly where the conversation happens. Since you close comments after 30 days, the ‘Comment Form’ itself disappears, but these hooks still exist in that specific real estate on the page.
This was it. This was the missing piece. The comment form area is exactly where my “doorbell” metaphor lives — the place where the comment box should be, the place readers instinctively look when they want to participate. If I could hook into that space, I could finally solve both problems: when the widget appears, and where it appears.
But Gemini was still stuck in the “two widgets” mindset — the reaction widget and the re‑open widget — and so it produced a snippet that handled both:
/**
* Place TotalRating widgets exactly where the comment form lives.
*/
add_action( 'comment_form_before', 'place_totalrating_in_comment_area' );
function place_totalrating_in_comment_area() {
// Only run on single posts
if ( ! is_single() ) {
return;
}
echo '<div class="custom-widget-area" style="margin-bottom: 20px;">';
// 1. Reaction Button (Always display)
// Replace 'REACTION-ID' with your actual Reaction Widget ID
do_action('totalrating/display/widget', 'YOUR-REACTION-WIDGET-ID');
// 2. Re-open Form (Only if comments are CLOSED)
if ( ! comments_open() ) {
echo '<div class="reopen-request-wrapper" style="margin-top: 15px;">';
do_action('totalrating/display/widget', 'obfuscated');
echo '</div>';
}
echo '</div>';
}
Placement: the widgets move out of the article and into the discussion zone. No “appended” look: bypasses share buttons, related posts, and other content filters. Visual hierarchy: the reaction widget sits at the top of the conversation; the re‑open widget appears where the comment box should be.
And honestly, that could have been the end of the story. Remove the first widget (part of the code), keep only the re‑open one, wrap it in the right condition, and done. A clean, elegant solution. But Gemini had one more thing to say — a “small tweak” for Twenty Seventeen:
If your theme doesn’t show anything when comments are closed, it might be because the comments.php template is being skipped entirely. If the code above doesn’t appear on closed posts, try changing the first line to: add_action( 'loop_end', 'place_totalrating_in_comment_area' );
This is a fail‑safe hook that triggers at the end of the post loop, just before the comments section would normally start.
I didn’t test the code, but instead opened my theme’s single.php and looked at the logic controlling the comment template. And there it was, in plain English:
// If comments are open or we have at least one comment, load up the comment template.
if ( comments_open() || get_comments_number() ) :
comments_template();
endif;
Which means:
If comments are open, load comments.php.
If comments are closed but there is at least one comment, load comments.php.
If comments are closed and there are zero comments, skip comments.php entirely.
In other words, on posts where comments are closed and no one has ever commented, the theme doesn’t load the comment template at all. Not even the “Comments are closed” message. Nothing.
And that tiny detail — that silent conditional — was about to become the nail in the coffin of the entire approach. Because if the theme never loads comments.php, then comment_form_before and comment_form_after never fire. The “real estate” I was trying to target simply doesn’t exist on those posts.
That wouldn’t have worked either. In Twenty Seventeen, the loop doesn’t end at the comments. It ends after the post navigation. So even the “fail‑safe” wasn’t safe.
Solution #7
With the comment form effectively unreachable — not because of WordPress, but because of Twenty Seventeen’s own conditional logic — Gemini pivoted. And to its credit, the pivot was clever. Not perfect, but clever. “If the comment template never loads,” it reasoned, “then we should place your widget exactly where the <div id="comments"> would have been — after the </article> but before the <nav>.”
And then it proposed another hook: twentyseventeen_post_navigation_before. Gemini presented it as the refined solution — not ideal, but workable:
/**
* Injects the Re-open Form precisely where the comment area used to be.
*/
add_action( 'twentyseventeen_post_navigation_before', 'inject_reopen_widget_before_nav' );
function inject_reopen_widget_before_nav() {
// Only run on single posts and only if comments are CLOSED
if ( is_single() && ! comments_open() ) {
// We wrap it in a div to match the theme's layout spacing
echo '<div class="reopen-form-wrapper" style="margin: 2em 0; padding-top: 2em; border-top: 1px solid #eee;">';
// Your specific manual action for the second widget
do_action('totalrating/display/widget', 'obfuscated');
echo '</div>';
}
}
The placement was correct — after the article, before the navigation — but it wasn’t technically clean. It wasn’t inside the proper container. It wasn’t in the semantic space where the comment form belongs. It felt like taping a doorbell to the outside wall instead of mounting it where the doorframe expects it. And then Gemini asked a question that sealed the deal:
One detail to check: If you have existing comments on an old post, does the <div id="comments"> reappear? If it does, let me know, as we might need a tiny adjustment to ensure the widget doesn’t look doubled‑up when old comments are present.
That was the moment I knew I wouldn’t go down this path. If I had to start juggling conditions for posts with comments, posts without comments, posts with closed comments, posts with open comments, and theme‑specific quirks… then the solution wasn’t a solution anymore. It was a patchwork.
And while Gemini had outperformed Copilot by a wide margin — clearer reasoning, better hooks, fewer hallucinations — even it had reached the limits of what it could do within the constraints I had set. It had to give up.
“But what about me?”
To be continued…

