Teletext | Version 4

Teletext Plugin API

The Teletext Generator provides a plugin API so that various aspects of its behaviour can be customized by implementing customer-specific plugins.

The example plugin

The example plugin (including sources) is available as the Maven artifact com.subshell.sophora.teletext:example-teletext-plugin. It is designed to work with an empty Sophora repository after the basic node types have been imported and configuration has been applied as described in the node types and configuration section.

Hence, the example plugin relies on the example story node type example-nt:story.

The example plugin uses Spring and Lombok.

Plugin Development

The following sections provide information about the various aspects of plugin development.

Maven dependencies

The following Maven dependency is required when developing a plugin using the plugin API:

<dependency>
    <groupId>com.subshell.sophora.teletext</groupId>
    <artifactId>teletext-plugin-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

Configuration

The Teletext Generator needs to know which Java packages to include in the initial package scan on startup to enable the plugin.

These packages have to be specified in the pluginPackages configuration key in the application.yml file.

The central plugin component

Every plugin must provide a central Spring component implementing the com.subshell.sophora.teletext.api.TeletextPlugin interface. The Teletext Generator will pick up this component upon startup and perform the registration of customizations.

You can verify that your plugin has been picked up, by looking at the log messages of the Teletext Generator during startup of the application.

The corresponding class in the example plugin is com.subshell.sophora.teletext.example.MyTeletextPlugin.

Sophora documents and Java models

To represent Sophora documents as Java objects, Spring Data Sophora (maven: com.subshell.sophora:spring-data-sophora) is used. Customers may declare their own model classes to represent sophora teletext documents with customer specific properties and/or child nodes.

The model class must extend com.subshell.sophora.teletext.api.model.TeletextDocumentEntity for Sophora documents and com.subshell.sophora.teletext.api.model.index.DocumentReference for their reference node types and must be annotated with an appropriate Spring Data Sophora annotation specifying the corresponding Sophora node type like com.subshell.sophora.spring.data.annotations.NodeType or com.subshell.sophora.spring.data.annotations.Mixins, etc.

Upon startup of the Teletext Generator, Spring Data Sophora will perform a package scan to pick up custom models and register them. Whenever a Java object is to be instantiated by Spring Data Sophora for a Sophora document, the most specific Java class found in the class path is picked and a new object of this class is populated with the data from the Sophora document.

You can verify which model classes are being used for which Sophora node types, by looking at the log messages of the teletext generator during startup of the application.

When custom node types, properties or child nodes are added by the plugin developer, please be advised that the sophora-teletext* Sophora namespace is reserved for core teletext features provided by subshell.

The example plugin defines a custom model class com.subshell.sophora.teletext.example.MyStory for the node type example-nt:story.

Renderers

Renderes are responsible for rendering the "body" part of a teletext document (the lines between header and footer) and for providing variables used in headers and footers.

Renderers have to implement the com.subshell.sophora.teletext.api.generation.TeletextRenderer interface and must be registered via the central plugin component.

The example plugin provides a custom renderer com.subshell.sophora.teletext.example.MyStoryRenderer for documents of type example-nt:story.

A renderer may signal that it is not responsible for rendering a given model by returning BodyRenderingResult.notRendered(). That way, other renderers will be asked to render the model until a result is produced or none of the renderers yield a rendered result.

Index Element Factory

Index pages contain references to other teletext documents, layout elements and provide means of grouping references together.

Of course you can write an individual renderer for index pages if you wish, but if you prefer to keep (most of) the behaviour of the default renderer but have to customize only the rendering of individual elements on an index page you can provide a custom class implementing the com.subshell.sophora.teletext.api.generation.index.IRenderableIndexElementFactory interface.

The example plugin provides a custom index element factory com.subshell.sophora.teletext.example.MyIndexElementFactory.

For any model of an IndexElement whose rendering you'd like to customize, you need to return a corresponding RenderableIndexElement that implements the rendering logic.

For the rendering of index elements that you don't want to customize, delegate to the default mechanisms by returning Optional.empty().

Transfer Service

The transfer service is responsible for writing teletext content to files and/or uploading it to external services.

The default implementation of a transfer service will write .etp files for each slot to the local file system and upload them via (s)ftp to remote servers.

To define a custom transfer service, implement the com.subshell.sophora.teletext.api.transfer.ITransferService interface and register the implementation through the central plugin component.

If you want to customize only some aspects of the default implementation of the transfer service, the default transfer service (com.subshell.sophora.teletext.api.transfer.IDefaultTransferService) can be accessed by using spring dependency injection.

This is demonstrated in the example pugin in the com.subshell.sophora.teletext.example.CustomTransferService component.

Preview

The teletext generator provides a preview of the teletext content for saved but not necessarily published sophora documents.

