MEDIAWIKI_LOAD_URL ?= http://localhost/w/load.php
-kss: nodecheck
-# FIXME: Use more up-to-date Ruby version
-
+kss: kssnodecheck
# Generates CSS of mediawiki.ui and mediawiki.ui.button using ResourceLoader, then applies it to the
# KSS style guide
$(eval KSS_RL_TMP := $(shell mktemp /tmp/tmp.XXXXXXXXXX))
@command -v xdg-open >/dev/null 2>&1 || { open ${PWD}/static/index.html; exit 0; }
@xdg-open ${PWD}/static/index.html
-nodecheck:
- @scripts/nodecheck.sh
+kssnodecheck:
+ @scripts/kss-node-check.sh
--- /dev/null
+#!/usr/bin/env bash
+
+if command -v npm > /dev/null ; then
+ npm install
+else
+ # If npm isn't installed, but kss-node is, exit normally.
+ # This allows setting it up on one machine, and running it on
+ # another (e.g. Tools Labs execution nodes) that doesn't have npm
+ # installed. However, "npm install" still needs to be run
+ # occasionally to keep kss updated.
+
+ KSS_NODE="${BASH_SOURCE%/*}/../node_modules/.bin/kss-node"
+ if ! [ -x "$KSS_NODE" ] ; then
+ echo "Neither kss-node nor npm are installed."
+ echo "To install npm, see http://nodejs.org/"
+ echo "When npm is installed, the Makefile can automatically"
+ echo "install kss-node."
+ exit 1
+ fi
+fi
+++ /dev/null
-#!/usr/bin/env bash
-if command -v npm > /dev/null ; then
- npm install
-else
- echo "You need to install Node.JS!"
- echo "See http://nodejs.org/"
- exit 1
-fi
'SiteStatsInit' => 'includes/SiteStats.php',
'Skin' => 'includes/Skin.php',
'SkinTemplate' => 'includes/SkinTemplate.php',
+ 'SkinFallback' => 'includes/SkinFallback.php',
+ 'SkinFallbackTemplate' => 'includes/SkinFallback.php',
'SquidPurgeClient' => 'includes/SquidPurgeClient.php',
'SquidPurgeClientPool' => 'includes/SquidPurgeClient.php',
'StatCounter' => 'includes/StatCounter.php',
*
* @since 1.24
*/
-$wgFallbackSkin = 'vector';
+$wgFallbackSkin = 'fallback';
/**
* Specify the names of skins that should not be presented in the list of
$wgSkipSkins[] = $wgSkipSkin;
}
+// Register a hidden "fallback" skin
+$wgValidSkinNames['fallback'] = 'Fallback'; // SkinFallback
+$wgSkipSkins[] = 'fallback';
+
if ( $wgLocalInterwiki ) {
array_unshift( $wgLocalInterwikis, $wgLocalInterwiki );
}
--- /dev/null
+<?php
+/**
+ * Skin file for the fallback skin.
+ *
+ * The structure is copied from the example skin (mediawiki/skins/Example).
+ *
+ * @since 1.24
+ * @file
+ */
+
+/**
+ * SkinTemplate class for the fallback skin
+ */
+class SkinFallback extends SkinTemplate {
+ var $skinname = 'fallback', $template = 'SkinFallbackTemplate';
+
+ /**
+ * Add CSS via ResourceLoader
+ *
+ * @param $out OutputPage
+ */
+ function setupSkinUserCss( OutputPage $out ) {
+ parent::setupSkinUserCss( $out );
+ $out->addModuleStyles( 'mediawiki.skinning.interface' );
+ }
+}
+
+/**
+ * BaseTemplate class for the fallback skin
+ */
+class SkinFallbackTemplate extends BaseTemplate {
+ /**
+ * @return array
+ */
+ private function findInstalledSkins() {
+ global $wgStyleDirectory;
+
+ // Get all subdirectories which might contains skins
+ $possibleSkins = scandir( $wgStyleDirectory );
+ $possibleSkins = array_filter( $possibleSkins, function ( $maybeDir ) {
+ global $wgStyleDirectory;
+ return $maybeDir !== '.' && $maybeDir !== '..' && is_dir( "$wgStyleDirectory/$maybeDir" );
+ } );
+
+ // Only keep the ones that contain a .php file with the same name inside
+ $possibleSkins = array_filter( $possibleSkins, function ( $skinDir ) {
+ global $wgStyleDirectory;
+ return is_file( "$wgStyleDirectory/$skinDir/$skinDir.php" );
+ } );
+
+ return $possibleSkins;
+ }
+
+ /**
+ * Inform the user why they are seeing this skin.
+ *
+ * @return string
+ */
+ private function buildHelpfulInformationMessage() {
+ global $wgDefaultSkin, $wgValidSkinNames;
+
+ $installedSkins = $this->findInstalledSkins();
+ $enabledSkins = $wgValidSkinNames;
+ $enabledSkins = array_change_key_case( $enabledSkins, CASE_LOWER );
+
+ if ( $installedSkins ) {
+ $skinsInstalledText = array();
+ $skinsInstalledSnippet = array();
+
+ foreach ( $installedSkins as $skin ) {
+ $normalizedKey = strtolower( $skin );
+ $isEnabled = array_key_exists( $normalizedKey, $enabledSkins );
+ if ( $isEnabled ) {
+ $skinsInstalledText[] = $this->getMsg( 'default-skin-not-found-row-enabled' )
+ ->params( $normalizedKey, $skin )->plain();
+ } else {
+ $skinsInstalledText[] = $this->getMsg( 'default-skin-not-found-row-disabled' )
+ ->params( $normalizedKey, $skin )->plain();
+ $skinsInstalledSnippet[] = "require_once \"\$IP/skins/$skin/$skin.php\";";
+ }
+ }
+
+ return $this->getMsg( 'default-skin-not-found' )->params(
+ $wgDefaultSkin,
+ implode( "\n", $skinsInstalledText ),
+ implode( "\n", $skinsInstalledSnippet )
+ )->parseAsBlock();
+ } else {
+ return $this->getMsg( 'default-skin-not-found-no-skins' )->params(
+ $wgDefaultSkin
+ )->parseAsBlock();
+ }
+ }
+
+ /**
+ * Outputs the entire contents of the page. No navigation (other than search box), just the big
+ * warning message and page content.
+ */
+ public function execute() {
+ $this->html( 'headelement' ) ?>
+
+ <div class="warningbox">
+ <?php echo $this->buildHelpfulInformationMessage() ?>
+ </div>
+
+ <form action="<?php $this->text( 'wgScript' ) ?>">
+ <input type="hidden" name="title" value="<?php $this->text( 'searchtitle' ) ?>" />
+ <h3><label for="searchInput"><?php $this->msg( 'search' ) ?></label></h3>
+ <?php echo $this->makeSearchInput( array( "id" => "searchInput" ) ) ?>
+ <?php echo $this->makeSearchButton( 'go' ) ?>
+ </form>
+
+ <div class="mw-body" role="main">
+ <h1 class="firstHeading">
+ <span dir="auto"><?php $this->html( 'title' ) ?></span>
+ </h1>
+
+ <div class="mw-body-content">
+ <?php $this->html( 'bodytext' ) ?>
+ <?php $this->html( 'catlinks' ) ?>
+ </div>
+ </div>
+
+ <?php $this->printTrail() ?>
+ </body></html>
+
+ <?php
+ }
+}
* @return bool|ResultWrapper Result or false (for Recentchangeslinked only)
*/
public function doMainQuery( $conds, $opts ) {
- global $wgAllowCategorizedRecentChanges;
-
$dbr = $this->getDB();
$user = $this->getUser();
);
// Build the final data
- if ( $wgAllowCategorizedRecentChanges ) {
+ if ( $this->getConfig()->get( 'AllowCategorizedRecentChanges' ) ) {
$this->filterByCategories( $rows, $opts );
}
* @return array
*/
private function getFeedQuery() {
- global $wgFeedLimit;
$query = array_filter( $this->getOptions()->getAllValues(), function ( $value ) {
// API handles empty parameters in a different way
return $value !== '';
} );
$query['action'] = 'feedrecentchanges';
- if ( $query['limit'] > $wgFeedLimit ) {
- $query['limit'] = $wgFeedLimit;
+ $feedLimit = $this->getConfig()->get( 'FeedLimit' );
+ if ( $query['limit'] > $feedLimit ) {
+ $query['limit'] = $feedLimit;
}
return $query;
* @param FormOptions $opts
*/
public function outputChangesList( $rows, $opts ) {
- global $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
-
$limit = $opts['limit'];
- $showWatcherCount = $wgRCShowWatchingUsers
+ $showWatcherCount = $this->getConfig()->get( 'RCShowWatchingUsers' )
&& $this->getUser()->getOption( 'shownumberswatching' );
$watcherCache = array();
$rc = RecentChange::newFromRow( $obj );
$rc->counter = $counter++;
# Check if the page has been updated since the last visit
- if ( $wgShowUpdatedMarker && !empty( $obj->wl_notificationtimestamp ) ) {
+ if ( $this->getConfig()->get( 'ShowUpdatedMarker' ) && !empty( $obj->wl_notificationtimestamp ) ) {
$rc->notificationtimestamp = ( $obj->rc_timestamp >= $obj->wl_notificationtimestamp );
} else {
$rc->notificationtimestamp = false; // Default
* @param int $numRows Number of rows in the result to show after this header
*/
public function doHeader( $opts, $numRows ) {
- global $wgScript;
-
$this->setTopText( $opts );
$defaults = $opts->getAllValues();
$t = $this->getPageTitle();
$out .= Html::hidden( 'title', $t->getPrefixedText() );
- $form = Xml::tags( 'form', array( 'action' => $wgScript ), $out );
+ $form = Xml::tags( 'form', array( 'action' => wfScript() ), $out );
$panel[] = $form;
$panelString = implode( "\n", $panel );
$extraOpts = array();
$extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
- global $wgAllowCategorizedRecentChanges;
- if ( $wgAllowCategorizedRecentChanges ) {
+ if ( $this->getConfig()->get( 'AllowCategorizedRecentChanges' ) ) {
$extraOpts['category'] = $this->categoryFilterForm( $opts );
}
* @return string
*/
function optionsPanel( $defaults, $nondefaults, $numRows ) {
- global $wgRCLinkLimits, $wgRCLinkDays;
-
$options = $nondefaults + $defaults;
$note = '';
}
# Sort data for display and make sure it's unique after we've added user data.
- $linkLimits = $wgRCLinkLimits;
+ $linkLimits = $this->getConfig()->get( 'RCLinkLimits' );
$linkLimits[] = $options['limit'];
sort( $linkLimits );
$linkLimits = array_unique( $linkLimits );
- $linkDays = $wgRCLinkDays;
+ $linkDays = $this->getConfig()->get( 'RCLinkDays' );
$linkDays[] = $options['days'];
sort( $linkDays );
$linkDays = array_unique( $linkDays );
# No match, generate an edit URL
$title = Title::newFromText( $term );
if ( !is_null( $title ) ) {
- global $wgGoToEdit;
wfRunHooks( 'SpecialSearchNogomatch', array( &$title ) );
wfDebugLog( 'nogomatch', $title->getFullText(), 'private' );
# If the feature is enabled, go straight to the edit page
- if ( $wgGoToEdit ) {
+ if ( $this->getConfig()->get( 'GoToEdit' ) ) {
$this->getOutput()->redirect( $title->getFullURL( array( 'action' => 'edit' ) ) );
return;
* @param string $term
*/
public function showResults( $term ) {
- global $wgDisableTextSearch, $wgSearchForwardUrl, $wgContLang, $wgScript;
+ global $wgContLang;
$profile = new ProfileSection( __METHOD__ );
$search = $this->getSearchEngine();
$out = $this->getOutput();
- if ( $wgDisableTextSearch ) {
- if ( $wgSearchForwardUrl ) {
- $url = str_replace( '$1', urlencode( $term ), $wgSearchForwardUrl );
+ if ( $this->getConfig()->get( 'DisableTextSearch' ) ) {
+ $searchFowardUrl = $this->getConfig()->get( 'SearchForwardUrl' );
+ if ( $searchFowardUrl ) {
+ $url = str_replace( '$1', urlencode( $term ), $searchFowardUrl );
$out->redirect( $url );
} else {
$out->addHTML(
array(
'id' => ( $this->profile === 'advanced' ? 'powersearch' : 'search' ),
'method' => 'get',
- 'action' => $wgScript
+ 'action' => wfScript(),
)
)
);
}
public function checkExecutePermissions( User $user ) {
- global $wgReadOnlyFile;
-
parent::checkExecutePermissions( $user );
# If the lock file isn't writable, we can do sweet bugger all
- if ( !file_exists( $wgReadOnlyFile ) ) {
+ if ( !file_exists( $this->getConfig()->get( 'ReadOnlyFile' ) ) ) {
throw new ErrorPageError( 'lockdb', 'databasenotlocked' );
}
}
}
public function onSubmit( array $data ) {
- global $wgReadOnlyFile;
-
if ( !$data['Confirm'] ) {
return Status::newFatal( 'locknoconfirm' );
}
+ $readOnlyFile = $this->getConfig()->get( 'ReadOnlyFile' );
wfSuppressWarnings();
- $res = unlink( $wgReadOnlyFile );
+ $res = unlink( $readOnlyFile );
wfRestoreWarnings();
if ( $res ) {
return Status::newGood();
} else {
- return Status::newFatal( 'filedeleteerror', $wgReadOnlyFile );
+ return Status::newFatal( 'filedeleteerror', $readOnlyFile );
}
}
}
function getQueryInfo() {
- global $wgCountCategorizedImagesAsUsed;
$retval = array(
'tables' => array( 'image', 'imagelinks' ),
'fields' => array(
'join_conds' => array( 'imagelinks' => array( 'LEFT JOIN', 'il_to = img_name' ) )
);
- if ( $wgCountCategorizedImagesAsUsed ) {
+ if ( $this->getConfig()->get( 'CountCategorizedImagesAsUsed' ) ) {
// Order is significant
$retval['tables'] = array( 'image', 'page', 'categorylinks',
'imagelinks' );
* @return bool Success
*/
private function outputThumbFromStash( $file, $params ) {
- // this global, if it exists, points to a "scaler", as you might find in
+ $flags = 0;
+ // this config option, if it exists, points to a "scaler", as you might find in
// the Wikimedia Foundation cluster. See outputRemoteScaledThumb(). This
// is part of our horrible NFS-based system, we create a file on a mount
// point here, but fetch the scaled file from somewhere else that
// happens to share it over NFS.
- global $wgUploadStashScalerBaseUrl;
-
- $flags = 0;
- if ( $wgUploadStashScalerBaseUrl ) {
+ if ( $this->getConfig()->get( 'UploadStashScalerBaseUrl' ) ) {
$this->outputRemoteScaledThumb( $file, $params, $flags );
} else {
$this->outputLocallyScaledThumb( $file, $params, $flags );
* @return bool Success
*/
private function outputRemoteScaledThumb( $file, $params, $flags ) {
- // This global probably looks something like
+ // This option probably looks something like
// 'http://upload.wikimedia.org/wikipedia/test/thumb/temp'. Do not use
// trailing slash.
- global $wgUploadStashScalerBaseUrl;
- $scalerBaseUrl = $wgUploadStashScalerBaseUrl;
+ $scalerBaseUrl = $this->getConfig()->get( 'UploadStashScalerBaseUrl' );
if ( preg_match( '/^\/\//', $scalerBaseUrl ) ) {
// this is apparently a protocol-relative URL, which makes no sense in this context,
"action-pagelang": "change the page language",
"log-name-pagelang": "Change language log",
"log-description-pagelang": "This is a log of changes in page languages.",
- "logentry-pagelang-pagelang": "$1 {{GENDER:$2|changed}} page language for $3 from $4 to $5."
+ "logentry-pagelang-pagelang": "$1 {{GENDER:$2|changed}} page language for $3 from $4 to $5.",
+ "default-skin-not-found": "Whoops! The default skin for your wiki (<code>$wgDefaultSkin</code>), <code>$1</code>, is not available.\n\nYour installation seems to include the following skins. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable them and choose the default.\n\n$2\n\n; If you have just installed MediaWiki:\n: You probably installed from git, or directly from the source code using some other method. This is expected.\n:* Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory].\n:* Download the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it.\n: Doing this should not interfere with your git repository if you're a MediaWiki developer.\n\n; If you have just upgraded MediaWiki:\n: MediaWiki 1.24 and newer no longer automatically enables installed skins (see [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). You can paste the following lines into <code>LocalSettings.php</code> to enable all currently installed skins:\n\n<pre>$3</pre>\n\n; If you have just modified <code>LocalSettings.php</code>:\n: Double-check the skin names for typos.",
+ "default-skin-not-found-no-skins": "Whoops! The default skin for your wiki (<code>$wgDefaultSkin</code>), <code>$1</code>, is not available.\n\nYou have no installed skins.\n\n; If you have just installed MediaWiki:\n: You probably installed from git, or directly from the source code using some other method. This is expected.\n:* Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory].\n:* Download the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it.\n: Doing this should not interfere with your git repository if you're a MediaWiki developer. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable skins and choose the default.\n",
+ "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (enabled)",
+ "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''disabled''')"
}
"action-pagelang": "{{Doc-action|pagelang}}",
"log-name-pagelang": "Display entry for log name for changes in page language in Special:Log.",
"log-description-pagelang": "Display description for log name for changes in page language in Special:Log.",
- "logentry-pagelang-pagelang": "{{Logentry}}\nAdditional parameters:\n* $4 - old language code, or \"[def]\" (hard-coded)\n* $5 - new language code, or \"[def]\" (hard-coded)"
+ "logentry-pagelang-pagelang": "{{Logentry}}\nAdditional parameters:\n* $4 - old language code, or \"[def]\" (hard-coded)\n* $5 - new language code, or \"[def]\" (hard-coded)",
+ "default-skin-not-found": "Message shown when the default skin for this MediaWiki installation can not be found.\n\nParameters:\n* $1: skin identifier for the default skin\n* $2: list of installed skins, composed using {{msg-mw|default-skin-not-found-row-enabled}} and {{msg-mw|default-skin-not-found-row-disabled}}\n* $3: code snippet to use to enable installed skins",
+ "default-skin-not-found-no-skins": "Message shown when the default skin for this MediaWiki installation can not be found and the installation has no skins at all.\n\nParameters:\n* $1: name of the default skin",
+ "default-skin-not-found-row-enabled": "One row of the list of installed skins shown as a part of {{msg-mw|default-skin-not-found}}, for an enabled skin.\n\nParameters:\n* $1: skin identifier\n$2: human-readable skin name",
+ "default-skin-not-found-row-disabled": "One row of the list of installed skins shown as a part of {{msg-mw|default-skin-not-found}}, for a disabled skin.\n\nParameters:\n* $1: skin identifier\n$2: human-readable skin name"
}
'</table>'
);
$table.tablesorter();
- assert.equal( 0,
- $table.find( '#A2' ).prop( 'headerIndex' ),
- 'A2 should be a sort header'
+ assert.equal( $table.find( '#A2' ).prop( 'headerIndex' ),
+ 0,
+ 'A2 should not be a sort header'
);
- assert.equal( 1, // should be 2
- $table.find( '#C1' ).prop( 'headerIndex' ),
+ assert.equal( $table.find( '#C1' ).prop( 'headerIndex' ),
+ 1,
'C1 should be a sort header, but will sort the wrong column'
);
} );
'</tbody></table>' );
$table.tablesorter();
- assert.equal( 2, $table.find( 'tr:eq(1) th:eq(1)').prop('headerIndex'), 'Incorrect index of sort header' );
+ assert.equal( $table.find( 'tr:eq(1) th:eq(1)').prop('headerIndex'),
+ 2,
+ 'Incorrect index of sort header' );
}
);