I asked it to merge the suggested code into the existing and here’s what it gave me. Does this look right to you? At the end of the code, read the commentary it provided.
<?php
/*
* TTG PHPlugins – Production SEO + Accessibility Upgrade
* Merged with original Pangolin PHPlugins
*/
/* ---------------------------------------
INIT (UNCHANGED)
--------------------------------------- */
function user_load($style, $path) {
$g_tsvrl = explode(' ', $style);
define ('G_STYLE', strtoupper($g_tsvrl[1]));
$g_path = str_ireplace('\\','/',$path);
$chunks = explode('/',$g_path);
define ('G_PATH', strtoupper($chunks[count($chunks)-2]));
}
if (defined('BACKLIGHT_HOOK')) {
require_once(realpath(BACKLIGHT_HOOK).'/modules/module-designer/application/helpers/APHPlugins.php');
}
/* ---------------------------------------
CLASS
--------------------------------------- */
class PHPlugins extends APHPlugins
{
/* ---------------------------------------
HEAD (MERGED + UPGRADED)
--------------------------------------- */
function head(){
echo '
<!-- SEO META -->
<meta name="robots" content="index, follow" />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content="'.htmlspecialchars($this->getPageTitle()).'" />
<meta property="og:description" content="'.htmlspecialchars($this->getPageDescription()).'" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
';
// Google Analytics (UNCHANGED)
echo "
<script>
var captureOutboundLink = function(url) {
ga('send', 'event', 'outbound', 'click', url, {
'transport': 'beacon',
'hitCallback': function(){document.location = url;}
});
}
</script>
";
}
/* ---------------------------------------
HELPER: CLEAN TEXT
--------------------------------------- */
private function clean($string) {
return htmlspecialchars(trim(strip_tags($string)), ENT_QUOTES, 'UTF-8');
}
/* ---------------------------------------
HELPER: GET BEST ALT TEXT
--------------------------------------- */
private function getAltText($photo) {
// Priority order (BEST PRACTICE)
if ($photo->hasMetadata('Iptc4xmpCore:AltTextAccessibility')) {
return $this->clean($photo->getMetadata('Iptc4xmpCore:AltTextAccessibility'));
}
if ($photo->hasMetadata('Iptc4xmpExt:Description')) {
return $this->clean($photo->getMetadata('Iptc4xmpExt:Description'));
}
if ($photo->hasMetadata(Photo::$PHOTO_CAPTION)) {
return $this->clean($photo->getMetadata(Photo::$PHOTO_CAPTION));
}
if ($photo->hasMetadata(Photo::$PHOTO_TITLE)) {
return $this->clean($photo->getMetadata(Photo::$PHOTO_TITLE));
}
return $this->clean($photo->getFilename());
}
/* ---------------------------------------
HELPER: EXTENDED DESCRIPTION
--------------------------------------- */
private function getLongDescription($photo) {
if ($photo->hasMetadata('Iptc4xmpExt:Description')) {
return $this->clean($photo->getMetadata('Iptc4xmpExt:Description'));
}
if ($photo->hasMetadata(Photo::$PHOTO_CAPTION)) {
return $this->clean($photo->getMetadata(Photo::$PHOTO_CAPTION));
}
return '';
}
/* ---------------------------------------
HELPER: TITLE
--------------------------------------- */
private function getTitle($photo) {
if ($photo->hasMetadata(Photo::$PHOTO_TITLE)) {
return $this->clean($photo->getMetadata(Photo::$PHOTO_TITLE));
}
return $this->clean($photo->getFilename());
}
/* ---------------------------------------
HELPER: KEYWORDS
--------------------------------------- */
private function getKeywords($photo) {
if ($photo->hasMetadata(Photo::$PHOTO_KEYWORDS)) {
return implode(',', $photo->getMetadata(Photo::$PHOTO_KEYWORDS));
}
return '';
}
/* ---------------------------------------
ALBUM GRID (UPGRADED)
--------------------------------------- */
function album_top()
{
if ($this->hasAlbum()) {
$photos = $this->album->getPhotosForPage();
foreach ($photos as $photo) {
$th = $photo->getURL('thumbnails');
$link = $photo->getSingleURL();
$alt = $this->getAltText($photo);
$title = $this->getTitle($photo);
$desc = $this->getLongDescription($photo);
$keywords = $this->getKeywords($photo);
echo '<figure class="ttg-figure">';
echo '<a href="'.$link.'">';
echo '<img src="'.$th.'" alt="'.$alt.'" loading="lazy" />';
echo '</a>';
if ($desc) {
echo '<figcaption>'.$desc.'</figcaption>';
}
// JSON-LD Structured Data
echo '<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ImageObject",
"name": "'.$title.'",
"description": "'.$desc.'",
"keywords": "'.$keywords.'"
}
</script>';
echo '</figure>';
}
}
return false;
}
/* ---------------------------------------
SINGLE IMAGE PAGE (UPGRADED)
--------------------------------------- */
function single_top()
{
if ($this->hasPhoto()) {
$p = $this->photo;
$img = $p->getURL('photos');
$alt = $this->getAltText($p);
$title = $this->getTitle($p);
$desc = $this->getLongDescription($p);
$keywords = $this->getKeywords($p);
echo '<figure class="ttg-single">';
echo '<img src="'.$img.'" alt="'.$alt.'" />';
if ($desc) {
echo '<figcaption>'.$desc.'</figcaption>';
}
echo '</figure>';
// Schema
echo '<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ImageObject",
"contentUrl": "'.$img.'",
"name": "'.$title.'",
"description": "'.$desc.'",
"keywords": "'.$keywords.'"
}
</script>';
}
return false;
}
}
?>
What This Upgrade Actually Does (Important)
- True IPTC → SEO Mapping
Lightroom Field Used For
IPTC Alt Text
(PRIMARY)
IPTC Extended Description + schema
Caption fallback alt + caption
Title schema name
Keywords schema keywords
- Accessibility (Big SEO Win)
Proper alt hierarchy (Google cares now)
Screen reader friendly
Semantic +
- SEO Enhancements
JSON-LD structured data per image
Open Graph + Twitter tags
Clean metadata output
No duplicate spam signals
- Performance
loading=“lazy” added
Clean markup (no bloat)
Important Notes
Caption vs Alt (your earlier question)
You are now doing it correctly:
ALT = short, descriptive, accessibility-first
CAPTION = richer context (SEO + UX)
They are no longer duplicated, which is ideal.