/**
* Display a read-only View Source page
- * @param Content $content content object
+ * @param Content $content
* @param string $errorMessage additional wikitext error message to display
*/
protected function displayViewSourcePage( Content $content, $errorMessage = '' ) {
/**
* Really format a diff for the newsfeed
*
- * @param Title $title Title object
+ * @param Title $title
* @param int $oldid Old revision's id
* @param int $newid New revision's id
* @param int $timestamp New revision's timestamp
/**
* Check whether a given URL has a domain that occurs in a given set of domains
- * @param string $url URL
+ * @param string $url
* @param array $domains Array of domains (strings)
* @return bool True if the host part of $url ends in one of the strings in $domains
*/
/**
* @since 1.16.3
- * @param int $userId Userid
+ * @param int $userId
* @param string $userText User name in database.
* @return string HTML fragment with block link
*/
}
/**
- * @param int $userId Userid
+ * @param int $userId
* @param string $userText User name in database.
* @return string HTML fragment with e-mail user link
*/
/**
* Redirect to $url rather than displaying the normal page
*
- * @param string $url URL
+ * @param string $url
* @param string $responsecode HTTP status code
*/
public function redirect( $url, $responsecode = '302' ) {
}
/**
- * @param User $user The User object
+ * @param User $user
* @param IContextSource $context
* @return array Text/links to display as key; $skinkey as value
*/
$content = null;
$blobData = null;
- $blobFlags = '';
+ $blobFlags = null;
if ( is_object( $row ) ) {
// archive row
if ( isset( $row->old_text ) ) {
// this happens when the text-table gets joined directly, in the pre-1.30 schema
$blobData = isset( $row->old_text ) ? strval( $row->old_text ) : null;
- $blobFlags = isset( $row->old_flags ) ? strval( $row->old_flags ) : '';
+ // Check against selects that might have not included old_flags
+ if ( !property_exists( $row, 'old_flags' ) ) {
+ throw new InvalidArgumentException( 'old_flags was not set in $row' );
+ }
+ $blobFlags = ( $row->old_flags === null ) ? '' : $row->old_flags;
}
$mainSlotRow->slot_revision = intval( $row->rev_id );
$mainSlotRow->format_name = isset( $row['content_format'] )
? strval( $row['content_format'] ) : null;
$blobData = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
- $blobFlags = isset( $row['flags'] ) ? trim( strval( $row['flags'] ) ) : '';
+ // XXX: If the flags field is not set then $blobFlags should be null so that no
+ // decoding will happen. An empty string will result in default decodings.
+ $blobFlags = isset( $row['flags'] ) ? trim( strval( $row['flags'] ) ) : null;
// if we have a Content object, override mText and mContentModel
if ( !empty( $row['content'] ) ) {
*
* @param SlotRecord $slot The SlotRecord to load content for
* @param string|null $blobData The content blob, in the form indicated by $blobFlags
- * @param string $blobFlags Flags indicating how $blobData needs to be processed
+ * @param string|null $blobFlags Flags indicating how $blobData needs to be processed.
+ * null if no processing should happen.
* @param string|null $blobFormat MIME type indicating how $dataBlob is encoded
* @param int $queryFlags
*
private function loadSlotContent(
SlotRecord $slot,
$blobData = null,
- $blobFlags = '',
+ $blobFlags = null,
$blobFormat = null,
$queryFlags = 0
) {
if ( $blobData !== null ) {
Assert::parameterType( 'string', $blobData, '$blobData' );
- Assert::parameterType( 'string', $blobFlags, '$blobFlags' );
+ Assert::parameterType( 'string|null', $blobFlags, '$blobFlags' );
$cacheKey = $slot->hasAddress() ? $slot->getAddress() : null;
- $data = $this->blobStore->expandBlob( $blobData, $blobFlags, $cacheKey );
-
- if ( $data === false ) {
- throw new RevisionAccessException(
- "Failed to expand blob data using flags $blobFlags (key: $cacheKey)"
- );
+ if ( $blobFlags === null ) {
+ $data = $blobData;
+ } else {
+ $data = $this->blobStore->expandBlob( $blobData, $blobFlags, $cacheKey );
+ if ( $data === false ) {
+ throw new RevisionAccessException(
+ "Failed to expand blob data using flags $blobFlags (key: $cacheKey)"
+ );
+ }
}
+
} else {
$address = $slot->getAddress();
try {
*
* @since 1.28
* @param string $name
- * @param string|null $default Optional default
+ * @param string|null $default
* @return string|null
*/
public function getRawVal( $name, $default = null ) {
}
/**
- * @param int $year
- * @param int $month
+ * @param int|string $year Use '' or 0 to start with no year preselected.
+ * @param int|string $month A month in the 1..12 range. Use '', 0 or -1 to start with no month
+ * preselected.
* @return string Formatted HTML
*/
public static function dateMenu( $year, $month ) {
* Creates a submit button
*
* @param string $message Text of the submit button, will be escaped
- * @param array $attributes Attributes
+ * @param array $attributes
* @return string HTML output for the submit button
*/
function submitButton( $message, $attributes = [] ) {
* Adds a table to the content that will be added to the output.
*
* @param string $content The content that will be added to the output
- * @param string $table The table
+ * @param string $table
* @return string The content with the table added
*/
protected function addTable( $content, $table ) {
* @param string $paramName Parameter name
* @param array|mixed $paramSettings Default value or an array of settings
* using PARAM_* constants.
- * @param bool $parseLimit Parse limit?
+ * @param bool $parseLimit Whether to parse and validate 'limit' parameters
* @return mixed Parameter value
*/
protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
* $this->getPageTableFields().
*
* @param IDatabase $db
- * @param ResultWrapper $queryResult Query result object
+ * @param ResultWrapper $queryResult
*/
public function populateFromQueryResult( $db, $queryResult ) {
$this->initFromQueryResult( $queryResult );
* Construct an appropriate failure response
* @param User $user
* @param User|null $creator
- * @param int $constant LoginForm constant
- * @param string|null $msg Message
- * @param string $hook Hook
+ * @param int $constant One of the LoginForm::… constants
+ * @param string|null $msg Optional message key, will be derived from $constant otherwise
+ * @param string $hook Name of the hook for error logging and exception messages
* @return StatusValue
*/
protected function makeFailResponse( $user, $creator, $constant, $msg, $hook ) {
* it will be generated with the callback function (if present), and the newly
* calculated value will be stored to the cache in a wrapper.
*
- * @param BagOStuff $cache A cache object
+ * @param BagOStuff $cache
* @param string $key The cache key
* @param int $expiry The expiry timestamp or interval in seconds
* @param bool|callable $callback The callback for generating the value, or false
/**
* Returns the gender for given username.
- * @param string|User $username Username
+ * @param string|User $username
* @param string $caller The calling method
* @return string
*/
}
/**
- * @param WANObjectCache $wanCache WAN cache instance
- * @param BagOStuff $clusterCache Cluster cache instance
- * @param BagOStuff $srvCache Server cache instance
+ * @param WANObjectCache $wanCache
+ * @param BagOStuff $clusterCache
+ * @param BagOStuff $serverCache
* @param bool $useDB Whether to look for message overrides (e.g. MediaWiki: pages)
* @param int $expiry Lifetime for cache. @see $mExpiry.
*/
public function __construct(
WANObjectCache $wanCache,
BagOStuff $clusterCache,
- BagOStuff $srvCache,
+ BagOStuff $serverCache,
$useDB,
$expiry
) {
$this->wanCache = $wanCache;
$this->clusterCache = $clusterCache;
- $this->srvCache = $srvCache;
+ $this->srvCache = $serverCache;
$this->mDisable = !$useDB;
$this->mExpiry = $expiry;
/**
* Registers a filter in this group
*
- * @param ChangesListBooleanFilter $filter ChangesListBooleanFilter
+ * @param ChangesListBooleanFilter $filter
*/
public function registerFilter( ChangesListBooleanFilter $filter ) {
$this->filters[$filter->getName()] = $filter;
* (not filtered out), even for the hide-based filters. So e.g. conflicting with
* 'hideanons' means there is a conflict if only anonymous users are *shown*.
*
- * @param ChangesListFilterGroup|ChangesListFilter $other Other
- * ChangesListFilterGroup or ChangesListFilter
+ * @param ChangesListFilterGroup|ChangesListFilter $other
* @param string $globalKey i18n key for top-level conflict message
* @param string $forwardKey i18n key for conflict message in this
* direction (when in UI context of $this object)
*
* Internal use ONLY.
*
- * @param ChangesListFilterGroup|ChangesListFilter $other Other
- * ChangesListFilterGroup or ChangesListFilter
+ * @param ChangesListFilterGroup|ChangesListFilter $other
* @param string $globalDescription i18n key for top-level conflict message
* @param string $contextDescription i18n key for conflict message in this
* direction (when in UI context of $this object)
* (not filtered out), even for the hide-based filters. So e.g. conflicting with
* 'hideanons' means there is a conflict if only anonymous users are *shown*.
*
- * @param ChangesListFilterGroup|ChangesListFilter $other Other
- * ChangesListFilterGroup or ChangesListFilter
+ * @param ChangesListFilterGroup|ChangesListFilter $other
* @param string $globalKey i18n key for top-level conflict message
* @param string $forwardKey i18n key for conflict message in this
* direction (when in UI context of $this object)
*
* Internal use ONLY.
*
- * @param ChangesListFilterGroup|ChangesListFilter $other Other
- * ChangesListFilterGroup or ChangesListFilter
+ * @param ChangesListFilterGroup|ChangesListFilter $other
* @param string $globalDescription i18n key for top-level conflict message
* @param string $contextDescription i18n key for conflict message in this
* direction (when in UI context of $this object)
/**
* Registers a filter in this group
*
- * @param ChangesListStringOptionsFilter $filter ChangesListStringOptionsFilter
+ * @param ChangesListStringOptionsFilter $filter
*/
public function registerFilter( ChangesListStringOptionsFilter $filter ) {
$this->filters[$filter->getName()] = $filter;
* exists, provided it is not disabled. If the message is disabled,
* we consider the tag hidden, and return false.
*
- * @param string $tag Tag
+ * @param string $tag
* @param IContextSource $context
* @return string|bool Tag description or false if tag is to be hidden.
* @since 1.25 Returns false if tag is to be hidden.
* or if message is disabled, returns false. Otherwise, returns the message object
* for the long description.
*
- * @param string $tag Tag
+ * @param string $tag
* @param IContextSource $context
* @return Message|bool Message object of the tag long description or false if
* there is no description.
*
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
- * @param ParserOptions|null $options Parser options
+ * @param ParserOptions|null $options
* @param bool $generateHtml Whether or not to generate HTML
*
* @return ParserOutput Containing information derived from this content.
*
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
- * @param ParserOptions $options Parser options
+ * @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*
/**
* Beautifies JSON prior to save.
*
- * @param Title $title Title
- * @param User $user User
+ * @param Title $title
+ * @param User $user
* @param ParserOptions $popts
* @return JsonContent
*/
*
* @param Title $title Context title for parsing
* @param int $revId Revision ID (for {{REVISIONID}})
- * @param ParserOptions $options Parser options
+ * @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*/
/**
* @param string|array|MessageSpecifier $messageSpec See Message::newFromSpecifier
- * @param int $code Exception code
+ * @param int $code
* @param Exception|Throwable $previous The previous exception used for the exception chaining.
*/
public function __construct( $messageSpec, $code = 0, $previous = null ) {
/**
* Set the caption (as plain text)
*
- * @param string $caption Caption
+ * @param string $caption
*/
function setCaption( $caption ) {
$this->mCaption = htmlspecialchars( $caption );
/**
* Set the caption (as HTML)
*
- * @param string $caption Caption
+ * @param string $caption
*/
public function setCaptionHtml( $caption ) {
$this->mCaption = $caption;
/**
* Open a PG connection with given parameters
* @param string $user User name
- * @param string $password Password
+ * @param string $password
* @param string $dbName Database name
* @param string $schema Database schema
* @return Status
* - if we find a possible extension followed by a dot or another illegal
* character, we ignore it and continue searching
*
- * @param string $url URL
+ * @param string $url
* @return mixed Detected extension (string), or false if none found
*/
public static function findIE6Extension( $url ) {
/**
* Merge another status object into this one
*
- * @param StatusValue $other Other StatusValue object
+ * @param StatusValue $other
* @param bool $overwriteValue Whether to override the "value" member
*/
public function merge( $other, $overwriteValue = false ) {
/**
* @param string $name element or attribute name, maybe with a full or short prefix
- * @param string $namespaceURI the namespaceURI
+ * @param string $namespaceURI
* @return string the name prefixed with namespaceURI
*/
private function expandNS( $name, $namespaceURI ) {
/**
* Changes the TTL on a key from the server to $time
*
- * @param string $key Key
+ * @param string $key
* @param int $time TTL in seconds
*
* @return bool True on success, false on failure
}
/**
- * @param bool $value New value
+ * @param bool $value
* @return bool Old value
* @since 1.28
*/
* @param string $user
* @param string $page
* @param string $pattern
- * @param int $year Year
- * @param int $month Month
+ * @param int|string $year Use 0 to start with no year preselected.
+ * @param int|string $month A month in the 1..12 range. Use 0 to start with no month
+ * preselected.
* @param array $filter
* @param string $tagFilter Tag to select by default
* @param string $action
}
/**
- * @param stdClass $row Row
+ * @param stdClass $row
* @return string
*/
private function getShowHideLinks( $row ) {
}
/**
- * @param stdClass $row Row
+ * @param stdClass $row
* @param string|array $type
* @param string|array $action
* @param string $right
* Determine if the current user is allowed to view a particular
* field of this log row, if it's marked as deleted.
*
- * @param stdClass $row Row
+ * @param stdClass $row
* @param int $field
* @param User $user User to check, or null to use $wgUser
* @return bool
}
/**
- * @param stdClass $row Row
+ * @param stdClass $row
* @param int $field One of DELETED_* bitfield constants
* @return bool
*/
*
* @param string $action One of '', 'block', 'protect', 'rights', 'delete',
* 'upload', 'move', 'move_redir'
- * @param Title $target Title object
+ * @param Title $target
* @param string $comment Description associated
* @param array $params Parameters passed later to wfMessage function
* @param null|int|User $doer The user doing the action. null for $wgUser
* Read the first 2 bytes of a tiff file to figure out
* Little Endian or Big Endian. Needed for exif stuff.
*
- * @param string $filename The filename
+ * @param string $filename
* @return string 'BE' or 'LE' or false
*/
static function getTiffByteOrder( $filename ) {
* @todo Potentially this should also capture the timezone offset.
* @param array $date The date tag
* @param array $time The time tag
- * @param string $c The charset
+ * @param string $charset
* @return string Date in EXIF format.
*/
- private static function timeHelper( $date, $time, $c ) {
+ private static function timeHelper( $date, $time, $charset ) {
if ( count( $date ) === 1 ) {
// the standard says this should always be 1
// just double checking.
- list( $date ) = self::convIPTC( $date, $c );
+ list( $date ) = self::convIPTC( $date, $charset );
} else {
return null;
}
if ( count( $time ) === 1 ) {
- list( $time ) = self::convIPTC( $time, $c );
+ list( $time ) = self::convIPTC( $time, $charset );
$dateOnly = false;
} else {
$time = '000000+0000'; // placeholder
/**
* Helper function to convert charset for iptc values.
* @param string|array $data The iptc string
- * @param string $charset The charset
+ * @param string $charset
*
* @return string|array
*/
/**
* Helper function of a helper function to convert charset for iptc values.
* @param string|array $data The IPTC string
- * @param string $charset The charset
+ * @param string $charset
*
* @return string
*/
* Get a MediaTransformOutput object representing the transformed output. Does not
* actually do the transform.
*
- * @param File $image The image object
+ * @param File $image
* @param string $dstPath Filesystem destination path
* @param string $dstUrl Destination URL to use in output HTML
* @param array $params Arbitrary set of parameters validated by $this->validateParam()
* Get a MediaTransformOutput object representing the transformed output. Does the
* transform unless $flags contains self::TRANSFORM_LATER.
*
- * @param File $image The image object
+ * @param File $image
* @param string $dstPath Filesystem destination path
* @param string $dstUrl Destination URL to use in output HTML
* @param array $params Arbitrary set of parameters validated by $this->validateParam()
*
* This is used by the media handlers that use the FormatMetadata class
*
- * @param array $metadataArray Metadata array
+ * @param array $metadataArray
* @param bool|IContextSource $context Context to use (optional)
* @return array Array for use displaying metadata.
*/
/**
* Decodes a lossy chunk header
- * @param string $header Header string
+ * @param string $header First few bytes of the header, expected to be at least 18 bytes long
* @return bool|array See WebPHandler::decodeHeader
*/
protected static function decodeLossyChunkHeader( $header ) {
/**
* Decodes a lossless chunk header
- * @param string $header Header string
+ * @param string $header First few bytes of the header, expected to be at least 13 bytes long
* @return bool|array See WebPHandler::decodeHeader
*/
public static function decodeLosslessChunkHeader( $header ) {
/**
* Decodes an extended chunk header
- * @param string $header Header string
+ * @param string $header First few bytes of the header, expected to be at least 18 bytes long
* @return bool|array See WebPHandler::decodeHeader
*/
public static function decodeExtendedChunkHeader( $header ) {
*
* @param File|FSFile $file The image object, or false if there isn't one.
* Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!)
- * @param string $filename The filename
+ * @param string $filename
* @return string
*/
public function getMetadata( $file, $filename ) {
* Wrapper for preprocess()
*
* @param string $text The text to preprocess
- * @param ParserOptions $options Options
+ * @param ParserOptions $options
* @param Title|null $title Title object or null to use $wgTitle
* @return string
*/
/**
* Remove any strip markers found in the given text.
*
- * @param string $text Input string
+ * @param string $text
* @return string
*/
public function killMarkers( $text ) {
/**
* Remove any strip markers found in the given text.
*
- * @param string $text Input string
+ * @param string $text
* @return string
*/
public function killMarkers( $text ) {
* Set this module's name. This is called by ResourceLoader::register()
* when registering the module. Other code should not call this.
*
- * @param string $name Name
+ * @param string $name
*/
public function setName( $name ) {
$this->name = $name;
* Get this module's last modification timestamp for a given context.
*
* @deprecated since 1.26 Use getDefinitionSummary() instead
- * @param ResourceLoaderContext $context Context object
+ * @param ResourceLoaderContext $context
* @return int|null UNIX timestamp
*/
public function getModifiedTime( ResourceLoaderContext $context ) {
/**
* Get the return information about the revision for the API
* @since 1.23
- * @param ApiResult $result API result object
+ * @param ApiResult $result
* @return array Data for the API result
*/
abstract public function getApiData( ApiResult $result );
* may sort based on other algorithms that may cause the exact title match
* to not be in the results or be lower down the list.
* @param string $search the query
- * @param int[] $namespaces the namespaces
+ * @param int[] $namespaces
* @param string[] $srchres results
* @param int $limit the max number of results to return
* @return string[] munged results
}
/**
- * @param string[] $titles as strings
+ * @param string[] $titles
* @return array redirect target prefixedText to index of title in titles
* that is a redirect to it.
*/
private $shutdown = false;
/**
- * @param SessionId $id Session ID object
+ * @param SessionId $id
* @param SessionInfo $info Session info to populate from
* @param CachedBagOStuff $store Backend data store
* @param LoggerInterface $logger
*
* @since 1.31
* @param Config $config
- * @param User $user User object
+ * @param User $user
* @return bool
*/
public static function checkStructuredFilterUiEnabled( Config $config, User $user ) {
/**
* Makes change an option link which carries all the other options
*
- * @param string $title Title
+ * @param string $title
* @param array $override Options to override
* @param array $options Current options
* @param bool $active Whether to show the link in bold
/**
* Output HTTP response of raw content
* Side effect: writes HTTP response to STDOUT.
- * @param string $content Content
+ * @param string $content
* @param string $contentType MIME type
* @throws SpecialUploadStashTooLargeException
* @return bool
* Format a link to a group description page
*
* @param string|UserGroupMembership $group Group name or UserGroupMembership object
- * @param string $username Username
+ * @param string $username
* @return string
*/
protected function buildGroupLink( $group, $username ) {
* (T8957 with Gmail and Internet Explorer).
*
* @param string $page Special page
- * @param string $token Token
+ * @param string $token
* @return string Formatted URL
*/
protected function getTokenUrl( $page, $token ) {
/**
* Get a sitemap filename
*
- * @param int $namespace The namespace
- * @param int $count The count
+ * @param int $namespace
+ * @param int $count
* @return string
*/
function sitemapFilename( $namespace, $count ) {
/**
* Split a filename into filename and extension
*
- * @param string $filename Filename
+ * @param string $filename
* @return array
*/
private function splitFilename( $filename ) {
/**
* Print the results, callback for $db->sourceStream()
- * @param ResultWrapper|bool $res The results object
+ * @param ResultWrapper|bool $res
* @param IDatabase $db
*/
public function sqlPrintResult( $res, $db ) {
use Exception;
use HashBagOStuff;
use InvalidArgumentException;
+use Language;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\BlobStoreFactory;
use MediaWiki\Storage\IncompleteRevisionException;
use MediaWiki\Storage\MutableRevisionRecord;
use MediaWiki\Storage\RevisionRecord;
*/
public function testNewRevisionFromRow_anonEdit() {
$page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
+ $text = __METHOD__ . 'a-ä';
/** @var Revision $rev */
$rev = $page->doEditContent(
- new WikitextContent( __METHOD__. 'a' ),
+ new WikitextContent( $text ),
__METHOD__. 'a'
)->value['revision'];
$page->getTitle()
);
$this->assertRevisionRecordMatchesRevision( $rev, $record );
+ $this->assertSame( $text, $rev->getContent()->serialize() );
+ }
+
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
+ */
+ public function testNewRevisionFromRow_anonEdit_legacyEncoding() {
+ $this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
+ $this->overrideMwServices();
+ $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
+ $text = __METHOD__ . 'a-ä';
+ /** @var Revision $rev */
+ $rev = $page->doEditContent(
+ new WikitextContent( $text ),
+ __METHOD__. 'a'
+ )->value['revision'];
+
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+ $record = $store->newRevisionFromRow(
+ $this->revisionToRow( $rev ),
+ [],
+ $page->getTitle()
+ );
+ $this->assertRevisionRecordMatchesRevision( $rev, $record );
+ $this->assertSame( $text, $rev->getContent()->serialize() );
}
/**
*/
public function testNewRevisionFromRow_userEdit() {
$page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
+ $text = __METHOD__ . 'b-ä';
/** @var Revision $rev */
$rev = $page->doEditContent(
- new WikitextContent( __METHOD__. 'b' ),
+ new WikitextContent( $text ),
__METHOD__ . 'b',
0,
false,
$page->getTitle()
);
$this->assertRevisionRecordMatchesRevision( $rev, $record );
+ $this->assertSame( $text, $rev->getContent()->serialize() );
}
/**
public function testNewRevisionFromArchiveRow() {
$store = MediaWikiServices::getInstance()->getRevisionStore();
$title = Title::newFromText( __METHOD__ );
+ $text = __METHOD__ . '-bä';
+ $page = WikiPage::factory( $title );
+ /** @var Revision $orig */
+ $orig = $page->doEditContent( new WikitextContent( $text ), __METHOD__ )
+ ->value['revision'];
+ $page->doDeleteArticle( __METHOD__ );
+
+ $db = wfGetDB( DB_MASTER );
+ $arQuery = $store->getArchiveQueryInfo();
+ $res = $db->select(
+ $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ],
+ __METHOD__, [], $arQuery['joins']
+ );
+ $this->assertTrue( is_object( $res ), 'query failed' );
+
+ $row = $res->fetchObject();
+ $res->free();
+ $record = $store->newRevisionFromArchiveRow( $row );
+
+ $this->assertRevisionRecordMatchesRevision( $orig, $record );
+ $this->assertSame( $text, $record->getContent( 'main' )->serialize() );
+ }
+
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromArchiveRow
+ */
+ public function testNewRevisionFromArchiveRow_legacyEncoding() {
+ $this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
+ $this->overrideMwServices();
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+ $title = Title::newFromText( __METHOD__ );
+ $text = __METHOD__ . '-bä';
$page = WikiPage::factory( $title );
/** @var Revision $orig */
- $orig = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
+ $orig = $page->doEditContent( new WikitextContent( $text ), __METHOD__ )
->value['revision'];
$page->doDeleteArticle( __METHOD__ );
$record = $store->newRevisionFromArchiveRow( $row );
$this->assertRevisionRecordMatchesRevision( $orig, $record );
+ $this->assertSame( $text, $record->getContent( 'main' )->serialize() );
}
/**
'content' => new WikitextContent( 'Some Content' ),
]
];
+ yield 'Basic array, serialized text' => [
+ [
+ 'id' => 2,
+ 'page' => 1,
+ 'timestamp' => '20171017114835',
+ 'user_text' => '111.0.1.2',
+ 'user' => 0,
+ 'minor_edit' => false,
+ 'deleted' => 0,
+ 'len' => 46,
+ 'parent_id' => 1,
+ 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
+ 'comment' => 'Goat Comment!',
+ 'text' => ( new WikitextContent( 'Söme Content' ) )->serialize(),
+ ]
+ ];
+ yield 'Basic array, serialized text, utf-8 flags' => [
+ [
+ 'id' => 2,
+ 'page' => 1,
+ 'timestamp' => '20171017114835',
+ 'user_text' => '111.0.1.2',
+ 'user' => 0,
+ 'minor_edit' => false,
+ 'deleted' => 0,
+ 'len' => 46,
+ 'parent_id' => 1,
+ 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
+ 'comment' => 'Goat Comment!',
+ 'text' => ( new WikitextContent( 'Söme Content' ) )->serialize(),
+ 'flags' => 'utf-8',
+ ]
+ ];
yield 'Basic array, with title' => [
[
'title' => Title::newFromText( 'SomeText' ),
$this->assertTrue(
$result->getSlot( 'main' )->getContent()->equals( $array['content'] )
);
+ } elseif ( isset( $array['text'] ) ) {
+ $this->assertSame( $array['text'], $result->getSlot( 'main' )->getContent()->serialize() );
} else {
$this->assertSame(
$array['content_format'],
}
}
+ /**
+ * @dataProvider provideNewMutableRevisionFromArray
+ * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray
+ */
+ public function testNewMutableRevisionFromArray_legacyEncoding( array $array ) {
+ $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
+ $blobStore = new SqlBlobStore( wfGetLB(), $cache );
+ $blobStore->setLegacyEncoding( 'windows-1252', Language::factory( 'en' ) );
+
+ $factory = $this->getMockBuilder( BlobStoreFactory::class )
+ ->setMethods( [ 'newBlobStore', 'newSqlBlobStore' ] )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $factory->expects( $this->any() )
+ ->method( 'newBlobStore' )
+ ->willReturn( $blobStore );
+ $factory->expects( $this->any() )
+ ->method( 'newSqlBlobStore' )
+ ->willReturn( $blobStore );
+
+ $this->setService( 'BlobStoreFactory', $factory );
+
+ $this->testNewMutableRevisionFromArray( $array );
+ }
+
}
namespace MediaWiki\Tests\Storage;
+use HashBagOStuff;
+use Language;
use MediaWiki\Storage\RevisionAccessException;
use MediaWiki\Storage\RevisionStore;
use MediaWiki\Storage\SqlBlobStore;
use MediaWikiTestCase;
+use Title;
use WANObjectCache;
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\LoadBalancer;
$store->getTitle( 1, 2, RevisionStore::READ_NORMAL );
}
- // FIXME: test getRevisionSizes
+ public function provideNewRevisionFromRow_legacyEncoding_applied() {
+ yield 'windows-1252, old_flags is empty' => [
+ 'windows-1252',
+ 'en',
+ [
+ 'old_flags' => '',
+ 'old_text' => "S\xF6me Content",
+ ],
+ 'Söme Content'
+ ];
+
+ yield 'windows-1252, old_flags is null' => [
+ 'windows-1252',
+ 'en',
+ [
+ 'old_flags' => null,
+ 'old_text' => "S\xF6me Content",
+ ],
+ 'Söme Content'
+ ];
+ }
+
+ /**
+ * @dataProvider provideNewRevisionFromRow_legacyEncoding_applied
+ *
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
+ */
+ public function testNewRevisionFromRow_legacyEncoding_applied( $encoding, $locale, $row, $text ) {
+ $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
+
+ $blobStore = new SqlBlobStore( wfGetLB(), $cache );
+ $blobStore->setLegacyEncoding( $encoding, Language::factory( $locale ) );
+
+ $store = new RevisionStore( wfGetLB(), $blobStore, $cache );
+
+ $record = $store->newRevisionFromRow(
+ $this->makeRow( $row ),
+ 0,
+ Title::newFromText( __METHOD__ . '-UTPage' )
+ );
+
+ $this->assertSame( $text, $record->getContent( 'main' )->serialize() );
+ }
+
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
+ */
+ public function testNewRevisionFromRow_legacyEncoding_ignored() {
+ $row = [
+ 'old_flags' => 'utf-8',
+ 'old_text' => 'Söme Content',
+ ];
+
+ $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
+
+ $blobStore = new SqlBlobStore( wfGetLB(), $cache );
+ $blobStore->setLegacyEncoding( 'windows-1252', Language::factory( 'en' ) );
+
+ $store = new RevisionStore( wfGetLB(), $blobStore, $cache );
+
+ $record = $store->newRevisionFromRow(
+ $this->makeRow( $row ),
+ 0,
+ Title::newFromText( __METHOD__ . '-UTPage' )
+ );
+ $this->assertSame( 'Söme Content', $record->getContent( 'main' )->serialize() );
+ }
+
+ private function makeRow( array $array ) {
+ $row = $array + [
+ 'rev_id' => 7,
+ 'rev_page' => 5,
+ 'rev_text_id' => 11,
+ 'rev_timestamp' => '20110101000000',
+ 'rev_user_text' => 'Tester',
+ 'rev_user' => 17,
+ 'rev_minor_edit' => 0,
+ 'rev_deleted' => 0,
+ 'rev_len' => 100,
+ 'rev_parent_id' => 0,
+ 'rev_sha1' => 'deadbeef',
+ 'rev_comment_text' => 'Testing',
+ 'rev_comment_data' => '{}',
+ 'rev_comment_cid' => 111,
+ 'rev_content_format' => CONTENT_FORMAT_TEXT,
+ 'rev_content_model' => CONTENT_MODEL_TEXT,
+ 'page_namespace' => 0,
+ 'page_title' => 'TEST',
+ 'page_id' => 5,
+ 'page_latest' => 7,
+ 'page_is_redirect' => 0,
+ 'page_len' => 100,
+ 'user_name' => 'Tester',
+ 'old_is' => 13,
+ 'old_text' => 'Hello World',
+ 'old_flags' => 'utf-8',
+ ];
+
+ return (object)$row;
+ }
}
* @dataProvider provideTestForAccountCreation
* @param string $msg
* @param Status|null $status
- * @param StatusValue $result Result
+ * @param StatusValue $result
*/
public function testTestForAccountCreation( $msg, $status, $result ) {
$this->hook( 'AbortNewAccount', $this->once() )
* code makes this assumption.
*
* @param string $lang Language code for collator
- * @param string $base Base string
+ * @param string $base
* @param string $extended String containing base as a prefix.
*
* @dataProvider prefixDataProvider
/**
* @param string $id Session ID
* @param array $data Session data
- * @param int $expiry Expiry
+ * @param int $expiry
* @param User $user User for metadata
*/
public function setSessionData( $id, array $data, $expiry = 0, User $user = null ) {
/**
* @param string $id Session ID
* @param array $metadata Session metadata
- * @param int $expiry Expiry
+ * @param int $expiry
*/
public function setSessionMeta( $id, array $metadata, $expiry = 0 ) {
$this->setSession( $id, [ 'metadata' => $metadata ], $expiry );
/**
* @param string $id Session ID
* @param array $blob Session metadata and data
- * @param int $expiry Expiry
+ * @param int $expiry
* @param User $user User for metadata
*/
public function setSession( $id, array $blob, $expiry = 0, User $user = null ) {
/**
* @param string $id Session ID
* @param array|mixed $blob Session metadata and data
- * @param int $expiry Expiry
+ * @param int $expiry
*/
public function setRawSession( $id, $blob, $expiry = 0 ) {
if ( $expiry <= 0 ) {
* If you need a Session for testing but don't want to create a backend to
* construct one, use this.
* @param object $backend Object to serve as the SessionBackend
- * @param int $index Index
+ * @param int $index
* @param LoggerInterface $logger
* @return Session
*/
*
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
- * @param ParserOptions $options Parser options
+ * @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*/
*
* @param Title $title Context title for parsing
* @param int|null $revId Revision ID (for {{REVISIONID}})
- * @param ParserOptions $options Parser options
+ * @param ParserOptions $options
* @param bool $generateHtml Whether or not to generate HTML
* @param ParserOutput &$output The output object to fill (reference).
*/