1
0
mirror of https://github.com/e107inc/e107.git synced 2025-03-13 08:59:44 +01:00

#5422 Support for schema. News schema added and FAQs schema template simplified.

This commit is contained in:
camer0n 2025-01-31 11:54:19 -08:00
parent ec61237c03
commit b05fea833c
11 changed files with 599 additions and 211 deletions

View File

@ -112,7 +112,7 @@ if(isset($_POST['updateprefs']))
if(isset($_POST['contact_info']) && is_array($_POST['contact_info']))
{
$core_pref->set('contact_info', []); // reset to type array if string had been used in the past.
$core_pref->set('contact_info', []); // reset to type array if string had been used in the past.
}
$smtp_opts = array();

View File

@ -99,6 +99,11 @@ class news_shortcodes extends e_shortcode
e107::getBB()->clearClass();
if($parm == 'raw')
{
$news_body = strip_tags($news_body);
}
return $news_body;
}
@ -507,7 +512,12 @@ class news_shortcodes extends e_shortcode
{
$imgParms['loading'] = $parm['loading'];
}
if(!empty($parm['return']))
{
$imgParms['return'] = $parm['return'];
$parm['type'] = 'meta';
}
$imgTag = $tp->toImage($srcPath,$imgParms);
@ -522,6 +532,10 @@ class news_shortcodes extends e_shortcode
return empty($src) ? e_IMAGE_ABS."generic/nomedia.png" : $src;
break;
case 'meta':
return $tp->replaceConstants($imgTag, 'full');
break;
case 'url':
return "<a href='".e107::getUrl()->create('news/view/item', $this->news_item)."'>".$imgTag."</a>";
break;
@ -622,6 +636,10 @@ class news_shortcodes extends e_shortcode
$ret = $con->convert_date($date, 'forum');
break;
case 'atom':
$ret = date(DATE_ATOM, $date);
break;
default :
$ret = $tp->toDate($date, $parm);
break;
@ -640,6 +658,10 @@ class news_shortcodes extends e_shortcode
function sc_news_modified($parm=null)
{
if(empty($this->news_item['news_modified']))
{
return null;
}
return $this->formatDate($this->news_item['news_modified'], $parm);
}
@ -1097,7 +1119,9 @@ class news_shortcodes extends e_shortcode
function sc_news_url($parm=null)
{
return e107::getUrl()->create('news/view/item', $this->news_item);
$options = (!empty($parm) && is_array($parm)) ? $parm : array();
return e107::getUrl()->create('news/view/item', $this->news_item, $options);
}

View File

