Live Data Macro

Last modified by Admin on 2024/04/30 15:41

tableDisplay dynamic lists of data.
TypeJAR
CategoryMacro
Developed by

XWiki Development Team

Rating
1 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard

Compatibility

Since 12.10.3 and 13.0.

Installable with the Extension Manager

Description

Displays dynamic lists of data using various layouts (table, cards) providing support for pagination, selection (for batch actions), multi-sort, advanced filtering and in-line data editing.

The Live Data feature is a replacement for the older Live Table feature. You should migrate to use Live Data as Live Table will be removed eventually.

Usage

In Wiki Syntax

{{liveData
  id="recentPages"
  properties="doc.title,doc.location,doc.author,doc.date"
  source="liveTable"
  sourceParameters="translationPrefix=platform.index."
  filters="doc.location=help"
  sort="doc.date:desc"
  limit="5"
/}}

Using Velocity

XWiki 16.0.0+ 

Whenever XWiki syntax is available, it is preferable to use the macro instead of the Velocity script service.

The script service should be used only when the macro cannot be used (for example in .vm templates, rendering macros cannot be used and only Velocity scripting can be used).

$services.liveData.render({
 'id': "recentPages",
 'properties': "doc.title,doc.location,doc.author,doc.date",
 'source': "liveTable",
 'sourceParameters': "translationPrefix=platform.index.",
 'filters': "doc.location=help",
 'sort': "doc.date:desc",
 'limit': 5
})

See the Script Service section for more details.

liveData-table.png

Parameters

NameDescriptionDefault ValueSince
idThe live data instance id.  
propertiesThe comma-separated list of properties to fetch and display.  
sourceThe live data source to use, specified as a component hint.  
sourceParametersThe live data source parameters, specified as a URL query string.  
filtersThe initial filters to apply on the live data, specified as a URL query string.  
sortThe properties to sort on the live data initially, specified as a comma-separated list of property names, where each property name can be optionally suffixed with the sort order using :asc or :desc.  
limitThe maximum number of live data entries to show on a page.15 
offsetThe index of the first live data entry to show.0 
layoutsThe comma-separated list of layouts the user can choose from to display the live data. The first layout in the list will be loaded initially.table,cards 
showPageSizeDropdownShow or hide the page size drop-down that allows the user to change the number of entries displayed per page.false 
pageSizesThe comma-separated list of page sizes to display in the page size drop-down.15,25,50,100 
descriptionAn optional textual description of the Live Data. 16.0.0

You can use the macro content to pass advanced Live Data configuration using the JSON format specified below.

{{liveData ...}}{
  "meta": {
    "propertyDescriptors": [
      {
        "id": "actions",
        "displayer": "html",
        "sortable": false,
        "filterable": false
      }
    ]
  }
}{{/liveData}}

The final configuration is obtained by merging the configuration build from the macro parameters with the configuration read from the macro content, giving priority to the former (i.e., macro parameters overwrite the corresponding advanced configuration property).

Live Data Sources

A live data source is implemented as a component and provides both the data (the live data entries) and information on how to interact with the data (how to display and edit the properties of a live data entry).

Here's a list of known live data sources:

  • liveTable: exposes any live table results page as a live data source, allowing you to easily replace an existing live table with a live data instance.
  • in-line data: the data is specified within the macro content, using the advanced live data configuration

Live Table Results

The liveTable live data source allows us to reuse any live table results page as a live data source. Moreover, you can preview the live data widget on any page that has a live table by adding useLiveData=true to the URL query string.

