Create Your Own Scaffolds

From DBSight Full-Text Search Engine/Platform Wiki

Table of contents

Scaffold Introduction

Benefit

After generating a template, usually you want to customize it. Customizing a template is not that easy, but sometimes we have to repeat those efforts, because, for example, we want to create a similar template on another data source.

You can actually create your own scaffold. It's not THAT difficult. With some effort spent, you can enjoy easily reusing some logic.

One more thing: you don't need to create a whole new scaffold. You can create a partial scaffold, which just overwrite one part of an existing template! For example, if you prefer a different style of pagination, sorting, search box, etc, you can create your own!

Prerequisite

  1. Played with DBSight's shipped scaffold and generated freemarker-based templates
  2. Knowledge of Freemarker, especially <#noparse></#noparse> tag
  3. Checked Javadoc of class SearchResult, DatasetConfiguration

How it works?

In a scaffold, there are many existing files.

  • All files with extension .scaffold are treated as FreeMarker file, and apply some logic to generate the target file.
  • All files without extension .scaffold are simply copied over.

So, most of the scaffold files you can simply create them as is.

But quite often, you need to apply some logic to control template generation from the scaffold. For example, depending on whether "Need Spell Checking" is selected, scaffold will conditionally generate corresponding code.

To do this, DBSight actually uses FreeMarker scaffolds to generate FreeMarker templates.

Basically, in any *.scaffold files, DBSight use Freemarker's tag <#noparse> to escape to-be-generated template content, and outside the tag <#noparse> , DBSight control the content generation via variables defined in scaffold.xml file.

It's pretty simple and powerful, although sometimes it could be confusing when generating FreeMarker with FreeMarker. But you can easily modify and try, without any server restart. We created the scaffolds too, so we don't make it too hard. :)

How files are organized?

All scaffolding files are stored under

dbs_scaffolding/scaffolds/

Some existing folders are

dbs_scaffolding/scaffolds/default/  #all DBSight shipped scaffolds, will be overwritten when upgrade.
dbs_scaffolding/scaffolds/custom/   #empty folder, you can put your own scaffolds here

You can also create other random folders like these to organize to your tastes.

dbs_scaffolding/scaffolds/flavor_one/
dbs_scaffolding/scaffolds/flavor_two/other/

Under any of the above directories, you can have a scaffold like this

<scaffold_name>/scaffold.xml
<scaffold name>/preview.png            //can be preview.jpg, preview.gif
<scaffold name>/content/

Transforming scaffold into template

Usually, you will see

<scaffold name>/content/main.ftl.scaffold
<scaffold name>/content/documents.ftl.scaffold
<scaffold name>/content/narrowBy.ftl

Any file with .scaffold extension will be treated as Freemarker syntax, and transformed to files without the .scaffold extension. For example, main.ftl.scaffold will be changed to main.ftl when creating the template based on the scaffold.

Transforming Context

As you should already know, the Freemarker need some context to transform files.

The variables in the context are

 dc : an instance of net.javacoding.xsearch.config.DatasetConfiguration
 templateName : a String of the to-be-created template name

And variables defined in scaffold.xml.

The scaffold.xml acts like a variable definition list for the scaffold. For example,

   <string-variable name="title">
     <prompt>Title</prompt>
     <description>The title of the page</description>
     <defaultValue class="string-value">
       <value>My Search Engine</value>
     </defaultValue>
   </string-variable>

This defined a variable named "title". The prompt, description, and default value are used when rendering the UI to the user to creating the template. For example, the user may provide value like

My DBSight Search Title

And in a .scaffold file, you can use

${title} 

to print value of "My DBSight Search Title" to the generated template.

Available Variables Types

String variable

like the "title" just mentioned above.

Boolean variable

for example:

   <boolean-variable name="includeNarrowBy">
     <prompt>Facet Search</prompt>
     <description><![CDATA[Narrow search results by filterable columns]]></description>
     <defaultValue class="boolean-value">
       <value>true</value>
     </defaultValue>
   </boolean-variable>

You can use it to do some basic flow control, for example

