Article Extending the Administration UI

Depending on the functionality that you are implementing, it may be necessary to expose some administrative options within the Administration UI of Verint Community. The Administration UI is completely defined using plugins so its easy to add to.

Types of administration UI extensions

The administration UI in Verint Community is used by moderators and administrators to configure global options and enable specific functionality.

It is built using three types of plugins:

  1. Categories. Categories are the top-level navigation items ("Activity Stream" in the left-most column above) within the administration UI and are used for categorizing panels, which implement specific functionality.
  2. Panels. Panels live within a category and are listed with other panels in the same category ("Options" in the second column above) and have a specific UI when selected (third column above)
  3. Plugin Type Panels. Plugin type panels are a special grouping of plugins of a specific type within a category ("Activity Story Types" in the second column above). Their UI (third column above) is defined by the platform and available plugin type editors.
  4. Plugin Type Editors. Plugin type editors are used by the platform to extend platform-defined editing of plugins within Plugin Type Panels. Plugin type editors get to add to the UI (third column above) when editing a plugin that matches certain criteria defined by the plugin type editor.
  5. Explicit Panels. Explicit panels are panels that are only accessible via a direct link. For example, editing a member in the administration UI is available in a few different panels and categories, but is implemented once by linking to the explicit panel that edits a member. Explicit panels are useful when the same functionality will need to be accessed from multiple locations.

Administration categories

To add a top-level category to the administration UI, a plugin implementing the IAdministrationPanelCategory interface must be defined and enabled. The IAdministrationPanelCategory interface is defined in the Telligent.Evolution.Extensibility.Administration.Version1 namespace of Telligent.Evolution.Core.dll. The following sample creates a category named "Category":

using System;
using Telligent.Evolution.Extensibility.Administration.Version1;

namespace Samples
{
	public class SampleAdministrationCategory : IAdministrationPanelCategory
	{
		private readonly Guid _id = new Guid("f3222021-a025-411a-a855-078fe5d1c15e");

		#region IPlugin

		// ...

		#endregion

		#region IAdministrationPanelCategory

		public Guid AdministrationPanelCategoryId { get { return _id; } }

		public string AvatarUrl { get { return null; } }

		public string CategoryName { get { return "Category"; } }

		public int? DisplayOrder { get { return null; } }

		#endregion
	}
}

The AdministrationPanelCategoryId should be a unique identifier used to uniquely reference this category. Note that an administration category without any panels will not render in the site. When it has panels that are accessible to a user, the panel will be shown in the administration UI:

Administration panels

To add a new administration panel to the administration UI, a plugin implementing the IAdministrationPanel interface must be defined and enabled. The IAdministrationPanel interface is defined in Telligent.Evolution.Core.dll. A very basic sample of a panel (added to the category created above) is below:

using System;
using Telligent.Evolution.Extensibility.Administration.Version1;

namespace Samples
{
	public class SampleAdministrationPanel : IAdministrationPanel
	{
		private readonly Guid _id = new Guid("4ddc50bb-76d0-49f5-ab7a-6466309e89dd");

		#region IPlugin

        // ...
		
		#endregion
		
		#region IAdministrationPanel

		public Guid PanelId { get { return _id; } }

		public string PanelName { get { return "Panel"; } }

		public string PanelDescription { get { return "Panel description."; } }

		public bool VaryCacheByUser { get { return false; } }

		public string GetViewHtml() { return "<p>Panel content</p>"; }

		public bool HasAccess(int userId) { return true; }

		public Guid AdministrationPanelCategoryId { get { return new Guid("f3222021-a025-411a-a855-078fe5d1c15e"); } }

		public string CssClass { get { return string.Empty; } }

		public int? DisplayOrder {  get { return null; } }

		public bool IsCacheable { get { return true; } }
		
		#endregion
	}
}

The PanelId should be a unique identifier for this panel. The AdministrationPanelCategoryId should reference the identifier of an administration category (the sample references the category we created above). The PanelName and PanelDescription properties identify the panel in the UI and the GetViewHtml() method returns the HTML that populates the large content area (3rd column) of the administration UI when this panel is active.

The full content of the panel could be defined in code, but all administration panels that are part of the platform make use of plugin-defined widgets.