The preview is availabe as an HTTP endpoint. The path for the default preview is /preview.

To provide a custom preview, create a Spring web controller class annotated with @Controller and set the controller's URL as a preview url in Sophora. An com.subshell.sophora.teletext.api.generation.preview.IPreviewGenerator can be auto-wired into the controller instance to re-use the core preview implementation.

The example plugin defines a custom controller in the com.subshell.sophora.teletext.example.MyController class.

Document Repository

The Teletext Generator will call the various plugin extension points with readily instantiated Java objects representing Sophora documents.

If you need to query the Sophora repository directly for documents, you need to make use of the Spring Data Sophora Repository mechanism to do as follows.

Define an interface extending com.subshell.sophora.spring.data.repository.SophoraDocumentRepository. Spring will then supply an implementation of this interface that can be auto-wired into your code.

The example plugin includes the com.subshell.sophora.teletext.example.MyStoryRepository interface to demonstrate this.

Partitioning Teletext Pages

The Teletext Plugin API provides means to easily partition the teletext content of a Sophora document onto a number of teletext subpages. The following helper classes are instantiated with builders that enable you to control the behavior of the page partitioning on a high level while the actual work gets done by the API.

PagePartitioner

The PagePartitioner helps partitioning a page into subpages. It reads headline and body from the given decorator objects and distributes the resulting lines across pages of the given size.

Use the builder(HeadlineDecorator, BodyDecorator) method to create an object of this class. The builder can be configured with the following parameters:

  • headlineDecorator - Required: provides the headlines for each page. See below for details.
  • bodyDecorator - Required: provides the body lines for each page. See below for details.
  • linesPerPage - The number of lines which are available for a page body on each page.
  • fillUnusedLines - indicates whether unused lines at the end of each page should be filled with blank lines. Defaults to true.

Once the partitioner is built, all that is left to do is to call partition() to receive a readily partitioned BodyRenderingResult containing a number of page bodies with the specified content and potential notifications that arose during the generation that need to be passed on to the user.

HeadlineDecorator

The HeadlineDecorator reads headlines from a fixed size text field, decorates them with colors, an optional counter, appends a blank line or any combination of these.

Use the builder(TeletextDocumentEntity, FixedSizeTextField) method to create a new object of this class. The individual builder methods will have the following effect:

  • entity - Required: the sophora entity this headline will be built for. Used for associating any notifications that are generated during the process.
  • textField - Required: the fixed size text field to be used to get the headlines from.
  • defaultColor - Default color for not explicitly colored elements of the headline. Defaults to cyan.
  • headlineOnlyOnFirstPage - Set to true if only the first page will get a headline. Defaults to false.
  • withCounter - Set to true if a counter should be added if the total pages is > 1 (and the headline will be displayed on more than only the first page). Defaults to false.
  • counterColor - Color to be used for coloring an optional counter. Defaults to white.
  • additionalBlankLines - Number of blank lines to be added at the end of the headline section. Defaults to 0.

Pass the resulting HeadlineDecorator into the builder of a PagePartitioner (see above).

BodyDecorator

The BodyDecorator reads body lines from a fixed size text field and assigns a default text color for the purpose of feeding the lines to a PagePartitioner.

Use the builder(FixedSizeTextField, Color) method to create a new object of this class. The builder can be configured with the following parameters:

  • textField - Required: the fixed size text field to be used to get the body lines from.
  • color - the default color for not explicitly colored parts of the body lines.

Pass the resulting BodyDecorator into the builder of a PagePartitioner (see above).

IndexLinesFormatter

IndexLinesFormatter is a helper for creating reference lines in index pages. Those are lines that usually consist of a short bit of text, followed by a number of dots to fill up the space and finally, a slot number. For example: Title of referenced page ......... 123 The text is provided as a FixedSizeTextField and the slot number as an integer. Obtain an instance of IndexLinesFormatter by calling one of its static of methods.

The formatter offers methods to customize the colors of the resulting lines. Also, if your original text already contains more than one line, you may use the method shiftUnderLines to control the indent of any line following the first one, provided the indented lines still fit the teletext page. The result could look something like this, if we assume an indentation of 2: A slightly longer title of a referenced page ................ 123

TeletextDocumentReferenceCollector

TeletextDocumentReferenceCollector is a helper for extracting all reference model objects of a certain type from an Index. This is useful because an Index might contain a rather complex tree structure of elements, only few of which are actually reference model objects. Traversing through this tree manually can be troublesome, so you might want to use this helper instead.

To use it, you first have to instantiate a TeletextDocumentReferenceCollector by either specifying the desired subtype of TeletextDocumentReference to only extract model objects of this specific type and its subtypes or without a parameter, in which case all objects of the type TeletextDocumentReference or any of its subtypes will be extracted from the index element tree structure.