@ -854,27 +854,118 @@ 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
* Parses a JSON schema template, processes placeholders, and reconstructs the JSON with optional main entity and extra codes.
*
* @param string $text The JSON schema template to be parsed.
* @param bool $parseSCFiles Whether to enable the parsing of shortcode files. Defaults to true.
* @param object|null $extraCodes Optional extra codes object for placeholder parsing.
* @param array|null $mainEntity Optional data array to replace the 'mainEntity' structure in the schema.
* @return string|false The processed JSON schema string on success, or false if the input JSON is invalid.
*/
public function parseSchemaTemplate($text, $parseSCFiles = true, $extraCodes = null, $eVars = null)
public function parseSchemaTemplate($text, $parseSCFiles = true, $extraCodes = null, $mainEntity = null)
{
// Initialize the parser
$parse = e107::getScParser();
$parse->setMode('schema');
$text = e107::getScParser()->parseCodes($text, $parseSCFiles, $extraCodes, $eVars);
$text = str_replace('<!-- >', '', $text); // cleanup
$parse->setMode('schema'); // Set parsing mode for schema
// Step 1: Decode the JSON input into an array
$jsonArray = json_decode($text, true);
// Step 2: Validate JSON decoding
if(json_last_error() !== JSON_ERROR_NONE)
{
error_log('Invalid JSON: ' . json_last_error_msg());
return false;
}
// Step 3: Recursive function to process the JSON structure
$processItems = function (&$item) use (&$processItems, $parse, $parseSCFiles, $extraCodes, $mainEntity)
{
if(is_array($item))
{
// Check if the current item contains 'mainEntity', the target of our processing
if(isset($item['mainEntity']) && is_array($mainEntity))
{
// Get the first template item from the 'mainEntity' array to use as the structure
$schemaTemplate = $item['mainEntity'][0];
$item['mainEntity'] = []; // Reset the 'mainEntity' array to prevent duplication
foreach($mainEntity as $dataRow)
{
// Create a fresh copy of the schema template for this specific dataRow
$duplicatedItem = json_decode(json_encode($schemaTemplate), true);
// Update the extraCodes for the current data row
if(method_exists($extraCodes, 'setVars'))
{
$extraCodes->setVars($dataRow); // Inject new placeholders from this row
}
// Process placeholders in the duplicated item
foreach($duplicatedItem as &$value)
{
if(is_string($value) && strpos($value, '{') !== false)
{
// Parse placeholders for current dataRow
$value = $parse->parseCodes($value, $parseSCFiles, $extraCodes);
$value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$value = strip_tags($value);
}
elseif(is_array($value))
{
// Recursively process arrays (e.g., nested structures)
$processItems($value);
}
}
// Append the processed item to the 'mainEntity' array
$item['mainEntity'][] = $duplicatedItem;
}
}
else
{
// Recursively process other parts of the JSON structure
foreach($item as &$value)
{
$processItems($value);
}
}
}
elseif(is_string($item))
{
// Parse string placeholders, if any
if(strpos($item, '{') !== false)
{
$item = $parse->parseCodes($item, $parseSCFiles, $extraCodes);
$item = str_replace('&amp;', '&', $item);
$item = html_entity_decode($item, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$item = strip_tags($item);
}
}
};
// Step 4: Initiate processing for the entire JSON structure
$processItems($jsonArray);
// Reset the parse mode after processing
$parse->setMode('default');
return $text;
// Step 5: Encode the final result back into JSON
return json_encode($jsonArray, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
/**
* Simple parser
*
@ -2174,9 +2265,10 @@ class e_parse
$search = array('&amp;#039;', '&amp;#036;', '&#039;', '&#036;', '&#092;', '&amp;#092;');
$replace = array("'", '$', "'", '$', "\\", "\\");
$text = str_replace($search, $replace, $text);
return $text;
return str_replace($search, $replace, $text);
}
@ -4729,6 +4821,12 @@ class e_parse
return null;
}
if(varset($parm['return']) === 'url')
{
$path = $tp->createConstants($path, 'mix');
return $tp->replaceConstants($path, 'full');
}
$html .= "<img {$id}class=\"{$class}\" src=\"" . $path . '" alt="' . $alt . '" ' . $srcset . $width . $height . $style . $loading . $title . ' />';
// $html .= ($this->convertToWebP) ? "\n</picture>" : '';

View File

@ -385,11 +385,6 @@ class faq
$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']);
@ -407,7 +402,7 @@ class faq
// $text = $tp->parseTemplate($FAQ_START, true, $sc);
// $text = "";
$start = false;
if($this->pref['list_type'] == 'ol')
@ -419,17 +414,14 @@ class faq
$FAQ_LISTALL['end'] = str_replace($tsrch,$trepl, $FAQ_LISTALL['end']);
}
$schemaItems = [];
foreach ($data as $rw)
{
$rw['faq_sef'] = eHelper::title2sef($tp->toText($rw['faq_question']),'dashl');
$sc->setVars($rw);
if(!empty($schemaTemplate['item']))
{
$schemaItems[] = $tp->parseSchemaTemplate($schemaTemplate['item'],false,$sc);
}
if($sc->item == $rw['faq_id'])
{
@ -445,7 +437,7 @@ class faq
}
$text .= "\n\n<!-- FAQ Start ".$rw['faq_info_order']."-->\n\n";
$text .= $tp->parseTemplate($FAQ_LISTALL['start'], true, $sc);
$start = TRUE;
$start = true;
}
$text .= $tp->parseTemplate($FAQ_LISTALL['item'], true, $sc);
@ -453,24 +445,27 @@ class faq
$sc->counter++;
}
if(!empty($schemaItems))
{
$schema .= implode(",", $schemaItems);
}
$text .= ($start) ? $tp->parseTemplate($FAQ_LISTALL['end'], true, $sc) : "";
if(!empty($schemaTemplate['end']))
if(!empty($schemaTemplate))
{
$schema .= $tp->parseSchemaTemplate($schemaTemplate['end'],false,$sc);
}
if(isset($schemaTemplate['end']) && isset($schemaTemplate['item']) && isset($schemaTemplate['start']))
{
$schemaTpl = $schemaTemplate['start']."\n".$schemaTemplate['item']."\n".$schemaTemplate['end'];
$schema = $tp->parseSchemaTemplate($schemaTpl, true, $sc, $data);
}
elseif(is_string($schemaTemplate))
{
$schema = $tp->parseSchemaTemplate($schemaTemplate, true, $sc, $data);
}
if(!empty($schema))
{
e107::schema($schema);
if(!empty($schema))
{
e107::schema($schema);
}
}
// $text .= $tp->parseTemplate($FAQ_END, true, $sc);
return $text;

View File

@ -80,7 +80,7 @@ class faqs_shortcodes extends e_shortcode
}
function sc_faq_question($parm='')
function sc_faq_question($parm=[])
{
$tp = e107::getParser();
$parm = eHelper::scDualParams($parm);
@ -93,7 +93,7 @@ class faqs_shortcodes extends e_shortcode
$faqNew = ($this->var['faq_datestamp'] > $newDate) ? " faq-new" : "";
if($param == 'expand' && !empty($this->var['faq_answer']))
if($param === 'expand' && !empty($this->var['faq_answer']))
{
$id = "faq_".$this->var['faq_id'];
@ -130,6 +130,11 @@ class faqs_shortcodes extends e_shortcode
}
else
{
if(isset($parm['html']) && empty($parm['html']))
{
return e107::getParser()->toText($this->var['faq_question']);
}
$text = $tp->toHTML($this->var['faq_question'],true, 'TITLE');
}
return $text;
@ -154,8 +159,13 @@ class faqs_shortcodes extends e_shortcode
return "<a class='faq-question' href='". e107::url('faqs', 'item', $this->var)."' >".$tp -> toHTML($this->var['faq_question'],true,'TITLE')."</a>";
}
function sc_faq_answer()
function sc_faq_answer($parm=[])
{
if(isset($parm['html']) && empty($parm['html']))
{
return e107::getParser()->toText($this->var['faq_answer']);
}
return e107::getParser()->toHTML($this->var['faq_answer'],true,'BODY');
}

View File

@ -35,20 +35,20 @@ $FAQS_TEMPLATE['caption'] = "{FAQ_CAPTION} <small>{FAQ_COUNT}</small>";
/** @experimental */
$FAQS_TEMPLATE['schema']['start'] = '{
$FAQS_TEMPLATE['schema'] = '
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [';
$FAQS_TEMPLATE['schema']['item'] = '{
"@type": "Question",
"name": "{FAQ_QUESTION}",
"acceptedAnswer": {
"@type": "Answer",
"text": "{FAQ_ANSWER}"
}
}';
$FAQS_TEMPLATE['schema']['end'] = ']
}
';
"mainEntity": [
{
"@type": "Question",
"name": "{FAQ_QUESTION: html=0}",
"acceptedAnswer":
{
"@type": "Answer",
"text": "{FAQ_ANSWER: html=0}"
}
}
]
}
';

View File

@ -44,6 +44,8 @@ class news_front
private $tagAuthor = null;
private $comments = array();
private $pagination;
private $schema = '';
// private $interval = 1;
function __construct()
@ -824,6 +826,7 @@ class news_front
$e107cache->set($cache_tag."_diz", defined("META_DESCRIPTION") ? META_DESCRIPTION : '');
$e107cache->set($cache_tag."_rows", e107::serialize($rowData,'json'));
$e107cache->set($cache_tag."_schema", $this->schema);
}
@ -1238,6 +1241,12 @@ class news_front
$this->setNewsFrontMeta($rows);
$text = $this->renderCache($caption, $newsCachedPage); // This exits if cache used
$this->comments = $rows;
if($shema = $this->getNewsCache($this->cacheString,'schema'))
{
e107::schema($shema);
}
return $text;
}
else
@ -1354,6 +1363,12 @@ class news_front
$render = true;
}
if(!empty($tmp['schema']))
{
$this->schema = e107::getParser()->parseSchemaTemplate($tmp['schema'], true, $nsc);
e107::schema($this->schema);
}
unset($tmp);
}

