User:Daniel Kinzler (WMDE)/MCR-SlotRoleHandler

Sketch of the interface for SlotRoleHandler and SlotRoleRegistry, as well as SlotParserOutputProvider, RenderedRevision, and RevisionRenderer:

SlotRoleHandler
Considerations:

SlotRoleHandler is a base class. It can be extended to override some behaviors. This allows us to add more methods later, without making a breaking change. Since most slots do not need custom behavior, this seems a good approach.

The below design assumes that a given SlotRoleHandler instance may support multiple content models. The default SlotRoleHandler implementation should indeed work for any model.

Interface:


 * getRole returns the role name this SlotRoleHandler is associated with.
 * getRawHtml( SlotRecord $record, SlotParserOutputProvider $provider, ParserOutput $target = null ).
 * Returns HTML for the content of record, wrapped in a div, perhaps together with some kind of heading. Calling code is expected to combine this HTML with the HTML from other slots and then pass it to target->setText.
 * $provider is a SlotParserOutputProvider used to acquire a ParserOutput object for the slot's content, if this is needed to generate the HTML. This is typically but not necessarily the case.
 * If $target is passed to getRawHtml, all the necessary head-items and directives will be registered in it, but no link tracking data is copied. Calling code is expected to do that. This enforces a separation of meta-data needed for display from derived data to be stored in the database.
 * getOutputPlacementHints returns an associative array of hints that tell calling code where on the page the HTML returned by getRawHtml should be placed. Calling code is free to ignore these hints.
 * __construct( $role, MessageLocalizer $localizer )
 * $role the name of the role handled by this handler.
 * $localizer used to determine the heading text. They message key is constructed using the role name as a suffix.

We will probably add more methods later, e.g. for determining whether content "counts" as an article, whether it constitutes a redirect, etc.

SlotRoleRegistry
Interface:


 * defineRole( $role, $defaultModel, callable $instantiator ):
 * $role the name of the role to define.
 * $defaultModel the default content model to use for this role. May be overwritten, especially for the main slot.
 * $instantiator a callback returning a SlotRoleHandler
 * we may want to split this into defineCustomRole( $role, $defaultModel, callable $instantiator ) and defineRole( $role, $defaultModel, $outputPlacementHints ). This makes it easier for extensions that want to define a new slot but do not need to override any behavior, which would be the usual case.
 * getRoleHandler( $role, $title ) Provides a handler for the given role. Note that the same role may have different handlers depending on the page title - particularly for the main slot (maybe only for the main slot)
 * returns a SlotRoleHandler.
 * $title could be a PageIdentity (to be introduced) instead of a LinkTarget
 * getDefaultContentModel( $role, LinkTarget $title ) returns the default model to use for the given slot on the given page. This should only be used when creating a new page, otherwise a slot will just keep the model it has in the latest revision. The default content model returned here may be different from the one registered with the role, specially for the main slot.
 * we could instead make getDefaultContentModel( $title ) a method of the SlotRoleHandler.
 * we may also want isSupportedContentModel( $role, LinkTarget $title ). Many slots will only support one very specific model, while some may support a wide range of models.
 * getAllowedRoles( $title ): returns a list of roles supported for the given title. This informs the UI layer which roles should be offered when creating or editing the page. This does not guarantee that no other slots may exist on the page. If additional slots exist, they must at least be "looped through" all edits, and must be accessible when addressed explicitly.
 * getRequiredRoles( $title ): returns a list of roles required for the given title. This informs the UI which roles to require during user interaction. This does not guarantee that no revisions exist on the page that do not have all of these slots. All operations must still be possible if any of the slots missing (except the main slot).
 * getDefinedRoles returns the list of roles defined by calling defineRole.
 * getKnownRoles returns the list of known roles, including the ones returned by getDefinedRoles, and roles that exist in the database. This means the SlotRoleRegistery needs access to the appropriate NameTableStore instance.

SlotParserOutputProvider
The SlotParserOutputProvider provides access to rendered content of a slot. Implementations of SlotParserOutputProvider typically provide caching and lazy initialization.

The main purpose for this interface to exist is to prevent circular a dependency between SlotRoleHandler and RenderedRevision. We may choose to make SlotParserOutputProvider a concrete class and use it inside RenderedRevision, instead of having RenderedRevision implement it as an interface.

Interface:


 * getSlotParserOutput( $role, $generateHtml = true ) returns a ParserOutput for the given slot.

We may want to introduce an interface to use instead of ParserOutput, to avoid binding to all the thins ParserOutput depends on.

RenderedRevision
A RenderedRevision provides (cached, lazy) access to a ParserOutput object that represents the revision's content rendered for some audience. It implements SlotParserOutputProvider (or contains a SlotParserOutputProvider).

Interface:


 * getParserOutput returns a ParserOutput that combines the output of the slots of the revision, as generated by RevisionRenderer. The ParserOutput is generated when this emthod is first called, and then cached internally.
 * getSlotParserOutput( $role, $generateHtml = true ) returns a ParserOutput for the given slot. This ParserOutput is constructed by calling Revision::getParserOutput, and then cached for later use.
 * __construct( RevisionRecord $revision, ParserOptions $options, SlotOutputCombiner $outputCombiner, User $forUser = null )
 * $revision is the revision this RenderedRevision represents the rendering of.
 * $options the parser options to use when constructing ParserOutput.
 * $outputCombiner the SlotOutputCombiner service to use to construct the combined ParserOutput.
 * $forUser the target audience, for access restrictions.

Open: what constructs a RenderedRevision? We could have RenderedRevision construct RenderedRevision, but that would be circular. We'd want to introduce a service interface that avoids the circular dependency.

RevisionRenderer
Stateless service that acts as a factory for RenderedRevision instances.

Interface:


 * combineSlotOutput see below
 * getRenderedRevision( RevisionRecord $rev, ParserOptions $options = null, User $forUser = null )
 * $rev is the revision to render
 * $options the options to use. If not given and $forUser is not given, canonical options will be used. If $forUser is given, user-specific options will be used.
 * $forUser a user, for access permissions and custom parser options. Optional.

We may want to introduce an additional method, getRevisionOutput or renderRevison, to use instead of getRenderedRevision->getParserOutput.

SlotOutputCombiner
Interface that exposes combineSlotOutput to RenderedRevision while avoiding a circular dependency between RevisionRenderer and RenderedRevision.

We could also make this a concrete class used inside RevisionRenderer.

Interface:


 * combineSlotOutput( RevisionRecord $rev, SlotParserOutputProvider $outputProvider, ParserOptions $options ) combines the HTML for all slots into a single ParserOutput object. Relies on SlotRoleHandler::getRaswHtml internally.