Amazon.co.uk Widgets
A twitter card from one of my sites generated by this Joomla 4 Plugin
A twitter card from one of my sites generated by this Joomla 4 Plugin

I wanted to make my Joomla sites interactions with Twitter a little bit more engaging. Twitter cards already provide a nice solution for that. Experiments showed me that I got more traffic if they were enabled. So I set about automating it for anyone who tweets a link to my content. Heres how I did it and the code

I had to learn how to write a plugin and how to use some arcane to me formats like XML and the php strstr command. The objective is to set the meta tags automatically for each article. this is what they look like:

<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@Your Twitter username">
<meta name="twitter:title" content="Your articles title">
<meta name="twitter:description" content="Your articles introtext">
<meta name="twitter:image" content="link to your image">

these tags need to be placed on each page inside the <head> tag, with unique content from the Joomla article, Read on to read about how I was able to do it, and to see all the source code for the plugin:

  • User Story,
  • Possible methods,
  • Different kinds of Plugin in Joomla,
  • Events,
  • XML Installation file
  • Plugin PHP file,
  • Localisation strings

User Story

As a Joomla Administrator, I want to automatically add the meta data required to generate a nice twitter card containing a useful image for links to my Joomla 4 sites. This will make tweets containing links to my content more attractive and generate more engagement.

Possible methods

There are some extensions that do this already but they aren't for Joomla 4. All my sites are Joomla 4 now. I can't risk running any extensions that arent tested for Joomla 4 compatibility.

Search engines show some examples of stuffing the <head> tag in the template with the right content inspected in some way from the page elements using Javascript. That seems to me to be a brittle solution, and I dont know enough Javascript or enough about HMTL Elements objects to be able to do that.

Then there is the Joomla Plugin approach. I know next to nothing about writing a Plugin for Joomla but thought I would give it a go. Theres some useful documentation on the Joomla documentation site:

Creating a Plugin for Joomla

Different kinds of Plugin in Joomla

There are a wealth of different kinds of Plugin available for Joomla. This feels like a content Plugin. Im familar with some of these through usage, like emailcloak for example.

  • api-authentication
  • authentication
  • behaviour
  • captcha
  • content
    • confirmconsent
    • contact
    • emailcloak
    • fields
    • finder
    • joomla
    • loadmodule
    • pagebreak
    • pagenavigation
    • vote
  • editors-xtd
  • editors
  • fields
  • filesystem/local
  • finder
  • installer
  • media-action
  • privacy
  • quickicon
  • sampledata
  • system
  • twofactorauth
  • user
  • webservices
  • workflow

Events

Joomla Plugins are code installed into your Joomla site which performs a function when a particular event occurs. Joomla has a set of core Plugin events and you can create your own. As a novice here, Im just looking for the simplest, minimum amount of code so I'll need a core Plugin event for my content plugin. You can find a list of these core Plugin events at Plugin/Events/Content which has been lightly edited to include 4.x.

What is needed here is 'onContentBeforeDisplay'. The description says it is for information 'that should be placed immediately before the generated content. For views that generate HTML, this might include the use of styles that are specified as part of the content or related parameters'. That sounds like it will fit the bill. My Plugin will add tags in the <head> of the document.

The socialcards plugin

XML Installation file

Tiresomely, to me, the Installation file has to be in XML format. It seems anachronistice but this is the format for all extensions in Joomla, and is required in order to be able to properly install and uninstall them.

The XML file contains information about the Joomla extension, and denotes it as a 'type="plugin" group="content" method="upgrade"' as well as the author, copyright, licence, version and description, which is a variable indicating it is to be sourced from a localisation strings file. method="upgrade simply means you can install new versions over existing ones which is fine for my use case.

Then it defines the files and folders to install in the Joomla site.

Lastly it describes the fields and their data for the plugins own parameters.

The format is picky. It is XML after all. You should make sure it validates as part of your testing.

socialcards.xml

<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="content" method="upgrade">
    <name>Social Cards</name>
    <creationDate>Jan 2021</creationDate>
    <author>Angus Fox</author>
    <authorEmail>This email address is being protected from spambots. You need JavaScript enabled to view it.</authorEmail>
    <authorUrl>www.multizone.co.uk</authorUrl>
    <copyright>Copyright 2021 Multizone Limited</copyright>
    <license>GNU/GPLv3</license>
    <version>1.0.2</version>
    <description>PLG_SOCIALCARDS_DESC</description>
    <files>
        <filename plugin="socialcards">socialcards.php</filename>
        <folder>language</folder>
    </files>
    <config>
        <fields name="params">
            <fieldset name="basic">
                <field 
                    name="social_card" 
                    type="text" 
                    default="summary" 
                    label="PLG_SOCIALCARDS_CARD_LABEL" 
                    description="PLG_SOCIALCARDS_CARD_DESCRIPTION"
                    readonly="readonly"
                    >
                </field>
                <field 
                    name="social_site" 
                    type="text" 
                    required="true" 
                    label="PLG_SOCIALCARDS_SITE_LABEL" 
                    description="PLG_SOCIALCARDS_SITE_DESCRIPTION"
                    >
                  </field>                
            </fieldset>
        </fields>
    </config>
</extension>

