instead of the site content language
* (bug 37926) Deleterevision will no longer allow users to delete log entries,
the new deletelogentry permission is required for this.
+* (bug 14237) Allow PAGESINCATEGORY to distinguish between 'all', 'pages', 'files'
+ and 'subcats'
=== Bug fixes in 1.20 ===
* (bug 30245) Use the correct way to construct a log page title.
who don't have access to /tmp can specify an alternative.
* (bug 27283) SqlBagOStuff breaks PostgreSQL transactions.
* (bug 35727) mw.Api ajax() should put token parameter last.
-* (bug 260) Handle <pre> overflow automatically with a scroll bar.
* (bug 37708) mw.Uri.clone() should make a deep copy.
* (bug 38024) ResourceLoader should not create empty stylesheets for modules
that don't have stylesheets.
values are used instead of just the fixed values from when the tablesorter
was initialized.
* (bug 38093) Gender of changed user groups missing in Special:Log/rights
+* (bug 35893) Special:Block needs to load mediawiki.special.block.js.
=== API changes in 1.20 ===
* (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
* (bug 36987) API avoids mangling fields in continuation parameters
* (bug 30836) siteinfo prop=specialpagealiases will no longer return nonexistent special pages
* (bug 38190) Add "required" flag to some token params for hint in api docs.
+* (bug 27567) Add file repo support to prop=duplicatefiles.
+* (bug 27610) Add archivename for non-latest image version to list=filearchive
=== Languages updated in 1.20 ===
}
$outputPage = $this->getContext()->getOutput();
+ $user = $this->getContext()->getUser();
// If the user is not allowed to see it...
- if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
+ if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
$outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
'rev-deleted-text-permission' );
'FormatMetadata' => 'includes/media/FormatMetadata.php',
'GIFHandler' => 'includes/media/GIF.php',
'GIFMetadataExtractor' => 'includes/media/GIFMetadataExtractor.php',
- 'ImageHandler' => 'includes/media/Generic.php',
+ 'ImageHandler' => 'includes/media/ImageHandler.php',
'IPTC' => 'includes/media/IPTC.php',
'JpegHandler' => 'includes/media/Jpeg.php',
'JpegMetadataExtractor' => 'includes/media/JpegMetadataExtractor.php',
- 'MediaHandler' => 'includes/media/Generic.php',
+ 'MediaHandler' => 'includes/media/MediaHandler.php',
'MediaTransformError' => 'includes/media/MediaTransformOutput.php',
'MediaTransformOutput' => 'includes/media/MediaTransformOutput.php',
'PNGHandler' => 'includes/media/PNG.php',
/**
* Anti-lock flags - bitfield
- * - ALF_PRELOAD_LINKS:
- * Preload links during link update for save
- * - ALF_PRELOAD_EXISTENCE:
- * Preload cur_id during replaceLinkHolders
* - ALF_NO_LINK_LOCK:
* Don't use locking reads when updating the link table. This is
* necessary for wikis with a high edit rate for performance
* reasons, but may cause link table inconsistency
- * - ALF_NO_BLOCK_LOCK:
- * As for ALF_LINK_LOCK, this flag is a necessity for high-traffic
- * wikis.
*/
$wgAntiLockFlags = 0;
* @see LogFormatter
*/
$wgLogActionsHandlers = array(
- // move, move_redir
- 'move/*' => 'MoveLogFormatter',
- // delete, restore, revision, event
- 'delete/*' => 'DeleteLogFormatter',
+ 'move/move' => 'MoveLogFormatter',
+ 'move/move_redir' => 'MoveLogFormatter',
+ 'delete/delete' => 'DeleteLogFormatter',
+ 'delete/restore' => 'DeleteLogFormatter',
+ 'delete/revision' => 'DeleteLogFormatter',
+ 'delete/event' => 'DeleteLogFormatter',
'suppress/revision' => 'DeleteLogFormatter',
'suppress/event' => 'DeleteLogFormatter',
'suppress/delete' => 'DeleteLogFormatter',
* Anti-lock flags
* See DefaultSettings.php for a description
*/
-define( 'ALF_PRELOAD_LINKS', 1 );
-define( 'ALF_PRELOAD_EXISTENCE', 2 );
+define( 'ALF_PRELOAD_LINKS', 1 ); // unused
+define( 'ALF_PRELOAD_EXISTENCE', 2 ); // unused
define( 'ALF_NO_LINK_LOCK', 4 );
define( 'ALF_NO_BLOCK_LOCK', 8 );
/**@}*/
if ( $t->isRedirect() ) {
# Page is a redirect
$colour = 'mw-redirect';
- } elseif ( $threshold > 0 &&
- $t->exists() && $t->getLength() < $threshold &&
- $t->isContentPage() ) {
+ } elseif ( $threshold > 0 && $t->isContentPage() &&
+ $t->exists() && $t->getLength() < $threshold
+ ) {
# Page is a stub
$colour = 'stub';
}
$wgLogTypes[] = 'newusers';
$wgLogNames['newusers'] = 'newuserlogpage';
$wgLogHeaders['newusers'] = 'newuserlogpagetext';
- # newusers, create, create2, autocreate
- $wgLogActionsHandlers['newusers/*'] = 'NewUsersLogFormatter';
+ $wgLogActionsHandlers['newusers/newusers'] = 'NewUsersLogFormatter';
+ $wgLogActionsHandlers['newusers/create'] = 'NewUsersLogFormatter';
+ $wgLogActionsHandlers['newusers/create2'] = 'NewUsersLogFormatter';
+ $wgLogActionsHandlers['newusers/autocreate'] = 'NewUsersLogFormatter';
}
if ( $wgCookieSecure === 'detect' ) {
return $this->context->getTitle();
}
+ /**
+ * Returns the name of the action that will be executed.
+ *
+ * @return string: action
+ */
+ public function getAction() {
+ static $action = null;
+
+ if ( $action === null ) {
+ $action = Action::getActionName( $this->context );
+ }
+
+ return $action;
+ }
+
+ /**
+ * Create an Article object of the appropriate class for the given page.
+ *
+ * @deprecated in 1.18; use Article::newFromTitle() instead
+ * @param $title Title
+ * @param $context IContextSource
+ * @return Article object
+ */
+ public static function articleFromTitle( $title, IContextSource $context ) {
+ wfDeprecated( __METHOD__, '1.18' );
+ return Article::newFromTitle( $title, $context );
+ }
+
/**
* Performs the request.
* - bad titles
wfProfileOut( __METHOD__ );
}
- /**
- * Create an Article object of the appropriate class for the given page.
- *
- * @deprecated in 1.18; use Article::newFromTitle() instead
- * @param $title Title
- * @param $context IContextSource
- * @return Article object
- */
- public static function articleFromTitle( $title, IContextSource $context ) {
- wfDeprecated( __METHOD__, '1.18' );
- return Article::newFromTitle( $title, $context );
- }
-
- /**
- * Returns the name of the action that will be executed.
- *
- * @return string: action
- */
- public function getAction() {
- static $action = null;
-
- if ( $action === null ) {
- $action = Action::getActionName( $this->context );
- }
-
- return $action;
- }
-
/**
* Initialize the main Article object for "standard" actions (view, etc)
* Create an Article object for the page, following redirects if needed.
return $article;
}
- /**
- * Do a job from the job queue
- */
- private function doJobs() {
- global $wgJobRunRate;
-
- if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
- return;
- }
- if ( $wgJobRunRate < 1 ) {
- $max = mt_getrandmax();
- if ( mt_rand( 0, $max ) > $max * $wgJobRunRate ) {
- return;
- }
- $n = 1;
- } else {
- $n = intval( $wgJobRunRate );
- }
-
- while ( $n-- && false != ( $job = Job::pop() ) ) {
- $output = $job->toString() . "\n";
- $t = - microtime( true );
- $success = $job->run();
- $t += microtime( true );
- $t = round( $t * 1000 );
- if ( !$success ) {
- $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
- } else {
- $output .= "Success, Time: $t ms\n";
- }
- wfDebugLog( 'jobqueue', $output );
- }
- }
-
- /**
- * Ends this task peacefully
- */
- public function restInPeace() {
- // Do any deferred jobs
- DeferredUpdates::doUpdates( 'commit' );
-
- // Execute a job from the queue
- $this->doJobs();
-
- // Log message usage, if $wgAdaptiveMessageCache is set to true
- MessageCache::logMessages();
-
- // Log profiling data, e.g. in the database or UDP
- wfLogProfilingData();
-
- // Commit and close up!
- $factory = wfGetLBFactory();
- $factory->commitMasterChanges();
- $factory->shutdown();
-
- wfDebug( "Request ended normally\n" );
- }
-
/**
* Perform one of the "standard" actions
*
wfProfileOut( __METHOD__ );
}
+
+ /**
+ * Ends this task peacefully
+ */
+ public function restInPeace() {
+ // Do any deferred jobs
+ DeferredUpdates::doUpdates( 'commit' );
+
+ // Execute a job from the queue
+ $this->doJobs();
+
+ // Log message usage, if $wgAdaptiveMessageCache is set to true
+ MessageCache::logMessages();
+
+ // Log profiling data, e.g. in the database or UDP
+ wfLogProfilingData();
+
+ // Commit and close up!
+ $factory = wfGetLBFactory();
+ $factory->commitMasterChanges();
+ $factory->shutdown();
+
+ wfDebug( "Request ended normally\n" );
+ }
+
+ /**
+ * Do a job from the job queue
+ */
+ private function doJobs() {
+ global $wgJobRunRate;
+
+ if ( $wgJobRunRate <= 0 || wfReadOnly() ) {
+ return;
+ }
+ if ( $wgJobRunRate < 1 ) {
+ $max = mt_getrandmax();
+ if ( mt_rand( 0, $max ) > $max * $wgJobRunRate ) {
+ return;
+ }
+ $n = 1;
+ } else {
+ $n = intval( $wgJobRunRate );
+ }
+
+ while ( $n-- && false != ( $job = Job::pop() ) ) {
+ $output = $job->toString() . "\n";
+ $t = - microtime( true );
+ $success = $job->run();
+ $t += microtime( true );
+ $t = round( $t * 1000 );
+ if ( !$success ) {
+ $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
+ } else {
+ $output .= "Success, Time: $t ms\n";
+ }
+ wfDebugLog( 'jobqueue', $output );
+ }
+ }
}
'皇庄' => '皇莊',
'皓发' => '皓髮',
'皮制服' => '皮制服',
+'皮肤' => '皮膚',
'皮里春秋' => '皮裡春秋',
'皮里阳秋' => '皮裡陽秋',
'皮制' => '皮製',
'跌扑' => '跌扑',
'跌荡' => '跌蕩',
'路签' => '路籤',
+'路面' => '路面',
'跳梁小丑' => '跳樑小丑',
'跳荡' => '跳蕩',
'跳表' => '跳錶',
'攜帶型' => '便携式',
'資訊理論' => '信息论',
'母音' => '元音',
-'游標' => '光标',
'光碟' => '光盘',
'光碟機' => '光驱',
'柯林頓' => '克林顿',
'笨豬跳' => '绑紧跳',
'蹦极跳' => '绑紧跳',
'笑星' => '谐星',
-);
\ No newline at end of file
+);
'NOTE: generator parameter names must be prefixed with a \'g\', see examples' ),
'redirects' => 'Automatically resolve redirects',
'converttitles' => array( "Convert titles to other variants if necessary. Only works if the wiki's content language supports variant conversion.",
- 'Languages that support variant conversion include gan, iu, kk, ku, shi, sr, tg, zh' ),
+ 'Languages that support variant conversion include ' . implode( ', ', LanguageConverter::$languagesWithVariants ) ),
'indexpageids' => 'Include an additional pageids section listing all returned page IDs',
'export' => 'Export the current revisions of all given or generated pages',
'exportnowrap' => 'Return the export XML without wrapping it in an XML result (same format as Special:Export). Can only be used with export',
}
$images = $namespaces[NS_FILE];
- $this->addTables( 'image', 'i1' );
- $this->addTables( 'image', 'i2' );
- $this->addFields( array(
- 'i1.img_name AS orig_name',
- 'i2.img_name AS dup_name',
- 'i2.img_user_text AS dup_user_text',
- 'i2.img_timestamp AS dup_timestamp'
- ) );
-
- $this->addWhere( array(
- 'i1.img_name' => array_keys( $images ),
- 'i1.img_sha1 = i2.img_sha1',
- 'i1.img_name != i2.img_name',
- ) );
+ if( $params['dir'] == 'descending' ) {
+ $images = array_reverse( $images );
+ }
+ $skipUntilThisDup = false;
if ( isset( $params['continue'] ) ) {
$cont = explode( '|', $params['continue'] );
if ( count( $cont ) != 2 ) {
$this->dieUsage( 'Invalid continue param. You should pass the ' .
'original value returned by the previous query', '_badcontinue' );
}
- $op = $params['dir'] == 'descending' ? '<' : '>';
- $db = $this->getDB();
- $orig = $db->addQuotes( $cont[0] );
- $dup = $db->addQuotes( $cont[1] );
- $this->addWhere(
- "i1.img_name $op $orig OR " .
- "(i1.img_name = $orig AND " .
- "i2.img_name $op= $dup)"
- );
+ $fromImage = $cont[0];
+ $skipUntilThisDup = $cont[1];
+ // Filter out any images before $fromImage
+ foreach ( $images as $image => $pageId ) {
+ if ( $image < $fromImage ) {
+ unset( $images[$image] );
+ } else {
+ break;
+ }
+ }
}
- $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
- // Don't order by i1.img_name if it's constant in the WHERE clause
- if ( count( $this->getPageSet()->getGoodTitles() ) == 1 ) {
- $this->addOption( 'ORDER BY', 'i2.img_name' . $sort );
- } else {
- $this->addOption( 'ORDER BY', array(
- 'i1.img_name' . $sort,
- 'i2.img_name' . $sort
- ));
- }
- $this->addOption( 'LIMIT', $params['limit'] + 1 );
+ $files = RepoGroup::singleton()->findFiles( array_keys( $images ) );
- $res = $this->select( __METHOD__ );
+ $fit = true;
$count = 0;
$titles = array();
- foreach ( $res as $row ) {
- if ( ++$count > $params['limit'] ) {
- // We've reached the one extra which shows that
- // there are additional pages to be had. Stop here...
- $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
- break;
+
+ $sha1s = array();
+ foreach ( $files as $file ) {
+ $sha1s[$file->getName()] = $file->getSha1();
+ }
+
+ // find all files with the hashes, result format is: array( hash => array( dup1, dup2 ), hash1 => ... )
+ $filesBySha1s = RepoGroup::singleton()->findBySha1s( array_unique( array_values( $sha1s ) ) );
+
+ // iterate over $images to handle continue param correct
+ foreach( $images as $image => $pageId ) {
+ if( !isset( $sha1s[$image] ) ) {
+ continue; //file does not exist
}
- if ( !is_null( $resultPageSet ) ) {
- $titles[] = Title::makeTitle( NS_FILE, $row->dup_name );
- } else {
- $r = array(
- 'name' => $row->dup_name,
- 'user' => $row->dup_user_text,
- 'timestamp' => wfTimestamp( TS_ISO_8601, $row->dup_timestamp )
- );
- $fit = $this->addPageSubItem( $images[$row->orig_name], $r );
- if ( !$fit ) {
- $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
+ $sha1 = $sha1s[$image];
+ $dupFiles = $filesBySha1s[$sha1];
+ if( $params['dir'] == 'descending' ) {
+ $dupFiles = array_reverse( $dupFiles );
+ }
+ foreach ( $dupFiles as $dupFile ) {
+ $dupName = $dupFile->getName();
+ if( $image == $dupName ) {
+ continue; //ignore the file itself
+ }
+ if( $skipUntilThisDup !== false && $dupName < $skipUntilThisDup ) {
+ continue; //skip to pos after the image from continue param
+ }
+ $skipUntilThisDup = false;
+ if ( ++$count > $params['limit'] ) {
+ $fit = false; //break outer loop
+ // We're one over limit which shows that
+ // there are additional images to be had. Stop here...
+ $this->setContinueEnumParameter( 'continue', $image . '|' . $dupName );
break;
}
+ if ( !is_null( $resultPageSet ) ) {
+ $titles[] = $file->getTitle();
+ } else {
+ $r = array(
+ 'name' => $dupName,
+ 'user' => $dupFile->getUser( 'text' ),
+ 'timestamp' => wfTimestamp( TS_ISO_8601, $dupFile->getTimestamp() )
+ );
+ $fit = $this->addPageSubItem( $pageId, $r );
+ if ( !$fit ) {
+ $this->setContinueEnumParameter( 'continue', $image . '|' . $dupName );
+ break;
+ }
+ }
+ }
+ if( !$fit ) {
+ break;
}
}
if ( !is_null( $resultPageSet ) ) {
public function getParamDescription() {
return array(
- 'limit' => 'How many files to return',
+ 'limit' => 'How many duplicate files to return',
'continue' => 'When more results are available, use this to continue',
'dir' => 'The direction in which to list',
);
}
public function getDescription() {
- return 'List all files that are duplicates of the given file(s)';
+ return 'List all files that are duplicates of the given file(s) based on hash values';
}
public function getPossibleErrors() {
$fld_mediatype = isset( $prop['mediatype'] );
$fld_metadata = isset( $prop['metadata'] );
$fld_bitdepth = isset( $prop['bitdepth'] );
+ $fld_archivename = isset( $prop['archivename'] );
$this->addTables( 'filearchive' );
$this->addFieldsIf( 'fa_media_type', $fld_mediatype );
$this->addFieldsIf( 'fa_metadata', $fld_metadata );
$this->addFieldsIf( 'fa_bits', $fld_bitdepth );
+ $this->addFieldsIf( 'fa_archive_name', $fld_archivename );
if ( !is_null( $params['continue'] ) ) {
$cont = explode( '|', $params['continue'] );
if ( $fld_mime ) {
$file['mime'] = "$row->fa_major_mime/$row->fa_minor_mime";
}
+ if ( $fld_archivename && !is_null( $row->fa_archive_name ) ) {
+ $file['archivename'] = $row->fa_archive_name;
+ }
if ( $row->fa_deleted & File::DELETED_FILE ) {
$file['filehidden'] = '';
'mime',
'mediatype',
'metadata',
- 'bitdepth'
+ 'bitdepth',
+ 'archivename',
),
),
);
' mediatype - Adds the media type of the image',
' metadata - Lists EXIF metadata for the version of the image',
' bitdepth - Adds the bit depth of the version',
+ ' archivename - Adds the file name of the archive version for non-latest versions'
),
);
}
),
'mediatype' => array(
'mediatype' => 'string'
- )
+ ),
+ 'archivename' => array(
+ 'archivename' => 'string'
+ ),
);
}
// Get normal protections for existing titles
if ( count( $this->titles ) ) {
$this->resetQueryParams();
- $this->addTables( array( 'page_restrictions', 'page' ) );
- $this->addWhere( 'page_id=pr_page' );
+ $this->addTables( 'page_restrictions' );
$this->addFields( array( 'pr_page', 'pr_type', 'pr_level',
- 'pr_expiry', 'pr_cascade', 'page_namespace',
- 'page_title' ) );
+ 'pr_expiry', 'pr_cascade' ) );
$this->addWhereFld( 'pr_page', array_keys( $this->titles ) );
$res = $this->select( __METHOD__ );
foreach ( $res as $row ) {
+ $title = $this->titles[$row->pr_page];
$a = array(
'type' => $row->pr_type,
'level' => $row->pr_level,
if ( $row->pr_cascade ) {
$a['cascade'] = '';
}
- $this->protections[$row->page_namespace][$row->page_title][] = $a;
-
- // Also check old restrictions
- if ( $this->pageRestrictions[$row->pr_page] ) {
- $restrictions = explode( ':', trim( $this->pageRestrictions[$row->pr_page] ) );
+ $this->protections[$title->getNamespace()][$title->getDBkey()][] = $a;
+ }
+ // Also check old restrictions
+ foreach( $this->titles as $pageId => $title ) {
+ if ( $this->pageRestrictions[$pageId] ) {
+ $namespace = $title->getNamespace();
+ $dbKey = $title->getDBkey();
+ $restrictions = explode( ':', trim( $this->pageRestrictions[$pageId] ) );
foreach ( $restrictions as $restrict ) {
$temp = explode( '=', trim( $restrict ) );
if ( count( $temp ) == 1 ) {
if ( $restriction == '' ) {
continue;
}
- $this->protections[$row->page_namespace][$row->page_title][] = array(
+ $this->protections[$namespace][$dbKey][] = array(
'type' => 'edit',
'level' => $restriction,
'expiry' => 'infinity',
);
- $this->protections[$row->page_namespace][$row->page_title][] = array(
+ $this->protections[$namespace][$dbKey][] = array(
'type' => 'move',
'level' => $restriction,
'expiry' => 'infinity',
if ( $restriction == '' ) {
continue;
}
- $this->protections[$row->page_namespace][$row->page_title][] = array(
+ $this->protections[$namespace][$dbKey][] = array(
'type' => $temp[0],
'level' => $restriction,
'expiry' => 'infinity',
}
public function getAllowedParams() {
- global $wgLogTypes, $wgLogActions;
+ global $wgLogTypes, $wgLogActions, $wgLogActionsHandlers;
return array(
'prop' => array(
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => $wgLogTypes
),
'action' => array(
- ApiBase::PARAM_TYPE => array_keys( $wgLogActions )
+ ApiBase::PARAM_TYPE => array_keys( array_merge( $wgLogActions, $wgLogActionsHandlers ) )
),
'start' => array(
ApiBase::PARAM_TYPE => 'timestamp'
* aims to be both simple and very flexible. It is centered around an associative
* array of fields and various methods to do common interaction with the database.
*
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
+ *
* 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
<?php
/**
* Interface for objects representing a single database table.
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
*
* 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
* ORMIterator that takes a ResultWrapper object returned from
* a select operation returning IORMRow objects (ie IORMTable::select).
*
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
+ *
* 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
* aims to be both simple and very flexible. It is centered around an associative
* array of fields and various methods to do common interaction with the database.
*
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
+ *
* 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
<?php
/**
* Abstract base class for representing a single database table.
+ * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
*
* 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
* @return IORMTable
*/
public static function singleton() {
- $class = function_exists( 'get_called_class' ) ? get_called_class() : self::get_called_class();
+ $class = get_called_class();
if ( !array_key_exists( $class, self::$instanceCache ) ) {
self::$instanceCache[$class] = new $class;
return self::$instanceCache[$class];
}
- /**
- * Compatibility fallback function so the singleton method works on PHP < 5.3.
- * Code borrowed from http://www.php.net/manual/en/function.get-called-class.php#107445
- *
- * @since 1.20
- *
- * @return string
- */
- protected static function get_called_class() {
- $bt = debug_backtrace();
- $l = count($bt) - 1;
- $matches = array();
- while(empty($matches) && $l > -1){
- $lines = file($bt[$l]['file']);
- $callerLine = $lines[$bt[$l]['line']-1];
- preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l--]['function'].'/',
- $callerLine,
- $matches);
- }
- if (!isset($matches[1])) $matches[1]=NULL; //for notices
- if ($matches[1] == 'self') {
- $line = $bt[$l]['line']-1;
- while ($line > 0 && strpos($lines[$line], 'class') === false) {
- $line--;
- }
- preg_match('/class[\s]+(.+?)[\s]+/si', $lines[$line], $matches);
- }
- return $matches[1];
- }
-
/**
* Get an array with fields from a database result,
* that can be fed directly to the constructor or
/**
* @see ORMTable::newRowFromFromDBResult
*
- * @deprecated use newRowFromFromDBResult instead
+ * @deprecated use newRowFromDBResult instead
* @since 1.20
*
* @param stdClass $result
return array();
}
+ /**
+ * Get an array of arrays or iterators of file objects for files that
+ * have the given SHA-1 content hashes.
+ *
+ * @param $hashes array An array of hashes
+ * @return array An Array of arrays or iterators of file objects and the hash as key
+ */
+ public function findBySha1s( array $hashes ) {
+ $result = array();
+ foreach ( $hashes as $hash ) {
+ $files = $this->findBySha1( $hash );
+ if ( count( $files ) ) {
+ $result[$hash] = $files;
+ }
+ }
+ return $result;
+ }
+
/**
* Get the public root URL of the repository
*
return $result;
}
+ /**
+ * Get an array of arrays or iterators of file objects for files that
+ * have the given SHA-1 content hashes.
+ *
+ * Overrides generic implementation in FileRepo for performance reason
+ *
+ * @param $hashes array An array of hashes
+ * @return array An Array of arrays or iterators of file objects and the hash as key
+ */
+ function findBySha1s( array $hashes ) {
+ if( !count( $hashes ) ) {
+ return array(); //empty parameter
+ }
+
+ $dbr = $this->getSlaveDB();
+ $res = $dbr->select(
+ 'image',
+ LocalFile::selectFields(),
+ array( 'img_sha1' => $hashes ),
+ __METHOD__,
+ array( 'ORDER BY' => 'img_name' )
+ );
+
+ $result = array();
+ foreach ( $res as $row ) {
+ $file = $this->newFileFromRow( $row );
+ $result[$file->getSha1()][] = $file;
+ }
+ $res->free();
+
+ return $result;
+ }
+
/**
* Get a connection to the slave DB
* @return DatabaseBase
return $result;
}
+ /**
+ * Find all instances of files with this keys
+ *
+ * @param $hashes Array base 36 SHA-1 hashes
+ * @return Array of array of File objects
+ */
+ function findBySha1s( array $hashes ) {
+ if ( !$this->reposInitialised ) {
+ $this->initialiseRepos();
+ }
+
+ $result = $this->localRepo->findBySha1s( $hashes );
+ foreach ( $this->foreignRepos as $repo ) {
+ $result = array_merge_recursive( $result, $repo->findBySha1s( $hashes ) );
+ }
+ //sort the merged (and presorted) sublist of each hash
+ foreach( $result as $hash => $files ) {
+ usort( $result[$hash], 'File::compare' );
+ }
+ return $result;
+ }
+
/**
* Get the repo instance with a given key.
* @param $index string|int
* Setting this to '*' effectively makes a container public.
* - .rlistings:<regex> : Grants access if the request is from a referrer host that
* matches the expression and the request for a listing.
- *
+ *
* $writeGrps is a list of the possible criteria for a request to have
* access to write to a container. Each item is of the following format:
* - account:user : Grants access if the request is by the given user
protected $dir; // string; storage directory
protected $suffixStart; // integer
- const PAGE_SIZE = 5000; // file listing buffer size
+ const PAGE_SIZE = 9000; // file listing buffer size
/**
* @param $backend SwiftFileBackend
+++ /dev/null
-<?php
-/**
- * Media-handling base classes and generic functionality.
- *
- * 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
- * @ingroup Media
- */
-
-/**
- * Base media handler class
- *
- * @ingroup Media
- */
-abstract class MediaHandler {
- const TRANSFORM_LATER = 1;
- const METADATA_GOOD = true;
- const METADATA_BAD = false;
- const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
- /**
- * Instance cache
- */
- static $handlers = array();
-
- /**
- * Get a MediaHandler for a given MIME type from the instance cache
- *
- * @param $type string
- *
- * @return MediaHandler
- */
- static function getHandler( $type ) {
- global $wgMediaHandlers;
- if ( !isset( $wgMediaHandlers[$type] ) ) {
- wfDebug( __METHOD__ . ": no handler found for $type.\n");
- return false;
- }
- $class = $wgMediaHandlers[$type];
- if ( !isset( self::$handlers[$class] ) ) {
- self::$handlers[$class] = new $class;
- if ( !self::$handlers[$class]->isEnabled() ) {
- self::$handlers[$class] = false;
- }
- }
- return self::$handlers[$class];
- }
-
- /**
- * Get an associative array mapping magic word IDs to parameter names.
- * Will be used by the parser to identify parameters.
- */
- abstract function getParamMap();
-
- /**
- * Validate a thumbnail parameter at parse time.
- * Return true to accept the parameter, and false to reject it.
- * If you return false, the parser will do something quiet and forgiving.
- *
- * @param $name
- * @param $value
- */
- abstract function validateParam( $name, $value );
-
- /**
- * Merge a parameter array into a string appropriate for inclusion in filenames
- *
- * @param $params array
- */
- abstract function makeParamString( $params );
-
- /**
- * Parse a param string made with makeParamString back into an array
- *
- * @param $str string
- */
- abstract function parseParamString( $str );
-
- /**
- * Changes the parameter array as necessary, ready for transformation.
- * Should be idempotent.
- * Returns false if the parameters are unacceptable and the transform should fail
- * @param $image
- * @param $params
- */
- abstract function normaliseParams( $image, &$params );
-
- /**
- * Get an image size array like that returned by getimagesize(), or false if it
- * can't be determined.
- *
- * @param $image File: the image object, or false if there isn't one
- * @param $path String: the filename
- * @return Array Follow the format of PHP getimagesize() internal function. See http://www.php.net/getimagesize
- */
- abstract function getImageSize( $image, $path );
-
- /**
- * Get handler-specific metadata which will be saved in the img_metadata field.
- *
- * @param $image File: the image object, or false if there isn't one.
- * Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!)
- * @param $path String: the filename
- * @return String
- */
- function getMetadata( $image, $path ) { return ''; }
-
- /**
- * Get metadata version.
- *
- * This is not used for validating metadata, this is used for the api when returning
- * metadata, since api content formats should stay the same over time, and so things
- * using ForiegnApiRepo can keep backwards compatibility
- *
- * All core media handlers share a common version number, and extensions can
- * use the GetMetadataVersion hook to append to the array (they should append a unique
- * string so not to get confusing). If there was a media handler named 'foo' with metadata
- * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata
- * version is 2, the end version string would look like '2;foo=3'.
- *
- * @return string version string
- */
- static function getMetadataVersion () {
- $version = Array( '2' ); // core metadata version
- wfRunHooks('GetMetadataVersion', Array(&$version));
- return implode( ';', $version);
- }
-
- /**
- * Convert metadata version.
- *
- * By default just returns $metadata, but can be used to allow
- * media handlers to convert between metadata versions.
- *
- * @param $metadata Mixed String or Array metadata array (serialized if string)
- * @param $version Integer target version
- * @return Array serialized metadata in specified version, or $metadata on fail.
- */
- function convertMetadataVersion( $metadata, $version = 1 ) {
- if ( !is_array( $metadata ) ) {
-
- //unserialize to keep return parameter consistent.
- wfSuppressWarnings();
- $ret = unserialize( $metadata );
- wfRestoreWarnings();
- return $ret;
- }
- return $metadata;
- }
-
- /**
- * Get a string describing the type of metadata, for display purposes.
- *
- * @return string
- */
- function getMetadataType( $image ) { return false; }
-
- /**
- * Check if the metadata string is valid for this handler.
- * If it returns MediaHandler::METADATA_BAD (or false), Image
- * will reload the metadata from the file and update the database.
- * MediaHandler::METADATA_GOOD for if the metadata is a-ok,
- * MediaHanlder::METADATA_COMPATIBLE if metadata is old but backwards
- * compatible (which may or may not trigger a metadata reload).
- * @return bool
- */
- function isMetadataValid( $image, $metadata ) {
- return self::METADATA_GOOD;
- }
-
-
- /**
- * Get a MediaTransformOutput object representing an alternate of the transformed
- * output which will call an intermediary thumbnail assist script.
- *
- * Used when the repository has a thumbnailScriptUrl option configured.
- *
- * Return false to fall back to the regular getTransform().
- * @return bool
- */
- function getScriptedTransform( $image, $script, $params ) {
- return false;
- }
-
- /**
- * Get a MediaTransformOutput object representing the transformed output. Does not
- * actually do the transform.
- *
- * @param $image File: the image object
- * @param $dstPath String: filesystem destination path
- * @param $dstUrl String: Destination URL to use in output HTML
- * @param $params Array: Arbitrary set of parameters validated by $this->validateParam()
- * @return MediaTransformOutput
- */
- final function getTransform( $image, $dstPath, $dstUrl, $params ) {
- return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
- }
-
- /**
- * Get a MediaTransformOutput object representing the transformed output. Does the
- * transform unless $flags contains self::TRANSFORM_LATER.
- *
- * @param $image File: the image object
- * @param $dstPath String: filesystem destination path
- * @param $dstUrl String: destination URL to use in output HTML
- * @param $params Array: arbitrary set of parameters validated by $this->validateParam()
- * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER
- *
- * @return MediaTransformOutput
- */
- abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
-
- /**
- * Get the thumbnail extension and MIME type for a given source MIME type
- * @return array thumbnail extension and MIME type
- */
- function getThumbType( $ext, $mime, $params = null ) {
- $magic = MimeMagic::singleton();
- if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
- // The extension is not valid for this mime type and we do
- // recognize the mime type
- $extensions = $magic->getExtensionsForType( $mime );
- if ( $extensions ) {
- return array( strtok( $extensions, ' ' ), $mime );
- }
- }
-
- // The extension is correct (true) or the mime type is unknown to
- // MediaWiki (null)
- return array( $ext, $mime );
- }
-
- /**
- * True if the handled types can be transformed
- * @return bool
- */
- function canRender( $file ) { return true; }
- /**
- * True if handled types cannot be displayed directly in a browser
- * but can be rendered
- * @return bool
- */
- function mustRender( $file ) { return false; }
- /**
- * True if the type has multi-page capabilities
- * @return bool
- */
- function isMultiPage( $file ) { return false; }
- /**
- * Page count for a multi-page document, false if unsupported or unknown
- * @return bool
- */
- function pageCount( $file ) { return false; }
- /**
- * The material is vectorized and thus scaling is lossless
- * @return bool
- */
- function isVectorized( $file ) { return false; }
- /**
- * False if the handler is disabled for all files
- * @return bool
- */
- function isEnabled() { return true; }
-
- /**
- * Get an associative array of page dimensions
- * Currently "width" and "height" are understood, but this might be
- * expanded in the future.
- * Returns false if unknown or if the document is not multi-page.
- *
- * @param $image File
- * @param $page Unused, left for backcompatibility?
- * @return array
- */
- function getPageDimensions( $image, $page ) {
- $gis = $this->getImageSize( $image, $image->getLocalRefPath() );
- return array(
- 'width' => $gis[0],
- 'height' => $gis[1]
- );
- }
-
- /**
- * Generic getter for text layer.
- * Currently overloaded by PDF and DjVu handlers
- * @return bool
- */
- function getPageText( $image, $page ) {
- return false;
- }
-
- /**
- * Get an array structure that looks like this:
- *
- * array(
- * 'visible' => array(
- * 'Human-readable name' => 'Human readable value',
- * ...
- * ),
- * 'collapsed' => array(
- * 'Human-readable name' => 'Human readable value',
- * ...
- * )
- * )
- * The UI will format this into a table where the visible fields are always
- * visible, and the collapsed fields are optionally visible.
- *
- * The function should return false if there is no metadata to display.
- */
-
- /**
- * @todo FIXME: I don't really like this interface, it's not very flexible
- * I think the media handler should generate HTML instead. It can do
- * all the formatting according to some standard. That makes it possible
- * to do things like visual indication of grouped and chained streams
- * in ogg container files.
- * @return bool
- */
- function formatMetadata( $image ) {
- return false;
- }
-
- /** sorts the visible/invisible field.
- * Split off from ImageHandler::formatMetadata, as used by more than
- * one type of handler.
- *
- * This is used by the media handlers that use the FormatMetadata class
- *
- * @param $metadataArray Array metadata array
- * @return array for use displaying metadata.
- */
- function formatMetadataHelper( $metadataArray ) {
- $result = array(
- 'visible' => array(),
- 'collapsed' => array()
- );
-
- $formatted = FormatMetadata::getFormattedData( $metadataArray );
- // Sort fields into visible and collapsed
- $visibleFields = $this->visibleMetadataFields();
- foreach ( $formatted as $name => $value ) {
- $tag = strtolower( $name );
- self::addMeta( $result,
- in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
- 'exif',
- $tag,
- $value
- );
- }
- return $result;
- }
-
- /**
- * Get a list of metadata items which should be displayed when
- * the metadata table is collapsed.
- *
- * @return array of strings
- * @access protected
- */
- function visibleMetadataFields() {
- $fields = array();
- $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) );
- foreach( $lines as $line ) {
- $matches = array();
- if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
- $fields[] = $matches[1];
- }
- }
- $fields = array_map( 'strtolower', $fields );
- return $fields;
- }
-
-
- /**
- * This is used to generate an array element for each metadata value
- * That array is then used to generate the table of metadata values
- * on the image page
- *
- * @param &$array Array An array containing elements for each type of visibility
- * and each of those elements being an array of metadata items. This function adds
- * a value to that array.
- * @param $visibility string ('visible' or 'collapsed') if this value is hidden
- * by default.
- * @param $type String type of metadata tag (currently always 'exif')
- * @param $id String the name of the metadata tag (like 'artist' for example).
- * its name in the table displayed is the message "$type-$id" (Ex exif-artist ).
- * @param $value String thingy goes into a wikitext table; it used to be escaped but
- * that was incompatible with previous practise of customized display
- * with wikitext formatting via messages such as 'exif-model-value'.
- * So the escaping is taken back out, but generally this seems a confusing
- * interface.
- * @param $param String value to pass to the message for the name of the field
- * as $1. Currently this parameter doesn't seem to ever be used.
- *
- * Note, everything here is passed through the parser later on (!)
- */
- protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
- $msg = wfMessage( "$type-$id", $param );
- if ( $msg->exists() ) {
- $name = $msg->text();
- } else {
- // This is for future compatibility when using instant commons.
- // So as to not display as ugly a name if a new metadata
- // property is defined that we don't know about
- // (not a major issue since such a property would be collapsed
- // by default).
- wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id . "\n" );
- $name = wfEscapeWikiText( $id );
- }
- $array[$visibility][] = array(
- 'id' => "$type-$id",
- 'name' => $name,
- 'value' => $value
- );
- }
-
- /**
- * @param $file File
- * @return string
- */
- function getShortDesc( $file ) {
- global $wgLang;
- return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
- }
-
- /**
- * @param $file File
- * @return string
- */
- function getLongDesc( $file ) {
- global $wgLang;
- return wfMessage( 'file-info', htmlspecialchars( $wgLang->formatSize( $file->getSize() ) ),
- $file->getMimeType() )->parse();
- }
-
- /**
- * @param $file File
- * @return string
- */
- static function getGeneralShortDesc( $file ) {
- global $wgLang;
- return $wgLang->formatSize( $file->getSize() );
- }
-
- /**
- * @param $file File
- * @return string
- */
- static function getGeneralLongDesc( $file ) {
- global $wgLang;
- return wfMessage( 'file-info', $wgLang->formatSize( $file->getSize() ),
- $file->getMimeType() )->parse();
- }
-
- /**
- * Calculate the largest thumbnail width for a given original file size
- * such that the thumbnail's height is at most $maxHeight.
- * @param $boxWidth Integer Width of the thumbnail box.
- * @param $boxHeight Integer Height of the thumbnail box.
- * @param $maxHeight Integer Maximum height expected for the thumbnail.
- * @return Integer.
- */
- public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
- $idealWidth = $boxWidth * $maxHeight / $boxHeight;
- $roundedUp = ceil( $idealWidth );
- if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
- return floor( $idealWidth );
- } else {
- return $roundedUp;
- }
- }
-
- function getDimensionsString( $file ) {
- return '';
- }
-
- /**
- * Modify the parser object post-transform
- */
- function parserTransformHook( $parser, $file ) {}
-
- /**
- * File validation hook called on upload.
- *
- * If the file at the given local path is not valid, or its MIME type does not
- * match the handler class, a Status object should be returned containing
- * relevant errors.
- *
- * @param $fileName string The local path to the file.
- * @return Status object
- */
- function verifyUpload( $fileName ) {
- return Status::newGood();
- }
-
- /**
- * Check for zero-sized thumbnails. These can be generated when
- * no disk space is available or some other error occurs
- *
- * @param $dstPath string The location of the suspect file
- * @param $retval int Return value of some shell process, file will be deleted if this is non-zero
- * @return bool True if removed, false otherwise
- */
- function removeBadFile( $dstPath, $retval = 0 ) {
- if( file_exists( $dstPath ) ) {
- $thumbstat = stat( $dstPath );
- if( $thumbstat['size'] == 0 || $retval != 0 ) {
- $result = unlink( $dstPath );
-
- if ( $result ) {
- wfDebugLog( 'thumbnail',
- sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
- $thumbstat['size'], $dstPath ) );
- } else {
- wfDebugLog( 'thumbnail',
- sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
- $thumbstat['size'], $dstPath ) );
- }
- return true;
- }
- }
- return false;
- }
-
- /**
- * Remove files from the purge list
- *
- * @param array $files
- * @param array $options
- */
- public function filterThumbnailPurgeList( &$files, $options ) {
- // Do nothing
- }
-}
-
-/**
- * Media handler abstract base class for images
- *
- * @ingroup Media
- */
-abstract class ImageHandler extends MediaHandler {
-
- /**
- * @param $file File
- * @return bool
- */
- function canRender( $file ) {
- return ( $file->getWidth() && $file->getHeight() );
- }
-
- function getParamMap() {
- return array( 'img_width' => 'width' );
- }
-
- function validateParam( $name, $value ) {
- if ( in_array( $name, array( 'width', 'height' ) ) ) {
- if ( $value <= 0 ) {
- return false;
- } else {
- return true;
- }
- } else {
- return false;
- }
- }
-
- function makeParamString( $params ) {
- if ( isset( $params['physicalWidth'] ) ) {
- $width = $params['physicalWidth'];
- } elseif ( isset( $params['width'] ) ) {
- $width = $params['width'];
- } else {
- throw new MWException( 'No width specified to '.__METHOD__ );
- }
- # Removed for ProofreadPage
- #$width = intval( $width );
- return "{$width}px";
- }
-
- function parseParamString( $str ) {
- $m = false;
- if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
- return array( 'width' => $m[1] );
- } else {
- return false;
- }
- }
-
- function getScriptParams( $params ) {
- return array( 'width' => $params['width'] );
- }
-
- /**
- * @param $image File
- * @param $params
- * @return bool
- */
- function normaliseParams( $image, &$params ) {
- $mimeType = $image->getMimeType();
-
- if ( !isset( $params['width'] ) ) {
- return false;
- }
-
- if ( !isset( $params['page'] ) ) {
- $params['page'] = 1;
- } else {
- if ( $params['page'] > $image->pageCount() ) {
- $params['page'] = $image->pageCount();
- }
-
- if ( $params['page'] < 1 ) {
- $params['page'] = 1;
- }
- }
-
- $srcWidth = $image->getWidth( $params['page'] );
- $srcHeight = $image->getHeight( $params['page'] );
-
- if ( isset( $params['height'] ) && $params['height'] != -1 ) {
- # Height & width were both set
- if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
- # Height is the relative smaller dimension, so scale width accordingly
- $params['width'] = self::fitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
-
- if ( $params['width'] == 0 ) {
- # Very small image, so we need to rely on client side scaling :(
- $params['width'] = 1;
- }
-
- $params['physicalWidth'] = $params['width'];
- } else {
- # Height was crap, unset it so that it will be calculated later
- unset( $params['height'] );
- }
- }
-
- if ( !isset( $params['physicalWidth'] ) ) {
- # Passed all validations, so set the physicalWidth
- $params['physicalWidth'] = $params['width'];
- }
-
- # Because thumbs are only referred to by width, the height always needs
- # to be scaled by the width to keep the thumbnail sizes consistent,
- # even if it was set inside the if block above
- $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight,
- $params['physicalWidth'] );
-
- # Set the height if it was not validated in the if block higher up
- if ( !isset( $params['height'] ) || $params['height'] == -1 ) {
- $params['height'] = $params['physicalHeight'];
- }
-
-
- if ( !$this->validateThumbParams( $params['physicalWidth'],
- $params['physicalHeight'], $srcWidth, $srcHeight, $mimeType ) ) {
- return false;
- }
- return true;
- }
-
- /**
- * Validate thumbnail parameters and fill in the correct height
- *
- * @param $width Integer: specified width (input/output)
- * @param $height Integer: height (output only)
- * @param $srcWidth Integer: width of the source image
- * @param $srcHeight Integer: height of the source image
- * @param $mimeType
- * @return bool False to indicate that an error should be returned to the user.
- */
- function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
- $width = intval( $width );
-
- # Sanity check $width
- if( $width <= 0) {
- wfDebug( __METHOD__.": Invalid destination width: $width\n" );
- return false;
- }
- if ( $srcWidth <= 0 ) {
- wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
- return false;
- }
-
- $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
- if ( $height == 0 ) {
- # Force height to be at least 1 pixel
- $height = 1;
- }
- return true;
- }
-
- /**
- * @param $image File
- * @param $script
- * @param $params
- * @return bool|ThumbnailImage
- */
- function getScriptedTransform( $image, $script, $params ) {
- if ( !$this->normaliseParams( $image, $params ) ) {
- return false;
- }
- $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
- $page = isset( $params['page'] ) ? $params['page'] : false;
-
- if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
- return new ThumbnailImage( $image,
- $url, $params['width'], $params['height'], false, $page );
- }
- }
-
- function getImageSize( $image, $path ) {
- wfSuppressWarnings();
- $gis = getimagesize( $path );
- wfRestoreWarnings();
- return $gis;
- }
-
- function isAnimatedImage( $image ) {
- return false;
- }
-
- /**
- * @param $file File
- * @return string
- */
- function getShortDesc( $file ) {
- global $wgLang;
- $nbytes = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
- $widthheight = wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->escaped();
-
- return "$widthheight ($nbytes)";
- }
-
- /**
- * @param $file File
- * @return string
- */
- function getLongDesc( $file ) {
- global $wgLang;
- $pages = $file->pageCount();
- $size = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
- if ( $pages === false || $pages <= 1 ) {
- $msg = wfMessage( 'file-info-size' )->numParams( $file->getWidth(),
- $file->getHeight() )->params( $size,
- $file->getMimeType() )->parse();
- } else {
- $msg = wfMessage( 'file-info-size-pages' )->numParams( $file->getWidth(),
- $file->getHeight() )->params( $size,
- $file->getMimeType() )->numParams( $pages )->parse();
- }
- return $msg;
- }
-
- /**
- * @param $file File
- * @return string
- */
- function getDimensionsString( $file ) {
- $pages = $file->pageCount();
- if ( $pages > 1 ) {
- return wfMessage( 'widthheightpage' )->numParams( $file->getWidth(), $file->getHeight(), $pages )->text();
- } else {
- return wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->text();
- }
- }
-}
--- /dev/null
+<?php
+/**
+ * Media-handling base classes and generic functionality.
+ *
+ * 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
+ * @ingroup Media
+ */
+
+/**
+ * Media handler abstract base class for images
+ *
+ * @ingroup Media
+ */
+abstract class ImageHandler extends MediaHandler {
+
+ /**
+ * @param $file File
+ * @return bool
+ */
+ function canRender( $file ) {
+ return ( $file->getWidth() && $file->getHeight() );
+ }
+
+ function getParamMap() {
+ return array( 'img_width' => 'width' );
+ }
+
+ function validateParam( $name, $value ) {
+ if ( in_array( $name, array( 'width', 'height' ) ) ) {
+ if ( $value <= 0 ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ function makeParamString( $params ) {
+ if ( isset( $params['physicalWidth'] ) ) {
+ $width = $params['physicalWidth'];
+ } elseif ( isset( $params['width'] ) ) {
+ $width = $params['width'];
+ } else {
+ throw new MWException( 'No width specified to '.__METHOD__ );
+ }
+ # Removed for ProofreadPage
+ #$width = intval( $width );
+ return "{$width}px";
+ }
+
+ function parseParamString( $str ) {
+ $m = false;
+ if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
+ return array( 'width' => $m[1] );
+ } else {
+ return false;
+ }
+ }
+
+ function getScriptParams( $params ) {
+ return array( 'width' => $params['width'] );
+ }
+
+ /**
+ * @param $image File
+ * @param $params
+ * @return bool
+ */
+ function normaliseParams( $image, &$params ) {
+ $mimeType = $image->getMimeType();
+
+ if ( !isset( $params['width'] ) ) {
+ return false;
+ }
+
+ if ( !isset( $params['page'] ) ) {
+ $params['page'] = 1;
+ } else {
+ if ( $params['page'] > $image->pageCount() ) {
+ $params['page'] = $image->pageCount();
+ }
+
+ if ( $params['page'] < 1 ) {
+ $params['page'] = 1;
+ }
+ }
+
+ $srcWidth = $image->getWidth( $params['page'] );
+ $srcHeight = $image->getHeight( $params['page'] );
+
+ if ( isset( $params['height'] ) && $params['height'] != -1 ) {
+ # Height & width were both set
+ if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
+ # Height is the relative smaller dimension, so scale width accordingly
+ $params['width'] = self::fitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
+
+ if ( $params['width'] == 0 ) {
+ # Very small image, so we need to rely on client side scaling :(
+ $params['width'] = 1;
+ }
+
+ $params['physicalWidth'] = $params['width'];
+ } else {
+ # Height was crap, unset it so that it will be calculated later
+ unset( $params['height'] );
+ }
+ }
+
+ if ( !isset( $params['physicalWidth'] ) ) {
+ # Passed all validations, so set the physicalWidth
+ $params['physicalWidth'] = $params['width'];
+ }
+
+ # Because thumbs are only referred to by width, the height always needs
+ # to be scaled by the width to keep the thumbnail sizes consistent,
+ # even if it was set inside the if block above
+ $params['physicalHeight'] = File::scaleHeight( $srcWidth, $srcHeight,
+ $params['physicalWidth'] );
+
+ # Set the height if it was not validated in the if block higher up
+ if ( !isset( $params['height'] ) || $params['height'] == -1 ) {
+ $params['height'] = $params['physicalHeight'];
+ }
+
+
+ if ( !$this->validateThumbParams( $params['physicalWidth'],
+ $params['physicalHeight'], $srcWidth, $srcHeight, $mimeType ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Validate thumbnail parameters and fill in the correct height
+ *
+ * @param $width Integer: specified width (input/output)
+ * @param $height Integer: height (output only)
+ * @param $srcWidth Integer: width of the source image
+ * @param $srcHeight Integer: height of the source image
+ * @param $mimeType
+ * @return bool False to indicate that an error should be returned to the user.
+ */
+ function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
+ $width = intval( $width );
+
+ # Sanity check $width
+ if( $width <= 0) {
+ wfDebug( __METHOD__.": Invalid destination width: $width\n" );
+ return false;
+ }
+ if ( $srcWidth <= 0 ) {
+ wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
+ return false;
+ }
+
+ $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
+ if ( $height == 0 ) {
+ # Force height to be at least 1 pixel
+ $height = 1;
+ }
+ return true;
+ }
+
+ /**
+ * @param $image File
+ * @param $script
+ * @param $params
+ * @return bool|ThumbnailImage
+ */
+ function getScriptedTransform( $image, $script, $params ) {
+ if ( !$this->normaliseParams( $image, $params ) ) {
+ return false;
+ }
+ $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
+ $page = isset( $params['page'] ) ? $params['page'] : false;
+
+ if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
+ return new ThumbnailImage( $image,
+ $url, $params['width'], $params['height'], false, $page );
+ }
+ }
+
+ function getImageSize( $image, $path ) {
+ wfSuppressWarnings();
+ $gis = getimagesize( $path );
+ wfRestoreWarnings();
+ return $gis;
+ }
+
+ function isAnimatedImage( $image ) {
+ return false;
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ function getShortDesc( $file ) {
+ global $wgLang;
+ $nbytes = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
+ $widthheight = wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->escaped();
+
+ return "$widthheight ($nbytes)";
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ function getLongDesc( $file ) {
+ global $wgLang;
+ $pages = $file->pageCount();
+ $size = htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
+ if ( $pages === false || $pages <= 1 ) {
+ $msg = wfMessage( 'file-info-size' )->numParams( $file->getWidth(),
+ $file->getHeight() )->params( $size,
+ $file->getMimeType() )->parse();
+ } else {
+ $msg = wfMessage( 'file-info-size-pages' )->numParams( $file->getWidth(),
+ $file->getHeight() )->params( $size,
+ $file->getMimeType() )->numParams( $pages )->parse();
+ }
+ return $msg;
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ function getDimensionsString( $file ) {
+ $pages = $file->pageCount();
+ if ( $pages > 1 ) {
+ return wfMessage( 'widthheightpage' )->numParams( $file->getWidth(), $file->getHeight(), $pages )->text();
+ } else {
+ return wfMessage( 'widthheight' )->numParams( $file->getWidth(), $file->getHeight() )->text();
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Media-handling base classes and generic functionality.
+ *
+ * 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
+ * @ingroup Media
+ */
+
+/**
+ * Base media handler class
+ *
+ * @ingroup Media
+ */
+abstract class MediaHandler {
+ const TRANSFORM_LATER = 1;
+ const METADATA_GOOD = true;
+ const METADATA_BAD = false;
+ const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
+ /**
+ * Instance cache
+ */
+ static $handlers = array();
+
+ /**
+ * Get a MediaHandler for a given MIME type from the instance cache
+ *
+ * @param $type string
+ *
+ * @return MediaHandler
+ */
+ static function getHandler( $type ) {
+ global $wgMediaHandlers;
+ if ( !isset( $wgMediaHandlers[$type] ) ) {
+ wfDebug( __METHOD__ . ": no handler found for $type.\n");
+ return false;
+ }
+ $class = $wgMediaHandlers[$type];
+ if ( !isset( self::$handlers[$class] ) ) {
+ self::$handlers[$class] = new $class;
+ if ( !self::$handlers[$class]->isEnabled() ) {
+ self::$handlers[$class] = false;
+ }
+ }
+ return self::$handlers[$class];
+ }
+
+ /**
+ * Get an associative array mapping magic word IDs to parameter names.
+ * Will be used by the parser to identify parameters.
+ */
+ abstract function getParamMap();
+
+ /**
+ * Validate a thumbnail parameter at parse time.
+ * Return true to accept the parameter, and false to reject it.
+ * If you return false, the parser will do something quiet and forgiving.
+ *
+ * @param $name
+ * @param $value
+ */
+ abstract function validateParam( $name, $value );
+
+ /**
+ * Merge a parameter array into a string appropriate for inclusion in filenames
+ *
+ * @param $params array
+ */
+ abstract function makeParamString( $params );
+
+ /**
+ * Parse a param string made with makeParamString back into an array
+ *
+ * @param $str string
+ */
+ abstract function parseParamString( $str );
+
+ /**
+ * Changes the parameter array as necessary, ready for transformation.
+ * Should be idempotent.
+ * Returns false if the parameters are unacceptable and the transform should fail
+ * @param $image
+ * @param $params
+ */
+ abstract function normaliseParams( $image, &$params );
+
+ /**
+ * Get an image size array like that returned by getimagesize(), or false if it
+ * can't be determined.
+ *
+ * @param $image File: the image object, or false if there isn't one
+ * @param $path String: the filename
+ * @return Array Follow the format of PHP getimagesize() internal function. See http://www.php.net/getimagesize
+ */
+ abstract function getImageSize( $image, $path );
+
+ /**
+ * Get handler-specific metadata which will be saved in the img_metadata field.
+ *
+ * @param $image File: the image object, or false if there isn't one.
+ * Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!)
+ * @param $path String: the filename
+ * @return String
+ */
+ function getMetadata( $image, $path ) { return ''; }
+
+ /**
+ * Get metadata version.
+ *
+ * This is not used for validating metadata, this is used for the api when returning
+ * metadata, since api content formats should stay the same over time, and so things
+ * using ForiegnApiRepo can keep backwards compatibility
+ *
+ * All core media handlers share a common version number, and extensions can
+ * use the GetMetadataVersion hook to append to the array (they should append a unique
+ * string so not to get confusing). If there was a media handler named 'foo' with metadata
+ * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata
+ * version is 2, the end version string would look like '2;foo=3'.
+ *
+ * @return string version string
+ */
+ static function getMetadataVersion () {
+ $version = Array( '2' ); // core metadata version
+ wfRunHooks('GetMetadataVersion', Array(&$version));
+ return implode( ';', $version);
+ }
+
+ /**
+ * Convert metadata version.
+ *
+ * By default just returns $metadata, but can be used to allow
+ * media handlers to convert between metadata versions.
+ *
+ * @param $metadata Mixed String or Array metadata array (serialized if string)
+ * @param $version Integer target version
+ * @return Array serialized metadata in specified version, or $metadata on fail.
+ */
+ function convertMetadataVersion( $metadata, $version = 1 ) {
+ if ( !is_array( $metadata ) ) {
+
+ //unserialize to keep return parameter consistent.
+ wfSuppressWarnings();
+ $ret = unserialize( $metadata );
+ wfRestoreWarnings();
+ return $ret;
+ }
+ return $metadata;
+ }
+
+ /**
+ * Get a string describing the type of metadata, for display purposes.
+ *
+ * @return string
+ */
+ function getMetadataType( $image ) { return false; }
+
+ /**
+ * Check if the metadata string is valid for this handler.
+ * If it returns MediaHandler::METADATA_BAD (or false), Image
+ * will reload the metadata from the file and update the database.
+ * MediaHandler::METADATA_GOOD for if the metadata is a-ok,
+ * MediaHanlder::METADATA_COMPATIBLE if metadata is old but backwards
+ * compatible (which may or may not trigger a metadata reload).
+ * @return bool
+ */
+ function isMetadataValid( $image, $metadata ) {
+ return self::METADATA_GOOD;
+ }
+
+
+ /**
+ * Get a MediaTransformOutput object representing an alternate of the transformed
+ * output which will call an intermediary thumbnail assist script.
+ *
+ * Used when the repository has a thumbnailScriptUrl option configured.
+ *
+ * Return false to fall back to the regular getTransform().
+ * @return bool
+ */
+ function getScriptedTransform( $image, $script, $params ) {
+ return false;
+ }
+
+ /**
+ * Get a MediaTransformOutput object representing the transformed output. Does not
+ * actually do the transform.
+ *
+ * @param $image File: the image object
+ * @param $dstPath String: filesystem destination path
+ * @param $dstUrl String: Destination URL to use in output HTML
+ * @param $params Array: Arbitrary set of parameters validated by $this->validateParam()
+ * @return MediaTransformOutput
+ */
+ final function getTransform( $image, $dstPath, $dstUrl, $params ) {
+ return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
+ }
+
+ /**
+ * Get a MediaTransformOutput object representing the transformed output. Does the
+ * transform unless $flags contains self::TRANSFORM_LATER.
+ *
+ * @param $image File: the image object
+ * @param $dstPath String: filesystem destination path
+ * @param $dstUrl String: destination URL to use in output HTML
+ * @param $params Array: arbitrary set of parameters validated by $this->validateParam()
+ * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER
+ *
+ * @return MediaTransformOutput
+ */
+ abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
+
+ /**
+ * Get the thumbnail extension and MIME type for a given source MIME type
+ * @return array thumbnail extension and MIME type
+ */
+ function getThumbType( $ext, $mime, $params = null ) {
+ $magic = MimeMagic::singleton();
+ if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
+ // The extension is not valid for this mime type and we do
+ // recognize the mime type
+ $extensions = $magic->getExtensionsForType( $mime );
+ if ( $extensions ) {
+ return array( strtok( $extensions, ' ' ), $mime );
+ }
+ }
+
+ // The extension is correct (true) or the mime type is unknown to
+ // MediaWiki (null)
+ return array( $ext, $mime );
+ }
+
+ /**
+ * True if the handled types can be transformed
+ * @return bool
+ */
+ function canRender( $file ) { return true; }
+ /**
+ * True if handled types cannot be displayed directly in a browser
+ * but can be rendered
+ * @return bool
+ */
+ function mustRender( $file ) { return false; }
+ /**
+ * True if the type has multi-page capabilities
+ * @return bool
+ */
+ function isMultiPage( $file ) { return false; }
+ /**
+ * Page count for a multi-page document, false if unsupported or unknown
+ * @return bool
+ */
+ function pageCount( $file ) { return false; }
+ /**
+ * The material is vectorized and thus scaling is lossless
+ * @return bool
+ */
+ function isVectorized( $file ) { return false; }
+ /**
+ * False if the handler is disabled for all files
+ * @return bool
+ */
+ function isEnabled() { return true; }
+
+ /**
+ * Get an associative array of page dimensions
+ * Currently "width" and "height" are understood, but this might be
+ * expanded in the future.
+ * Returns false if unknown or if the document is not multi-page.
+ *
+ * @param $image File
+ * @param $page Unused, left for backcompatibility?
+ * @return array
+ */
+ function getPageDimensions( $image, $page ) {
+ $gis = $this->getImageSize( $image, $image->getLocalRefPath() );
+ return array(
+ 'width' => $gis[0],
+ 'height' => $gis[1]
+ );
+ }
+
+ /**
+ * Generic getter for text layer.
+ * Currently overloaded by PDF and DjVu handlers
+ * @return bool
+ */
+ function getPageText( $image, $page ) {
+ return false;
+ }
+
+ /**
+ * Get an array structure that looks like this:
+ *
+ * array(
+ * 'visible' => array(
+ * 'Human-readable name' => 'Human readable value',
+ * ...
+ * ),
+ * 'collapsed' => array(
+ * 'Human-readable name' => 'Human readable value',
+ * ...
+ * )
+ * )
+ * The UI will format this into a table where the visible fields are always
+ * visible, and the collapsed fields are optionally visible.
+ *
+ * The function should return false if there is no metadata to display.
+ */
+
+ /**
+ * @todo FIXME: I don't really like this interface, it's not very flexible
+ * I think the media handler should generate HTML instead. It can do
+ * all the formatting according to some standard. That makes it possible
+ * to do things like visual indication of grouped and chained streams
+ * in ogg container files.
+ * @return bool
+ */
+ function formatMetadata( $image ) {
+ return false;
+ }
+
+ /** sorts the visible/invisible field.
+ * Split off from ImageHandler::formatMetadata, as used by more than
+ * one type of handler.
+ *
+ * This is used by the media handlers that use the FormatMetadata class
+ *
+ * @param $metadataArray Array metadata array
+ * @return array for use displaying metadata.
+ */
+ function formatMetadataHelper( $metadataArray ) {
+ $result = array(
+ 'visible' => array(),
+ 'collapsed' => array()
+ );
+
+ $formatted = FormatMetadata::getFormattedData( $metadataArray );
+ // Sort fields into visible and collapsed
+ $visibleFields = $this->visibleMetadataFields();
+ foreach ( $formatted as $name => $value ) {
+ $tag = strtolower( $name );
+ self::addMeta( $result,
+ in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
+ 'exif',
+ $tag,
+ $value
+ );
+ }
+ return $result;
+ }
+
+ /**
+ * Get a list of metadata items which should be displayed when
+ * the metadata table is collapsed.
+ *
+ * @return array of strings
+ * @access protected
+ */
+ function visibleMetadataFields() {
+ $fields = array();
+ $lines = explode( "\n", wfMsgForContent( 'metadata-fields' ) );
+ foreach( $lines as $line ) {
+ $matches = array();
+ if( preg_match( '/^\\*\s*(.*?)\s*$/', $line, $matches ) ) {
+ $fields[] = $matches[1];
+ }
+ }
+ $fields = array_map( 'strtolower', $fields );
+ return $fields;
+ }
+
+
+ /**
+ * This is used to generate an array element for each metadata value
+ * That array is then used to generate the table of metadata values
+ * on the image page
+ *
+ * @param &$array Array An array containing elements for each type of visibility
+ * and each of those elements being an array of metadata items. This function adds
+ * a value to that array.
+ * @param $visibility string ('visible' or 'collapsed') if this value is hidden
+ * by default.
+ * @param $type String type of metadata tag (currently always 'exif')
+ * @param $id String the name of the metadata tag (like 'artist' for example).
+ * its name in the table displayed is the message "$type-$id" (Ex exif-artist ).
+ * @param $value String thingy goes into a wikitext table; it used to be escaped but
+ * that was incompatible with previous practise of customized display
+ * with wikitext formatting via messages such as 'exif-model-value'.
+ * So the escaping is taken back out, but generally this seems a confusing
+ * interface.
+ * @param $param String value to pass to the message for the name of the field
+ * as $1. Currently this parameter doesn't seem to ever be used.
+ *
+ * Note, everything here is passed through the parser later on (!)
+ */
+ protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
+ $msg = wfMessage( "$type-$id", $param );
+ if ( $msg->exists() ) {
+ $name = $msg->text();
+ } else {
+ // This is for future compatibility when using instant commons.
+ // So as to not display as ugly a name if a new metadata
+ // property is defined that we don't know about
+ // (not a major issue since such a property would be collapsed
+ // by default).
+ wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id . "\n" );
+ $name = wfEscapeWikiText( $id );
+ }
+ $array[$visibility][] = array(
+ 'id' => "$type-$id",
+ 'name' => $name,
+ 'value' => $value
+ );
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ function getShortDesc( $file ) {
+ global $wgLang;
+ return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ function getLongDesc( $file ) {
+ global $wgLang;
+ return wfMessage( 'file-info', htmlspecialchars( $wgLang->formatSize( $file->getSize() ) ),
+ $file->getMimeType() )->parse();
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ static function getGeneralShortDesc( $file ) {
+ global $wgLang;
+ return $wgLang->formatSize( $file->getSize() );
+ }
+
+ /**
+ * @param $file File
+ * @return string
+ */
+ static function getGeneralLongDesc( $file ) {
+ global $wgLang;
+ return wfMessage( 'file-info', $wgLang->formatSize( $file->getSize() ),
+ $file->getMimeType() )->parse();
+ }
+
+ /**
+ * Calculate the largest thumbnail width for a given original file size
+ * such that the thumbnail's height is at most $maxHeight.
+ * @param $boxWidth Integer Width of the thumbnail box.
+ * @param $boxHeight Integer Height of the thumbnail box.
+ * @param $maxHeight Integer Maximum height expected for the thumbnail.
+ * @return Integer.
+ */
+ public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
+ $idealWidth = $boxWidth * $maxHeight / $boxHeight;
+ $roundedUp = ceil( $idealWidth );
+ if( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
+ return floor( $idealWidth );
+ } else {
+ return $roundedUp;
+ }
+ }
+
+ function getDimensionsString( $file ) {
+ return '';
+ }
+
+ /**
+ * Modify the parser object post-transform
+ */
+ function parserTransformHook( $parser, $file ) {}
+
+ /**
+ * File validation hook called on upload.
+ *
+ * If the file at the given local path is not valid, or its MIME type does not
+ * match the handler class, a Status object should be returned containing
+ * relevant errors.
+ *
+ * @param $fileName string The local path to the file.
+ * @return Status object
+ */
+ function verifyUpload( $fileName ) {
+ return Status::newGood();
+ }
+
+ /**
+ * Check for zero-sized thumbnails. These can be generated when
+ * no disk space is available or some other error occurs
+ *
+ * @param $dstPath string The location of the suspect file
+ * @param $retval int Return value of some shell process, file will be deleted if this is non-zero
+ * @return bool True if removed, false otherwise
+ */
+ function removeBadFile( $dstPath, $retval = 0 ) {
+ if( file_exists( $dstPath ) ) {
+ $thumbstat = stat( $dstPath );
+ if( $thumbstat['size'] == 0 || $retval != 0 ) {
+ $result = unlink( $dstPath );
+
+ if ( $result ) {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
+ $thumbstat['size'], $dstPath ) );
+ } else {
+ wfDebugLog( 'thumbnail',
+ sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
+ $thumbstat['size'], $dstPath ) );
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove files from the purge list
+ *
+ * @param array $files
+ * @param array $options
+ */
+ public function filterThumbnailPurgeList( &$files, $options ) {
+ // Do nothing
+ }
+}
function _load_items( $sock, &$ret ) {
while ( 1 ) {
$decl = fgets( $sock );
- if( $decl === false ) {
- $this->_debugprint( "Error reading socket for a memcached response\n" );
- return 0;
- } elseif ( $decl == "END\r\n" ) {
+ if ( $decl == "END\r\n" ) {
return true;
} elseif ( preg_match( '/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match ) ) {
list( $rkey, $flags, $len ) = array( $match[1], $match[2], $match[3] );
}
} else {
- $peer = $peerAddress = $peerPort = '';
- $gotPeer = socket_getpeername( $sock, $peerAddress, $peerPort );
- if( $gotPeer ) {
- $peer = " from [$peerAddress:$peerPort";
- }
- $this->_debugprint( "Error parsing memcached response{$peer}\n" );
+ $this->_debugprint( "Error parsing memcached response\n" );
return 0;
}
}
}
/**
- * Return the number of pages in the given category, or 0 if it's nonexis-
- * tent. This is an expensive parser function and can't be called too many
- * times per page.
+ * Return the number of pages, files or subcats in the given category,
+ * or 0 if it's nonexistent. This is an expensive parser function and
+ * can't be called too many times per page.
* @return string
*/
- static function pagesincategory( $parser, $name = '', $raw = null ) {
+ static function pagesincategory( $parser, $name = '', $arg1 = null, $arg2 = null ) {
+ static $magicWords = null;
+ if ( is_null( $magicWords ) ) {
+ $magicWords = new MagicWordArray( array(
+ 'pagesincategory_all',
+ 'pagesincategory_pages',
+ 'pagesincategory_subcats',
+ 'pagesincategory_files'
+ ) );
+ }
static $cache = array();
- $category = Category::newFromName( $name );
- if( !is_object( $category ) ) {
- $cache[$name] = 0;
+ // split the given option to its variable
+ if( self::isRaw( $arg1 ) ) {
+ //{{pagesincategory:|raw[|type]}}
+ $raw = $arg1;
+ $type = $magicWords->matchStartToEnd( $arg2 );
+ } else {
+ //{{pagesincategory:[|type[|raw]]}}
+ $type = $magicWords->matchStartToEnd( $arg1 );
+ $raw = $arg2;
+ }
+ if( !$type ) { //backward compatibility
+ $type = 'pagesincategory_all';
+ }
+
+ $title = Title::makeTitleSafe( NS_CATEGORY, $name );
+ if( !$title ) { # invalid title
return self::formatRaw( 0, $raw );
}
- # Normalize name for cache
- $name = $category->getName();
+ // Normalize name for cache
+ $name = $title->getDBkey();
- $count = 0;
- if( isset( $cache[$name] ) ) {
- $count = $cache[$name];
- } elseif( $parser->incrementExpensiveFunctionCount() ) {
- $count = $cache[$name] = (int)$category->getPageCount();
+ if( !isset( $cache[$name] ) ) {
+ $category = Category::newFromTitle( $title );
+
+ $allCount = $subcatCount = $fileCount = $pagesCount = 0;
+ if( $parser->incrementExpensiveFunctionCount() ) {
+ // $allCount is the total number of cat members,
+ // not the count of how many members are normal pages.
+ $allCount = (int)$category->getPageCount();
+ $subcatCount = (int)$category->getSubcatCount();
+ $fileCount = (int)$category->getFileCount();
+ $pagesCount = $allCount - $subcatCount - $fileCount;
+ }
+ $cache[$name]['pagesincategory_all'] = $allCount;
+ $cache[$name]['pagesincategory_pages'] = $pagesCount;
+ $cache[$name]['pagesincategory_subcats'] = $subcatCount;
+ $cache[$name]['pagesincategory_files'] = $fileCount;
}
+
+ $count = $cache[$name][$type];
return self::formatRaw( $count, $raw );
}
}
// Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}}
+ // or {{filepath|300px}}, {{filepath|200x300px}}, {{filepath|nowiki|200x300px}}, {{filepath|200x300px|nowiki}}
public static function filepath( $parser, $name='', $argA='', $argB='' ) {
$file = wfFindFile( $name );
- $size = '';
- $argA_int = intval( $argA );
- $argB_int = intval( $argB );
-
- if ( $argB_int > 0 ) {
- // {{filepath: | option | size }}
- $size = $argB_int;
- $option = $argA;
-
- } elseif ( $argA_int > 0 ) {
- // {{filepath: | size [|option] }}
- $size = $argA_int;
- $option = $argB;
+ $isNowiki = false;
+ if( $argA == 'nowiki' ) {
+ // {{filepath: | option [| size] }}
+ $isNowiki = true;
+ $parsedWidthParam = $parser->parseWidthParam( $argB );
} else {
- // {{filepath: [|option] }}
- $option = $argA;
+ // {{filepath: [| size [|option]] }}
+ $parsedWidthParam = $parser->parseWidthParam( $argA );
+ $isNowiki = ($argB == 'nowiki');
}
if ( $file ) {
$url = $file->getFullUrl();
// If a size is requested...
- if ( is_integer( $size ) ) {
- $mto = $file->transform( array( 'width' => $size ) );
+ if ( count( $parsedWidthParam ) ) {
+ $mto = $file->transform( $parsedWidthParam );
// ... and we can
if ( $mto && !$mto->isError() ) {
// ... change the URL to point to a thumbnail.
$url = wfExpandUrl( $mto->getUrl(), PROTO_RELATIVE );
}
}
- if ( $option == 'nowiki' ) {
+ if ( $isNowiki ) {
return array( $url, 'nowiki' => true );
}
return $url;
# Special case; width and height come in one variable together
if ( $type === 'handler' && $paramName === 'width' ) {
- $m = array();
- # (bug 13500) In both cases (width/height and width only),
- # permit trailing "px" for backward compatibility.
- if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
- $width = intval( $m[1] );
- $height = intval( $m[2] );
+ $parsedWidthParam = $this->parseWidthParam( $value );
+ if( isset( $parsedWidthParam['width'] ) ) {
+ $width = $parsedWidthParam['width'];
if ( $handler->validateParam( 'width', $width ) ) {
$params[$type]['width'] = $width;
$validated = true;
}
+ }
+ if( isset( $parsedWidthParam['height'] ) ) {
+ $height = $parsedWidthParam['height'];
if ( $handler->validateParam( 'height', $height ) ) {
$params[$type]['height'] = $height;
$validated = true;
}
- } elseif ( preg_match( '/^[0-9]*\s*(?:px)?\s*$/', $value ) ) {
- $width = intval( $value );
- if ( $handler->validateParam( 'width', $width ) ) {
- $params[$type]['width'] = $width;
- $validated = true;
- }
- } # else no validation -- bug 13436
+ }
+ # else no validation -- bug 13436
} else {
if ( $type === 'handler' ) {
# Validate handler parameter
function isValidHalfParsedText( $data ) {
return isset( $data['version'] ) && $data['version'] == self::HALF_PARSED_VERSION;
}
+
+ /**
+ * Parsed a width param of imagelink like 300px or 200x300px
+ *
+ * @param $value String
+ *
+ * @return array
+ * @since 1.20
+ */
+ public function parseWidthParam( $value ) {
+ $parsedWidthParam = array();
+ if( $value === '' ) {
+ return $parsedWidthParam;
+ }
+ $m = array();
+ # (bug 13500) In both cases (width/height and width only),
+ # permit trailing "px" for backward compatibility.
+ if ( preg_match( '/^([0-9]*)x([0-9]*)\s*(?:px)?\s*$/', $value, $m ) ) {
+ $width = intval( $m[1] );
+ $height = intval( $m[2] );
+ $parsedWidthParam['width'] = $width;
+ $parsedWidthParam['height'] = $height;
+ } elseif ( preg_match( '/^[0-9]*\s*(?:px)?\s*$/', $value ) ) {
+ $width = intval( $value );
+ $parsedWidthParam['width'] = $width;
+ }
+ return $parsedWidthParam;
+ }
}
* @return String
*/
protected function preText(){
+ $this->getOutput()->addModules( 'mediawiki.special.block' );
+
$text = $this->msg( 'blockiptext' )->parse();
$otherBlockMessages = array();
$svnInfo = self::getSvnInfo( $IP );
if ( !$svnInfo && !$gitInfo ) {
$version = $wgVersion;
+ } elseif ( $gitInfo && $flags === 'nodb' ) {
+ $shortSha1 = substr( $gitInfo, 0, 7 );
+ $version = "$wgVersion ($shortSha1)";
} elseif ( $gitInfo ) {
$shortSha1 = substr( $gitInfo, 0, 7 );
$shortSha1 = wfMessage( 'parentheses' )->params( $shortSha1 )->escaped();
乙太網 以太网
點陣圖 位图
常式 例程
-游標 光标
光碟 光盘
光碟機 光驱
全形 全角
細如髮
繫於一髮
膚髮
+皮膚
生華髮
蒼髮
被髮佯狂
棺材裡
注釋
月面
+路面
修杰楷
修杰麟
學裡
* @param $format Bool|Int true to process using language functions, or TS_ constant
* to return the expiry in a given timestamp
* @return String
+ * @since 1.18
*/
public function formatExpiry( $expiry, $format = true ) {
static $infinity, $infinityMsg;
* @maintainers fdcn <fdcn64@gmail.com>, shinjiman <shinjiman@gmail.com>, PhiLiP <philip.npc@gmail.com>
*/
class LanguageConverter {
+
+ /**
+ * languages supporting variants
+ * @since 1.20
+ * @var array
+ */
+ static public $languagesWithVariants = array(
+ 'gan',
+ 'iu',
+ 'kk',
+ 'ku',
+ 'shi',
+ 'sr',
+ 'tg',
+ 'zh',
+ );
+
var $mMainLanguageCode;
var $mVariants, $mVariantFallbacks, $mVariantNames;
var $mTablesLoaded = false;
'url_query' => array( 0, 'QUERY' ),
'defaultsort_noerror' => array( 0, 'noerror' ),
'defaultsort_noreplace' => array( 0, 'noreplace' ),
+ 'pagesincategory_all' => array( 0, 'all' ),
+ 'pagesincategory_pages' => array( 0, 'pages' ),
+ 'pagesincategory_subcats' => array( 0, 'subcats' ),
+ 'pagesincategory_files' => array( 0, 'files' ),
);
/**
*
*/
+$rtl = true;
+
# Inherit everything for now
$fallback = 'kk-arab, kk-cyrl';
* @author Marmzok
*/
+$rtl = true;
+
$fallback = 'ckb';
$digitTransformTable = array(
$input = fopen( $this->input, "rt" );
$result = $this->readDump( $input );
- if ( WikiError::isError( $result ) ) {
- throw new MWException( $result->getMessage() );
- }
-
if ( $this->spawnProc ) {
$this->closeSpawn();
}
}
}
+ /**
+ * @throws MWException Failure to parse XML input
+ * @return true
+ */
function readDump( $input ) {
$this->buffer = "";
$this->openElement = false;
$chunk = fread( $input, $bufferSize );
if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) {
wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" );
- return new WikiXmlError( $parser, 'XML import parse failure', $chunk, $offset );
+
+ $byte = xml_get_current_byte_index( $parser );
+ $msg = wfMsgHtml( 'xml-error-string',
+ 'XML import parse failure',
+ xml_get_current_line_number( $parser ),
+ xml_get_current_column_number( $parser ),
+ $byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte -$offset, 16 ) . '"' ) ),
+ xml_error_string( xml_get_error_code( $parser ) ) );
+
+ xml_parser_free( $parser );
+
+ throw new MWException( $msg );
}
$offset += strlen( $chunk );
} while ( $chunk !== false && !feof( $input ) );
<?php
/**
- * Deletes a batch of pages
+ * Deletes a batch of pages.
* Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
* where
* [listfile] is a file where each line contains the title of a page to be
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
<?php
/**
- * Creates a sitemap for the site
+ * Creates a sitemap for the site.
*
* Copyright © 2005, Ævar Arnfjörð Bjarmason, Jens Frank <jeluf@gmx.de> and
* Brion Vibber <brion@pobox.com>
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that generates a sitemap for the site.
+ *
+ * @ingroup Maintenance
+ */
class GenerateSitemap extends Maintenance {
const GS_MAIN = -2;
const GS_TALK = -1;
<?php
/**
+ * Display replication lag times.
+ *
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that displays replication lag times.
+ *
+ * @ingroup Maintenance
+ */
class GetLagTimes extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * This script reports the hostname of a slave server.
+ * Reports the hostname of a slave server.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that reports the hostname of a slave server.
+ *
+ * @ingroup Maintenance
+ */
class GetSlaveServer extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * Outputs page text to stdout, useful for command-line editing automation.
+ * Outputs page text to stdout.
+ * Useful for command-line editing automation.
* Example: php getText.php "page title" | sed -e '...' | php edit.php "page title"
*
* This program is free software; you can redistribute it and/or modify
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that outputs page text to stdout.
+ *
+ * @ingroup Maintenance
+ */
class GetTextMaint extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
+ * Import XML dump files into the current wiki.
+ *
+ * Copyright © 2005 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
/**
+ * Maintenance script that imports XML dump files into the current wiki.
+ *
* @ingroup Maintenance
*/
class BackupReader extends Maintenance {
<?php
/**
- * Support functions for the importImages script
+ * Support functions for the importImages.php script
*
* 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
<?php
-
/**
- * Maintenance script to import one or more images from the local file system into
- * the wiki without using the web-based interface.
+ * Import one or more images from the local file system into the wiki without
+ * using the web-based interface.
*
* "Smart import" additions:
* - aim: preserve the essential metadata (user, description) when importing medias from an existing wiki
<?php
/**
- * Maintenance script to import all scripts in the MediaWiki namespace from a
- * local site.
+ * Import all scripts in the MediaWiki namespace from a local site.
*
* 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
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to import all scripts in the MediaWiki namespace from a
+ * local site.
+ *
+ * @ingroup Maintenance
+ */
class ImportSiteScripts extends Maintenance {
public function __construct() {
parent::__construct();
$this->addArg( 'index', 'index.php base url' );
$this->addOption( 'username', 'User name of the script importer' );
}
-
+
public function execute() {
global $wgUser;
$user = User::newFromName( $this->getOption( 'username', 'ScriptImporter' ) );
$wgUser = $user;
-
+
$baseUrl = $this->getArg( 1 );
$pageList = $this->fetchScriptList();
$this->output( 'Importing ' . count( $pageList ) . " pages\n" );
-
+
foreach ( $pageList as $page ) {
$title = Title::makeTitleSafe( NS_MEDIAWIKI, $page );
if ( !$title ) {
}
$this->output( "Importing $page\n" );
- $url = wfAppendQuery( $baseUrl, array(
- 'action' => 'raw',
+ $url = wfAppendQuery( $baseUrl, array(
+ 'action' => 'raw',
'title' => "MediaWiki:{$page}" ) );
$text = Http::get( $url );
$wikiPage = WikiPage::factory( $title );
$wikiPage->doEdit( $text, "Importing from $url", 0, false, $user );
}
-
+
}
-
+
protected function fetchScriptList() {
- $data = array(
+ $data = array(
'action' => 'query',
'format' => 'php',//'json',
'list' => 'allpages',
'apnamespace' => '8',
- 'aplimit' => '500',
+ 'aplimit' => '500',
);
$baseUrl = $this->getArg( 0 );
$pages = array();
-
+
do {
$url = wfAppendQuery( $baseUrl, $data );
$strResult = Http::get( $url );
//$result = FormatJson::decode( $strResult ); // Still broken
$result = unserialize( $strResult );
-
+
if ( !empty( $result['query']['allpages'] ) ) {
foreach ( $result['query']['allpages'] as $page ) {
if ( substr( $page['title'], -3 ) === '.js' ) {
$this->output( "Fetching new batch from {$data['apfrom']}\n" );
}
} while ( isset( $result['query-continue'] ) );
-
+
return $pages;
-
+
}
}
<?php
-
/**
- * Maintenance script allows creating or editing pages using
- * the contents of a text file
+ * Create or edit pages using the contents of a text file.
*
* 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
<?php
/**
- * Maintenance script to re-initialise or update the site statistics table
+ * Re-initialise or update the site statistics table.
*
* 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
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to re-initialise or update the site statistics table
+ *
+ * @ingroup Maintenance
+ */
class InitStats extends Maintenance {
public function __construct() {
parent::__construct();
<?php
-
/**
+ * CLI-based MediaWiki installation and configuration.
+ *
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
- * @see wfWaitForSlaves()
*/
if ( !function_exists( 'version_compare' ) || ( version_compare( phpversion(), '5.3.2' ) < 0 ) ) {
require_once( dirname( dirname( __FILE__ ) )."/maintenance/Maintenance.php" );
+/**
+ * Maintenance script to install and configure MediaWiki
+ *
+ * @ingroup Maintenance
+ */
class CommandLineInstaller extends Maintenance {
function __construct() {
parent::__construct();
<?php
/**
- * Maintenance script to do test JavaScript validity parses using jsmin+'s parser
+ * Test JavaScript validity parses using jsmin+'s parser
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to do test JavaScript validity parses using jsmin+'s parser
+ *
+ * @ingroup Maintenance
+ */
class JSParseHelper extends Maintenance {
var $errs = 0;
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to show database lag.
+ *
+ * @ingroup Maintenance
+ */
class DatabaseLag extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * This script makes several 'set', 'incr' and 'get' requests on every
- * memcached server and shows a report.
+ * Makes several 'set', 'incr' and 'get' requests on every memcached
+ * server and shows a report.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that makes several 'set', 'incr' and 'get' requests
+ * on every memcached server and shows a report.
+ *
+ * @ingroup Maintenance
+ */
class mcTest extends Maintenance {
public function __construct() {
parent::__construct();
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
$maintClass = 'MergeMessageFileList';
$mmfl = false;
+
+/**
+ * Maintenance script that merges $wgExtensionMessagesFiles from various
+ * extensions to produce a single array containing all message files.
+ *
+ * @ingroup Maintenance
+ */
class MergeMessageFileList extends Maintenance {
function __construct() {
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that re-assigns users from an old group to a new one.
+ *
+ * @ingroup Maintenance
+ */
class MigrateUserGroup extends Maintenance {
public function __construct() {
parent::__construct();
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that minifies a file or set of files.
+ *
+ * @ingroup Maintenance
+ */
class MinifyScript extends Maintenance {
var $outDir;
<?php
/**
- * Maintenance script to move a batch of pages
+ * Move a batch of pages.
*
* 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
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @author Tim Starling
*
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script to move a batch of pages.
+ *
+ * @ingroup Maintenance
+ */
class MoveBatch extends Maintenance {
public function __construct() {
parent::__construct();
/**
* Check for articles to fix after adding/deleting namespaces
*
- * Copyright (C) 2005-2007 Brion Vibber <brion@pobox.com>
+ * Copyright © 2005-2007 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that checks for articles to fix after
+ * adding/deleting namespaces.
+ *
+ * @ingroup Maintenance
+ */
class NamespaceConflictChecker extends Maintenance {
/**
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @todo Make this work on PostgreSQL and maybe other database servers
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that picks a database that has pending jobs.
+ *
+ * @ingroup Maintenance
+ */
class nextJobDB extends Maintenance {
public function __construct() {
parent::__construct();
<?php
-
/**
* Remove pages with only 1 revision from the MediaWiki namespace, without
* flooding recent changes, delete logs, etc.
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @author Steve Sanbeg
* based on nukePage by Rob Church
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that removes pages with only one revision from the
+ * MediaWiki namespace.
+ *
+ * @ingroup Maintenance
+ */
class NukeNS extends Maintenance {
public function __construct() {
parent::__construct();
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that erases a page record from the database.
+ *
+ * @ingroup Maintenance
+ */
class NukePage extends Maintenance {
public function __construct() {
parent::__construct();
<?php
/**
- * Look for 'orphan' revisions hooked to pages which don't exist
- * And 'childless' pages with no revisions.
+ * Look for 'orphan' revisions hooked to pages which don't exist and
+ * 'childless' pages with no revisions.
* Then, kill the poor widows and orphans.
* Man this is depressing.
*
- * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
+ * Copyright © 2005 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @file
* @author <brion@pobox.com>
* @ingroup Maintenance
*/
require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+/**
+ * Maintenance script that looks for 'orphan' revisions hooked to pages which
+ * don't exist and 'childless' pages with no revisions.
+ *
+ * @ingroup Maintenance
+ */
class Orphans extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Look for 'orphan' revisions hooked to pages which don't exist\n" .
- "And 'childless' pages with no revisions\n" .
+ "and 'childless' pages with no revisions\n" .
"Then, kill the poor widows and orphans\n" .
"Man this is depressing";
$this->addOption( 'fix', 'Actually fix broken entries' );
if ( $status->isOK() ) {
$lastOKPos = max( $lastOKPos, $lastPosInBatch );
} else {
- $this->output( print_r( $status->getErrorsArray(), true ) );
+ $this->error( print_r( $status->getErrorsArray(), true ) );
break; // no gaps; everything up to $lastPos must be OK
}
'jquery.textSelection',
'jquery.byteLimit',
),
+ 'position' => 'top',
),
'mediawiki.action.history' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.history.js',
-( function ( $, mw ) {
- var isReady, toolbar, currentFocused;
+( function ( mw, $ ) {
+ var isReady, toolbar, currentFocused, queue, $toolbar, slice;
isReady = false;
+ queue = [];
+ $toolbar = false;
+ slice = Array.prototype.slice;
+
+ /**
+ * Internal helper that does the actual insertion
+ * of the button into the toolbar.
+ * See mw.toolbar.addButton for parameter documentation.
+ */
+ function insertButton( b /* imageFile */, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
+ // Backwards compatibility
+ if ( typeof b !== 'object' ) {
+ b = {
+ imageFile: b,
+ speedTip: speedTip,
+ tagOpen: tagOpen,
+ tagClose: tagClose,
+ sampleText: sampleText,
+ imageId: imageId,
+ selectText: selectText
+ };
+ }
+ var $image = $('<img>', {
+ width : 23,
+ height: 22,
+ src : b.imageFile,
+ alt : b.speedTip,
+ title : b.speedTip,
+ id : b.imageId || undefined,
+ 'class': 'mw-toolbar-editbutton'
+ } ).click( function () {
+ toolbar.insertTags( b.tagOpen, b.tagClose, b.sampleText, b.selectText );
+ return false;
+ } );
+
+ $toolbar.append( $image );
+ return true;
+ }
toolbar = {
- $toolbar: false,
- buttons: [],
/**
- * If you want to add buttons, use
- * mw.toolbar.addButton( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText );
+ * Add buttons to the toolbar.
+ * Takes care of race conditions and time-based dependencies
+ * by placing buttons in a queue if this method is called before
+ * the toolbar is created.
+ * @param {Object} button: Object with the following properties:
+ * - imageFile
+ * - speedTip
+ * - tagOpen
+ * - tagClose
+ * - sampleText
+ * - imageId
+ * - selectText
+ * For compatiblity, passing the above as separate arguments
+ * (in the listed order) is also supported.
*/
addButton: function () {
if ( isReady ) {
- toolbar.insertButton.apply( toolbar, arguments );
+ insertButton.apply( toolbar, arguments );
} else {
- toolbar.buttons.push( [].slice.call( arguments ) );
- }
- },
- insertButton: function ( imageFile, speedTip, tagOpen, tagClose, sampleText, imageId, selectText ) {
- var image = $('<img>', {
- width : 23,
- height: 22,
- src : imageFile,
- alt : speedTip,
- title : speedTip,
- id : imageId || '',
- 'class': 'mw-toolbar-editbutton'
- } ).click( function () {
- mw.toolbar.insertTags( tagOpen, tagClose, sampleText, selectText );
- return false;
- } );
-
- toolbar.$toolbar.append( image );
- return true;
+ // Convert arguments list to array
+ queue.push( slice.call( arguments ) );
+ }
},
/**
- * apply tagOpen/tagClose to selection in textarea,
+ * Apply tagOpen/tagClose to selection in textarea,
* use sampleText instead of selection if there is none.
*/
insertTags: function ( tagOpen, tagClose, sampleText, selectText ) {
}
},
- // For backwards compatibility
+ // For backwards compatibility,
+ // Called from EditPage.php, maybe in other places as well.
init: function () {}
};
window.addButton = toolbar.addButton;
window.insertTags = toolbar.insertTags;
- // Explose publicly
+ // Explose API publicly
mw.toolbar = toolbar;
$( document ).ready( function () {
- var buttons, i, c, iframe;
+ var buttons, i, b, iframe;
// currentFocus is used to determine where to insert tags
currentFocused = $( '#wpTextbox1' );
- // Populate the selector cache for $toolbar
- toolbar.$toolbar = $( '#toolbar' );
+ // Populate the selector cache for $toolbar
+ $toolbar = $( '#toolbar' );
// Legacy: Merge buttons from mwCustomEditButtons
- buttons = [].concat( toolbar.buttons, window.mwCustomEditButtons );
+ buttons = [].concat( queue, window.mwCustomEditButtons );
+ // Clear queue
+ queue.length = 0;
for ( i = 0; i < buttons.length; i++ ) {
- if ( $.isArray( buttons[i] ) ) {
- // Passes our button array as arguments
- toolbar.insertButton.apply( toolbar, buttons[i] );
+ b = buttons[i];
+ if ( $.isArray( b ) ) {
+ // Forwarded arguments array from mw.toolbar.addButton
+ insertButton.apply( toolbar, b );
} else {
- // Legacy mwCustomEditButtons is an object
- c = buttons[i];
- toolbar.insertButton( c.imageFile, c.speedTip, c.tagOpen,
- c.tagClose, c.sampleText, c.imageId, c.selectText );
+ // Raw object from legacy mwCustomEditButtons
+ insertButton( b );
}
}
}
});
-}( jQuery, mediaWiki ) );
+}( mediaWiki, jQuery ) );
* @constructor
* @param {Object|String} URI string, or an Object with appropriate properties (especially another URI object to clone).
* Object must have non-blank 'protocol', 'host', and 'path' properties.
+ * This parameter is optional. If omitted (or set to undefined, null or empty string), then an object will be created
+ * for the default uri of this constructor (e.g. document.location for mw.Uri in MediaWiki core).
* @param {Object|Boolean} Object with options, or (backwards compatibility) a boolean for strictMode
* - strictMode {Boolean} Trigger strict mode parsing of the url. Default: false
* - overrideKeys {Boolean} Wether to let duplicate query parameters override eachother (true) or automagically
overrideKeys: false
}, options );
- if ( uri !== undefined && uri !== null || uri !== '' ) {
+ if ( uri !== undefined && uri !== null && uri !== '' ) {
if ( typeof uri === 'string' ) {
this.parse( uri, options );
} else if ( typeof uri === 'object' ) {
this.query = {};
}
}
+ } else {
+ // If we didn't get a URI in the constructor, use the default one.
+ return defaultUri.clone();
}
// protocol-relative URLs
// Allow calling by single module name
if ( typeof dependencies === 'string' ) {
dependencies = [dependencies];
- if ( registry[dependencies[0]] !== undefined ) {
- // Cache repetitively accessed deep level object member
- regItemDeps = registry[dependencies[0]].dependencies;
- // Cache to avoid looped access to length property
- regItemDepLen = regItemDeps.length;
- for ( n = 0; n < regItemDepLen; n += 1 ) {
- dependencies[dependencies.length] = regItemDeps[n];
- }
- }
}
// Add ready and error callbacks if they were given
}
}
- if (filtered.length === 0) {
+ if ( filtered.length === 0 ) {
return;
}
// Resolve entire dependency map
border: 1px dashed #2f6fab;
color: black;
background-color: #f9f9f9;
- /* Handle overflow (bug 260) */
- overflow: auto;
- /* IE 7 is the first IE to support overflow but got it wrong */
- /* IE 8+ is fine */
- *padding-bottom: 21px;
- *overflow-y: hidden;
+
+ /*
+ * Wrap properly.
+ * - pre-wrap: causes the browser to naturally wrap by displaying
+ * words on the next line if they don't fit on the same line
+ * within the box (does not cut off words).
+ * - break-word: forces the browser to wrap anywhere (even within
+ * a word) if it is (still) too long for the line.
+ * When only using break-word in a <pre>, the browser only uses
+ * the force behavior and as a result almost always cuts half-way
+ * a word. When only using pre-wrap, too-long words will still
+ * cause the page layout to break. The combination is magic :).
+ * See also https://bugzilla.wikimedia.org/show_bug.cgi?id=260#c20
+ */
+ white-space: pre-wrap;
+ word-wrap: break-word;
}
/* Tables */
h1:lang(kn),
h1:lang(ml),
h1:lang(mr),
+h1:lang(my),
h1:lang(or),
h1:lang(pa),
h1:lang(sa),
h1:lang(ta),
h1:lang(te) {
- line-height: 1.5em !important;
+ line-height: 1.6em !important;
}
h2:lang(as), h3:lang(as), h4:lang(as), h5:lang(as), h6:lang(as),
h2:lang(bho), h3:lang(bho), h4:lang(bho), h5:lang(bho), h6:lang(bho),
h2:lang(kn), h3:lang(kn), h4:lang(kn), h5:lang(kn), h6:lang(kn),
h2:lang(ml), h3:lang(ml), h4:lang(ml), h5:lang(ml), h6:lang(ml),
h2:lang(mr), h3:lang(mr), h4:lang(mr), h5:lang(mr), h6:lang(mr),
+h2:lang(my), h3:lang(my), h4:lang(my), h5:lang(my), h6:lang(my),
h2:lang(or), h3:lang(or), h4:lang(or), h5:lang(or), h6:lang(or),
h2:lang(pa), h3:lang(pa), h4:lang(pa), h5:lang(pa), h6:lang(pa),
h2:lang(sa), h3:lang(sa), h4:lang(sa), h5:lang(sa), h6:lang(sa),
test( 'Bad calls', function () {
var uri;
- expect( 5 );
-
- raises(
- function () {
- new mw.Uri();
- },
- function ( e ) {
- return e.message === 'Bad constructor arguments';
- },
- 'throw error on no arguments to constructor'
- );
-
- raises(
- function () {
- new mw.Uri( '' );
- },
- function ( e ) {
- return e.message === 'Bad constructor arguments';
- },
- 'throw error on empty string as argument to constructor'
- );
+ expect( 3 );
raises(
function () {
equal( href, testProtocol + testServer + ':' + testPort + testPath, 'Root-relative URL gets host, protocol, and port supplied' );
} );
+
+QUnit.test( 'Constructor falls back to default location', function (assert) {
+ var testuri, MyUri, uri;
+ QUnit.expect( 4 );
+
+ testuri = 'http://example.org/w/index.php';
+ MyUri = mw.UriRelative( testuri );
+
+ uri = new MyUri();
+ assert.equal( uri.toString(), testuri, 'no arguments' );
+
+ uri = new MyUri( undefined );
+ assert.equal( uri.toString(), testuri, 'undefined' );
+
+ uri = new MyUri( null );
+ assert.equal( uri.toString(), testuri, 'null' );
+
+ uri = new MyUri( '' );
+ assert.equal( uri.toString(), testuri, 'empty string' );
+} );