Secondly, you have to apply the instantiated collector to an Index by calling the index's accept(TeletextDocumentReferenceVisitor) method. Please note that the TeletextDocumentReferenceCollector is stateful and can therefore only be applied to an Index once.

Once applied to an index, call the collector's getCollectedElements() method to receive your result.

In case you design your own IndexElement that is a container for other elements (e.g. a bundling group), make sure you override the accept(TeletextDocumentReferenceVisitor) method and let every contained element accept the visitor recursively, otherwise the TeletextDocumentReferenceCollector (and any other TeletextDocumentReferenceVisitor for that matter) cannot traverse the subtree underneath your custom IndexElement.

TextLinkReplacer

The markup in FixedSizeTextFields may contain text links to other Sophora documents and therefore to other teletext content. The TextLinkReplacer helps you substituting link texts according to the following rules:

  • * If the referenced document has a slot number assigned, every occurrence of the letters XXX within the link text will be substituted for the (first) slot number that is occupied by the document.
  • * If the referenced document does not have a slot number assigned, the entire link text is substituted for a string of the same length consisting of space characters only.

The TextLinkReplacer is a Spring component and has to be obtained through Spring dependency injection.

Use it's replaceTextLinksInFixedSizeTextField(FixedSizeTextField, SlotAllocations, SophoraDocumentEntity) method to receive a copy of the given FixedSizeTextField in which all text links have been substituted according to the above mentioned rules and the given SlotAllocations. The SophoraDocumentEntity is only needed to associate any notifications that arose in the process with.

As an example, consider the following fixed size text:

This is a text. For further infor-  
mation please refer to XXX. And here  
is some more text.

If the entire second sentence constituted a link to a document that occupies slot 123, the result of the substitution would be this:

This is a text. For further infor-  
mation please refer to 123. And here  
is some more text.

However, if the referenced document did not have a slot number assigned, the result would be as follows:

This is a text.  
                            And here  
is some more text.

Variables

On top of rendering the body lines of any given model object, TeletextRenderers may provide a variable assignment that will be applied to variable placeholders in the teletext template. See the section on templates in "Node Types and configuration" for further details on variables in general.

There are two ways to provide your own variable assignment from within your custom TeletextRenderer. You can simply add them to the TeletextPageBodys in the BodyRenderingResult produced by your render(TeletextDocumentEntity, int, SlotAllocations) method or you may implement/override the method additionalVariables(TeletextDocumentEntity, BodyRenderingResult, SlotAllocations) to provide a VariableAssignment separately from the body rendering routine.

The latter is particularly useful if you want to decouple body rendering and variable assignment or if you don't want to implement body rendering alltogether (and effectively leave it to default renderers). For example, you might find that the default body rendering for stories works just fine for your use case but you just want to provide a few extra variables used in your teletext template.

Please note that variables may have different values assigned for different subpages. The variable assignments in the list returned from additionalVariables(…) are applied to the subpages in order.

Default Assignments for Stories

When it comes to stories, some variable assignments will always be provided by default. Those are:

  • header1: Contains the text content of the field sophora-teletext:headerField1.
  • header2: Contains the text content of the field sophora-teletext:headerField2.
  • header1and2: Contains the text content of the two previous fields concatenated with a control character for cyan text color in between.
  • footer1: Contains the text content of the field sophora-teletext:footerField1.
  • footer2: Contains the text content of the field sophora-teletext:footerField2.
  • footer1and2: Contains the text content of the two previous fields concatenated.
  • footerWithNextHeadline: If the two footer fields are filled, this will contain the same as footer1and2. Otherwise it will simply contain the text > mehr as long as there are further subpages or <next headline>..................<slot> on the last subpage, provided the document occupying the next higher slot has sophora-teletext:headline set. In that case <next headline> is filled with that text and <slot> will be the next higher slot number so that the total length of that line is 40 characters. If that next document has no sophora-teletext:headline set, the variable will only contain > <slot> with <slot> being the next higher slot number.
  • footerWithSubpageCounter: If the two footer fields are filled, this will contain the same as footer1and2. Otherwise it will contain the subpage index and the total subpage count in brackets, centered in the line and > mehr at the end, provided there are more subpages. For example: (1/4) > mehr on the first of four subpages or (4/4) on the last subpage.

Default Assignments for Index Pages

For index pages, the variable footer1 is defined.

  • footer1: Contains the text content of the field sophora-teletext:indexPageFootlines. Each entry of this multi-string field is used for the corresponding teletext sub page. If there are more sub pages than field entries, the last entry will be used for the remaining sub pages.

Last modified on 9/7/22

The content of this page is licensed under the CC BY 4.0 License. Code samples are licensed under the MIT License.

Icon