Examples

  • Reuse the default live table results that correspond to a specified class name:
    {{liveData
      id="users"
      properties="_avatar,doc.name,first_name,last_name"
      source="liveTable"
      sourceParameters="className=XWiki.XWikiUsers&translationPrefix=xe.userdirectory."
    /}}
  • Result the default live table results that correspond to a specific class name, with a filter on a property:
    {{liveData
      id="users"
      properties="_avatar,doc.name,first_name,last_name"
      source="liveTable"
      sourceParameters="className=XWiki.XWikiUsers&translationPrefix=xe.userdirectory.&last_name=Doe"
    /}}
  • Reuse custom live table results page:
    {{liveData
      id="wikis"
      properties="wikiprettyname,description,owner,doc.creationDate"
      source="liveTable"
      sourceParameters="resultPage=WikiManager.WikisLiveTableResults&className=XWiki.XWikiServerClass&translationPrefix=platform.wiki.browse."
    /}}
  • Reuse custom live table results template:
    {{liveData
      id="groups"
      properties="name,members"
      source="liveTable"
      sourceParameters="template=getgroups.vm&translationPrefix=xe.admin.groups."
    /}}
  • Reuse custom live table results page with advanced configuration:
    {{liveData
      id="logging"
      properties="logger,level,actions"
      source="liveTable"
      sourceParameters="resultPage=XWiki.LoggingAdminTableJson&translationPrefix=logging.admin.livetable."
    }}{
      "meta": {
        "propertyDescriptors": [
          {
            "id": "actions",
            "displayer": "html",
            "sortable": false,
            "filterable": false
          }
        ]
      }
    }{{/liveData}}

Parameters

The liveTable live data source supports the following live table parameters:

  • className
  • resultPage
  • queryFilters (default value: currentlanguage,hidden)
    • see Query Module for more details on the filters
    • with those filters, the content is translated to the locale of the user and hidden pages are only displayed when Display hidden pages is set to Yes in the user preferences
  • translationPrefix

Besides these, the following custom parameters are also supported:

NameDescription
templateUse this in case you have a live table with results generated from a Velocity template. The value of this parameter should match the template specified in the url live table configuration using the xpage query string.

In-line Data

Please note that currently, In-Line Data does not support filtering, sorting, and pagination.

The live data can be specified in-line within the macro content, using the JSON format:

{{liveData
  id="movies"
  properties="title,genre,releaseDate,director"
}}{
  "data": {
    "count": 2,
    "entries": [
      {
        "title": "Meet John Doe",
        "url": "https://www.imdb.com/title/tt0033891/",
        "genre": ["Comedy", "Romance"],
        "releaseDate": -904615200,
        "director": "Frank Capra",
        "directorURL": "https://www.imdb.com/name/nm0001008/"
      },
      {
        "title": "Modern Times",
        "url": "https://www.imdb.com/title/tt0027977/",
        "genre": ["Comedy", "Drama"],
        "releaseDate": -1068256800,
        "director": "Charlie Chaplin",
        "directorURL": "https://www.imdb.com/name/nm0000122/"
      }
    ]
  },
  "meta": {
    "propertyDescriptors": [
      {
        "id": "title",
        "name": "Title",
        "visible": true,
        "displayer": {"id": "link", "propertyHref": "url"}
      },
      {
        "id": "genre",
        "name": "Genre",
        "visible": true
      },
      {
        "id": "releaseDate",
        "name": "Release Date",
        "visible": true,
        "displayer": "date"
      },
      {
        "id": "director",
        "name": "Director",
        "visible": true,
        "displayer": {"id": "link", "propertyHref": "directorURL"}
      }
    ]
  }
}{{/liveData}}

User Features

Layouts

The live data can be displayed using multiple layouts. The following layouts are currently supported:

NameDescriptionSince
tableDisplays the live data entries using a table, allowing the user to sort and filter from the table header. 
cardsDisplays the live data entries using cards arranged in a grid. Sorting and filtering is performed through dedicated panels that can be accessed from the live data menu. 

Table

Displays the live data entries using a table, allowing the user to sort and filter from the table header. The user can also change the column (property) order and resize the columns with drag and drop or keyboard arrows. To reset the column width you can double click on the resize handler that is visible when hovering the column header, or press Escape when focusing it.

liveData-table.png

This layout is responsive on small screens.

liveData-table-responsive.png

When the Live Data is empty, a message is displayed at the bottom.

table-no-entries.png

Cards

Displays the live data entries using cards arranged in a grid. Sorting and filtering is performed through dedicated panels that can be accessed from the live data menu. The user can change the property order using drag & drop.

liveData-cards.png

Properties Panel

The Properties panel, accessible from the hamburger menu on the right, allows you to hide / show live data properties and to change their order with drag & drop. Changing the property order is also possible directly from the Table and Cards layouts using the 3-dot icon that is visible when hovering the property name (column name).

liveData-panel-properties.png

When the Live Data is empty, a message is displayed at the bottom.

cards-no-entries.png

Sort Panel

The Sort panel allows you to sort on multiple properties, provided the properties are sortable and provided the live data source supports sorting on single or multiple properties. Some layouts, like the Table layout, might have shortcuts for sorting (e.g., click on the column header).

liveData-panel-sort.png

Filter Panel

The Filter panel allows you to filter on multiple properties. For each property you can add multiple constraints, provided the property is filterable (as indicated by the live data configuration). Some layouts, like the Table layout, might have shortcuts for filtering (e.g., the filter row below the table header). The filter input (widget) depends on the property type and on the constraint operator. The following filter widgets are currently supported: text, number, list (suggest), date, boolean.

liveData-panel-filter.png

In-place Edit

When the live data is editable (i.e., if the live data source supports updating the data and the current user is allowed to make changes), double-clicking on a property value (e.g., on a table cell in the table layout) will switch that property to edit mode. The displayed property value is then replaced by a form, allowing you to update the property value. Pressing the escape key will cancel the modification and switch back to display value. Clicking outside the edited content will save the modifications and reload the live data to reflect the change.

When a property of a Live Data is editable (i.e., if the Live Data source supports updating the data and the current user is allowed to make changes), hovering on an editable property value (e.g., on a cell in the table layout) shows a popover containing an edit action button. Clicking on this button switches the property to edit mode, displaying a form allowing to update the property value in-place. Pressing the escape key will cancel the modification and switch back to display value. Clicking outside the edited content will save the modifications and reload the live data to reflect the change.
On touch-based devices, the popover is displayed when touching an editable property. In addition, touching a link contained on any Live Data property shows the popover too, with a follow link action (when touching a link inside an editable property, both actions are proposed in the popover).

Selection

When selection is enabled, the user can select live data entries.

liveData-table-selection.png

Persistent State

The current page URL is updated whenever the live data state changes through pagination, sorting, filtering, hiding or re-ordering properties, etc. which means the live data state is bookmarkable. The live data state is preserved if you reload the page or if you give the page URL to someone else.

Macro Configuration guide

Actions

Allow an action

The action descriptors have a allowProperty field. This field references another property of the entry, holding a boolean value indicating whether the action is allowed for the current entry.
See the example below for an action A with an allow property allowA.

{
 "data": {
   "count": 2,
   "entries": [
      {
       "title": "Allowed Entry",
       "allowA": true
      },
      {
       "title": "Disallowed Entry",
       "allowA": false
      }
    ]
  },
 "meta": {
   "propertyDescriptors": [
      {
       "id": "title",
       "type": "String"
      },
      {
       "id": "allowA",
       "type": "Boolean"
      },
      {
       "id": "_actions",
       "name": "_actions",
       "visible": true,
       "displayer": { "id": "actions", "actions": ["actionA"] }
      }
    ],
   "entryDescriptor": {
     "idProperty": "title"
    },
   "actions": [
      {
       "id": "actionA",
       "name": "Action A",
       "description": "Perform action A",
       "allowProperty": "allowA"
      }
    ]
  }
}

Developer Features

Live Data JSON

The following JSON configuration is supported:

