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
"Danny S" dannys712.enwiki@gmail.com wrote in message news:CADJdptz-8ZbDhTJVzGQw5LwdaYrQE3-OHi=n63T49udobj+HcA@mail.gmail.com...
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.
Awesome! I have been looking into doing this migration, but have not found any decent information about it.
I posted a question on MW.org about the status of this, about a year ago, but didn't really get an answer. [1]
And I was literally coming onto this newsgroup to ask about this (and one other thing, already posted), so it's very fortuitous timing! I'll take a look and if I have any questions, I'll post on the talk page.
Thank you,
- Mark Clements (HappyDog)
wikitech-l@lists.wikimedia.org