From MediaWiki.org
[edit] AdvancedSkinSystem.php
<?php
if (!defined('MEDIAWIKI')) die();
/**
* A Special Page extension to dynamically generate skins from Wikipages and cache them
* for better performance, runnable by users with updateskins righs
*
* @ingroup Extensions
*
* @author Jan M. Simons <jamasi at piratenpartei.de>
* @copyright Copyright © 2007, Jan M. Simons
* @license http://www.gnu.org/licenses/agpl.html GNU Affero General Public License 3.0 or later
* loosely based on RenameUser Extension:
* Copyright © 2005, Ævar Arnfjörð Bjarmason
* license http://www.gnu.org/licenses/gpl.html GNU General Public License 2.0 or later
*/
# Configurationfile file
require_once( 'ASS_Settings.php' );
# Internationalisation file
require_once( 'ASS.i18n.php' );
# Fixes to better display raw HTML, PHP, ... in the ASS namespaces
require_once( 'ASS_DisplayFixes.php' );
# setup restrictions and namespaces
// restrict edits in the ASS namespaces, as one can use ASS to get malicious code run by the server
$wgAvailableRights[] = 'updateskins';
$wgGroupPermissions['*']['updateskins'] = false;
$wgGroupPermissions['bureaucrat']['updateskins'] = true;
$wgGroupPermissions['sysop']['updateskins'] = true;
$wgGroupPermissions['updateskins']['updateskins'] = true;
// you can enforce a four eye principle with this splitting of rights
$wgAvailableRights[] = 'editskins';
$wgGroupPermissions['*']['editskins'] = false;
$wgGroupPermissions['bureaucrat']['editskins'] = true;
$wgGroupPermissions['sysop']['editskins'] = true;
$wgGroupPermissions['editskins']['editskins'] = true;
# register namespaces
$wgExtraNamespaces[$wgASSTemplateNameSpaceNumber] = $wgASSTemplateNameSpaceName;
$wgExtraNamespaces[$wgASSTemplateNameSpaceNumber + 1] = $wgASSTemplateNameSpaceName. "_talk";
$wgNamespaceProtection[$wgASSTemplateNameSpaceNumber] = array( 'editskins' );
$wgExtraNamespaces[$wgASSFillerNameSpaceNumber] = $wgASSFillerNameSpaceName;
$wgExtraNamespaces[$wgASSFillerNameSpaceNumber + 1] = $wgASSFillerNameSpaceName ."_talk";
$wgNamespaceProtection[$wgASSFillerNameSpaceNumber] = array( 'editskins' );
# register specialpage
$wgExtensionFunctions[] = 'wfSpecialUpdateSkins';
$wgExtensionCredits['specialpage'][] = array(
'name' => 'UpdateSkins',
'author' => 'Jan M. Simons',
'version' => '0.9.0',
'url' => 'http://www.mediawiki.org/wiki/Extension:AdvancedSkinSystem',
'description' => 'UpdateSkins page (from the Advanced Skin System)',
);
# Add a new log type
global $wgLogTypes, $wgLogNames, $wgLogHeaders, $wgLogActions;
$wgLogTypes[] = 'updateskins';
$wgLogNames['updateskins'] = 'updateskinslogpage';
$wgLogHeaders['updateskins'] = 'updateskinslogpagetext';
$wgLogActions['updateskins/updateskins'] = 'updateskinslogentry';
# Register the special page
$wgSpecialPages['UpdateSkins'] = 'UpdateSkins';
function wfSpecialUpdateSkins() {
# Add messages
global $wgMessageCache, $wgASSMessages;
foreach( $wgASSMessages as $key => $value ) {
$wgMessageCache->addMessages( $wgASSMessages[$key], $key );
}
}
[edit] SpecialUpdateSkins_body.php
<?php
if (!defined('MEDIAWIKI')) die();
class UpdateSkins extends SpecialPage {
function UpdateSkins() {
SpecialPage::SpecialPage('UpdateSkins', 'updateskins');
}
function execute() {
global $wgOut, $wgUser, $wgTitle, $wgRequest, $wgContLang, $wgLang;
global $wgVersion, $wgMaxNameChars, $wgCapitalLinks;
global $wgASSTemplateNameSpaceNumber;
$fname = 'UpdateSkins::execute';
$this->setHeaders();
if ( !$wgUser->isAllowed( 'updateskins' ) ) {
$wgOut->permissionRequired( 'updateskins' );
return;
}
if ( wfReadOnly() ) {
$wgOut->readOnlyPage();
return;
}
if ( version_compare( $wgVersion, '1.10.0', '<' ) ) {
$wgOut->versionRequired( '1.10.0' );
return;
}
$showBlockLog = $wgRequest->getBool( 'submit-showBlockLog' );
$token = $wgUser->editToken();
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page',
array('page_title','page_id'),
array('page_namespace' => $wgASSTemplateNameSpaceNumber),
$fname);
$wgOut->addHTML(
Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'updateskins' ) ) .
Xml::openElement( 'table' ) .
"<tr>
<td align='left'>" .
Xml::label( wfMsg( 'updateskinstemplates' ).":", 'skintempltename' ) .
"</td>
</tr>"
);
while ( $row = $dbr->fetchObject( $res ) ) {
$is_checked[$row->page_id] = true;
if ( $wgRequest->wasPosted() && ! $wgRequest->getCheck( 'dopage-'. $row->page_id ) ) {
$is_checked[$row->page_id] = false;
}
$wgOut->addHTML(
"<tr>
<td align='left'>" .
Xml::checkLabel( $row->page_title, 'dopage-'. $row->page_id, 'dopage-'. $row->page_id, $is_checked[$row->page_id] ) .
"</td>
</tr>"
);
if ( $wgRequest->wasPosted() && $is_checked[$row->page_id] ) {
# generate skin
$wgOut->addHTML(
"<tr><td align='left'>" .
$this->generateSkinFromTemplate( $row->page_id ) .
"</td></tr>"
);
}
}
$dbr->freeResult( $res );
$wgOut->addHTML(
Xml::closeElement( 'table' ) .
"<br />" .
Xml::submitButton( wfMsg( 'updateskinssubmit' ), array( 'name' => 'submit', 'id' => 'submit' ) ) .
Xml::hidden( 'token', $token ) .
Xml::closeElement( 'form' ) . "\n"
);
}
function generateSkinFromTemplate( $skin_id ) {
global $wgUser, $wgASSOutputDir, $wgASSdefaultSkin, $wgASSTemplateNameSpaceNumber, $wgASSParsedFiles, $wgParser;
$ASSTemplate = new Article(Title::newFromID($skin_id));
// Check if the article to parse belongs to the correct namespace.
// This might seem redundant, but it's important for security as
// one would be able to circomvent namespace protections otherwise.
if ($ASSTemplate->getTitle()->getNamespace() != $wgASSTemplateNameSpaceNumber) die($skin_id." is not a valid skin template ID.");
// pre-parse article
$ASScontent = $ASSTemplate->fetchContent(0,false,false);
$ASSparsedcontent = $this->parseTemplate( $ASScontent );
$ASSfilename = $wgASSOutputDir."/".$ASSTemplate->getTitle()->getDBkey().".php";
// echo $ASSfilename;
file_put_contents ( $ASSfilename, $ASSparsedcontent );
$flags = explode("\n", $this->getTaggedText( "<skinflags>", "</skinflags>", $ASScontent));
foreach ($flags as $flag) {
switch (strtolower(trim($flag, " "))) {
case "present_to_user":
//creatre a skin-file based on the template.
$this->PlaceSkinTemplate($ASSTemplate->getTitle()->getDBkey());
break;
//case "":
// break;
}
}
$this->PlaceSkinTemplate("standard");
$wgASSParsedFiles = "";
}
function parseTemplate( $TemplateContent ) {
global $wgUser, $wgASSOutputPath, $wgASSOutputDir, $wgTitle, $wgParser, $wgASSParsedFiles, $wgASSDebugFile;
//file_put_contents ( $wgASSDebugFile, "################################################\n", FILE_APPEND);
//file_put_contents ( $wgASSDebugFile, "################################################\n", FILE_APPEND);
//file_put_contents ( $wgASSDebugFile, "debug: parsetemplate ".$TemplateContent."\n-----------------------\n".$wgASSParsedFiles."\n\n", FILE_APPEND);
//file_put_contents ( $wgASSDebugFile, "------------------------------------------------\n", FILE_APPEND);
// create helper-files
$externals = explode( "\n", $this->getTaggedText( "<external>", "</external>", $TemplateContent ) );
foreach ($externals as $link) {
$repl= explode( "==", $link );
if ( trim( $repl[0], " " ) ) {
$token[] = trim($repl[0], " ");
$repl[1] = trim(trim(trim($repl[1]," "),"]]"),"[[");
if ( strpos(strtolower($repl[1]), "media:") === false && strpos(strtolower($repl[1]), "image:") === false ) {
// copy external file from article to folder
$ASSLinkedArticle = new Article(Title::newFromText($repl[1]));
$ASSFcontent = $ASSLinkedArticle->fetchContent(0,false,false);
$ASSFilename = $wgASSOutputDir."/".$ASSLinkedArticle->getTitle()->getDBkey();
// Check if it has already been parsed, skip if it has been parsed
if (strpos( $wgASSParsedFiles, "|*".$repl[1]."*" ) === false ) {
$ASSFcontent = $this->parseTemplate( $ASSFcontent );
file_put_contents ( $ASSFilename, $ASSFcontent );
// add parsed file to to list to skip parsing it again.
$wgASSParsedFiles.= "|*".$repl[1]."*";
}
//echo $ASSFilename."<br>";
$replacewith[] = str_replace($wgASSOutputDir, $wgASSOutputPath, $ASSFilename);
} else {
$imageTitle = Title::makeTitleSafe("Image", $repl[1]);
if($imageTitle == NULL) {
echo "<!-- WarnImageNotFound: $repl[1] -->";
}
$img = Image::newFromTitle($imageTitle);
if($img->exists() != true) {
echo "<!-- WarnImageNotFound: $repl[1] -->";
}
$replacewith[] = $img->getViewURL(false);
}
}
}
// get include-files
$internals = explode("\n", $this->getTaggedText( "<internal>", "</internal>", $TemplateContent));
foreach ($internals as $link) {
$repl= explode( "==", $link );
if (trim($repl[0], " ")) {
$token[] = trim($repl[0], " ");
// copy external content to include into array
$ASSLinkedArticle = new Article(Title::newFromText(trim(trim($repl[1],"]]"),"[[")));
$ASSFcontent = $ASSLinkedArticle->fetchContent(0,false,false);
$replacewith[] = $this->parseTemplate( $ASSFcontent );
}
}
// get dynamic content to be included
$dynamic = explode("\n", $this->getTaggedText( "<dynamic>", "</dynamic>", $TemplateContent));
foreach ($dynamic as $link) {
$repl= explode( "==", $link );
if (trim($repl[0], " ")) {
$token[] = trim($repl[0], " ");
// copy external content to include into array
$DynBit = file_get_contents (dirname(__FILE__)."/template/dynamic.php" );
$DynBit = str_replace ("___TO_REPLACE___", trim(trim($repl[1],"]]"),"[["), $DynBit);
$replacewith[] = $DynBit;
}
}
// get wikibits (=(rendered) wikitext to be included)
$wikibits = explode("\n", $this->getTaggedText( "<wikibits>", "</wikibits>", $TemplateContent));
foreach ($wikibits as $link) {
$repl= explode( "==", $link );
if (trim($repl[0], " ")) {
$token[] = trim($repl[0], " ");
// fetch article content
$boxarticle = new Article(Title::newFromText(trim(trim($repl[1],"]]"),"[[")));
$WikiBit = $boxarticle->fetchContent(0,false,false);
if (strpos($WikiBit, '__RAW__') === false) {
$parseoptions = ParserOptions::newFromUser($wgUser);
$parsedoutput = $wgParser->parse(preg_replace('/^\\s+/m','',$WikiBit),$wgTitle,$parseoptions,true,true);
$WikiBit = $parsedoutput->getText();
} else {
$WikiBit = str_replace('__RAW__','',$WikiBit);
}
$replacewith[] = $WikiBit;
}
}
$stripedcontent = $this->getTaggedText( "<skintemplate>", "</skintemplate>", $TemplateContent);
if (trim($stripedcontent, " ") == "") $stripedcontent = $this->getTaggedText( "<skinfiller>", "</skinfiller>", $TemplateContent);
if (trim($stripedcontent, " ") == "") $stripedcontent = $TemplateContent;
$parsedcontent = str_replace ($token, $replacewith, $stripedcontent);
//file_put_contents ( $wgASSDebugFile, "return: ".$parsedcontent."\n", FILE_APPEND);
return $parsedcontent;
}
function PlaceSkinTemplate( $TemplateName ) {
global $wgStyleDirectory;
if (!copy(dirname(__FILE__)."/template/advancedskin.deps.php", $wgStyleDirectory."/".$TemplateName.".deps.php")) {
return ("failed to copy file...<br>\n");
}
$skintemplate = file_get_contents (dirname(__FILE__)."/template/advancedskin.php" );
$skinfile = str_replace ("___TO_REPLACE___", $TemplateName, $skintemplate);
return file_put_contents ( $wgStyleDirectory."/".$TemplateName.".php", $skinfile );
}
function getTaggedText ( $StartTag, $EndTag, $Text ) {
// get start position of the content of the tag
$start = strpos( $Text, $StartTag );
if ($start === false)
$start = -1;
else
$start += strlen($StartTag);
// get end position of the content of the tag
$end = strpos( $Text, $EndTag );
if ($end === false)
$end = -1;
// if tag is found, return text between
if (($start >= 0) && ($end > 0) && ($end > $start)) {
return substr( $Text, $start, $end-$start );
} else {
return "";
}
}
function showLogExtract( $username, $type, &$out ) {
global $wgOut;
# Show relevant lines from the logs:
$wgOut->addHtml( "<h2>" . htmlspecialchars( LogPage::logName( $type ) ) . "</h2>\n" );
$logViewer = new LogViewer(
new LogReader(
new FauxRequest(
array( 'page' => $username->getPrefixedText(),
'type' => $type ) ) ) );
$logViewer->showList( $out );
}
}
[edit] ASS.i18n.php
<?php
/**
* Internationalisation file for UpdateSkins extension.
*
* @ingroup Extensions
*/
$wgASSMessages = array();
$wgASSMessages['en'] = array(
'updateskins' => 'Update dynamic skins',
'updateskinstemplates' => 'skin templates',
'updateskinssubmit' => 'Submit',
'updateskinslogpage' => 'Skin Update log',
'updateskinslogpagetext' => 'This is a log of (re)generation events for dynamic skins',
'updateskinslogentry' => '', # Don't translate this
'updateskinslog' => 'Generated SkinClass "$1" from Pages: "$2"',
);
$wgASSMessages['de'] = array(
'updateskins' => 'Dynamische Skins aktualisieren',
'updateskinstemplates' => 'Skin-Vorlagen',
'updateskinssubmit' => 'Erneuern',
'updateskinslogpage' => 'Aktualisierungs-Logbug für Skin-ÃÂnderungen',
'updateskinslogpagetext' => 'In diesem Logbuch werden die Neuerstellungen von dynamischen Skins protokolliert.',
'updateskinslog' => 'Skin-Klasse "$1" wurde erzeugt aus den Seiten: "$2"',
);
[edit] ASS_Settings.php
<?php
$wgASSDebug = false; // Debug log on if set to true
$wgASSTemplateNameSpaceNumber = 100; // Number of the namespace for dynamic skin templates
$wgASSTemplateNameSpaceName = "Skin"; // Name of the namespace for dynamic skin templates
$wgASSFillerNameSpaceNumber = 102; // Number of the namespace for dynamic skin fillers
$wgASSFillerNameSpaceName = "Filler"; // Name of the namespace for dynamic skin fillers
$wgASSCategoryPrefix = "Skin:"; // Name of the category-prefix for dynamic skin selection
$wgASSOutputName = "advancedskin"; // Name of the directory for dynamic skin files into
$wgASSOutputPath = $wgStylePath."/".$wgASSOutputName; // Path to place dynamic skin files into
$wgASSOutputDir = $IP."/skins/".$wgASSOutputName; // Directory to place dynamic skin files into
$wgASSDebugFile = $IP."/extensions/AdvancedSkinSystem/debug.txt"; // Path to the debug log
[edit] ASS_DisplayFixes.php
<?php
if (!defined('MEDIAWIKI')) die();
/**
* A Special Page extension to dynamically generate skins from Wikipages and cache them
* for better performance, runnable by users with updateskins righs
*
* @ingroup Extensions
*
* @author Jan M. Simons <jamasi at piratenpartei.de>
* @copyright Copyright © 2007, Jan M. Simons
* @license http://www.gnu.org/licenses/agpl.html GNU Affero General Public License 3.0 or later
* loosely based on RenameUser extesnsion:
* Copyright © 2005, Ævar Arnfjörð Bjarmason
* license http://www.gnu.org/licenses/gpl.html GNU General Public License 2.0 or later
* and Poem extension:
* Copyright © 2005 Nikola Smolenski <smolensk@eunet.yu>, Brion Vibber, Steve Sanbeg
*/
# configurationfile file
require_once( 'ASS_Settings.php' );
# Internationalisation file
require_once( 'ASS.i18n.php' );
$wgExtensionFunctions[] = 'wfAdvancedSkinSystem';
$wgExtensionCredits['parserhook'][] = array(
'name' => 'DisplayFixes',
'author' => 'Jan M. Simons',
'version' => '0.9.0',
'url' => 'http://www.mediawiki.org/wiki/Extension:AdvancedSkinSystem',
'description' => 'Displays pages of the template systems namespaces as sourcecode (part of the Advanced Skin System)',
);
function wfAdvancedSkinSystem() {
# Add messages
global $wgMessageCache, $wgASSMessages;
foreach( $wgASSMessages as $key => $value ) {
$wgMessageCache->addMessages( $wgASSMessages[$key], $key );
}
$GLOBALS['wgParser']->setHook("skintemplate","ASS_EscapeCode");
$GLOBALS['wgParser']->setHook("skinfiller","ASS_EscapeCode");
//$GLOBALS['wgParser']->setHook("internal","ATS_EscapeCode");
//$GLOBALS['wgParser']->setHook("external","ATS_EscapeCode");
}
function ASS_EscapeCode( $in, $param=array(), $parser=null ) {
global $wgASSTemplateNameSpaceNumber, $wgASSFillerNameSpaceNumber;
global $wgTitle;
//echo "blobbb".$wgTitle->getNamespace()."<br /> ";
if (($wgTitle->getNamespace() == $wgASSTemplateNameSpaceNumber) || ($wgTitle->getNamespace() == $wgASSFillerNameSpaceNumber)) {
if (method_exists($parser, 'recursiveTagParse')) {
//new methods in 1.8 allow nesting <nowiki> in <poem>.
$tag = $parser->insertStripItem("<br />", $parser->mStripState);
$text = "<nowiki>".preg_replace(
array("/</","/>/","/^\n/","/\n$/D","/\n/"),
array("<",">","", "", "$tag\n"),
$in )."</nowiki>";
$text = $parser->recursiveTagParse($text);
} else {
$text = preg_replace(
array("/</","/>/","/^\n/","/\n$/D","/\n/", "/^( +)/me"),
array("<",">","", "", "$tag\n","str_replace(' ',' ','\\1')"),
$in );
$ret = $parser->parse(
$text,
$parser->getTitle(),
$parser->getOptions(),
// We begin at line start
true,
// Important, otherwise $this->clearState()
// would get run every time <ref> or
// <references> is called, fucking the whole
// thing up.
false
);
$text = $ret->getText();
}
} else {
$text = $in;
}
global $wgVersion;
if( version_compare( $wgVersion, "1.7alpha" ) >= 0 ) {
// Pass HTML attributes through to the output.
$attribs = Sanitizer::validateTagAttributes( $param, 'div' );
} else {
// Can't guarantee safety on 1.6 or older.
$attribs = array();
}
// Wrap output in a <div> with "code" class.
if( isset( $attribs['class'] ) ) {
$attribs['class'] = 'code ' . $attribs['class'];
} else {
$attribs['class'] = 'code';
}
return wfOpenElement( 'div', $attribs ) .
"\n" .
trim( $text ) .
"\n</div>";
}
[edit] template/advancedskin.deps.php
<?php
// This file exists to ensure that base classes are preloaded before
// MonoBook.php is compiled, working around a bug in the APC opcode
// cache on PHP 5, where cached code can break if the include order
// changed on a subsequent page view.
// see http://mail.wikipedia.org/pipermail/wikitech-l/2006-January/033660.html
require_once('includes/SkinTemplate.php');
[edit] template/advancedskin.php
<?php
/**
* Advanced Skin selection skin
*
* @todo document
* @subpackage Skins
*/
if( !defined( 'MEDIAWIKI' ) )
die( 1 );
/** */
require_once('includes/SkinTemplate.php');
/**
* Inherit main code from SkinTemplate, set the CSS and template filter.
* @todo document
* @subpackage Skins
*/
class Skin___TO_REPLACE___ extends SkinTemplate {
function initPage( &$out ) {
SkinTemplate::initPage( $out );
$this->skinname = '___TO_REPLACE___';
$this->stylename = '___TO_REPLACE___';
$this->template = '___TO_REPLACE___Template';
}
}
/**
* @todo document
* @subpackage Skins
*/
class ___TO_REPLACE___Template extends QuickTemplate {
function execute() {
global $wgASSCategoryPrefix, $wgASSOutputDir, $wgDefaultSkin;
global $wgUser, $wgTitle, $wgValidSkinNames;
$skin = $wgUser->getSkin();
// Suppress warnings to prevent notices about missing indexes in $this->data
wfSuppressWarnings();
// categories override skinselection from namespace
$WebsiteCategory = $this->data['catlinks'];
$match = strrpos($WebsiteCategory, $wgASSCategoryPrefix);
if ($match === false) {
// it's a normal page, use normal skin if it exists
$MySkinfileClass = $this->data['stylename'];
// check for namespace mappings
$Mappings = wfMsg('ASSNamespace2Skin');
if ($Mappings) {
$Mappings = explode("\n", $Mappings);
$MyNS = $wgTitle->getNsText();
foreach ($Mappings as $Map) {
// remove spaces prefix Marker
$Map = "*".trim($Map);
#echo "<!-- ".$Map." / ".$MyNS." -->\n";
// check for occurance of the namespace of the page in mappings
$match = strpos($Map, "*".$MyNS."=");
if (is_numeric($match)) {
$match = strrpos($Map, "=");
$MySkinfileClass = substr($Map, $match + 1);
break;
}
}
}
} else {
// get skin from category
$end = strpos($WebsiteCategory, "</a>", $match);
$match += strlen($wgASSCategoryPrefix);
$SkinToUse = substr($WebsiteCategory, $match, $end-$match);
$MySkinfileClass = $SkinToUse;
}
#echo "<!-- ".$MySkinfileClass." -->\n";
$MySkinfileClass = $wgASSOutputDir. "/" .$MySkinfileClass. ".php";
// if skin does not exist, use the default skin.
if (file_exists($MySkinfileClass) == false) {
echo "<!-- ".$MySkinfileClass." not found falling back to default -->\n";
$MySkinfileClass = $wgValidSkinNames[$wgDefaultSkin]. ".php";
}
{ include($MySkinfileClass); }
#echo "<!-- using ".$MySkinfileClass." -->\n";
wfRestoreWarnings();
}
}
[edit] template/dynamic.php
<?php
global $wgUser,$wgTitle,$wgParser;
$boxarticle = new Article(Title::newFromText('___TO_REPLACE___'));
$content = $boxarticle->fetchContent(0,false,false);
$parseoptions = ParserOptions::newFromUser($wgUser);
$parsedoutput = $wgParser->parse(preg_replace('/^\\s+/m','',$content),$wgTitle,$parseoptions,true,true);
$content = $parsedoutput->getText();
echo $content;