wp/wp-content/plugins/include-mastodon-feed/plugin.php
changeset 23 417f20492bf7
parent 21 48c4eec2b7e6
--- a/wp/wp-content/plugins/include-mastodon-feed/plugin.php	Fri Sep 05 18:52:52 2025 +0200
+++ b/wp/wp-content/plugins/include-mastodon-feed/plugin.php	Mon Sep 08 19:44:41 2025 +0200
@@ -3,9 +3,11 @@
   Plugin Name: Include Mastodon Feed
 	Plugin URI: https://wolfgang.lol/code/include-mastodon-feed-wordpress-plugin
 	Description: Plugin providing [include-mastodon-feed] shortcode
-	Version: 1.9.4
+	Version: 1.13.1
 	Author: wolfgang.lol
 	Author URI: https://wolfgang.lol
+  License: MIT
+  License URI: https://directory.fsf.org/wiki/License:Expat
 */
 
 namespace IncludeMastodonFeedPlugin;
@@ -49,6 +51,14 @@
     'value' => false,
   ],
   [
+    'key' => 'INCLUDE_MASTODON_FEED_IMAGE_SIZE',
+    'value' => 'preview',
+  ],
+  [
+    'key' => 'INCLUDE_MASTODON_FEED_IMAGE_LINK',
+    'value' => 'status',
+  ],
+  [
     'key' => 'INCLUDE_MASTODON_FEED_TAGGED',
     'value' => false,
   ],
