Manual:Hooks/SkinAfterPortlet

SkinAfterPortlet
Available from version 1.35.0
Occurs whenever a page is rendered and allows the addition of HTML after portlets have been put out.
Define function:
public static function onSkinAfterPortlet( $skin, $portletName, &$html ) { ... }
Attach hook: In extension.json:
{
	"Hooks": {
		"SkinAfterPortlet": "MediaWiki\\Extension\\MyExtension\\Hooks::onSkinAfterPortlet"
	}
}
Called from: File(s): skins/Skin.php
Interface: SkinAfterPortletHook.php

For more information about attaching hooks, see Manual:Hooks .
For examples of extensions using this hook, see Category:SkinAfterPortlet extensions.


Allow custom HTML to be injected after the section after the portlets' output. Any use of the hook needs to handle escaping. Note that if the portlet is empty and $html is nonempty, the portlet will be rendered.

Details

edit
  • $skin: Skin
  • $portletName: string portlet name
  • &$html: string The HTML code to display. It will be wrapped into a div tag, but apart from that, it will be outputted directly into the page.
Escape dangerous signs in HTML!

Example

edit

This example will place an ad in the sidebar following the Toolbox ("tb") portlet. Replace the data-ad-client and data-ad-slot with your values.

$wgHooks['SkinAfterPortlet'][] = function($skin, $portletName, &$html) {
    $user = $skin->getUser();
    if ($user->isRegistered() && $portletName === "tb") {
        $html = <<< EOT
        <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
                <ins class="adsbygoogle"
                     style="display:inline-block;width:160px;height:1000px;margin:20px 0 0 -20px;"
                     data-ad-client="ca-pub-00000000000000"
                     data-ad-slot="000000000">
                </ins>
        <script>
                (adsbygoogle = window.adsbygoogle || []).push({});
        </script>
        EOT;
        return true;
    }
};

Example since REL1_35

edit

See: Handling hooks in MediaWiki 1.35 and later

This example will place ads in the sidebar. The keys are to be set in MediaWiki:Sidebar. If the key is not set, the ad will not be displayed.

extension.json

edit

In extension.json:

	"Hooks": {
		"SkinAfterPortlet": "main"
	},
	"HookHandlers": {
		"main": {
			"class": "MediaWiki\\Extension\\ExampleExtension\\Hooks",
			"services": [
				"MainConfig"
			]
		}
	},
	"AutoloadClasses": {
		"MediaWiki\\Extension\\ExampleExtension\\Hooks": "includes/Hooks.php",
	},
	"config": {
		"GoogleAdSenseClient": {
			"type": "string",
			"value": "none"
		},
		"GoogleAdSenseHost": {
			"type": "string",
			"value": "none"
		},
		"GoogleAdSenseSrc": {
			"type": "string",
			"value": "//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
		},
		"SidebarAd1Code": {
			"type": "string",
			"value": "none"
		},
		"SidebarAd2Code": {
			"type": "string",
			"value": "none"
		}
	}
	"manifest_version": 2

Hooks.json

edit

In includes/Hooks.php:

namespace MediaWiki\Extension\ExampleExtension;

use MediaWiki\Hook\BeforePageDisplayHook;
use MediaWiki\Skins\Hook\SkinAfterPortletHook;

use GlobalVarConfig;
use MediaWiki\MediaWikiServices;

