Custom Layout Editor

What the Custom Layout Editor Provides

The Custom Layout Editor allows you to have complete control over the look and feel of TACTIC using many of the standard web technologies (HTML, CSS and Javascript). With this tool, you can build your own TACTIC components (called widgets) that have the ability to interact with one another intelligently, making it easier for you to design your very own TACTIC interface.

HTML

Custom Layouts enable the laying out of custom widgets using standard HTML.

Element Tag

TACTIC Custom Layout introduces a new html tag <element> which allows for TACTIC widgets to be embedded into HTML.

There are two formats for a TACTIC element: a short form and a long form:

short form:

<element view='forms/my_form'/>

long form:

<element>
  <display class='tactic.ui.panel.CustomLayoutWdg'>
    <view>forms/my_form</view>
  </display>
</element>

This ability to reference other views and elements makes it easy to keep a top level view that draws from other views.

For display class names of other widgets, see section on Common Widgets.

Styles

You can create styles for each view in the Styles tab. However, most of the time it will be useful to reference a central stylesheet for a number of views.

In order to include a top level stylesheet, you can create an empty view with only styles defined and include these styles into other top level views, just as how you would reference a normal view.

For example, you can create a view called 'common/styles' and add this line to the HTML of a view where you want the styles to appear.

<element view='common/styles'/>

Behaviors

TACTIC's behavior system makes use of standard JavaScript behaviors with the added functionality of some built-in classes.

Here are two ways to add an alert behavior to a button class called 'my_button'.

<behavior class=”my_button” event='click_up'>
alert('Hello World'); 
</behavior> 

<behavior class='my_button'>{ 
'type': 'click_up', 
'cbjs_action': "' 

alert('Hello World');

"' 
}</behavior>

Here are the types of events that the TACTIC behavior system has built-in support for:

click_up | click | wheel | double_click | drag | hover | move | change | blur | mouseover | mouseout | keyup | keydown | listen 

You can set the behavior class to activate upon the firing of another event using the 'listen' type event.

<behavior class='my_button'>{
'type': 'click_up', 'cbjs_action': "'
spt.named_events.fire_event('my_event_trigger'); "'
}</behavior>

<behavior class='my_class'>{
'type': 'listen',
'event_name': 'my_event_trigger',
'cbjs_action': "'

alert('Hello World');

"'
}</behavior>

When the behavior is applicable to a specific HTML element (eg. click, click_up, mouseover, etc.), you can get element for which the behavior originated from using the 'bvr.src_el' (Behavior Source Element) tag.

var table = bvr.src_el.getParent('.my_table');
var cells = table.getElements('.my_cells');
cells.setStyle('background', 'red'); 

TACTIC’s powerful framework comes with many API functions that make developing for TACTIC easier. Here are some common ones.

Show loading popup:

spt.app_busy.show('Saving data...') 

Hide loading popup:

spt.app_busy.hide() 

Load an element:

spt.panel.load(element_name, class_name, kwargs) 

Load an element into a popup:

spt.panel.load_popup(element_name, class_name, kwargs) 

Close a popup:

spt.popup.close(popup_element) 

Images

Images can be checked into TACTIC and used in interface design. In the Files tab, you can check in images using the Check-in wizard.

Once the file is checked in, you use the web path as the URL of the image.

Mako

The custom layout engine embeds the Mako, a powerful templating engine which allows you to embed Python scripts and logic within HTML. Here is a simple example for its usage.

<div> 
<% 
my_car = 'A ferrari'
%> 
</div>
<p>${my_car}</p> 

Mako makes passing and accessing of data in TACTIC easy, especially combined with the support of XML by TACTIC widgets for passing arguments.

The 'kwargs.get' function can be used to get the value of an XML attribute of an element, whether it is an attribute already supported by the element or an arbitrary one. Here is an example of setting a value for an arbitrary attribute.

HTML code in top level view:

<element> 
  <display class='tactic.ui.panel.CustomLayoutWdg'> 
    <view>my_forms.photoshoot_form</view>
    <args>Hello</args> 
  </display>
</element> 

HTML code in a view named 'my_forms.photoshoot_form':

<element> 
  <display class='tactic.ui.input.TextInputWdg'> 
    <default>${kwargs.get("args")}</default> 
  </display> 
</element> 

For the example above, the text field will be populated with the string 'Hello'.

Most of the time, it will be beneficial to use Mako to pass search keys from one view to another. That's covered in a bit more detail in the Creating Forms section of this document.

Creating Forms

Forms provide an interface for updating TACTIC data. The Custom Layout Editor makes the creation of forms easy with built-in widgets and functions.

TACTIC already has some predefined input widgets that can be used as input fields for forms, and they are referenced like any other widget.

TextInputWdg

SelectWdg

TextAreaWdg

CalendarInputWdg

ActionButtonWdg

<element name='my_text_input_field'>
  <display class='tactic.ui.input.TextInputWdg'>
    <default>Hello</default> 
    <width>100px</width>
  </display>
</element> 

You can find more details on the exact XML attributes that are supported by each widget in the Common Widgets section.

Here are some useful functions for generating forms.

spt.api.get_input_values(div_container) 

This gets the values of the all the input fields of a div as an array with the attributes being the names of the element names.

server.update(search_key, data) 

This updates an sobject with data that is passed in as an array.

The search key is a key that uniquely identifies an sobject.

Here is an example of usage of both for updating a TACTIC task through a form.

In this example, the search key of an sobject is passed into the view through a list of keyword arguments, and it is kept as a hidden input for ease of access. The clicking of the save button activates the behavior for saving the form.

HTML: <div class='spt_form'> 
  <input type="hidden" name="spt_search_key" value="${kwargs.get('search_key')}"/> 
    <element name='spt_status'>
      <display class='SelectWdg'>
        <values>Assigned|Pending|Approved|Waiting</values>
        <search_key>${kwargs.get("search_key")}</search_key>
      </display>  
    </element> 

  <input type="button" class="spt_save_button" value="Save >>"/> 
</div> 

JavaScript: 

<behavior class="spt_save_button> { 
"type": "click", 
"cbjs_action": ''' 
  //gets the parent of the behavior source element
  var top = bvr.src_el.getParent('.spt_form'); 

  //gets all the input values 
  var values = spt.api.get_input_values(top); 

  var data = { 
    //gets value of element named 'spt_status' 
    //sets it as the value of the 'status' column for the task sobject 
    status: values.spt_status; 
  } 

  search_key = values.spt_search_key; 
  server.update(search_key, data) ''' 
}

Tips and Techniques

Handling None:

The default value for the empty string in Python is the word "None". This does not help very much when you want to obtain something like the search key of an sobject because if there is no search key, instead of getting an empty string, you get the string "None". And if you try to pass "None" into an element, an error will likely result.

The way to work around that is to add an "or" at the end of your kwargs.get function.

ie: kwargs.get("search_key") or "" 

Embed Elements:

A shortcut for embedding elements into the HTML is by clicking on the gear menu.

Similarly, if you would like to inject another view into your current view, you can do so by right clicking on the view you want to inject.

Element Name as Column of sObject:

If you pass a search key into an element, it automatically takes the element name as the column if you do not specify one. In the example below, the text input will display the id of the sObject with the given search key.

<element name="id">
  <display class="tactic.ui.input.TextInputWdg"> 
    <search_key>${search_key}</search_key>
    <width>100px</width>
  </display>
</element>