Changeset 3769261
- Timestamp:
- 10/27/2026 06:50:35 PM (4 days ago)
- Location:
- your-seo-squad
- Files:
-
- 4 edited
- 1 copied
-
tags/1.11.6 (copied) (copied from your-seo-squad/trunk )
-
tags/1.11.6/readme.txt (modified) (2 diffs)
-
tags/1.11.6/your-seo-squad.php (modified) (6 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/your-seo-squad.php (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
your-seo-squad/tags/1.11.6/readme.txt
r3767057 r3769261 4 4 Tested up to: 7.1 5 5 Requires PHP: 8.1 6 Stable tag: 1.11. 56 Stable tag: 1.11.6 7 7 License: GPL-2.0-or-later 8 8 License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html … … 85 85 == Changelog == 86 86 87 = 1.11.6 = 88 * Images: can describe one specific image whose alt text is empty, such as an image used as a link, after you approve that exact change. Every other image stays as it is, and undo restores the empty alt text. 89 87 90 = 1.11.5 = 88 91 * Page drafts: the connector no longer removes a draft it created with version 1.10.4 or earlier, because those versions did not record everything an owner can add to a draft (custom fields, social or schema settings). Review and remove such drafts yourself in WordPress. Drafts created with this version are still protected and removable exactly as before. -
your-seo-squad/tags/1.11.6/your-seo-squad.php
r3767057 r3769261 3 3 * Plugin Name: Your SEO Squad 4 4 * Description: Connect WordPress to Your SEO Squad to apply individually approved SEO changes, with revision checks and guarded undo. 5 * Version: 1.11. 55 * Version: 1.11.6 6 6 * Author: Your SEO Squad 7 7 * Author URI: https://yourseosquad.com/ … … 187 187 if (strtolower((string)$engine) !== 'innodb') { $reasons[] = 'TRANSACTION_STORAGE_UNSUPPORTED'; break; } 188 188 } 189 return ['connectorVersion' => '1.11. 5', 'supported' => !$reasons, 'fields' => ['title', 'description'], 'reasons' => $reasons, 'content' => self::content_capabilities(), 'drafts' => self::draft_capabilities(), 'markup' => self::markup_capabilities(), 'robots' => self::robots_capabilities($options), 'adapter' => 'companion_native_v1', 'seoPlugin' => $seo, 'seoPluginVersion'=>$version, 'otherPlugins' => $plugins, 'muPlugins' => $mu_plugins, 'dropIns' => $dropins, 'theme' => (string)($options['stylesheet'] ?? ''), 'categoryArchives' => !$reasons && self::term_supported($options)];189 return ['connectorVersion' => '1.11.6', 'supported' => !$reasons, 'fields' => ['title', 'description'], 'reasons' => $reasons, 'content' => self::content_capabilities(), 'drafts' => self::draft_capabilities(), 'markup' => self::markup_capabilities(), 'robots' => self::robots_capabilities($options), 'adapter' => 'companion_native_v1', 'seoPlugin' => $seo, 'seoPluginVersion'=>$version, 'otherPlugins' => $plugins, 'muPlugins' => $mu_plugins, 'dropIns' => $dropins, 'theme' => (string)($options['stylesheet'] ?? ''), 'categoryArchives' => !$reasons && self::term_supported($options)]; 190 190 } 191 191 private static function term_supported(array $options): bool { … … 635 635 * 'alt', with 'occurrence' and optionally the public copy count when one copy is named. */ 636 636 private static function image_entry_keys(array $image): array { $keys = array_keys($image); sort($keys); return $keys; } 637 private static function valid_image_entry(array $image): bool { 638 $keys = self::image_entry_keys($image); 639 $named = in_array($keys, [['alt', 'occurrence', 'src'], ['alt', 'copies', 'occurrence', 'src'], ['alt', 'expectedAlt', 'occurrence', 'src'], ['alt', 'copies', 'expectedAlt', 'occurrence', 'src']], true); 640 if ($keys !== ['alt', 'src'] && (!$named || !is_int($image['occurrence']) || $image['occurrence'] < 0 || $image['occurrence'] > 10000000)) { return false; } 641 if (array_key_exists('copies', $image) && (!is_int($image['copies']) || $image['copies'] < 1 || $image['copies'] > 10000000)) { return false; } 642 if (array_key_exists('expectedAlt', $image) && $image['expectedAlt'] !== '') { return false; } 643 return is_string($image['src']) && strlen($image['src']) <= 2048 && preg_match('#^https?://#i', $image['src']) && self::plain($image['alt'], 200) !== null; 644 } 637 645 /** The same image however its address is written: resolved against the page's address, without a scheme, a leading 638 646 * "www.", a #fragment or WordPress's resize suffix ("photo-300x200.jpg" is "photo.jpg"). */ … … 656 664 $key = self::image_url_key((string)$image['src'], $base); 657 665 if (isset($image['occurrence'])) { 658 if (isset($named[$key][$image['occurrence']]) && $named[$key][$image['occurrence']] !== $image['alt']) { return null; } 659 $named[$key][$image['occurrence']] = $image['alt']; 666 $target = ['alt' => $image['alt'], 'expectedEmpty' => array_key_exists('expectedAlt', $image) && $image['expectedAlt'] === '']; 667 if (isset($named[$key][$image['occurrence']]) && $named[$key][$image['occurrence']] !== $target) { return null; } 668 $named[$key][$image['occurrence']] = $target; 660 669 } else { 661 670 if (isset($approved[$key]) && $approved[$key] !== $image['alt']) { return null; } … … 685 694 $at = $seen[$key] ?? 0; $seen[$key] = $at + 1; 686 695 $exact = isset($named[$key][$at]); 687 $alt = $exact ? $named[$key][$at] : ($approved[$key] ?? null); 688 if ($alt === null || $html->get_attribute('alt') !== null) { continue; } 696 $target = $exact ? $named[$key][$at] : null; 697 $alt = $exact ? $target['alt'] : ($approved[$key] ?? null); 698 $current = $html->get_attribute('alt'); 699 if ($alt === null || ($target['expectedEmpty'] ?? false ? $current !== '' : $current !== null)) { continue; } 689 700 if (!$html->set_attribute('alt', $alt)) { return null; } 690 701 $changed[$exact ? $key . '#' . $at : $key] = true; … … 743 754 $op_valid = is_array($op) && (($op['kind'] ?? null) === 'insert_section' && array_keys($op) === ['kind', 'position', 'sections'] && $op['position'] === 'end' && self::section_markup($op['sections']) !== null 744 755 || ($op['kind'] ?? null) === 'insert_link' && array_keys($op) === ['kind', 'passage', 'anchorText', 'href'] && self::plain($op['passage'], 500) !== null && self::plain($op['anchorText'], 120) !== null && is_string($op['href']) && self::same_site_url($op['href']) 745 || ($op['kind'] ?? null) === 'set_image_alts' && array_keys($op) === ['kind', 'images'] && is_array($op['images']) && array_is_list($op['images']) && $op['images'] && count($op['images']) <= 10 && count(array_filter($op['images'], fn($i) => is_array($i) && (self::image_entry_keys($i) === ['alt', 'src'] || (self::image_entry_keys($i) === ['alt', 'occurrence', 'src'] || self::image_entry_keys($i) === ['alt', 'copies', 'occurrence', 'src'] && is_int($i['copies']) && $i['copies'] >= 1 && $i['copies'] <= 10000000) && is_int($i['occurrence']) && $i['occurrence'] >= 0 && $i['occurrence'] <= 10000000) && is_string($i['src']) && strlen($i['src']) <= 2048 && preg_match('#^https?://#i', $i['src']) && self::plain($i['alt'], 200) !== null)) === count($op['images'])756 || ($op['kind'] ?? null) === 'set_image_alts' && array_keys($op) === ['kind', 'images'] && is_array($op['images']) && array_is_list($op['images']) && $op['images'] && count($op['images']) <= 10 && count(array_filter($op['images'], fn($i) => is_array($i) && self::valid_image_entry($i))) === count($op['images']) 746 757 || ($op['kind'] ?? null) === 'demote_headings' && array_keys($op) === ['kind', 'headings'] && is_array($op['headings']) && array_is_list($op['headings']) && $op['headings'] && count($op['headings']) <= 6 && count(array_filter($op['headings'], fn($h) => self::plain($h, 180) !== null)) === count($op['headings']) 747 758 || ($op['kind'] ?? null) === 'change_link' && array_keys($op) === ['kind', 'href', 'anchorText', 'replacementHref'] && is_string($op['href']) && strlen($op['href']) <= 2048 && preg_match('#^https?://[^\s<>"\']+$#iD', $op['href']) && self::plain($op['anchorText'], 120) !== null && ($op['replacementHref'] === null || is_string($op['replacementHref']) && self::same_site_url($op['replacementHref'])) -
your-seo-squad/trunk/readme.txt
r3767057 r3769261 4 4 Tested up to: 7.1 5 5 Requires PHP: 8.1 6 Stable tag: 1.11. 56 Stable tag: 1.11.6 7 7 License: GPL-2.0-or-later 8 8 License URI: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html … … 85 85 == Changelog == 86 86 87 = 1.11.6 = 88 * Images: can describe one specific image whose alt text is empty, such as an image used as a link, after you approve that exact change. Every other image stays as it is, and undo restores the empty alt text. 89 87 90 = 1.11.5 = 88 91 * Page drafts: the connector no longer removes a draft it created with version 1.10.4 or earlier, because those versions did not record everything an owner can add to a draft (custom fields, social or schema settings). Review and remove such drafts yourself in WordPress. Drafts created with this version are still protected and removable exactly as before. -
your-seo-squad/trunk/your-seo-squad.php
r3767057 r3769261 3 3 * Plugin Name: Your SEO Squad 4 4 * Description: Connect WordPress to Your SEO Squad to apply individually approved SEO changes, with revision checks and guarded undo. 5 * Version: 1.11. 55 * Version: 1.11.6 6 6 * Author: Your SEO Squad 7 7 * Author URI: https://yourseosquad.com/ … … 187 187 if (strtolower((string)$engine) !== 'innodb') { $reasons[] = 'TRANSACTION_STORAGE_UNSUPPORTED'; break; } 188 188 } 189 return ['connectorVersion' => '1.11. 5', 'supported' => !$reasons, 'fields' => ['title', 'description'], 'reasons' => $reasons, 'content' => self::content_capabilities(), 'drafts' => self::draft_capabilities(), 'markup' => self::markup_capabilities(), 'robots' => self::robots_capabilities($options), 'adapter' => 'companion_native_v1', 'seoPlugin' => $seo, 'seoPluginVersion'=>$version, 'otherPlugins' => $plugins, 'muPlugins' => $mu_plugins, 'dropIns' => $dropins, 'theme' => (string)($options['stylesheet'] ?? ''), 'categoryArchives' => !$reasons && self::term_supported($options)];189 return ['connectorVersion' => '1.11.6', 'supported' => !$reasons, 'fields' => ['title', 'description'], 'reasons' => $reasons, 'content' => self::content_capabilities(), 'drafts' => self::draft_capabilities(), 'markup' => self::markup_capabilities(), 'robots' => self::robots_capabilities($options), 'adapter' => 'companion_native_v1', 'seoPlugin' => $seo, 'seoPluginVersion'=>$version, 'otherPlugins' => $plugins, 'muPlugins' => $mu_plugins, 'dropIns' => $dropins, 'theme' => (string)($options['stylesheet'] ?? ''), 'categoryArchives' => !$reasons && self::term_supported($options)]; 190 190 } 191 191 private static function term_supported(array $options): bool { … … 635 635 * 'alt', with 'occurrence' and optionally the public copy count when one copy is named. */ 636 636 private static function image_entry_keys(array $image): array { $keys = array_keys($image); sort($keys); return $keys; } 637 private static function valid_image_entry(array $image): bool { 638 $keys = self::image_entry_keys($image); 639 $named = in_array($keys, [['alt', 'occurrence', 'src'], ['alt', 'copies', 'occurrence', 'src'], ['alt', 'expectedAlt', 'occurrence', 'src'], ['alt', 'copies', 'expectedAlt', 'occurrence', 'src']], true); 640 if ($keys !== ['alt', 'src'] && (!$named || !is_int($image['occurrence']) || $image['occurrence'] < 0 || $image['occurrence'] > 10000000)) { return false; } 641 if (array_key_exists('copies', $image) && (!is_int($image['copies']) || $image['copies'] < 1 || $image['copies'] > 10000000)) { return false; } 642 if (array_key_exists('expectedAlt', $image) && $image['expectedAlt'] !== '') { return false; } 643 return is_string($image['src']) && strlen($image['src']) <= 2048 && preg_match('#^https?://#i', $image['src']) && self::plain($image['alt'], 200) !== null; 644 } 637 645 /** The same image however its address is written: resolved against the page's address, without a scheme, a leading 638 646 * "www.", a #fragment or WordPress's resize suffix ("photo-300x200.jpg" is "photo.jpg"). */ … … 656 664 $key = self::image_url_key((string)$image['src'], $base); 657 665 if (isset($image['occurrence'])) { 658 if (isset($named[$key][$image['occurrence']]) && $named[$key][$image['occurrence']] !== $image['alt']) { return null; } 659 $named[$key][$image['occurrence']] = $image['alt']; 666 $target = ['alt' => $image['alt'], 'expectedEmpty' => array_key_exists('expectedAlt', $image) && $image['expectedAlt'] === '']; 667 if (isset($named[$key][$image['occurrence']]) && $named[$key][$image['occurrence']] !== $target) { return null; } 668 $named[$key][$image['occurrence']] = $target; 660 669 } else { 661 670 if (isset($approved[$key]) && $approved[$key] !== $image['alt']) { return null; } … … 685 694 $at = $seen[$key] ?? 0; $seen[$key] = $at + 1; 686 695 $exact = isset($named[$key][$at]); 687 $alt = $exact ? $named[$key][$at] : ($approved[$key] ?? null); 688 if ($alt === null || $html->get_attribute('alt') !== null) { continue; } 696 $target = $exact ? $named[$key][$at] : null; 697 $alt = $exact ? $target['alt'] : ($approved[$key] ?? null); 698 $current = $html->get_attribute('alt'); 699 if ($alt === null || ($target['expectedEmpty'] ?? false ? $current !== '' : $current !== null)) { continue; } 689 700 if (!$html->set_attribute('alt', $alt)) { return null; } 690 701 $changed[$exact ? $key . '#' . $at : $key] = true; … … 743 754 $op_valid = is_array($op) && (($op['kind'] ?? null) === 'insert_section' && array_keys($op) === ['kind', 'position', 'sections'] && $op['position'] === 'end' && self::section_markup($op['sections']) !== null 744 755 || ($op['kind'] ?? null) === 'insert_link' && array_keys($op) === ['kind', 'passage', 'anchorText', 'href'] && self::plain($op['passage'], 500) !== null && self::plain($op['anchorText'], 120) !== null && is_string($op['href']) && self::same_site_url($op['href']) 745 || ($op['kind'] ?? null) === 'set_image_alts' && array_keys($op) === ['kind', 'images'] && is_array($op['images']) && array_is_list($op['images']) && $op['images'] && count($op['images']) <= 10 && count(array_filter($op['images'], fn($i) => is_array($i) && (self::image_entry_keys($i) === ['alt', 'src'] || (self::image_entry_keys($i) === ['alt', 'occurrence', 'src'] || self::image_entry_keys($i) === ['alt', 'copies', 'occurrence', 'src'] && is_int($i['copies']) && $i['copies'] >= 1 && $i['copies'] <= 10000000) && is_int($i['occurrence']) && $i['occurrence'] >= 0 && $i['occurrence'] <= 10000000) && is_string($i['src']) && strlen($i['src']) <= 2048 && preg_match('#^https?://#i', $i['src']) && self::plain($i['alt'], 200) !== null)) === count($op['images'])756 || ($op['kind'] ?? null) === 'set_image_alts' && array_keys($op) === ['kind', 'images'] && is_array($op['images']) && array_is_list($op['images']) && $op['images'] && count($op['images']) <= 10 && count(array_filter($op['images'], fn($i) => is_array($i) && self::valid_image_entry($i))) === count($op['images']) 746 757 || ($op['kind'] ?? null) === 'demote_headings' && array_keys($op) === ['kind', 'headings'] && is_array($op['headings']) && array_is_list($op['headings']) && $op['headings'] && count($op['headings']) <= 6 && count(array_filter($op['headings'], fn($h) => self::plain($h, 180) !== null)) === count($op['headings']) 747 758 || ($op['kind'] ?? null) === 'change_link' && array_keys($op) === ['kind', 'href', 'anchorText', 'replacementHref'] && is_string($op['href']) && strlen($op['href']) <= 2048 && preg_match('#^https?://[^\s<>"\']+$#iD', $op['href']) && self::plain($op['anchorText'], 120) !== null && ($op['replacementHref'] === null || is_string($op['replacementHref']) && self::same_site_url($op['replacementHref']))
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)