From c17b985441d3d427b5937661fc8d16eeedb42edb Mon Sep 17 00:00:00 2001
From: Cameron <e107inc@gmail.com>
Date: Fri, 4 Feb 2022 12:04:21 -0800
Subject: [PATCH] Experimental schema template

---
 e107_core/templates/header_default.php |  7 ++++
 e107_handlers/e107_class.php           | 19 ++++++++-
 e107_handlers/e_parse_class.php        | 53 ++++++++++++++++++++++++++
 e107_handlers/shortcode_handler.php    | 13 ++++++-
 e107_plugins/faqs/faqs.php             | 27 ++++++++++++-
 5 files changed, 114 insertions(+), 5 deletions(-)

diff --git a/e107_core/templates/header_default.php b/e107_core/templates/header_default.php
index 1be4721c0..6b2854adc 100644
--- a/e107_core/templates/header_default.php
+++ b/e107_core/templates/header_default.php
@@ -208,6 +208,13 @@ if ($e_headers && is_array($e_headers))
 }
 unset($e_headers);
 
+/** @experimental - subject to change at any time */
+if($schema = e107::schema())
+{
+	echo '<script type="application/ld+json">'.$schema."</script>\n";
+}
+
+unset($schema);
 
 echo e107::getSingleton('eResponse')->renderMeta()."\n";  // render all the e107::meta() entries.
 
diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php
index 325b9eeef..39fa8d2f2 100644
--- a/e107_handlers/e107_class.php
+++ b/e107_handlers/e107_class.php
@@ -3327,7 +3327,7 @@ class e107
 	 * - $USER_TEMPLATE['short_start'] (if key is null, $USER_TEMPLATE will be returned)
 	 *
 	 * @param string $plug_name if null getCoreTemplate method will be called
-	 * @param string $id - file prefix, e.g. calendar for calendar_template.php
+	 * @param string $id - file prefix, e.g. calendar for calendar_template.php, or 'true' or 'null' for same as plugin name.
 	 * @param string|null $key $YOURTEMPLATE_TEMPLATE[$key]
 	 * @param boolean $override see {@link getThemeInfo()}
 	 * @param boolean $merge merge theme with plugin templates, default is false
@@ -4366,6 +4366,23 @@ class e107
 		self::getRedirect()->go($url, true, $http_response_code);
 	}
 
