STOWiki:STO skin family/StoCommon extension

Overview
This extension, when enabled for a given skin, overrides MediaWiki's loading of site and user styles and scripts. It adds support for an intermediate group of styles and scripts that affect all STO family skins, but no others.

Normal case
Normally, MediaWiki loads the styles and scripts in the following pages during a page load (for this case, we're assuming the user has selected the MonoBook skin):


 * MediaWiki:Common.css: styles for all skins.
 * MediaWiki:Monobook.css: site-wide styles for only the MonoBook skin.
 * Special:MyPage/monobook.css: user-specific styles for only the MonoBook skin.
 * MediaWiki:Common.js: scripts for all skins.
 * MediaWiki:Monobook.js: site-wide scripts for only the MonoBook skin.
 * Special:MyPage/monobook.js: user-specific scripts for only the MonoBook skin.

They are loaded in this order.

StoCommon case
When StoCommon is enabled for a skin, it determines what pages styles and scripts are loaded from during a page load. (This list assumes the user has selected the STOfederation skin):


 * MediaWiki:Common.css: styles for all skins.
 * MediaWiki:Stocommon.css: site-wide styles for only STO family skins.
 * MediaWiki:Stofederation.css: site-wide styles for only the STOfederation skin.
 * Special:MyPage/stocommon.css: user-specific styles for only STO family skins.
 * Special:MyPage/stofederation.css: user-specific styles for only the STOfederation skin.
 * MediaWiki:Common.js: scripts for all skins.
 * MediaWiki:Stocommon.js: site-wide scripts for only STO family skins.
 * MediaWiki:Stofederation.js: site-wide scripts for only the STOfederation skin.
 * Special:MyPage/stocommon.js: user-specific scripts for only STO family skins.
 * Special:MyPage/stofederation.js: user-specific scripts for only the STOfederation skin.

They are loaded in the order above.

Installation and configuration
A couple things the extension needs to do must happen before the skins are loaded during the process of generating a page. At this time, it doesn't know what skins are installed; thus, the extension needs to be manually configured to tell it what skins are in the STO skin family. This is done by adding lines like the following to :

$wgStoSkins['stofederation'] = true;

These lines must be placed before the include line for the extension, which is as follows:

require_once( "{$IP}/extensions/StoCommon/StoCommon.php" );

MediaWiki versions
At the time of this writing, the StoCommon is confirmed to work with MediaWiki 1.16.5 and 1.17. Note that it uses very different behavior for both due to the significant changes in style and script loading between the two versions.

For versions before 1.16.5, the extension uses a legacy mode where it does most of the work itself.

For versions 1.17 and later, the extension supports the new ResourceLoader. Some of the work has been offloaded to the skins themselves, but it wasn't possible to get the desired order of style and script loading without the extension due to skins being loaded too late in the page generation process.

Due to way the extension works, it is unfortunately more likely to be affected by updates to the MediaWiki software. This is because it takes over the loading of site and user styles and scripts. If it should fail after an update, it may be necessary to temporarily copy the contents of the pages in the above list that are in italics to the skin-specific pages. User:Eyes intends to try to ensure the extension is compatible with the version ahead of that currently used by STOWiki, though.

<?php

/** * A simple extension that adds support for Mediawiki:Sto-common.js * User:username/sto-common.js to supported skins. * * @addtogroup Extensions * * @link * * @author Eyes * @copyright Copyright © 2010 Eyes * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */

// If this is run directly from the web die as this is not a valid entry point. if( !defined( 'MEDIAWIKI' ) ) die( 'Invalid entry point.' );

// Extension credits. $wgExtensionCredits[ 'other' ][] = array( 'name'           => 'StoCommon',  'description'    => 'Adds support for common site and user CSS and JS for STO family skins.)', 'descriptionmsg' => 'stocommon-desc', 'author'        => 'Eyes', 'version'       => '1.1', );

global $wgVersion;

// Set up globals. Static class members would be preferred, but appears to be buggy. if ( !isset( $wgStoSkins ) ) $wgStoSkins = array; $wgStoSkin = null; $wgStoSkinName = null; $wgStoCommonUseSiteJs = false; $wgStoCommonAllowUserJs = false;

// Make sure this class is loaded early $wgAutoloadClasses['StoCommonExtension'] = dirname(__FILE__). '/StoCommon.php'; $wgExtensionFunctions[] = 'StoCommonExtension::initialize';

$version = explode( '.', $wgVersion, 2 );

if ( intval( $version[0] ) < 2 && intval( $version[1] ) < 17 ) { // We do things the more complicated way because there is no resource loader yet define( 'STOCOMMONLEGACY', true );

// Function that will now handle site and user CSS and JS functions $wgHooks['BeforePageDisplay'][] = 'StoCommonExtension::setupSiteAndUserCssAndJs';

// Hook into raw page view to add the content of MediaWiki:Sto-common.js. // We have to do this because the site JS is handled quite a bit differently than // the CSS and user JS, and we want it between MediaWiki:Common.js and the skin specific // site JS file. $wgHooks['RawPageViewBeforeOutput'][] = 'StoCommonExtension::addSiteJs';

// Set up globals. Static class members would be preferred, but appears to be buggy. $wgStoCommonUseSiteCss = false; $wgStoCommonAllowUserCss = false; } else { // We handle this in an entirely different fashion for 1.17 define( 'STOCOMMONSTD', true ); include( dirname( __FILE__ ) . "/STOResourceLoaderSiteModule.php" ); $wgAutoloadClasses['STOResourceLoaderSiteModule'] = dirname( __FILE__ ). "/STOResourceLoaderSiteModule.php"; $wgResourceModules['stosite'] = array( 'class' => 'STOResourceLoaderSiteModule' ); include( dirname( __FILE__ ) . "/STOResourceLoaderUserModule.php" ); $wgAutoloadClasses['STOResourceLoaderUserModule'] = dirname( __FILE__ ). "/STOResourceLoaderUserModule.php"; $wgResourceModules['stouser'] = array( 'class' => 'STOResourceLoaderUserModule' ); $wgHooks['BeforePageDisplay'][] = 'StoCommonExtension::addScripts'; }

class StoCommonExtension { /**  * Initializes the extension. */ static function initialize  { global $wgUser, $wgStoCommonEnabled, $wgStoSkin, $wgStoSkinName; // for some reason, this breaks 1.17 if ( defined('STOCOMMONLEGACY') ) { $wgStoSkin = $wgUser->getSkin; $wgStoSkinName = $wgStoSkin->getSkinName; }

self::overrideSiteAndUserCssAndJs; } static function isEnabled  { global $wgStoSkins, $wgStoSkinName; if ( defined('STOCOMMONLEGACY') ) { return isset( $wgStoSkins[$wgStoSkinName] ) && $wgStoSkins[$wgStoSkinName] === true; } else { return true; // in non-legacy mode, we have to make further checks } }  /**   * This extensions takes over handling of site and user CSS and JS options. * This sets the globals to false while saving their original values. */ static function overrideSiteAndUserCssAndJs  { global $wgUseSiteCss, $wgAllowUserCss, $wgUseSiteJs, $wgAllowUserJs; global $wgStoCommonUseSiteCss, $wgStoCommonAllowUserCss; global $wgStoCommonUseSiteJs, $wgStoCommonAllowUserJs; if ( self::isEnabled || defined('STOCOMMONSTD') ) { // Remember the original values... $wgStoCommonUseSiteJs = $wgUseSiteJs; $wgStoCommonAllowUserJs = $wgAllowUserJs; // ...because this extension is overriding and taking control of those functions $wgUseSiteJs = false; $wgAllowUserJs = false; if ( defined('STOCOMMONLEGACY') ) { // In legacy mode, we must do the same for CSS. $wgStoCommonUseSiteCss = $wgUseSiteCss; $wgStoCommonAllowUserCss = $wgAllowUserCss;

$wgUseSiteCss = false; $wgAllowUserCss = false; }   }  }  static function addScripts( $out, $sk ) {   global $wgStoSkins; // In non-legacy modes, STO skins can take care of CSS by themselves now that // the appropriate classes are loaded and resources are registered. // But we still need to take over JS registration. if ( isset( $wgStoSkins[$sk->getSkinName] ) && $wgStoSkins[$sk->getSkinName] === true ) { $out->addModuleScripts( 'stosite' ); $out->addModuleScripts( 'stouser' ); }   return true; } /**   * Branching function that directs handling of site CSS, user CSS, and user JS. * Site JS is handled after the MediaWiki generates the JS page to be sent to the * browser. */ static function setupSiteAndUserCssAndJs( &$out, &$sk ) { if ( self::isEnabled ) { $out->mAllowUserJs = false; self::setupSiteCss( $out, $sk ); self::setupUserCss( $out, $sk ); self::setupSiteJs( $out, $sk ); self::setupUserJs( $out, $sk ); }    return true; }

/**  * Sets up the dynamic site CSS in the same way the MediaWiki software would * (as of 1.16.5, anyway), aside from adding MediaWiki:Stocommon.css. * Note to self: original functionality found in setupUserCss in Skin.php *  * In 1.17, this has been moved to addModuleStyles in OutputPage.php. * Note that 1.17 loads differently; this should still work, but is likely to  * produce a different order of results. *  * TODO: Add 1.17's method of handling site css. */ static function setupSiteCss( &$out, &$sk ) {   global $wgStoSkinName, $wgStoCommonUseSiteCss, $wgSquidMaxage; if ( $wgStoCommonUseSiteCss ) { $siteargs = array(       'action' => 'raw',        'maxage' => $wgSquidMaxage,      ); global $wgHandheldStyle; $query = wfArrayToCGI( array( 'usemsgcache' => 'yes', 'ctype' => 'text/css', 'smaxage' => $wgSquidMaxage ) + $siteargs ); # Site settings must override extension css! (bug 15025) $out->addExtensionStyle( Skin::makeNSUrl( 'Common.css', $query, NS_MEDIAWIKI ) ); $out->addExtensionStyle( Skin::makeNSUrl( 'Print.css', $query, NS_MEDIAWIKI ), 'print' ); if( $wgHandheldStyle ) { $out->addExtensionStyle( Skin::makeNSUrl( 'Handheld.css', $query, NS_MEDIAWIKI ), 'handheld' ); }     $out->addExtensionStyle( Skin::makeNSUrl( 'Stocommon.css', $query, NS_MEDIAWIKI ) ); $out->addExtensionStyle( Skin::makeNSUrl( $wgStoSkinName. '.css', $query, NS_MEDIAWIKI ) ); } }  /**   * Sets up the dynamic user CSS in the same way the MediaWiki software would * (as of 1.16.5, anyway), aside from adding User:username/stocommon.css. * Note to self:original functionality found in setupUserCss in Skin.php *  * In 1.17, this has been moved to addModuleStyles in OutputPage.php. * Note that 1.17 loads differently; this should still work, but is likely to  * produce a different order of results. *  * TODO: Add 1.17's method of handling user css. * TODO: See $wgAllowUserCssPrefs in setupUserCss in Skin.php... need to take *      that over too? */ static function setupUserCss( &$out, &$sk ) { global $wgRequest, $wgUser; global $wgStoSkinName, $wgStoCommonAllowUserCss; if( $wgStoCommonAllowUserCss && $wgUser->isLoggedIn ) { $action = $wgRequest->getVal( 'action' ); # If we're previewing the CSS page, use it     if( $sk->mTitle->isCssSubpage && $sk->userCanPreview( $action ) ) { // @FIXME: properly escape the cdata! $out->addInlineStyle( $wgRequest->getText( 'wpTextbox1' ) ); } else { $out->addExtensionStyle( Skin::makeUrl( $wgUser->getUserPage. '/stocommon.css', 'action=raw&ctype=text/css' )       ); $out->addExtensionStyle( Skin::makeUrl( $wgUser->getUserPage. '/' . $wgStoSkinName. '.css', 'action=raw&ctype=text/css' )       ); }   }  }  /**   * Sets up the dynamic site JS in the same way the MediaWiki software would * (as of 1.16.5, anyway). It's only here to ensure this is done before * the user JS is handled. */ static function setupSiteJs( &$out, &$sk ) { global $wgUser, $wgStoSkinName, $wgStoCommonUseSiteJs; //add site JS if enabled: if( $wgStoCommonUseSiteJs ) { $jsCache = $wgUser->isLoggedIn ? '&smaxage=0' : ''; $out->addScriptFile( Skin::makeUrl( '-', "action=raw$jsCache&gen=js&useskin=". urlencode( $wgStoSkinName ) )       );    }  }  /**   * Sets up the dynamic user JS in the same way the MediaWiki software would * (as of 1.16.5, anyway), aside from adding User:username/sto-common.css. * Note to self:original functionality found in getHeadScripts in OutputPage.php */ static function setupUserJs( &$out, &$sk ) { global $wgRequest, $wgUser; global $wgStoSkinName, $wgStoCommonAllowUserJs; //add user js if enabled: if( $wgStoCommonAllowUserJs && $wgUser->isLoggedIn ) { $action = $wgRequest->getVal( 'action', 'view' ); if( $sk->mTitle && $sk->mTitle->isJsSubpage and $sk->userCanPreview( $action ) ) { # XXX: additional security check/prompt? $out->addInlineScript( $wgRequest->getText( 'wpTextbox1' ) ); } else { $userpage = $wgUser->getUserPage; self::addScriptFile( $userpage, "stocommon", $out ); self::addScriptFile( $userpage, $wgStoSkinName, $out ); }   }  }  /**   * Adds the script file for the indicated userpage and subpage name to the output. */ static function addScriptFile( $userpage, $subPage, &$out ) { global $wgJsMimeType; $scriptpage = Title::makeTitleSafe(     NS_USER,      $userpage->getDBkey . '/' . $subPage . '.js'    ); if ( $scriptpage && $scriptpage->exists ) { $userjs = Skin::makeUrl( $scriptpage->getPrefixedText, 'action=raw&ctype=' . $wgJsMimeType ); $out->addScriptFile( $userjs ); } }

/**  * Adds MediaWiki:Sto-common.js to the generated Javascript page created by the MediaWiki * software if MediaWiki:Sto-common.js exists. */ static function addSiteJs( &$rawPage, &$text ) { global $wgStoSkinName; if ( self::isEnabled && $rawPage->mGen == "js" ) { $comment = "/* MediaWiki:Stocommon.js\n  Added by StoCommon extension */\n";

// Ideal insertion point is before the specific skin JS message, so find it     $index = strpos( $text, "/* MediaWiki:" . ucfirst( $wgStoSkinName ) . ".js */" );

// Get the message now, if it exists. $common = wfMsgExt( 'Stocommon.js', 'content' ); if ( wfEmptyMsg( 'Stocommon.js', $common ) ) $common = "";

// if we've found the ideal insertion point, insert MediaWiki:Sto-common.js there // otherwise, shove it at the bottom, which in that case should be ideal because there // is no skin-specific site JS to load. if ( $index ) $text = substr( $text, 0, $index ). $comment. $common. "\n\n". substr ( $text, $index ); else $text = $text. $comment. $common; }   return true; } } ?>

<?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @author Eyes */

/** * Module for site customizations */ class STOResourceLoaderSiteModule extends ResourceLoaderSiteModule {

/* Protected Methods */

/**  * Gets list of pages used by this module *   * @return Array: List of pages */ protected function getPages( ResourceLoaderContext $context ) { global $wgHandheldStyle;

$pages = array(     'MediaWiki:Common.js' => array( 'type' => 'script' ),      'MediaWiki:Common.css' => array( 'type' => 'style' ),      'MediaWiki:Stocommon.js' => array( 'type' => 'script' ),      'MediaWiki:Stocommon.css' => array( 'type' => 'style' ),      'MediaWiki:' . ucfirst( $context->getSkin ) . '.js' => array( 'type' => 'script' ),      'MediaWiki:' . ucfirst( $context->getSkin ) . '.css' => array( 'type' => 'style' ),      'MediaWiki:Print.css' => array( 'type' => 'style', 'media' => 'print' ),    ); if ( $wgHandheldStyle ) { $pages['MediaWiki:Handheld.css'] = array(        'type' => 'style',         'media' => 'handheld' ); }   return $pages; }

/* Methods */

/**  * Gets group name *   * @return String: Name of group */ public function getGroup { return 'site'; } }

<?php /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * * @file * @author Eyes */

/** * Module for user customizations */ class STOResourceLoaderUserModule extends ResourceLoaderUserModule {

/* Protected Methods */

protected function getPages( ResourceLoaderContext $context ) { if ( $context->getUser ) { $username = $context->getUser; return array(       "User:$username/common.js" => array( 'type' => 'script' ),        "User:$username/stocommon.js" => array( 'type' => 'script' ),        "User:$username/" . $context->getSkin . '.js' =>           array( 'type' => 'script' ),        "User:$username/common.css" => array( 'type' => 'style' ),        "User:$username/stocommon.css" => array( 'type' => 'style' ),        "User:$username/" . $context->getSkin . '.css' =>           array( 'type' => 'style' ),      ); }   return array; } /* Methods */ public function getGroup { return 'user'; } }