@@ -76,7 +86,7 @@
   ],
   [
     'key' => 'INCLUDE_MASTODON_FEED_STYLE_ACCENT_COLOR',
-    'value' => 'rgb(99, 100, 255)',
+    'value' => 'rgb(86, 58, 204)',
   ],
   [
     'key' => 'INCLUDE_MASTODON_FEED_STYLE_ACCENT_FONT_COLOR',
@@ -101,7 +111,7 @@
   ],
   [
     'key' => 'INCLUDE_MASTODON_FEED_TEXT_NO_STATUSES',
-    'value' => 'No statuses availablae',
+    'value' => 'No statuses available',
   ],
   [
     'key' => 'INCLUDE_MASTODON_FEED_TEXT_BOOSTED',
@@ -149,7 +159,6 @@
 
 
 function init_styles() {
-  ob_start();
 ?>
   <style>
     :root {
@@ -160,7 +169,12 @@
       --include-mastodon-feed-border-radius: <?php echo filter_var( INCLUDE_MASTODON_FEED_STYLE_BORDER_RADIUS, FILTER_UNSAFE_RAW ); ?>;
     }
 
+    .include-mastodon-feed-wrapper .include-mastodon-feed {
+      list-style: none;
+      padding-left: 0;
+    }
     .include-mastodon-feed .status {
+      display: block;
       margin: 0.5rem 0 1.5rem;
       border-radius: var(--include-mastodon-feed-border-radius);
       padding: 0.5rem;
@@ -175,6 +189,7 @@
       text-decoration: underline;
     }
     .include-mastodon-feed .avatar {
+      display: inline-block;
       height: 1.25rem;
       border-radius: var(--include-mastodon-feed-border-radius);
       vertical-align: top;
@@ -225,13 +240,16 @@
     }
     .include-mastodon-feed .media {
       display: flex;
+      list-style: none;
+      padding: 0;
       justify-content: space-around;
       align-items: center;
       flex-wrap: wrap;
       gap: 0.5rem;
       margin: 1rem;
     }
-    .include-mastodon-feed .media > div {
+    .include-mastodon-feed .media > * {
+      display: block;
       flex-basis: calc(50% - 0.5rem);
       flex-grow: 1;
     }
@@ -303,12 +321,10 @@
     }
   </style>
 <?php
-  echo ob_get_clean();
 }
 add_action('wp_head', __NAMESPACE__ . '\init_styles', 7);
 
 function init_scripts() {
-  ob_start();
 ?>
   <script>
 
@@ -323,9 +339,12 @@
     const mastodonFeedCreateElementAccountLink = function(account) {
       let accountLinkElem = mastodonFeedCreateElement('a');
       accountLinkElem.href = account.url;
+      accountLinkElem.setAttribute('aria-label', 'Link to Mastodon account of ' + account.display_name);
 
       let accountImageElem = mastodonFeedCreateElement('img', 'avatar');
       accountImageElem.src = account.avatar_static;
+      accountImageElem.loading = 'lazy';
+      accountImageElem.alt = 'Mastodon avatar image of ' + account.display_name;
 
       accountLinkElem.addEventListener('mouseover', (event) => {
         accountLinkElem.querySelector('.avatar').src = account.avatar;
@@ -346,41 +365,46 @@
       return accountLinkElem;
     }
 
-    const mastodonFeedCreateElementPermalink = function(status, label) {
+    const mastodonFeedCreateElementPermalink = function(status, label, ariaLabel) {
       let linkElem = mastodonFeedCreateElement('a');
       linkElem.href = status.url;
       linkElem.appendChild(document.createTextNode(label));
+      linkElem.setAttribute('aria-label', ariaLabel);
       return linkElem;
     }
 
     const mastodonFeedCreateElementMediaAttachments = function(status, options) {
       let attachments = status.media_attachments;
-      let mediaWrapperElem = mastodonFeedCreateElement('div', 'media');
+      let mediaWrapperElem = mastodonFeedCreateElement('ol', 'media');
       for(let mediaIndex = 0; mediaIndex < attachments.length; mediaIndex++) {
         let media = attachments[mediaIndex];
-        let mediaElem = mastodonFeedCreateElement('div', media.type);
+        let mediaElem = mastodonFeedCreateElement('li', media.type);
         if('image' == media.type) {
           let mediaElemImgLink = mastodonFeedCreateElement('a');
+          let imageUrl = media.url;
+          if('full' !== options.images.size && null !== media.preview_url) {
+            imageUrl = media.preview_url;
+          }
           mediaElemImgLink.href = status.url;
-          if(null === media.remote_url) {
-            mediaElemImgLink.style.backgroundImage = 'url("' + media.preview_url + '")';
+          if('image' === options.images.link) {
+            mediaElemImgLink.href = media.remote_url ?? media.url;
+          }
+          let mediaElemImgImage = mastodonFeedCreateElement('img');
+          mediaElemImgImage.src = imageUrl;
+          mediaElemImgImage.loading = 'lazy';
+          if(null === media.description) {
+            mediaElemImgImage.alt = 'Image attachment of Mastodon post';
           }
           else {
-            mediaElemImgLink.style.backgroundImage = 'url("' + media.remote_url + '")';
-          }
-          if(null !== media.description) {
-            mediaElem.title = media.description;
+            mediaElemImgImage.alt = media.description;
           }
-          if(options.preserveImageAspectRatio) {
-            let mediaElemImgImage = mastodonFeedCreateElement('img');
-            if(null === media.remote_url) {
-              mediaElemImgImage.src = media.preview_url;
-            }
-            else {
-              mediaElemImgImage.src = media.remote_url;
-            }
-            mediaElemImgLink.appendChild(mediaElemImgImage);
+          if(!options.images.preserveImageAspectRatio) {
+            mediaElemImgLink.style.backgroundImage = 'url("' + imageUrl + '")';
+            mediaElemImgImage.style.width = '100%';
+            mediaElemImgImage.style.height = '100%';
+            mediaElemImgImage.style.opacity = 0;
           }
+          mediaElemImgLink.appendChild(mediaElemImgImage);
           mediaElem.appendChild(mediaElemImgLink);
         }
         else if('gifv' == media.type) {
@@ -395,8 +419,11 @@
           }
           mediaElemGifv.loop = true;
           mediaElemGifv.muted = 'muted';
-          if(null !== media.description) {
-            mediaElemGifv.title = media.description;
+          if(null === media.description) {
+            mediaElemGifv.alt = 'Video attachment of Mastodon post';
+          }
+          else {
+            mediaElemGifv.alt = media.description;
           }
           mediaElemGifvLink.appendChild(mediaElemGifv);
           mediaElem.appendChild(mediaElemGifvLink);
@@ -414,7 +441,7 @@
           //      currently only image and gifv support implemented
           mediaElem.innerHTML = 'Stripped ' + media.type + ' - only available on instance<br />';
           let permalinkElem = mastodonFeedCreateElement('span', 'permalink');
-          permalinkElem.appendChild(mastodonFeedCreateElementPermalink(status, options.text.viewOnInstance));
+          permalinkElem.appendChild(mastodonFeedCreateElementPermalink(status, options.text.viewOnInstance, 'Link to Mastodon post'));
           mediaElem.appendChild(permalinkElem);
         }
         mediaWrapperElem.appendChild(mediaElem);
@@ -431,7 +458,14 @@
         if(null !== card.image) {
           let cardElemImageWrapper = mastodonFeedCreateElement('div', 'image');
           let cardElemImage = mastodonFeedCreateElement('img');
+          if(null === card.image_description) {
+            cardElemImage.alt = 'Preview image content card';
+          }
+          else {
+            cardElemImage.alt = card.image_description;
+          }
           cardElemImage.src = card.image;
+          cardElemImage.loading = 'lazy';
           cardElemImageWrapper.appendChild(cardElemImage);
           cardElemMeta.appendChild(cardElemImageWrapper);
         }
@@ -450,6 +484,7 @@
         else {
           let cardElemLink = mastodonFeedCreateElement('a');
           cardElemLink.href = card.url;
+          cardElemLink.setAttribute('aria-label', 'Link embedded in Mastodon post');
           cardElemLink.appendChild(cardElemMeta);
           cardElem.appendChild(cardElemLink);
         }
@@ -467,7 +502,7 @@
         createdInfo.innerHTML += new Date(status.created_at).toLocaleString(options.localization.date.locale, options.localization.date.options);
       }
       else {
-        createdInfo.appendChild(mastodonFeedCreateElementPermalink(status, new Date(status.created_at).toLocaleString(options.localization.date.locale, options.localization.date.options)));
+        createdInfo.appendChild(mastodonFeedCreateElementPermalink(status, new Date(status.created_at).toLocaleString(options.localization.date.locale, options.localization.date.options), 'Link to Mastodon post'));
       }
       createdInfo.innerHTML += ' ' + options.text.permalinkPost;
       return createdInfo;
@@ -479,7 +514,6 @@
 
     const mastodonFeedRenderStatuses = function(statuses, rootElem, options) {
       if(statuses.length < 1) {
-        console.log(options);
         rootElem.innerHTML = options.text.noStatuses;
       }
       else {
@@ -488,7 +522,7 @@
           let isEdited = (null === status.edited_at ? true : false);
           let isReblog = (null === status.reblog ? false : true);
 
-          let statusElem = mastodonFeedCreateElement('div', 'status');
+          let statusElem = mastodonFeedCreateElement('li', 'status');
 
           // add account meta info
           if(!options.content.hideStatusMeta) {
@@ -539,6 +573,7 @@
 
             let cwLinkElem = mastodonFeedCreateElement('a');
             cwLinkElem.href = '#';
+            cwLinkElem.setAttribute('aria-label', 'Show content despite warning');
             cwLinkElem.onclick = function() {
               this.parentElement.style = 'display: none;';
               this.parentElement.nextSibling.style = 'display: block;';
@@ -578,11 +613,11 @@
           rootElem.appendChild(statusElem);
         }
       }
-      if('_self' != options.linkTarget) {
-        rootElem.querySelectorAll('a').forEach(function(e) {
+      rootElem.querySelectorAll('a').forEach(function(e) {
+        if('_self' != options.linkTarget) {
           e.target = options.linkTarget;
-        });
-      }
+        }
+      });
     }
 
     const mastodonFeedLoad = function(url, elementId, options) {
@@ -594,11 +629,13 @@
         const rootElem = document.getElementById(elementId);
         rootElem.innerHTML = '';
         <?php if(true === INCLUDE_MASTODON_FEED_DEBUG) : ?>
-          console.log("<?php echo __NAMESPACE__; ?>", url);
+          console.log("<?php echo __NAMESPACE__; ?>", 'url', url);
+          console.log("<?php echo __NAMESPACE__; ?>", 'elementId', elementId);
+          console.log("<?php echo __NAMESPACE__; ?>", 'options', options);
         <?php endif; ?>
         if (xhr.status === 200) {
           <?php if(true === INCLUDE_MASTODON_FEED_DEBUG) : ?>
-            console.log("<?php echo __NAMESPACE__; ?>", xhr.response);
+            console.log("<?php echo __NAMESPACE__; ?>", 'response', xhr.response);
           <?php endif; ?>
           if(options.excludeConversationStarters && statuses.length > 0) {
             const filteredStatuses = [];
@@ -625,7 +662,7 @@
         }
         else {
           <?php if(true === INCLUDE_MASTODON_FEED_DEBUG) : ?>
-            console.log("<?php echo __NAMESPACE__; ?>", xhr);
+            console.log("<?php echo __NAMESPACE__; ?>", 'response error', xhr);
           <?php endif; ?>
           rootElem.appendChild(document.createTextNode(xhr.response.error));
         }
@@ -634,7 +671,6 @@
     }
   </script>
 <?php
-  echo ob_get_clean();
 }
 add_action('wp_footer', __NAMESPACE__ . '\init_scripts');
 
@@ -651,6 +687,8 @@
           'onlypinned' => filter_var(esc_html(INCLUDE_MASTODON_FEED_ONLY_PINNED), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
           'onlymedia' => filter_var(esc_html(INCLUDE_MASTODON_FEED_ONLY_MEDIA), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
           'preserveimageaspectratio' => filter_var(esc_html(INCLUDE_MASTODON_FEED_PRESERVE_IMAGE_ASPECT_RATIO), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
+          'imagesize' => INCLUDE_MASTODON_FEED_IMAGE_SIZE,
+          'imagelink' => INCLUDE_MASTODON_FEED_IMAGE_LINK,
           'tagged' => INCLUDE_MASTODON_FEED_TAGGED,
           'linktarget' => INCLUDE_MASTODON_FEED_LINKTARGET,
           'showpreviewcards' => filter_var(esc_html(INCLUDE_MASTODON_FEED_SHOW_PREVIEWCARDS), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
@@ -665,7 +703,6 @@
           'text-permalinkpost' => INCLUDE_MASTODON_FEED_TEXT_PERMALINK_POST,
           'text-edited' => INCLUDE_MASTODON_FEED_TEXT_EDITED,
           'date-locale' => INCLUDE_MASTODON_FEED_DATE_LOCALE,
-          'date-options' => INCLUDE_MASTODON_FEED_DATE_OPTIONS,
           'darkmode' => filter_var(esc_html(INCLUDE_MASTODON_FEED_DARKMODE), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
       ), array_change_key_case($atts, CASE_LOWER)
   );
@@ -713,37 +750,41 @@
   <script>
     window.addEventListener("load", () => {
       mastodonFeedLoad(
-        "<?php echo sanitize_url( $apiUrl, ['https'] ); ?>",
+        "<?php echo esc_url( $apiUrl, ['https'], 'apicall' ); ?>",
         "<?php echo filter_var( $elemId, FILTER_UNSAFE_RAW ); ?>",
         {
-          linkTarget: "<?php echo filter_var( $atts['linktarget'], FILTER_UNSAFE_RAW ); ?>",
+          linkTarget: "<?php echo esc_js(filter_var( $atts['linktarget'], FILTER_UNSAFE_RAW )); ?>",
           showPreviewCards: <?php echo (filter_var( $atts['showpreviewcards'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
           excludeConversationStarters: <?php echo (filter_var( $atts['excludeconversationstarters'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
-          preserveImageAspectRatio: <?php echo (filter_var( $atts['preserveimageaspectratio'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
           content: {
             hideStatusMeta: <?php echo (filter_var( $atts['hidestatusmeta'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
             hideDateTime: <?php echo (filter_var( $atts['hidedatetime'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>
           },
+          images: {
+            preserveImageAspectRatio: <?php echo (filter_var( $atts['preserveimageaspectratio'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? "true" : "false"); ?>,
+            size: "<?php echo ( "full" === $atts['imagesize'] ? "full" : "preview" ); ?>",
+            link: "<?php echo ( "image" === $atts['imagelink'] ? "image" : "status" ); ?>",
+          },
           text: {
-            boosted: "<?php echo esc_js( $atts['text-boosted'] ); ?>",
+            boosted: "<?php echo esc_html( $atts['text-boosted'] ); ?>",
             noStatuses: "<?php echo esc_html( $atts['text-nostatuses'] ); ?>",
             viewOnInstance: "<?php echo esc_js( $atts['text-viewoninstance'] ); ?>",
-            showContent: "<?php echo esc_js( $atts['text-showcontent'] ); ?>",
-            permalinkPre: "<?php echo esc_js( $atts['text-permalinkpre'] ); ?>",
-            permalinkPost: "<?php echo esc_js( $atts['text-permalinkpost'] ); ?>",
-            edited: "<?php echo esc_js( $atts['text-edited'] ); ?>",
+            showContent: "<?php echo esc_html( $atts['text-showcontent'] ); ?>",
+            permalinkPre: "<?php echo esc_html( $atts['text-permalinkpre'] ); ?>",
+            permalinkPost: "<?php echo esc_html( $atts['text-permalinkpost'] ); ?>",
+            edited: "<?php echo esc_html( $atts['text-edited'] ); ?>",
           },
           localization: {
             date: {
-              locale: "<?php echo filter_var( $atts['date-locale'], FILTER_UNSAFE_RAW ); ?>",
-              options: <?php echo filter_var( $atts['date-options'], FILTER_UNSAFE_RAW ); ?>,
+              locale: "<?php echo esc_js( filter_var( $atts['date-locale'], FILTER_UNSAFE_RAW ) ); ?>",
+              options: <?php echo filter_var( INCLUDE_MASTODON_FEED_DATE_OPTIONS, FILTER_UNSAFE_RAW ); ?>,
             }
           }
         }
       );
     });
   </script>
-  <div class="include-mastodon-feed<?php echo (true == $atts['darkmode'] ? ' dark' : ''); ?>" id="<?php echo esc_attr( $elemId ); ?>"><?php echo esc_html( $atts['text-loading'] ); ?></div>
+  <div class="include-mastodon-feed-wrapper"><ol class="include-mastodon-feed<?php echo (true == $atts['darkmode'] ? ' dark' : ''); ?>" id="<?php echo esc_attr( $elemId ); ?>"><li><?php echo esc_html( $atts['text-loading'] ); ?></li></ol></div>
 <?php
   return ob_get_clean();
 }