When only a single panel is available within a category, the second level navigation is removed and navigating to the category or panel will render the single available panel. When more than a single panel is available to a user in a category, the second level navigation is shown and the top-most panel is selected when navigating into a category. When multiple panels are available and the category is selected, this sample panel will render as:

Plugin type panels

To add a list of plugins to an administration category by type, a plugin implementing the IPluginTypesAdministrationPanel interface should be defined and enabled. The IPluginTypesAdministrationPanel interface is defined in the Telligent.Evolution.Extensibility.Administration.Version1 namespace of Telligent.Evolution.Core.dll. A sample implementation that includes a list of content type plugins under the label "Plugin Types" is below:

using System;
using Telligent.Evolution.Extensibility.Administration.Version1;

namespace Samples
{
	public class SamplePluginTypesAdministrationPanel : IPluginTypesAdministrationPanel
	{
		#region IPlugin

		// ...

		#endregion

		public Guid AdministrationPanelCategoryId { get { return new Guid("f3222021-a025-411a-a855-078fe5d1c15e"); } }

		public string PluginTypesName { get { return "Plugin Types"; } }

		public IEnumerable<Type> PluginTypes { get { return new Type[] { typeof(Telligent.Evolution.Extensibility.Content.Version1.IContentType) }; } }

		public int? DisplayOrder { get { return null; } }
	}
}

The AdministrationPanelCategoryId property references the category into which this list of plugins should be added. The PluginTypesName property identifies the label to show above the list of plugins. The PluginTypes property lists the types of plugins that should be listed which can be interfaces or base or concrete classes. The platform renders the list of matching plugins which links to the platform-defined plugin editor.

The platform includes optimizations to provide filtering options when there are a reasonable number of matching plugins in the list and also collapses the list to appear like other panels when there is only one matching plugin.

The sample renders as:

Plugin type editors

The platform defines the UI for editing plugins within the administration UI. The plugin administration experience can be extended for specific types by defining and enabling plugins that implement the IPluginTypesEditor interface. The IPluginTypesEditor interface is defined in the Telligent.Evolution.Extensibility.Version1 namespace of Telligent.Components.dll. The sample implementation below adds a single named set of content ("Sample Types Editor") to the UI used to edit content types:

using System;
using Telligent.Evolution.Extensibility.Version1;

namespace Samples
{
	public class SamplePluginTypesEditor : IPluginTypesEditor
	{
		#region IPlugin

		// ...

		#endregion

		#region IPluginTypesEditor

		public IEnumerable<Type> PluginTypes {  get { return new Type[] { typeof(Telligent.Evolution.Extensibility.Content.Version1.IContentType) }; } }

		public string GetViewHtml(IPlugin plugin, string apiJson)
		{
			return string.Format(@"
<div id=""sample-plugin-type-editor"">
	Plugin type editor content for '{0}'
</div>
<script type=""text/javascript"">
	(function() {{
		var api = {1};
		api.registerContent({{
			name: 'Sample Type Editor',
			orderNumber: 0,
			selected: function() {{ $('#sample-plugin-type-editor').show(); }},
			unselected: function() {{ $('#sample-plugin-type-editor').hide(); }}
		}});
	}})();
</script>
",
	plugin.Name,
	apiJson);
		}

		#endregion
	}
}

The PluginTypes property identifies the types of plugins that this editor should be applied to and can be a list of interfaces and/or concrete classes. When a plugin matching the defined type is rendered within the administration UI, the plugin type editor's GetViewHtml() method is called and is provided with the current instance of the plugin being edited and a JSON API used to interact with the platform-defined plugin editing UI. The GetViewHtml() method should return the HTML and script required to implement the editor's behavior. In the sample above, it adds a named group of content, "Sample Type Editor", that contains the text "Plugin type editor content for " and the name name of the plugin being edited. 

The sample renders as:

The markup and script rendered by GetViewHtml() should interact with the JSON API provided. The API is a JSON object that contains the following members for use in defining the editor's UI and integrating with the platform plugin editor:

{
    registerSave: function(saveFunction) { /* ... */ },
    registerContent: function(contentDetails) { /* ... */ },
	validate: function() { /* forces all validation on the plugin process, returns true (valid) or false (invalid) */ }
}

Registering Content

