Markdown XPages UIControl
Often when I’m designing an xpage, there might be a section of the page in which I want to explain some instructions to the user. Some options here are to:
- write the Instructions using html and embed directly in the xpage markup
- write the Instructions directly in the design pane and format using designer’s ui e.g. bold, color, size etc
- use some native xpage controls to achieve the desired result.
- Write the instructions in a richtext field on a notes document that is loaded to display the info.
Option 1 works good but is annoying to type html opening and closing all those tags. Option 2 is quick to use but creates ugly html with hardcoded styles / colours etc. Option 3 is doable but still a little clunky, Option 4 is not a great move because it is very hard to include the instructions within source control.
A better option needed
Lightweight markup languages are a great way to let formatting ‘get out of the way’ and just let you focus on writing the content. There are many lightweight languages to choose from. Markdown is a popular one so I thought that would be great to be able to quickly write some markdown and let the formatting take care of itself.
If you aren’t familiar with the concept, it is basically a system of writing some plaintext content that can automatically be converted to html. For example the following text is plain text but easy to see the intended structure:
1 2 3 4 5 6 7 |
This is a heading ================= This is a paragraph * this is a list item * this is another item |
This will processed by Markdown into the following html
1 2 3 4 5 6 |
<h1>This is a Heading</h1> <p>This is a paragraph</p> <ul> <li>this is a list item</li> <li>this is another item</li> </ul> |
Side note: Asciidoctor is good too
I have also created a similar control for AsciiDoctor which is another lightweight format. I actually prefer to use Asciidoctor now, however since Markdown seems to be more common I thought I would share the markdown control first.
If you are interested in AsciiDoctor control let me know. It is a bit more intense though because it starts up a JRuby instance and maybe that is not something you’d like to do (because AsciiDoctor is ruby based).
Overview
A few years ago Martin Rolph from Oval UK shared a solution of a Markdown Custom Control. You can find the project on github at OvalUK/XPagesDemos. The solution used markdown4j which is a java-based markdown processor. I decided to build on Martin’s work and convert it from a custom control into an XspLibrary Control with a few extra options of where to retrieve the markdown from.
The markdown control allows you bind to a dynamic data source such as a NotesDocument / scoped variable etc. but also provides some options to load the source text from some static locations. You can have the source text:
- Bound to a String Field in a Datasource or Scope variable
- Supplied from a text file in the WEB-INF directory (read only)
- Supplied from a text file from within a plugin (read only)
- Supplied from a File in the ‘Resources’ Design Element (read only)
- Written directly within the control’s start and end tags (passthrough text) but this has some gotchas. (read only)
The control is an extension of the normal ‘inputTextArea’ control. When the control is in edit mode it just works exactly like a plain text area.
In Read-only mode, the supplied plaintext will be converted to html for display using the Markdown Processor.
Simple Example
In this simple example, I am binding the text to a viewScope variable ‘sampleText’
I have 2 markdown controls, one on the left in edit mode, and one on the right in read mode, showing a live preview of the markup using the onKeyUp event to refresh the preview control. Here is the markup for the page
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<h2>Live Preview example</h2> <xp:table id="tablePreview" style="width:800px;margin-bottom:50.0px;margin-top:10.0px"> <xp:tr> <xp:td style="width:50%" valign="top"> <gb:markdown id="markdown2" value="#{viewScope.sampleText}"> <xp:eventHandler event="onkeyup" submit="true" refreshMode="partial" refreshId="markdown3" execMode="partial" execId="tablePreview" disableValidators="true"> </xp:eventHandler> </gb:markdown> </xp:td> <xp:td valign="top"> <gb:markdown id="markdown3" value="#{viewScope.sampleText}" readonly="true"></gb:markdown> </xp:td> </xp:tr> </xp:table> |
And here is an example of how it looks
Examples of the different methods of supplying the text
You should be able to find the control in the Controls palette under Gregorbyte Library. Drag it onto the page.
Bound to a Datasource / Managed Bean
You can bind it to any text field just like you would normally. In this example it is bound to a document and in edit mode acts just like a normal text area.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?xml version="1.0" encoding="UTF-8"?> <xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:gb="http://www.gregorbyte.com/xsp/"> <xp:this.data> <xp:dominoDocument var="document1" formName="Markdown"></xp:dominoDocument> </xp:this.data> <xp:button value="Save" id="button1"> <xp:eventHandler event="onclick" submit="true" refreshMode="complete"> <xp:this.action> <xp:saveDocument></xp:saveDocument> </xp:this.action> </xp:eventHandler> </xp:button> <gb:markdown id="markdown1" value="#{document1.markdownText}"></gb:markdown> </xp:view> |
Then in edit mode:
In Read only mode:
Supplied from WEB-INF
To read a file from WEB-INF the ‘value’ should be the path to the file
- It must start with ‘/WEB-INF’
- It must finish with ‘.md’
- readonly property should be ‘true’
e.g.
1 2 |
<!-- Read from WEB-INF --> <gb:markdown id="markdown4" readonly="true" value="/WEB-INF/markdown/Sample.md"></gb:markdown> |
Supplied from a File in the Resources Design element
To supply the text from a File in the Resources Design element, give your file should the .md extension, and then supply ‘value’ as ‘/<yourfilename>.md’, also set readonly=”true” e.g.
1 2 |
<!-- Read from the File Resources --> <gb:markdown id="markdown1" readonly="true" value="/FileResource.md"></gb:markdown> |
Supplied from a TextFile in a plugin
To supply the text from a file in a plugin, make sure your file has .md extension. Right click on the file in eclipse and ‘Copy Qualified Name’. Paste this as the ‘value’ and set readonly = true.
You may run into some trouble if your file is not included in the build process, or it is built to a different location than it is in the source code, but basically the format should be ‘/com.your.plugin/your/classpath/to/resource/file.md’
1 2 |
<!-- Read from A plugin --> <gb:markdown id="markdown6" readonly="true" value="/com.gregorbyte.xsp/resources/sample/Markdown.md"></gb:markdown> |
Written directly within start and end tag
This is a quick and dirty method but it is a bit buggy. If you sometimes format your xsp markup with Ctrl + Shift + F then it will ruin your markdown with indents etc. The markdown text must be placed flush with the margin of the code editor
1 2 3 4 5 6 7 8 9 10 11 12 |
<gb:markdown id="markdown5" readonly="true"> This text is directly within the tags ===================================== It has some drawbacks though * The text must be lined up directly against the margin (No indents) * If you hit 'Ctrl + Shift + F' to format your xsp markup, it will indent the text and ruin everything * You cannot embed html tags in the text These bugs could be fixed but I just haven't got to it yet </gb:markdown> |
Installation
To install you do one of the following:
- install my GregorbyteXspLibrary to your designer + server
- Scavenge necessary files from the Github repo
Method 1: Install my library
Go to the latest releases of camac/GregorbyteXspLibrary, download the zip file and install the update site using your favourite methods (same method as you would install IBM Extension Library)
Method 2: Scavenge Necessary files from github repo
You will need These java files
- /com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/component/markdown/UIMarkdown.java
- /com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/renderkit/markdown/MarkdownRenderer.java
You will need this JAR
- /com.gregorbyte.lib.markdown4j/lib/markdown4j-2.2.jar
You will need this xsp-config (place in your WEB-INF
- /com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-markdown.xsp-config
You will need to include the contents of this file in your WEB-INF/faces-config.xml file
- /com.gregorbyte.xsp.controls/src/com/gregorbyte/xsp/config/gregorbyte-markdown-faces-config.xml
I am pretty sure that is all that is needed, let me know if you have any trouble.
Conclusion
Once again thanks to Martin Rolph for providing the markdown custom control in the first place, I have simply added a few more ways to supply the source and wrapped it in a control.
I’d love to hear what you think if this is useful to you or not. Personally I have been using it mainly to supply markdown from within a plugin. Mainly help/instructions/code examples in our demo database.
Great work, it’s something I’ve wanted to do in customer applications for a while, to avoid rich text formatting. But this goes way beyond what I had planned, in terms of where the markdown is stored. This sounds like an excellent addition to Extension Library. I’m sure you’ve got the environment already set up, but I blogged about it on OpenNTF a while ago https://www.openntf.org/main.nsf/blog.xsp?permaLink=PWIS-9QZTH2.
Thanks for the comment Paul! I have a few controls which are candidates for Extension Library submission.
Unfortunately the lack of activity on my existing pull requests has discouraged me from submitting anything else at the moment. But I see this has been acknowledged at IBM Connect so hopefully it will pick up.
Also when I asked in #extensionlibrary channel in OpenNTF slack, nobody seemed to be able to clarify for me the issues regarding licensing. e.g. When bootstrap was absorbed into extlib, Select2 was deliberately excluded due to licencing (I believe), but I’d like to know what the guidelines are on that?
Some of the controls I’ve developed require some 3rd party libraries. this Markdown control for example relies on Markdown4J. So I’d like to be able to figure out which controls I shouldn’t even bother submitting due to this licensing issue.
I still plan on making further pull-requests to the extension library, but for now I plan on releasing a few through my ‘GregorbyteXspLibrary’ and blogging here so at least people can grab it an use it straight away, (or suggest better options/features) and then if I get comments like yours I know that it is something that people would like to use, and then when I get around to making some pull requests I know which controls to start with first