View File

@ -88,6 +88,7 @@ $NEWS_VIEW_TEMPLATE['default']['item'] = '
';
/*
* <hr />
<h3>About the Author</h3>
@ -101,6 +102,38 @@ $NEWS_VIEW_TEMPLATE['default']['item'] = '
</div>
*/
$NEWS_VIEW_TEMPLATE['default']['schema'] = '
{
"@context": "https://schema.org",
"@type": "NewsArticle",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{NEWS_URL: full=1}"
},
"headline": "{NEWS_TITLE}",
"description": "{NEWS_DESCRIPTION}",
"image": [
"{SETIMAGE: w=800&h=800}{NEWS_IMAGE: item=1&return=url}"
],
"author": {
"@type": "Person",
"name": "{NEWS_AUTHOR=nolink}"
},
"publisher": {
"@type": "Organization",
"name": "{ORGANIZATION}",
"logo": {
"@type": "ImageObject",
"url": "{SITEBUTTON: type=url}"
}
},
"datePublished": "{NEWS_DATE=atom}",
"dateModified": "{NEWS_MODIFIED=atom}",
"articleBody": "{NEWS_BODY=raw}"
}
';
// @todo add more templates. eg. 'videos' , 'slideshow images', 'full width image' - help and ideas always appreciated.

View File

@ -15,12 +15,8 @@ class siteinfo_shortcodes // must match the folder name of the plugin.
{
$path = e107::getParser()->replaceConstants($_POST['sitebutton']);
}
else
{
$path = (strpos(SITEBUTTON, 'http:') !== false ? SITEBUTTON : e_IMAGE.SITEBUTTON);
}
if(varset($parm['type']) == 'email' || $parm == 'email') // (retain {} constants )
if(varset($parm['type']) == 'email' || $parm == 'email' || varset($parm['type']) == 'url') // (retain {} constants )
{
$h = !empty($parm['h']) ? $parm['h'] : 100;
@ -35,9 +31,19 @@ class siteinfo_shortcodes // must match the folder name of the plugin.
if(defined('e_MEDIA') && is_writable(e_MEDIA."temp/") && ($resized = e107::getMedia()->resizeImage($path, e_MEDIA."temp/".basename($realPath),'h='.$h)))
{
$path = e107::getParser()->createConstants($resized);
$path = e107::getParser()->createConstants($resized,'mix');
}
}
else
{
$path = (strpos(SITEBUTTON, 'http:') !== false || strpos(SITEBUTTON, e_IMAGE_ABS) !== false ? SITEBUTTON : e_IMAGE.SITEBUTTON);
}
if(varset($parm['type']) == 'url')
{
// return $path;
return e107::getParser()->replaceConstants($path,'full');
}
if(!empty($path))
{
@ -196,8 +202,14 @@ class siteinfo_shortcodes // must match the folder name of the plugin.
$opts['h'] = $dimensions[1];
}
// $imageStyle = (empty($dimensions)) ? '' : " style='width: ".$dimensions[0]."px; height: ".$dimensions[1]."px' ";
// $image = "<img class='logo img-responsive' src='".$logo."' ".$imageStyle." alt='".SITENAME."' />\n";
if(varset($parm['type']) == 'url')
{
return $tp->replaceConstants($logo, 'full');
}
elseif(varset($parm['type']) == 'email')
{
return $logo;
}
$image = $tp->toImage($logo,$opts);
@ -222,4 +234,13 @@ class siteinfo_shortcodes // must match the folder name of the plugin.
return (defined('THEME_DISCLAIMER') && $pref['displaythemeinfo'] ? THEME_DISCLAIMER : '');
}
function sc_organization()
{
$c = e107::getPref('contact_info');
$text = !empty($c['organization']) ? $c['organization'] : SITENAME;
return e107::getParser()->toText($text);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -51,16 +51,37 @@ class siteinfo_shortcodesTest extends \Codeception\Test\Unit
{
}
*/
public function testSc_sitebutton()
{
$result = $this->sc->sc_sitebutton();
$expected = 'class="sitebutton"><img src="/e107_images/button.png" alt="e107" /></a>';
self::assertStringContainsString($expected, $result);
$result = $this->sc->sc_sitebutton(['type'=>'email']);
$expected = 'button.png';
self::assertStringContainsString($expected, $result);
$result = $this->sc->sc_sitebutton(['type'=>'url']);
self::assertStringContainsString('http', $result);
}
public function testSc_sitelogo()
{
$result = $this->sc->sc_sitelogo(['w'=>100]);
self::assertStringContainsString('<img class="logo img-responsive img-fluid"', $result);
self::assertStringContainsString('width="100"', $result);
}*/
$result2 = $this->sc->sc_sitelogo(['type'=>'url']);
self::assertStringContainsString('http', $result2);
self::assertStringContainsString('logoHD.png', $result2);
$result3 = $this->sc->sc_sitelogo(['type'=>'email']);
self::assertStringContainsString('{e_IMAGE}logoHD.png', $result3);
}
public function testSc_logo()
{