Plugin PHP file

The Plugin is only for Joomla 4

The preamble comments describe it in the format required.

It uses CMSPlugin and the function itself is triggered by onContentBeforeDisplay.

Then, it sets the type of card to summary, the site to the twitter ID which must be set in the plugin parameters, the title from the article title, the description from the article introtext, truncated to fit the twitter card, then it sets the card image to the path to the article image if it can.

It works well. It really requires introtext and and article image to be set to be most effective.

socialcards.php

<?php
/**
 * @version     $Id: socialcards.php 2021-10-12 09:00 you $
 * @package     Joomla
 * @subpackage     Content Plugin Social Cards for Joomla! 4
 * @author     Angus Fox
 * @copyright     Copyright 2021 Multizone Limited
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 * Joomla! is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
**/

// no direct access
defined( '_JEXEC' ) or die;

// This is a plugin for Joomla 4 or later and uses CMSPlugin not JPlugin  
// see https://docs.joomla.org/J4.x:Creating_a_Plugin_for_Joomla

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Event\Event;
use Joomla\Event\SubscriberInterface;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;

class plgContentSocialcards extends CMSPlugin    {
    // Load the language file on instantiation
    protected $autoloadLanguage = true;

    function onContentBeforeDisplay($context, &$article, &$params, $limitstart)
    {
        /*
         * Add meta tags for twitter:site, twitter:title, twitter:description" and twitter:image
         * See https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards
         * For best results requires an article image (image_fulltext) to exist
         */

        // Set the Meta Data rom the plugin parameters
        $config = JFactory::getConfig();
        $document = JFactory::getDocument();

        // The type of card is set from the parameters to 'summary'.         
        $social_card = $this->params->get('social_card');        
        if (isset($social_card)) {
            $document->setMetaData('twitter:card', $social_card);
        }    else {
            echo "<div class=\"alert alert-danger\" role=\"alert\">" . $social_card_name . 
            "plugin error <a href=\"https://www.multizone.co.uk/\" class=\"alert-link\">Vendor</a>. Try updating, setting parameters and enabling.</div>";
        }

        // The site is set from the parameters. A site is a twitter username without the @
        $social_site = $this->params->get('social_site');        
        if (isset($social_site)) {
            $document->setMetaData('twitter:site', $social_site);
        }    else {
            echo "<div class=\"alert alert-danger\" role=\"alert\">" . $social_site_name . 
            "plugin error <a href=\"https://www.multizone.co.uk/\" class=\"alert-link\">Vendor</a>. Try updating, setting parameters and enabling.</div>";
        }
        // The Meta Data for the Title and Description are set from the Article Title and Introtext
        $document->setMetaData('twitter:title', $article->title);
        $introtext_notags = strip_tags($article->introtext);
        $introtext_truncated = substr($introtext_notags, 0, 160);
        $document->setMetaData('twitter:description', $introtext_truncated);
        // Need the images json to get the article image path 
        $images = json_decode($article->images);
        // Remove the text after the question mark to try to clean up the image path
        $image_truncated = strstr($images->image_fulltext, chr(63), true);
        // Need the current URI to get the root URL
        $uri = Uri::getInstance();
        // The Meta data for the Image link is set to be the root URL plus article image path
        $document->setMetaData('twitter:image', $uri->root() . $image_truncated);

        return '';
        //return 

        }
    }
?>

Localisation strings

Plugins have language strings files, one for the front-end and one for the back end. They are just simple mappings from variable names to strings. Irritatingly (to me), they are not syntactically the same structure as the XML so you can't just paste them.

en-GB.plg_content_socialcards.sys.ini

; $Id: en-GB.socialcards.ini
; Content Plugin Social Cards for Joomla! 4 - Backend Language strings
; Copyright 2021 Multizone Limited
; License GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
; Author Angus Fox

PLG_SOCIALCARDS_NAME="Social Cards"
PLG_SOCIALCARDS_DESC="The Social Cards plugin adds a few tags to the markup of your Joomla! article, so that users who Tweet links to your content will have a Twitter Card added to the Tweet that is visible to their followers"
PLG_SOCIALCARDS_CARD_DESCRIPTION="This must be 'summary'."
PLG_SOCIALCARDS_CARD_LABEL="Type of Twitter Card"
PLG_SOCIALCARDS_SITE_LABEL="Twitter username"
PLG_SOCIALCARDS_SITE_DESCRIPTION="Enter the Twitter site (the username without the @) to associate with the card"

en-GB.plg_content_socialcards.ini

; $Id: en-GB.socialcards.ini
; Content Plugin Social Cards for Joomla! 4 - Frontend Language strings
; Copyright 2021 Multizone Limited
; License GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
; Author Angus Fox

PLG_SOCIALCARDS_NAME="Social Cards"
PLG_SOCIALCARDS_DESC="The Social Cards plugin adds a few tags to the markup of your Joomla! article, so that users who Tweet links to your content will have a Twitter Card added to the Tweet that is visible to their followers"

See also:

From Joomla! Documentation

Creating a Plugin for Joomla
J4 Plugin example - Table of Contents
Plugin
Plugin/Events/Content

Joomla on Plugin examples on github

joomla-cms/plugins/content/