Dear all:
In light of the number of extensions making use of Revision objects, I've
documented some suggestions for how to replace each method. These
suggestions are also available at
https://www.mediawiki.org/wiki/Manual:Revision.php/Migration.
tl;dr: its complicated, but doable. I'm happy to review any patches, and if
you want to do part of the migration at a time, Jenkins will fail but can
be overridden.
----
For extensions that make widespread use of Revision objects, switching to
RevisionRecord may be challenging. It may be easier to split the migration
into multiple patches that each do part of the migration. Tests will still
fail, but if you add me (DannyS712) as a reviewer I'd be happy to review
all of the patches.
== Constructors ==
To replace uses of the static constructors (Revision::newFrom*) while still
having Revision objects, as a stop-gap measure use `new Revision(
$revisionRecord )` with the RevisionRecord constructed using the
RevisionLookup service. The RevisionLookup methods either return a
RevisionRecord or null, and the corresponding Revision methods returned
either a Revision or null:
Instead of
$revision = Revision::newFromId( $id, $flags );
use
$revisionRecord =
MediaWikiServices::getInstance()->getRevisionLookup()->getRevisionById(
$ids, $flags );
$revision = $revisionRecord ? new Revision( $revisionRecord ) : null;
For the other static constructors, simply replace `getRevisionById` with
the corresponding RevisionLookup method (note that the loadFrom* methods
accept different parameters than the newFrom*):
Revision::newFromTitle -> RevisionLookup::getRevisionByTitle
Revision::newFromPageId -> RevisionLookup::getRevisionByPageId
Revision::loadFromPageId -> RevisionLookup::loadRevisionFromPageId
Revision::loadFromTitle -> RevisionLookup::loadRevisionFromTitle
Revision::loadFromTimestamp -> RevisionLookup::loadRevisionFromTimestamp
The RevisionFactory service is used to replace Revision::newFromArchiveRow
and Revision::newFromRow; use RevisionFactory::newRevisionFromArchiveRow,
::newMutableRevisionFromArray, and ::newRevisionFromRow. Note, however,
that RevisionFactory::newMutableRevisionFromArray is itself deprecated, and
uses of it will need to be replaced with construction of a
MutableRevisionRecord.
For direct uses of `new Revision`, either existing ones or those added now,
see the notes below regarding the final removal of constructing Revision
objects.
== Fetching relative revisions ==
Revision::getNext was replaced by RevisionLookup::getNextRevision, which
returns a RevisionRecord object. Likewise, Revision::getPrevious was
replaced by RevisionLookup::getPreviousRevision.
== Revision text ==
Revision::getRevisionText returns a string of the text content for a
specific revision (based on a row from the database). To replace it, get
the RevisionRecord object corresponding to the row, and then use
RevisionRecord::getContent( SlotRecord::MAIN ) to get the relevant content
object, and if there is such an object, use Content::serialize to get the
text.
Revision::compressRevisionText can be replaced with
SqlBlobStore::compressData.
Revision::decompressRevisionText can be replaced with
SqlBlobStore::decompressData.
== Other static methods ==
The following static methods are replaced by non-static methods:
Revision::getQueryInfo -> RevisionStore::getQueryInfo
Revision::getArchiveQueryInfo -> RevisionStore::getArchiveQueryInfo
Revision::getParentLengths -> RevisionStore::getRevisionSizes
Revision::getTimestampFromId -> RevisionStore::getTimestampFromId (NOTE:
the Revision method's first parameter was a Title object that was ignored,
the RevisionStore method does not accept a Title, and only needs the
relevant id and any query flags)
Revision::countByPageId -> RevisionStore::countRevisionsByPageId
Revision::countByTitle -> RevisionStore::countRevisionsByTitle
Revision::userWasLastToEdit -> RevisionStore::userWasLastToEdit (NOTE: the
Revision method's first parameter was an int or an IDatabase object, the
RevisionStore method requires an IDatabase object. ADDITIONALLY, the
RevisionStore method is itself soft deprecated)
== userCan ==
Revision::userCanBitfield can be replaced with
RevisionRecord::userCanBitfield, and Revision::userCan can be replaced with
RevisionRecord::userCanBitfield with the bitfield being the int returned
from RevisionRecord::getVisibility. NOTE that both of the Revision methods
fell back to $wgUser if no user object was passed;
RevisionRecord::userCanBitfield requires that a User be provided.
== Corresponding Revision and RevisionRecord methods ==
The following Revision methods can be replaced with idential RevisionRecord
methods (though some have different names). Once a Revision object is
available in a relevant class or function, I suggest immediately retrieving
the corresponding RevisionRecord via Revision::getRevisionRecord and slowly
making use of the RevisionRecord instead of the Revision:
Revision::getId -> RevisionRecord::getId
Revision::getParentId -> RevisionRecord::getParentId
Revision::getPage -> RevisionRecord::getPageId
Revision::isMinor -> RevisionRecord::isMinor
Revision::isDeleted -> RevisionRecord::isDeleted
Revision::getVisibility -> RevisionRecord::getVisibility
Revision::getTimestamp -> RevisionRecord::getTimestamp
Revision::isCurrent -> RevisionRecord::isCurrent
The following Revision methods can be replaced with identical
RevisionRecord methods, BUT, while the Revision methods returned null if
the value was unknown, the RevisionRecord methods throw
RevisionAccessException exceptions:
Revision::getSize -> RevisionRecord::getSize
Revision::getSha1 -> RevisionRecord::getSha1
Revision::getTitle returned the relevant Title object for the revision. Its
replacement, RevisionRecord::getPageAsLinkTarget, returns a LinkTarget
instead of a Title. If a full Title object is needed, use
Title::newFromLinkTarget.
== Audience-based information ==
The Revision class had multiple methods that accepted a specific audience
for which a value (the editing user's id and username, the edit summary
used, or the revision content) was based on whether the audience could view
the information (since it may have been revision deleted).
The constants used for specifying an audience are identical in the Revision
and RevisionRecord classes:
Revision::FOR_PUBLIC -> RevisionRecord::FOR_PUBLIC
Revision::FOR_THIS_USER -> RevisionRecord::FOR_THIS_USER
Revision::RAW -> RevisionRecord::RAW
Replacements:
Revision::getUser returned the editing user's id, or 0, and
Revision::getUserText returned the editing user's username, or an empty
string. These were replaced with RevisionRecord::getUser, which returns a
UserIdentity object if the audience specifid can view the information, or
null otherwise. To get the user's id, use UserIdentity::getId, and for the
username, use UserIdentity::getName.
Revision::getComment returned the revision's edit summary (as a string), or
null. It was replaced with RevisionRecord::getComment, HOWEVER instead of a
string, RevisionRecord::getComment returns a CommentStoreComment.
Revision::getContent returned the content of the revision's main slot, or
null. It was replaced with RevisionRecord::getContent, HOWEVER the
RevisionRecord method requires that the slot for which the content should
be retrieved be specified. Use SlotRecord::MAIN to match the behaviour of
the Revision method. FURTHERMORE, the RevisionRecord method can throw a
RevisionAccessException, which the Revision method silently converted to
null.
NOTE: When the audience specified for ::getUser, ::getUserText,
::getComment, or ::getContent was FOR_THIS_USER, and no second parameter
was passed with the user, the Revision methods fell back to using $wgUser.
The RevisionRecord methods have no such fallback, and will throw an
InvalidArgumentException if attempting to use FOR_THIS_USER without passing
a User.
== Content handling ==
As part of the migration to multi-content revisions, there is no longer a
single content model or format for a revision, but rather there are content
models and formats for each slot.
Revision::getContentModel returned a string for the content model of the
revision's main slot (SlotRecord::MAIN), either the model set or the
default model. To replace it, get the RevisionRecord's main slot, and use
SlotRecord::getModel. If the revision has no main slot, fall back to the
default model for the title. Use the SlotRoleRegistry service to get the
SlotRoleHandler for the relevant role (in this case SlotRecord::MAIN), and
then use SlotRoleHandler::getDefaultModel.
Revision::getContentFormat returned a string for the content format of the
revision's main slot, or falls back to the default format for the content
model. To replace it, get the RevisionRecord's main slot, and use
SlotRecord::getFormat. If the revision has no main slot, or if
SlotRecord::getFormat returns null, fall back to the default format for the
content model using ContentHandler::getDefaultFormat with the relevant
ContentHandler.
Revision::getContentHandler returned the relevant ContentHandler object.
Use ContentHandlerFactory::getContentHandler with the relevant content
model as a replacement.
== Setting revision information ==
Revision::setId was only supported if the Revision object corresponded to a
MutableRevisionRecord. It can be replaced with MutableRevisionRecord::setId.
Revision::setUserIdAndName was only supported if the Revision object
corresponded to a MutableRevisionRecord. It can be replaced with
MutableRevisionRecord::setUser; NOTE that the MutableRevisionRecord method
requires a UserIdentity, rather than a user id and name.
Revision::setTitle was not supported, and threw an exception if it was
called with a different title than the one already set for the revision.
== Misc ==
Revision::getTextId -> use SlotRecord::getContentAddress for retrieving an
actual content address, or RevisionRecord::hasSameContent to compare content
Revision::isUnpatrolled -> RevisionStore::getRcIdIfUnpatrolled
Revision::getRecentChange -> RevisionStore::getRecentChange
Revision::getSerializedData -> use SlotRecord::getContent for retrieving a
content object, and Content::serialize for the serialized form
Revision::insertOn -> RevisionStore::insertRevisionOn
Revision::base36Sha1 -> SlotRecord::base36Sha1
Revision::newNullRevision -> RevisionStore::newNullRevision
Revision::newKnownCurrent -> RevisionLookup::getKnownCurrentRevision
== Constructing ==
For uses of `new Revision`, there are multiple relevant replacements:
If the Revision was constructed with a RevisionRecord, just use that
RevisionRecord directly
If the Revision was constructed with an array, use
RevisionFactory::newMutableRevisionFromArray
NOTE: If neither the `user` nor `user_text` fields were set, the Revision
class fell back to using $wgUser;
RevisionFactory::newMutableRevisionFromArray includes no such fallback, and
if no user is provided the MutableRevisionRecord returned simply won't have
a user set.
If the Revision was constructed with an object, use
RevisionFactory::newRevisionFromRow
----
Sorry if the suggestions were overwhelming. Let me know if there are any
questions. Hope this helps!
--DannyS712