The registerContent(contentDetails) function should be called to register conditionally rendered content (such as tabs). The contentDetails parameters is an object in the format:

{
    name: 'Name of the tab',
	orderNumber: 1, /* optional order for this UI compared to other UIs */
    selected: function() { /* called when the tab is selected */ },
    unselected: function() { /* called when the tab is unselected */ },
	validate: function() { /* called when tab should be validated, should return true (valid) or false (invalid) */ },
	actions: [
		{
			label: 'Label of the action',
			messageName: 'name of the message to publish when this action is invoked',
			messageData: 'data to provide when the message is published',
			show: /* either 'always' or 'contextually', default: 'contextually' */
		}
	]
}

Registering Saving

The registerSave(saveFunction) function should be called with a reference to a function of the custom UI that can be called to save the configuration form. The saveFunction parameter, when called by the platform, is provided a single object parameter with the following properties:

{
    success: function() { ... },
    error: function() { ... }
}

Forcing Validation

To force validation of all plugin type editors, the validate() method can be called.

Explicit administration panels

To create an administration panel that can be referenced from multiple other panels, an explicit administration panel can be defined. Explicit administration panels don't exist in the browseable hierarchy of the administration UI but can be opened by URL from any panel. To add an explicit administration panel, a plugin implementing the IAdministrationExplicitPanel interface can be defined and enabled. The IAdministrationExplicitPanel interface is defined in the Telligent.Evolution.Extensibility.Administration.Version1 namespace of Telligent.Evolution.Core.dll. A sample implementation is below:

using System;
using Telligent.Evolution.Extensibility.Administration.Version1;
using System.Collections.Specialized;
using System.Linq;

namespace Samples
{
	public class SampleExplicitAdministrationPanel : IAdministrationExplicitPanel
	{
		private readonly Guid _id = new Guid("cb7864ca-395e-49e0-b7b3-4bfece0f9db9");

		private IAdministrationExplicitPanelController _explicitPanelController;

		#region IPlugin

		// ...

		#endregion

		public void SetController(IAdministrationExplicitPanelController controller)
		{
			_explicitPanelController = controller;
		}

		public bool HasAccess(int userId, NameValueCollection parameters)
		{
			return true;
		}

		public string CssClass { get { return string.Empty; } }

		public int? DisplayOrder { get { return null; } }

		public bool IsCacheable { get { return true; } }

		public Guid PanelId { get { return _id; } }

		public bool VaryCacheByUser { get { return false; } }

		public string GetPanelName(NameValueCollection parameters)
		{
			return "Sample Explicit Panel";
		}

		public string GetPanelDescription(NameValueCollection parameters)
		{
			return "This is a sample explicit panel.";
		}

		public string GetViewHtml(NameValueCollection parameters)
		{
			if (parameters != null && parameters.Count > 0)
				return "<p>Sample explicit panel. Parameters: " + string.Join(", ", parameters.AllKeys.Select(k => k + " = " + parameters[k])) + "</p>";
			else
				return "<p>Sample explicit panel.</p>";
		}
	}
}

Explicit panels are provided with a controller via the SetController() method. This controller provides a method to generate a URL to this panel (with or without parameters). This URL can then be provided to other panels to enable them to generate links to this explicit panel and share its functionality. Most members are similar to the panel example above with a few exceptions:

  1. There is no categorization for an explicit panel. Explicit panels can be referenced from any administration panel or directly. When referenced from another administration panel, the selected category and non-explicit panel navigation is preserved and the explicit panel appears as an extension to the existing non-explicit panel. When referenced directly, no category is selected and the explicit panel will be loaded directly aside the category list.
  2. The panel name, description, and view HTML are provided parameters. GetPanelName(), GetPanelDescription() and GetViewHtml() are all provided a list of optional parameters that are passed from the URL generated from the controller. These parameters can be used to affect the naming and rendering of the panel.

If we added a link to this sample explicit panel from the sample panel created earlier (and the duplicate called "Panel 2'), clicking a link from "Panel" would result in:

Note that the sample category and panel are still highlighted and we can navigate back to the sample panel from the explicit panel.

If we linked to the same explicit panel from the "Panel 2" panel, it would look like:

And if we passed parameters when generating the URL, the explicit panel could retrieve and use those values: