Tree Widget

The XWiki Tree Widget is based on a JQuery plugin named jsTree and provides interactive trees. 

Configure a Tree

From JavaScript

You can pass a configuration object when creating a tree instance:

$('.someClass').xtree({
  "core" : {
    "themes" : {
      "variant" : "large"
    }
  },
  "checkbox" : {
    "keep_selected_style" : false
  },
  "plugins" : [ "wholerow", "checkbox" ]
});

Configuration Objects:

plugins is an array of strings which contains the names of the plugins you want active on that instance. The available plugins are:

  • checkbox renders checkbox icons in front of each node, making multiple selection much easier. It supports tri-state behavior which means that if a node has some of its children checked, it will be rendered as undetermined and the state will be propagated up.
  • contextmenu adds the possibility to right click nodes and shows a list of configurable actions in a menu.
  • dnd (drag & drop) adds the possibility to drag and drop tree nodes and rearrange the tree.
  • search adds the possibility to search for items in the tree and to show only matching nodes.
  • sort automatically arranges all sibling nodes according to a comparison config option function, which defaults to alphabetical order.
  • state saves all opened and selected nodes in the user's browser, so when returning to the same tree the previous state will be restored.
  • types makes it possible to add predefined types for groups of nodes, which allows to easily control nesting rules and icons for each group.
  • unique enforces that no nodes with the same name can coexist as siblings.
  • wholerow makes each node appear as block level which makes selection easier.

All options that do not depend on a plugin are contained in a key of the config object named core:

  • data
  • strings
  • check_callback
  • error
  • animation
  • multiple
  • themes
  • expand_selected_onload
  • worker
  • force_text

The options for each plugin are contained within a key with the same name as the plugin. For more details, check the jsTree documentation.

From HTML

Another way of configuring a tree is from HTML, using the $.jstree.defaults.core.data attributes. 

Attribute Name Description Default Value
data-checkboxesEnables the checkbox plugin.false
data-contextMenuEnables the contextmenu plugin.false
data-dragAndDropEnables the dnd plugin.false
data-edgesA boolean indicating if connecting dots are shown.false
data-iconsA boolean indicating if node icons are shown.false
data-responsiveA boolean specifying if a reponsive version of the theme should kick in on smaller screens.false
data-rootThe ID of the root node which is useful if you want to display a sub-tree that starts from a given node.None
data-urlThe URL of the theme's CSS file. In case you have manually included the theme CSS, you should leave this as false.None

Example:

<div class="myTree" data-url="$xwiki.getURL('Space.TreeStructure', 'get', 'outputSyntax=plain')"
 data-root="rootNodeId" data-responsive="true" ... />

XWiki Custom Tree API

The most important methods are:

  • $.jstree.reference (data reference) - get a reference to an existing instance

Example: var tree = $.jstree.reference($('.myTree'))

  • openTo: function(nodeId, callback) {} - Open the tree to a specified node, that is not necessarily loaded. The tree will ask the server side for the node path and it will open all the ancestors of the specified node.

Example: tree.openTo('nodeID', function() {
  console.log('Node loaded!');
});

  • refreshNode: function(node){} - refresh a node. The difference between this method and the jsTree  refresh_node (obj) is that it also works for the root node.

Example: tree.refreshNode('nodeID');

  • execute: function(action, node, params) {} - executes an action on a node.

Example: var targetNode = tree.get_node('nodeId');
var actionName = 'pack';
var actionParams = {
'format': 'xar'
};
var promise = tree.execute(actionName, targetNode, actionParams);

For a complete jsTree API documentation, check this link.

Events

To listen to events that are triggered on tree elements, use:

$('.myTree').on('customEvent', function (event, data) {
//
})

Apart from the jsTree events, XWiki comes with 2 custom events that are specific to the tree widget.

xtree.openContextMenu

This event is useful when wanting to enable or disable context menu items before the context menu is shown. The event data has 3 properties: tree, node and menu.

xtree.contextMenu.*

This event is triggered when a context menu item is clicked. The available action names are: 

  • refresh  
  • copy    
  • remove
  • create   
  • paste 
  • openLink
  • cut     
  • rename  
  • openLinkInNewTab

Context Menu

jsTree provides support for creating context menus but you need to specify the menu structure on the client side. The Tree Widget adds support for retrieving the context menu structure from the server in JSON format. 

jsTree menu:
{
  ...
'export': {
  'label': 'Export as XAR',
  'icon': 'fa fa-download',
  'action': function(data) {
    // Do something
   }
  }
  ...
}

Tree Widget menu:
// The context menu JSON
{
  ...
 'exportAsXAR': {
   'label': 'Export as XAR',
   'icon': 'fa fa-download',
   'action': 'export',
   'parameters': {
     'format': 'xar'
    }
  }
  ...
}

// The context menu item action
$('.myTree').on('xtree.contextMenu.export', function(event, data) {
if (data.parameters.format == 'xar') {
  // Export as XAR
} else {
  // Do something else
}
})

Menu Items Provided by Default

Menu Key / ActionDescription
copyMarks the selected nodes as copied.
createCreates a new node below the target node.
cutMarks the selected nodes as cut. 
openLinkOpens the link of the target node in the current browser window or tab. The URL used is by default the value of the 'href' attribute of the node's anchor element. You can specify a different URL using the urlProperty parameter for which the value is the name of a property from the node's data that holds the URL. 
openLinkInNewTabBehaves the same as openLink with the sole difference that the link of the target node is opened in a new browser window/tab.

Populate a Tree

Static Tree Data

To populate a static tree, all you need is a <ul> node and some nested <li>. You also need a container wrapping the <ul> and an instance on that container: $('someClass').xTree();