{
 //
 // The query
 //

 "query": {
   // The list of properties to fetch.
   "properties": ["title", "year", ...],

   "source": {
     // The component hint of the live data source
     "id": "...",

     // Parameters specific to each live data source implementation. This can also be used to implement hidden filters, that the user cannot change from the live data UI.
     "customParam1": "...",
      ...
    },

   // Filter the live data entries.
   "filters": [
      {
       "property": "title",
       "matchAll": true,
       "constraints": [
          {"operator": "contains", "value": "help"},
        ],
      },
    ],

   // The list of properties to sort on.
   "sort": [
      {
       "property": "birthdate",
       "descending": false
      },
    ],

   // Indicates where the current page starts.
   "offset": 0,

   // The number of entries to fetch (the page size).
   "limit": 10
  },

 //
 // The data
 //

 "data": {
   // The total number of entries available (on the server side).
   "count": 54,

   "entries": [
      {
       // property: value
       "title": "Work from home",
       "year": 2020,
        ...
      },
      ...
    ],
  },

 //
 // The meta data (used to control how we interact with the data)
 //

 "meta": {
   "defaultLayout": "table",

   "layouts": [
      {
       "id": "table",
       "name": "Table",
       "icon": {"iconSetName": "Font Awesome", "cssClass": "fa fa-table"},
      },
      {
       "id": "cards",
       "name": "Cards",
       "icon": {"iconSetName": "Font Awesome", "cssClass": "fa fa-th"},
       "titleProperty": "doc_title",
      },
    ],

   // Describes the properties that may appear in the data set. This determines the list of known (available)
   // properties. Creating new properties, removing existing properties as well as editing the property descriptor
   // should be done through this array.
   "propertyDescriptors": [
      {
       // Identifies the property that this descriptor corresponds to.
       "id": "title",

       // The property name. Could be displayed before the property value.
       "name": "Title",

       // Could be displayed when hovering the property name.
       "description": "...",

       // Could be displayed before the property name, if specified.
       "icon": {...},

       // The property type, selected when creating the property. It is used to prefill the property descriptor.
       // Could be mapped to an xclass property type.
       "type": "String",

       // Whether the user can sort on this property or not.
       "sortable": true,

       // Whether to user can edit in-place this property (the user also need to have the right to edit a given entry to edit it).
       "editable": true,

       // Whether to show this property or not.
       "visible": true,

       // Displayer configuration.
       "displayer": {
         "id": "link",

         // This is used only by the 'link' displayer (which receives the live data entry and the property descriptor).
         "linkType": "...",
         
         // Whether to allow HTML in the link content, used by the 'link' displayer
         "html": true
        },

       // Whether the user can filter by this property or not.
       "filterable": true,

       // Filter configuration.
       "filter": {
         "id": "text",

         // This is used only by the 'text' filter (which receives the property descriptor).
         "match": "prefix"
        },

       // Optional CSS class name to add to the HTML element used to display this property.
       "styleName": "..."
      }
    ],

   // The list of known property types. When creating a new property the user can select from this list and the
   // property descriptor will be prefilled based on the selected property type.
   "propertyTypes": [
      {"id": "string", "name": "String", "icon": {...}, "sortable": true, "displayer": {...}, "filterable": true, "filter": {...}},
      ...
    ],

   "defaultFilter": "text",

   // The list of known filters to choose from when editing the property descriptor.
   "filters": [
      {"id": "text", ...},
      {"id": "date", ...},
      {"id": "list", ...},
      {"id": "number", ...},
      {"id": "boolean", ...},
      ...
    ],

   // The list of known property displayers to choose from when editing the property descriptor.
   "displayers": [
      {"id": "text", ...},
      {"id": "html", ...},
      {"id": "link", ...},
      {"id": "actions", ...},
      {"id": "date", ...},
      {"id": "boolean", ...},
      {"id": "number", ...},
      {"id": "docTitle", ...},
      {"id": "xObjectProperty", ...},
      ...
    ],

   "defaultDisplayer": "text",

   // Configure the pagination display.
   "pagination": {
     // The maximum number of page links to display in the pagination.
     "maxShownPages": 10,
     "pageSizes": [15, 25, 50, 100],
     "showEntryRange": true,
     "showNextPrevious": true,
     "showFirstLast": false,
     "showPageSizeDropdown": false,
    },

   "entryDescriptor": {
     // The property that can be used to identify a live data entry. This is used for entry selection.
     "idProperty": "doc.fullName",
    },

   // The list of actions known / supported by this live data instance. This is used by the actions displayer.
   "actions": [
      {
       "id": "view",
       "name": "View",
       "description": "View entry",
       "icon": {...},
       "allowProperty": "doc.viewable",
       "urlProperty": "doc.url"
      },
     // Specify the edit action with an allowProperty to control who can in-place edit the entries
     // By default, the action is allowed.
     {
       "id": "edit",
       "allowProperty": "doc.editable"
      }
      ...
    ],

   // Selection configuration.
   "selection": {
     // Whether to enable or not the entry selection (e.g. for batch actions).
     "enabled": false
    }
  }
}

Script Service

/**
 * Scripting APIs for the Live Data component.
 *
 * @since 12.10
 */

@Component
@Named("liveData")
@Singleton
public class LiveDataScriptService implements ScriptService
{
   /**
     * Executes a live data query.
     *
     * @param queryConfig the live data query configuration
     * @return the live data entries that match the given query
     */

   public LiveData query(Map<String, Object> queryConfig)
   {
       // ...
   }

   /**
     * Executes a live data query.
     *
     * @param queryConfigJSON the live data query configuration
     * @return the live data entries that match the given query
     */

   public LiveData query(String queryConfigJSON)
   {
       // ...
   }