+	/**
+	 * Getter/Setter for schema. eg. Google structured data etc.
+	 * @param string $json
+	 * @return string|bool
+	 */
+	public static function schema($json = null)
+	{
+		if(empty($json))
+		{
+			return self::getRegistry('core/e107/schema', false);
+		}
+
+
+		return self::setRegistry('core/e107/schema',$json);
+
+	}
+
 
 	/**
 	 * Retrieve error page handler.
diff --git a/e107_handlers/e_parse_class.php b/e107_handlers/e_parse_class.php
index e10b2c0fa..b06ca0ee9 100644
--- a/e107_handlers/e_parse_class.php
+++ b/e107_handlers/e_parse_class.php
@@ -824,6 +824,24 @@ class e_parse
 		return e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars);
 	}
 
+	/**
+	 * @experimental
+	 * @param string $text
+	 * @param bool $parseSCFiles
+	 * @param object|array $extraCodes
+	 * @param object $eVars
+	 * @return string
+	 */
+	public function parseSchemaTemplate($text, $parseSCFiles = true, $extraCodes = null, $eVars = null)
+	{
+		$parse = e107::getScParser();
+		$parse->setMode('schema');
+		$text = e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars);
+		$parse->setMode('default');
+		return $text;
+
+	}
+
 
 	/**
 	 * Simple parser
@@ -1406,6 +1424,41 @@ class e_parse
 		return strip_tags($html, $parm);
 	}
 
+	public function stripAttributes($s, $allowedattr = array()) {
+
+		if(preg_match_all("/<[^>]*\\s([^>]*)\\/*>/msiU", $s, $res, PREG_SET_ORDER))
+		{
+			foreach($res as $r)
+			{
+				$tag = $r[0];
+				$attrs = array();
+				preg_match_all("/\\s.*=(['\"]).*\\1/msiU", " " . $r[1], $split, PREG_SET_ORDER);
+				foreach($split as $spl)
+				{
+					$attrs[] = $spl[0];
+				}
+				$newattrs = array();
+				foreach($attrs as $a)
+				{
+					$tmp = explode("=", $a);
+					if(trim($a) != "" && (!isset($tmp[1]) || (trim($tmp[0]) != "" && !in_array(strtolower(trim($tmp[0])), $allowedattr))))
+					{
+
+					}
+					else
+					{
+						$newattrs[] = $a;
+					}
+				}
+				$attrs = implode(" ", $newattrs);
+				$rpl = str_replace($r[1], $attrs, $tag);
+				$s = str_replace($tag, $rpl, $s);
+			}
+		}
+
+		return $s;
+}
+
 
 	/**
 	 * Converts the text (presumably retrieved from the database) for HTML output.
diff --git a/e107_handlers/shortcode_handler.php b/e107_handlers/shortcode_handler.php
index 4e6594765..a0fa99759 100644
--- a/e107_handlers/shortcode_handler.php
+++ b/e107_handlers/shortcode_handler.php
@@ -107,6 +107,7 @@ class e_parse_shortcode
 	protected $editableCodes        = array(); // Array of editable shortcode data.
 	protected $editableActive       = false;
 	protected $nowrap               = false;
+	private   $mode                 = 'default';
 
 	function __construct()
 	{
@@ -1446,13 +1447,21 @@ class e_parse_shortcode
 			
 		}
 
-
-
+		if($this->mode === 'schema' && !empty($ret))
+		{
+			$ret = e107::getParser()->stripAttributes($ret, ['href']);
+			$ret = str_replace('"','',$ret);
+		}
 
 		return isset($ret) ? $ret: '';
 
 	}
 
+	public function setMode($mode)
+	{
+		$this->mode = (string) $mode;
+	}
+
 
 	/**
 	 * Add Wrapper to Shortcode (when detected)
diff --git a/e107_plugins/faqs/faqs.php b/e107_plugins/faqs/faqs.php
index 6f85b42a0..8b59b71d4 100644
--- a/e107_plugins/faqs/faqs.php
+++ b/e107_plugins/faqs/faqs.php
@@ -274,8 +274,7 @@ class faq
 		$this->sc = e107::getScBatch('faqs',TRUE);
 		
 		$text = $tp->parseTemplate($template['start'], true, $this->sc); // header
-		
-		// var_dump($sc);
+
 		
 		$text .= "<div id='faqs-container'>";
 		
@@ -377,12 +376,20 @@ class faq
 		
 		$FAQ_LISTALL = e107::getTemplate('faqs', true, 'all');
 
+		$schemaTemplate = e107::getTemplate('faqs', true, 'schema');
+
+
 		$prevcat = "";
 		$sc = e107::getScBatch('faqs', true);
 		$sc->counter = 1;
 		$sc->tag = htmlspecialchars(varset($tag), ENT_QUOTES, 'utf-8');
 		$sc->category = varset($category);
 
+		if(!empty($schemaTemplate['start']))
+		{
+			$schema = $tp->parseSchemaTemplate($schemaTemplate['start'],false,$sc);
+		}
+
 		 if(!empty($_GET['id'])) // expand one specific FAQ.
 		{
 			$sc->item =intval($_GET['id']);
@@ -419,6 +426,11 @@ class faq
 
 			$sc->setVars($rw);
 
+			if(!empty($schemaTemplate['item']))
+			{
+				$schema .= $tp->parseSchemaTemplate($schemaTemplate['item'],false,$sc);
+			}
+
 			if($sc->item == $rw['faq_id'])
 			{
 				$this->pageTitle = $rw['faq_question'];
@@ -441,6 +453,17 @@ class faq
 			$sc->counter++;
 		}
 		$text .= ($start) ? $tp->parseTemplate($FAQ_LISTALL['end'], true, $sc) : "";
+
+		if(!empty($schemaTemplate['end']))
+		{
+			$schema .= $tp->parseSchemaTemplate($schemaTemplate['end'],false,$sc);
+		}
+
+		if(!empty($schema))
+		{
+
+			e107::schema($schema);
+		}
 //		$text .= $tp->parseTemplate($FAQ_END, true, $sc);
 
 		return $text;