class Hooks implements
	BeforePageDisplayHook,
	SkinAfterPortletHook {

	private GlobalVarConfig $config;

	public function __construct(
		GlobalVarConfig $config
	) {
		$this->config = $config;
	}

	public function onBeforePageDisplay( $out, $skin ): void {

		// Get Preferences
		$script_code = $this->config->get( 'GoogleAdSenseSrc' );

		if ( !empty( $script_code ) ) {
			// Insert Google AdSense JavaCode in the HTML header section
			$out->addHeadItem( 'google_ads_javacode', $script_code );
		}
	}

	public function onSkinAfterPortlet( $skin, $portletName, &$html ) {

		// Get Preferences
		$_googleAdSenseClient = $this->config->get( 'GoogleAdSenseClient' );
		$_googleAdSenseHost   = $this->config->get( 'GoogleAdSenseHost' );
		$_googleAdSenseSrc    = $this->config->get( 'GoogleAdSenseSrc' );

		// Validate Preferences
		// Pattern: [ 'slot_key', width (int), height (int), format (optional) ];
		// SidebarAd1Code: [ '4752916811', 120, 600, "rectangle" ];
		// SidebarAd1Code: [ '4752916811', 120, 600, "rectangle" ];
		// SidebarAd1Code: [ '4752916811', 145, 260, "horizontal" ];
		// SidebarAd1Code: [ '4752916811', 600, 260 ];
		// SidebarAd1Code: [ '4752916811', 480, 260, "vertical, rectangle" ];
		$_configArray['ad_client'] = ( empty( $_googleAdSenseClient ) || ( $_googleAdSenseClient === 'none' ) ) ? false : $_googleAdSenseClient;
		$_configArray['ad_host']   = ( empty( $_googleAdSenseHost )   || ( $_googleAdSenseHost === 'none' )   ) ? false : $_googleAdSenseHost;
		$_configArray['ad_src']    = !empty( $_googleAdSenseSrc ) ? $_googleAdSenseSrc : false;

		// Abort, if Preferences are invalid
		if ( $this->mConfigArray['ad_client'] === false ) {
			return; // Nothing to do!
		}
		if ( $this->mConfigArray['ad_src'] === false ) {
			return; // Nothing to do!
		}

		// Get Ad Data and validate
		$_ad_AD1 = self::getAdConfigArray( $this->config->get( 'SidebarAd1Code' ) );
		$_ad_AD2 = self::getAdConfigArray( $this->config->get( 'SidebarAd2Code' ) );

		// Set Ad Data if valid
		if ( $_ad_AD1 !== false ) {
			$_ad_key = 'wimaadvertising-ad1';
			$_ad_code = self::getAdCodePrivate( $_configArray, $_ad_AD1 );
			$sidebar_elements[$_ad_key] = $_ad_code;
		}
		if ( $_ad_AD2 !== false ) {
			$_ad_key = 'wimaadvertising-ad2';
			$_ad_code = self::getAdCodePrivate( $_configArray, $_ad_AD2 );
			$sidebar_elements[$_ad_key] = $_ad_code;
		}

		if ( array_key_exists( $portletName, $sidebar_elements ) ) {
			$element = $sidebar_elements[$portletName];
			if ( !empty( $element ) ) {
				$html = $element;
				return true;
			}
		}
	}

	private static function getAdCodePrivate( $general_data, $ad_data ) {

		$scriptsnippet_client_host_slot =
			empty( $general_data['ad_host'] )
			? ''
			: '
    data-ad-host="ca-host-pub-%2$s"';
		$scriptsnippet_format =
			empty( $ad_data['format'] )
			? ''
			: '
    data-ad-format="%6$s"';

		$script_pattern = '<ins class="adsbygoogle"
    style="display:inline-block;width:%4$dpx;height:%5$dpx"
    data-ad-client="ca-pub-%1$s"' .
    	$scriptsnippet_client_host_slot . '
    data-ad-slot="%3$s"' .
    	$scriptsnippet_format . '></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>';
		$script_code = sprintf( $script_pattern,
				$general_data['ad_client'],
				$general_data['ad_host'],
				$ad_data['slot'],
				$ad_data['width'],
				$ad_data['height'],
				$ad_data['format']
			);
		return $script_code;
	}

	private static function getAdConfigArray( $array ) {

		if ( is_array( $array ) ) {
			if ( ( count( $array ) !== 3 ) && ( count( $array ) !== 4 ) ) {
				wfLogWarning( 'getAdConfigArray expected an array with three or four values, but got this: "' . implode( ', ', $array ) . '"' . "\n" );
				return false;
			}
		} else {
			// Because this is obviously not an array, the variable is very probably not set. This is NOT an error!
			return false;
		}

		// Slot must be defined, not empty or 'none'
		$slot = empty( $array[0] ) ? 'none' : $array[0];
		if ( ( $slot === 'none' ) || ( $slot === 'slot' ) ) {
			wfLogWarning( 'getAdConfigArray did not detect a (valid) slot: "' . $slot . '"' . "\n" );
			return false;
		}

		// width and height must be int
		$width = self::getSizeValue( $array[1] );
		if ( !is_int( $width ) ) {
			wfLogWarning( 'getAdConfigArray not recognize a valid width value: "' . $array[1] . '"' . "\n" );
			return false;
		}
		$height = self::getSizeValue( $array[2] );
		if ( !is_int( $height ) ) {
			wfLogWarning( 'getAdConfigArray not recognize a valid height value: "' . $array[2] . '"' . "\n" );
			return false;
		}

		// Slot can be empty or 'auto', 'horizontal', 'vertical', 'rectangle'
		$format = empty( $array[3] ) ? '' : $array[3];

		return [ 'slot' => $slot, 'width' => $width, 'height' => $height, 'format' => $format ];
	}
<!-- [...] -->
}