   /**
     * Computes the effective live data configuration by normalizing the given configuration (i.e. transforming it to
     * match the format expected by the live data widget) and adding the (missing) default values.
     *
     * @param liveDataConfig the live data configuration to start with
     * @return the effective live data configuration, using the standard format and containing the default values
     */

   public Map<String, Object> effectiveConfig(Map<String, Object> liveDataConfig)
   {
       // ...
   }

   /**
     * Computes the effective live data configuration by normalizing the given configuration (i.e. transforming it to
     * match the format expected by the live data widget) and adding the (missing) default values.
     *
     * @param liveDataConfigJSON the live data configuration to start with
     * @return the effective live data configuration, using the standard format and containing the default values
     */

   public String effectiveConfig(String liveDataConfigJSON)
   {
       // ...
   }

   /**
     * Execute the Live Data and return a {@link Block}.
     *
     * @param parameters the parameters to pass to the Live Data renderer
     * @return the Live Data {@link Block}
     * @throws LiveDataException in case of error when rendering the Live Data
     * @since 16.0.0RC1
     */

   @Unstable
   public Block execute(Map<String, Object> parameters) throws LiveDataException
   {
       // ...
   }

   /**
     * Execute the Live Data and return a {@link Block}.
     *
     * @param parameters the parameters to pass to the Live Data renderer
     * @param advancedParameters the advanced parameters to pass to the Live Data renderer
     * @return the Live Data {@link Block}
     * @throws LiveDataException in case of error when rendering the Live Data
     * @since 16.0.0RC1
     */

   @Unstable
   public Block execute(Map<String, Object> parameters, Map advancedParameters) throws LiveDataException
   {
       // ...
   }

   /**
     * Renders a Live Data.
     *
     * @param parameters the parameters to pass to the Live Data executor
     * @return the result of {@link #execute(Map)} in the current syntax
     * @throws LiveDataException in case of error when rendering the Live Data
     * @since 16.0.0RC1
     */

   @Unstable
   public String render(Map<String, Object> parameters) throws LiveDataException
   {
       // ...
   }

   /**
     * Renders a Live Data.
     *
     * @param parameters the parameters to pass to the Live Data executor
     * @param advancedParameters the advanced parameters to pass to the Live Data executor
     * @return the result of {@link #execute(Map, Map)} in the current syntax
     * @throws LiveDataException in case of error when rendering the Live Data
     * @since 16.0.0RC1
     */

   @Unstable
   public String render(Map<String, Object> parameters, Map advancedParameters) throws LiveDataException
   {
       // ...
   }
}

JavaScript

JavaScript API

XWiki 14.4+ 

Live Data provides a JavaScript API that is accessible on the container of the instance via the jQuery data-property liveData (e.g., jQuery("#wikis").data("liveData")) and in Live Data events.

getLayoutIds

Returns the list of available layouts ids.

Example:

jQuery("#myld").data("liveData").getLayoutIds();

getPropertyDescriptors

Get the list of property descriptors.

Example:

jQuery("#myld").data("liveData").getPropertyDescriptors();

updateEntries

Reload the Live Data according to its current filters and sort configuration.

Example:

jQuery("#myld").data("liveData").updateEntries();

Adding a Panel

It is possible to extend Live Data by adding new panels that can be toggled via the menu via the registerPanel method, the following demonstrates a simple "Hello World"-panel:

document.addEventListener('xwiki:livedata:instanceCreated', function(e) {
 const panel = {
    id: 'myExtension',
    name: 'My Extension',
    title: 'Hello World',
    icon: 'camera',
    container: document.createElement('div'),
    component: 'LiveDataAdvancedPanelExtension',
    order: 4000
  };

  panel.container.textContent = 'Hello World!';

  e.detail.livedata.registerPanel(panel);
});

The id must be unique among all panels, the name is the text that is displayed in the menu, the title the text that is displayed in the title of the panel. The icon is displayed in the menu and the panel's title. The order is the display order for the panels, the default properties, sort and filter panels have order 1000, 2000 and 3000. The container must be a DOM node, it is automatically attached to the panel's body when it is not collapsed, when the panel is collapsed the container will be detached. The component should be LiveDataAdvancedPanelExtension for the supported API but in theory could be any Vue component. Note, however, that while Live Data is currently based on Vue 2 it will be upgraded to Vue 3 at some point which might break custom components, the component's API isn't considered stable. All properties of the panel must be present when it is registered but, apart from the order, they can be changed later, changes will be reflected in the UI. This could be used, e.g., to add and update a counter in the name or title.