<#if includeNarrowBy>
  ...
</#if>
Column Variable

for example:

   <column-variable name="titleColumn">
     <prompt>Title Column</prompt>
     <description>Title Column</description>
     <defaultValue class="string-column-value">
       <highlighted>true</highlighted>
       <summarized>false</summarized>
       <HTMLEscaped>true</HTMLEscaped>
     </defaultValue>
   </column-variable>

This would ask the user to select a column, with proper formatting, as "titleColumn". You can use it as:

<title>${titleColumn}</title>

Check javadoc for more details on Column object.

Columns Value
   <columns-variable name="additionalColumns">
     <prompt>Additional Columns</prompt>
     <description>Select Other Columns to display...</description>
     <columnSelector>isDisplayable</columnSelector>
   </columns-variable>

The "addtionalColumns" will be used as an array of "String" objects. In scaffold, you can do this:

<#foreach c in additionalColumns>
  <#noparse><br/><b></#noparse>
  ${c}
  <#noparse></b>:${summarizer.getHighlighted(doc.get("</#noparse>${c}<#noparse>")?html,"</#noparse>${c}<#noparse>")}</#noparse>
</#foreach>

Well, this example is convoluted with <#noparse> tags. It's ugly. But when you see the rendered template, you will understand.

The available values for <columnSelector> are

<columnSelector>isFilterable</columnSelector>
<columnSelector>isDisplayable</columnSelector>
<columnSelector>isSortable</columnSelector>

There are usually configured via the web under the "Configure Search" tab.

Also, these can also be used:

<columnSelector>isString</columnSelector>
<columnSelector>isDate</columnSelector>
<columnSelector>isNumber</columnSelector>

Attach to existing template

For partial scaffolding, usually you can overwrite one file, and get a new look and feel.

However, sometimes you want to add to some existing file. To do so, add an entry like this:

 <operations>
   <include type="prepend" file="narrowByTags.ftl" toFile="narrowBy_extra.ftl"/>
 </operations>

This way, this line

<#include "narrowByTags.ftl">

will be prepended to file "narrowBy_extra.ftl", if this line is not in that file already.

The value for "type" can be "append" also.

If you want to add to a specific place in a file, you would have to include an empty place holder file there as an extension point, and let the partial scaffold prepend/append to the place holder file.

Summarize of the steps

  1. Create a new directory under dbs_scaffolding/scaffolds/<any directory>, usually dbs_scaffolding/scaffolds/custom.
  2. Copy from other scaffold the these files: scaffold.xml, preview.png, and create the directory content/
  3. Adjust scaffold.xml, add/delete new variables
  4. Create the scaffold files inside "content/"
  5. For partial scaffolding, possibly adjust scaffold.xml "include operation".
  6. Test it by creating/overwriting a template
  7. Try the search, if error, go back to edit the scaffold files.
  8. Take a screen shot of the page of about 3:2 ration, better 180x120, save it as preview.png or preview.jpg
  9. Success!

Organizing Customized Scaffold

By default, DBSight ships with these two directories for scaffolding:

dbs_scaffolding/scaffolds/default
dbs_scaffolding/scaffolds/custom

You can create arbitrary directories under the custom directory, and when upgrading, DBSight will not overwrite your stuff.

Advanced Scaffold Topic

Scaffold Inheritance(Work in Progress)

Sometimes, you want to share a piece of scaffold among several scaffolds. Well, this happens to DBSight's own narrowBy.ftl. Whenever we make a change to the narrowBy.ftl, we need to copy the same file to several scaffolds.

With Scaffold Inheritance, it becomes easier. In scaffold.xml, now we can specify parent scaffolds:

 <inherits>
   <inherit scaffold="abstract/narrow_by"/>
   ...
 </inherits>

This way, all narrowBy features can be separated out into an abstract scaffold.

Inheritance Processing

All parent scaffold variables will be inherited. They will show up automatically, unless a new variable with the same name is also defined in the child scaffold.

All parent scaffold files will be processed first.

If multiple inheritances are defined, the ordering of the <inherit> tag matters. The leading ones are processed first.