From e80a33bf2ad4fe1b47037add7470a6c2770f4036 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Wed, 1 May 2024 15:41:13 +0000
Subject: [PATCH] feat(plugins): add siteHead hook to add custom meta tags to
 public pages

---
 app/Config/View.php                 |  3 ++-
 app/Helpers/rss_helper.php          |  4 ++--
 app/Views/Decorators/SiteHead.php   | 36 +++++++++++++++++++++++++++++
 modules/Plugins/BasePlugin.php      |  8 +++++--
 modules/Plugins/PluginInterface.php |  6 +++--
 modules/Plugins/Plugins.php         |  7 +++---
 6 files changed, 54 insertions(+), 10 deletions(-)
 create mode 100644 app/Views/Decorators/SiteHead.php

diff --git a/app/Config/View.php b/app/Config/View.php
index 7225324cb8..c43985de57 100644
--- a/app/Config/View.php
+++ b/app/Config/View.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Config;
 
+use App\Views\Decorators\SiteHead;
 use CodeIgniter\Config\View as BaseView;
 use CodeIgniter\View\ViewDecoratorInterface;
 use ViewComponents\Decorator;
@@ -53,5 +54,5 @@ class View extends BaseView
      *
      * @var list<class-string<ViewDecoratorInterface>>
      */
-    public array $decorators = [Decorator::class];
+    public array $decorators = [Decorator::class, SiteHead::class];
 }
diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php
index dc5e1cee45..bf7918ba94 100644
--- a/app/Helpers/rss_helper.php
+++ b/app/Helpers/rss_helper.php
@@ -298,7 +298,7 @@ if (! function_exists('get_rss_feed')) {
         }
 
         // run plugins hook at the end
-        $plugins->setChannelTag($podcast, $channel);
+        $plugins->channelTag($podcast, $channel);
 
         foreach ($episodes as $episode) {
             if ($episode->is_premium && ! $subscription instanceof Subscription) {
@@ -460,7 +460,7 @@ if (! function_exists('get_rss_feed')) {
                 ], $item);
             }
 
-            $plugins->setItemTag($episode, $item);
+            $plugins->itemTag($episode, $item);
         }
 
         return $rss->asXML();
diff --git a/app/Views/Decorators/SiteHead.php b/app/Views/Decorators/SiteHead.php
new file mode 100644
index 0000000000..ff8e91a1b0
--- /dev/null
+++ b/app/Views/Decorators/SiteHead.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Views\Decorators;
+
+use CodeIgniter\View\ViewDecoratorInterface;
+
+class SiteHead implements ViewDecoratorInterface
+{
+    private static int $renderedCount = 0;
+
+    public static function decorate(string $html): string
+    {
+        if (url_is(config('Admin')->gateway . '*') || url_is(config('Install')->gateway)) {
+            return $html;
+        }
+
+        if (static::$renderedCount > 0) {
+            return $html;
+        }
+
+        ob_start(); // Start output buffering
+        // run hook to add tags to <head>
+        service('plugins')->siteHead();
+        $metaTags = ob_get_contents(); // Store buffer in variable
+        ob_end_clean();
+
+        if (str_contains($html, '</head>')) {
+            $html = str_replace('</head>', "\n\t{$metaTags}\n</head>", $html);
+            ++static::$renderedCount;
+        }
+
+        return $html;
+    }
+}
diff --git a/modules/Plugins/BasePlugin.php b/modules/Plugins/BasePlugin.php
index d73683c1d4..973dc31eeb 100644
--- a/modules/Plugins/BasePlugin.php
+++ b/modules/Plugins/BasePlugin.php
@@ -59,11 +59,15 @@ abstract class BasePlugin implements PluginInterface
         // TODO: setup navigation and views?
     }
 
-    public function setChannelTag(Podcast $podcast, SimpleRSSElement $channel): void
+    public function channelTag(Podcast $podcast, SimpleRSSElement $channel): void
     {
     }
 
-    public function setItemTag(Episode $episode, SimpleRSSElement $item): void
+    public function itemTag(Episode $episode, SimpleRSSElement $item): void
+    {
+    }
+
+    public function siteHead(): void
     {
     }
 
diff --git a/modules/Plugins/PluginInterface.php b/modules/Plugins/PluginInterface.php
index 7c46f18b57..229ff29084 100644
--- a/modules/Plugins/PluginInterface.php
+++ b/modules/Plugins/PluginInterface.php
@@ -10,7 +10,9 @@ use App\Libraries\SimpleRSSElement;
 
 interface PluginInterface
 {
-    public function setChannelTag(Podcast $podcast, SimpleRSSElement $channel): void;
+    public function channelTag(Podcast $podcast, SimpleRSSElement $channel): void;
 
-    public function setItemTag(Episode $episode, SimpleRSSElement $item): void;
+    public function itemTag(Episode $episode, SimpleRSSElement $item): void;
+
+    public function siteHead(): void;
 }
diff --git a/modules/Plugins/Plugins.php b/modules/Plugins/Plugins.php
index 48c6a42a70..0995cc2a6c 100644
--- a/modules/Plugins/Plugins.php
+++ b/modules/Plugins/Plugins.php
@@ -9,8 +9,9 @@ use App\Entities\Podcast;
 use App\Libraries\SimpleRSSElement;
 
 /**
- * @method void setChannelTag(Podcast $podcast, SimpleRSSElement $channel)
- * @method void setItemTag(Episode $episode, SimpleRSSElement $item)
+ * @method void channelTag(Podcast $podcast, SimpleRSSElement $channel)
+ * @method void itemTag(Episode $episode, SimpleRSSElement $item)
+ * @method string siteHead()
  */
 class Plugins
 {
@@ -19,7 +20,7 @@ class Plugins
     /**
      * @var list<string>
      */
-    protected const HOOKS = ['setChannelTag', 'setItemTag'];
+    protected const HOOKS = ['channelTag', 'itemTag', 'siteHead'];
 
     /**
      * @var array<BasePlugin>
-- 
GitLab