<div class="myStaticTree" data-icons="true" data-responsive="true>
 <ul>
   <li>Root node 1</li>
   <li>Root node 2</li>
 </ul>
</div>

To create a node with child nodes it is enough to nest an <ul>:

<div class="myStaticTree" data-icons="true" data-responsive="true>
  <ul>
    <li>Root node 1
      <ul>
        <li>Child node 1</li>
        <li><a href="
#">Child node 2</a></li>
     </ul>
   </li>
 </ul>
</div>

To learn how to set the initial state of a tree or how to use AJAX to populate it, check this link.

Dynamic Tree Data

Dynamic tree data is loaded using the data-url attribute:

<div class="myDynamicTree" data-url="put/your/url/here" data-contextMenu="true"
 data-dragAndDrop="true" data-icons="true" data-edges="true" data-responsive="true" />

The resource behind the specified URL must implement the following API:

  • request data
  • request an action using ?action=<actionName>

jsTree needs a specific format to work with JSON:

{
  id          : "string" // will be autogenerated if omitted
  text        : "string" // node text
  icon        : "string" // string for custom
  state       : {
    opened    : boolean  // is the node open
    disabled  : boolean  // is the node disabled
    selected  : boolean  // is the node selected
  },
  children    : []  // array of strings or objects
  li_attr     : {}  // attributes for the generated LI node
  a_attr      : {}  // attributes for the generated A node
}

Besides what is supported by jsTree's JSON format for node data, the tree widget also supports:

{
// The node id
'id': 'document:xwiki:Main.WebHome',
  ...
'data': {
  // The id of the entity represented by this node
  'id': 'xwiki:Main.WebHome',
  // The node/entity type
  'type': 'document',
  // The type of nodes that can be children of this node
  'validChildren': ['translations', 'attachment', 'document', 'pagination'],
  // Whether this node has a context menu or not (when you right click on it)
  'hasContextMenu': true,
  // Whether you can drag & drop this node
  'draggable': true,
  // Whether this node can be deleted by the current user
  'canDelete': false,
  // Whether this node can be renamed by the current user
  'canRename': false,
  // Whether this node can be moved by the current user
  'canMove': true,
  // Whether the current user can copy this node
  'canCopy': true,
  // Custom action URL. This URL will be used instead of the tree source URL with ?action=delete
  'deleteURL': $docNode.getURL('delete', 'confirm=1')
  },
  ...
}

The XWiki Tree Widget also supports the pagination node type (apart from document). When the pagination node is clicked, the tree makes a request to get the children of the pagination parent node starting with the specified offset, then replaces the pagination node with the response. The pagination node is usually the last child in the response.

Example:

{
'id': "pagination:putParentIdHere",
'text': "36 more ...",
'icon': 'glyphicon glyphicon-flash',
'children': false,
'data': {
  'type': 'pagination',
  'validChildren': [],
  'canDelete': true,
  'offset': 15
  }
}

Data Request Examples

1. Request the children of the specified parent node from the specified offset (the resource decides how many child nodes to return): ?data=children&id=<parentNodeId>&offset=<offset>

The response is a JSON array with node data:

$('someClass').xTree({ 'core' : {
   'data' : [
      'Simple root node',
       {
        'text' : 'Root node 2',
        'state' : {
          'opened' : true,
          'selected' : true
         },
        'children' : [
           { 'text' : 'Child 1' },
          'Child 2'
         ]
      }
    ]
} });

2. Request the path of the specified node:?data=path&id=<nodeId>

The response is a JSON array that contains the ids of all the ancestors of the specified node (including itself) starting from the root of the tree.

3. Request the tree context menu: ?data=contextmenu

The response is a JSON object (map) where the key is the node type and the value is the context menu for that node type. 

4. Request the status for the specified job which is needed when a tree action is performed using asynchronous job: ?data=jobStatus&id=<jobId>.

The response is a JSON objectis similar to:

{
'id': $jobId,
'state': $jobStatus.state,
'request': {
  'type': $jobStatus.request.getProperty('job.type'),
  'user': "$jobStatus.request.getProperty('user.reference')"
  },
'progress': {
  'offset': $jobStatus.progress.offset,
  'currentLevelOffset': $jobStatus.progress.currentLevelOffset
  },
'startDate': $jobStatus.startDate,
'endDate': $jobStatus.endDate
}

Usage

Add a Maven Dependency

In order to use the tree widget in your extension, you would first need to add the tree widget as a dependency and also a dependency for the WebJars Integration Module:

<dependency>
<groupId>org.xwiki.platform</groupId>
<artifactId>xwiki-platform-tree-webjar</artifactId>
<version>6.3</version>
</dependency>

Load the Tree Widget

The recommended way to load the Tree Widget is using RequireJS, like for instance:

require.config({
  paths: {
    jsTree: [
    '//cdnjs.cloudflare.com/ajax/libs/jstree/3.0.8/jstree.min',
     "$!services.webjars.url('jstree', 'jstree.min.js')"
    ],
    JobRunner: "$!services.webjars.url('org.xwiki.platform:xwiki-platform-job-webjar', 'jobRunner.min.js')",
    tree: "$!services.webjars.url('org.xwiki.platform:xwiki-platform-tree-webjar', 'tree.min.js')"
  },
  shim: {
    jsTree: {
      deps: ['jquery']
    }
  }
});

Next, to create a tree, you have to call xtree instead of jstree,

require(['tree'], function($) {
  $('.someClass').xtree();
});

then finally to load the CSS (that will be evaluated) and obtain something like $services.webjars.url('org.xwiki.platform:xwiki-platform-tree-webjar', 'tree.min.css', {'evaluate': true})

 

Search this space