Recently a client requested that we give them the capability of controlling a pages inner content by allowing them to paste markup into a text field (or possibly a RTF), with the following requirements:
- Must be Content Porter friendly.
- Must be able to use Tridion hosted and external multimedia in the markup (including inline CSS), such as images, videos, pdfs etc.
- Images will be hosted insideÂ andÂ outside of Tridion.
- Videos will be hosted completely outside of Tridion, although the client would like the ability to host external Video URLs in Tridion.
- Must be able to use Tridion hosted items in inline styles.
- Simple to use syntax and interface for the content editors.
The schema we went with was quite straight forward; a single text field to contain the markup pasted by the editors, and a multivalued component link to multimedia types hosted in Tridion that the editors would like to inject into the markup. Initially, we planned on going with a Rich Text Field however I ran into problems with recognized nodes when introducing a syntax, and the RTF filter (XSLT) identifying malformed & empty XHTML tags and attempting to correct them, breaking the editorâ€™s markup. While it is possible to add the tags to the configuration, and write some C# to identify and correct the â€˜empty-tagsâ€™, we went for the simpler solution of using a regular text field as we saw no added bonus to using a RTF (markup will be written externally and transferred anyway). We also added an ‘External Video’ schema consisting of a single-line text field which accepts a URL to an external video and a thumbnail image to display in place of the video if the visitor has scripts disabled.
The requirement that the content editors be able to use multimedia hosted in Tridion was not as simple as resolving all of the TCM URIs in the markup (note that not all TCM IDs are automatically resolved, such as inline CSS), due to the requirement that the markup be Content Porter friendly.Â The solution we went with to solve the CP compliancy problem was to use a multivalued component link and introduce our own syntax to reference the linked components in the markup to be stripped out by a TBB, as demonstrated above.
The basics of the TBB were as follows.
- Loop through all of the component links.
- Check if the linked components title is contained in the markup surrounded by our newly introduced syntax (surrounded by square brackets, i.e. [component_title]).
- If the linked component is not an external video component (containing a URL to an external video), then call addBinary( ) on the linked component and swap the component title (and square brackets) with the URL returned by addBinary( ).
- Â In the case that the componentÂ isÂ a video component, then simply retrieve the external video URL from the component field and swap the component title (and square brackets) with the external URL.
- Finally, the updated markup is added to the package with the resolved component as an HTML item (package.CreateHtmlItem), and the key as ‘Package.OutputName’ so we did not have to use a component layout (ran into XSLT filtering issues where empty tags were being transformed to self-closing tags when using a layout, and it was redundant to have a component layout simply rendering the field so it was removed).
- In the Page Layout, the Component Presentation is simply rendered and the markup is placed on the page.
Overall, I liked the approach. It kept everything very simple for the client when entering markup, ensured that everything would be content porter friendly (referencing both hosted & external multimedia) and is flexible to the types of multimedia we want to host Â in Tridion and place in the markup. It also ensures that all of the Tridion links will be resolved, whereas before this implementation we were limited to referencing Tridion items in href and src attributes only. We could not reference Tridion images through inline styles, something that is now possible. Thanks for reading, this is my first post on TridionDeveloper.com so if you have any suggestions on clarity or content I would love to hear them. If anyone would like to see the code, let me know and I would be glad to share. Shoutout to John Winter & Nick Roussakov for their input on the implementation.