Async Actions

XWiki 16.2.0+ 

Is it possible to define an action as asynchronous. In this case, instead of following the link, the action will be performed in the background. The Live Data is refreshed once the asynchronous action is successful.

Parameters

The parameters are to be added to the async key of the action, located in the meta.actions array. The URL is the one resolved through the urlProperty of the action.

  • httpMethod: the HTTP method to use to call the action URL
  • loadingMessage: the localized message to display while the action is running asynchronously
  • successMessage: the localized message to display once the action finished successfully
  • failureMessage: the localized message to display once the action finished unsuccessfully
  • body: (optional) a value to use in the request body
  • headers (optional) a map of headers to use for the request

Example

{{liveData
  id="test"
  properties="name,_actions"
  source="liveTable"  sourceParameters="className=Space.MyClass"
}}{
  "meta": {
    "actions": [{
        "id": "delete",
        "async": {
          "httpMethod": "POST",
          "loadingMessage": "Loading",
          "successMessage": "Delete Success",
          "failureMessage": "Failed",
          "body": "newBacklinkTarget=&updateLinks=false&autoRedirect=false&form_token=${services.csrf.token}&confirm=1&async=true",
          "headers": {
            "Content-Type": "application/x-www-form-urlencoded"
          }
        }
      }]
  }
}
{{/liveData}}

Colored icons

XWiki 16.3.0+ 

Is is possible to add additional classes on icons (in addition to the one possible introduced by the icon sets) by adding the extraIconClasses property on icon descriptors.

On the example below, we add the tex-danger class on the delete action, making the delete action icon displayed in the danger color (i.e., red by default).
{"id": "delete", "icon": "cross", "extraIconClasses": "text-danger"}

Html Structure

This section lists interesting attributes that are useful to perform preciser selection of Live Data elements while staying agnostic of a given layout.

  • XWiki 16.2.0+ data-livedata-property-id attribute added on all property displayers. Contains the ID of the displayer.
  • XWiki 15.10.1+ data-livedata-entry-index attribute added to the root element of an entry. Contains the index of the displayed entry for the current pagination.
  • XWiki 15.10.1+ data-livedata-entry-id# attribute added to the root element of an entry. Contains the value of the identifier of the displayed entry.

Troubleshooting

Live Table Source

The live table results page used by the liveTable data source supports the sql request parameter that can be used to include in the JSON response the SQL performed on the database and its parameters. This can be useful when debugging problems with the Live Table Macro but unfortunately it can't be used (directly) to debug Live Data problems. You can obtain the same information though from a Live Data instance that uses the liveTable source, with a bit of tweaking:

  • First, obtain the URL used to fetch the live data entries, by inspecting the web page and looking at the Network tab from the browser's developer tools. Filter for /entries
  • Open the URL in a new browser tab and add this to the query string: &sourceParams.sql=1
  • Then edit XWiki.LiveTableResultsMacros and look for request.sql
  • Add these two lines:
    #set ($discard = $services.logging.getLogger('XWiki.LiveTableResultsMacros').debug("SQL: $sql"))
    #set ($discard = $services.logging.getLogger('XWiki.LiveTableResultsMacros').debug("SQL parameters: $sqlParams"))
  • Reload the live data entries URL and then go to the Logging administration section. Search for the "LiveTableResultsMacros" logger and set debug level.
  • Reload again the live data entries URL. The server logs should contain something like this:
    DEBUG X.LiveTableResultsMacros       - SQL:   where 1=1    order by lower(doc.fullName) asc, doc.fullName asc
    DEBUG X.LiveTableResultsMacros       - SQL parameters: {}

Prerequisites & Installation Instructions

We recommend using the Extension Manager to install this extension (Make sure that the text "Installable with the Extension Manager" is displayed at the top right location on this page to know if this extension can be installed with the Extension Manager).

You can also use the manual method which involves dropping the JAR file and all its dependencies into the WEB-INF/lib folder and restarting XWiki.

Dependencies

Dependencies for this extension (org.xwiki.platform:xwiki-platform-livedata-macro 16.3.0):

Get Connected