Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Current »

UCP for FreePBX 14 changed substantially. This is a working list of all the functions/methods and calls that can be used in UCP 14 from any module.

File Structure

The file structure of a UCP module is very similar to that of FreePBX. Firstly create a UCP directory under your module. In there you'll need to create a new class called: Rawname.class.php

There should also be an assets folder, within the assets folder you can have images, css, less and js.

Any javascript files placed in the js folder will be included globally in all of UCP so be careful of conflicts with other libraries!!

Less and CSS will be merged and compiled on the fly. Less is also global so make sure that you aren't rewriting the entire interface which may cause issues for other modules. It's best to use CSS selectors in LESS files or CSS to contain your styling changes to your module only:

.grid-stack-item[data-rawname=conferencespro] {

PHP Module Methods

Method

Description

Returns

Developing for UCP 14+#getSimpleWidgetList

Widgets to add to left bar

array

Developing for UCP 14+#getWidgetList

Widgets for page

array

Developing for UCP 14+#getSimpleWidgetDisplay

Get Simple Widget Display Content

array

Developing for UCP 14+#getWidgetDisplay

Get Widget display content

array

Developing for UCP 14+#getWidgetSettingsDisplay

Get widget settings display

array

Developing for UCP 14+#getSimpleWidgetSettingsDisplay

Get simple widget settings display

array

Developing for UCP 14+#getUserSettingsDisplay

Display a tab in the user settings modal

array

Developing for UCP 14+#poll

Used to send and get information to/from the UCP interface. On a 5 second interval

mixed

Developing for UCP 14+#getChatHistory

Used to get chat history when a new chat window is created

array

getSimpleWidgetList

Get Side Bar Widgets List. This will group all widgets for this module's sidebar under the module's group.

Generates:

 

image2017-1-24 16_15_7.png

Code:

/**
 * Get Simple Widget List
 * @method getSimpleWidgetList
 * @return array               Array of information
 */
function getSimpleWidgetList() {
    return array(
        "rawname" => "findmefollow",
        "display" => _("Find Me / Follow Me"),
        "icon" => "fa fa-binoculars",
        "list" => array(
            "1000" => array(
                "display" => "Dev4",
            )
        )
    );
}

Array:

Key

Type

Required

Notes

 

 

rawname

string

Yes

module rawname

 

 

display

string

Yes

The Widget Main Title

 

 

icon

string

Yes

The Widget Icon

 

 

list

array

Yes

List of Widgets this module provides

 

 

|__(ID)__>

Key

Type

Keys

Required

Notes

 

display

string

 

Yes

The widget sub title

description

string

No

The widget description

 

hasSettings

boolean

 

No

Set to true if this widget has settings. This will make the cog (gear) icon display on the widget display

 

icon

string

 

No

If set the widget in on the side bar will use this icon instead of the main icon

 

dynamic

boolean

 

No

If set to true then this widget can be added multiple times

getWidgetList

Get the Widget List provided by this module. Grouped by this module.

Generates:

image2017-1-24 15_33_51.png

Code:

/**
 * Get Widget List
 * @method getWidgetList
 * @return array               Array of information
 */
function getWidgetList() {
    return array(
        "rawname" => "findmefollow",
        "display" => _("Find Me / Follow Me"),
        "icon" => "fa fa-binoculars",
        "list" => array(
            "1000" => array(
                "display" => "Dev4",
                "hasSettings" => true,
                "defaultsize" => array("height" => 2, "width" => 3)
            )
        )
    );
}

Array:

Key

Type

Required

Notes

 

 

rawname

string

Yes

module rawname

 

 

display

string

Yes

The Widget Main Title

 

 

icon

string

Yes

The Widget Icon

 

 

list

array

Yes

List of Widgets this module provides

 

 

|__(ID)__>

Key

Type

Keys

Required

Notes

 

display

string

 

Yes

The widget sub title

description

string

No

The widget description

 

hasSettings

boolean

 

No

Set to true if this widget has settings. This will make the cog (gear) icon display on the widget

 

defaultsize

array

height, width

Yes

The default size of the widget when placed in the dashboard

 

minsize

array

height, width

No

The minimum size a widget can be when resized on the dashboard

 

maxsize

array

height, width

No

The max size a widget can be when resized on the dashboard

 

noresize

boolean

 

No

If set to true the widget will not be allowed to be resized

 

icon

string

 

No

If set the widget in the dashboard will use this icon instead of the main icon

 

dynamic

boolean

 

No

If set to true this widget can be added multiple times

noload

boolean

No

If set to true then this widget will not load html through ajax


getSimpleWidgetDisplay

Get the Side Bar Widget Display.

Generates:

image2017-1-24 15_32_25.png

 

image2017-1-24 15_31_44.png

Code:

/**
 * Get Simple Widget Display
 * @method getWidgetDisplay
 * @param  string           $id The widget id. This is the key of the 'list' array in getSimpleWidgetList
 * @param  string           $uuid The UUID of the widget
 * @return array               Array of information
 */
public function getSimpleWidgetDisplay($id,$uuid) {
    $displayvars = array();
    return array(
        'title' => _("Follow Me"),
        'html' => $this->load_view(__DIR__.'/views/widget.php',$displayvars)
    );
}

Array:

Key

Type

Required

Notes

title

string

Yes

Not Used at this time but still required (will be soon)

html

string

Yes

The widget HTML

getWidgetDisplay

This returns the HTML that will be displayed within the widget. The cog (Gear) will only display if getWidgetList stated there were settings available through "hasSettings" 

Generates:

image2017-1-24 16_51_48.png

Code:

/**
 * Get Widget Display
 * @method getWidgetDisplay
 * @param  string           $id The widget id. This is the key of the 'list' array in getWidgetList
 * @param  string           $uuid The UUID of the widget
 * @return array               Array of information
 */
public function getWidgetDisplay($id, $uuid) {
    $displayvars = array();
    return array(
        'title' => _("Follow Me"),
        'html' => $this->load_view(__DIR__.'/views/widget.php',$displayvars)
    );
}

Array:

Key

Type

Required

Notes

title

string

Yes

Not Used at this time but still required (will be soon)

html

string

Yes

The widget HTML

getWidgetSettingsDisplay

This returns the HTML to be displayed when the user hits the cog (gear) icon on the widget title bar.

Generates:

image2017-1-24 16_57_7.png

Code:

/**
 * Get Widget Settings Display
 * @method getWidgetDisplay
 * @param  string           $id The widget id. This is the key of the 'list' array in getWidgetList
 * @param  string           $uuid The UUID of the widget
 * @return array               Array of information
 */
public function getWidgetSettingsDisplay($id, $uuid) {
    $displayvars = array();
    return array(
        'title' => _("Follow Me"),
        'html' => $this->load_view(__DIR__.'/views/widget.php',$displayvars)
    );
}

Array:

Key

Type

Required

Notes

title

string

Yes

Not Used at this time but still required (will be soon)

html

string

Yes

The widget HTML

getSimpleWidgetSettingsDisplay

This returns the HTML to be displayed when the user hits the cog (gear) icon in the side bar.

Generates:

image2017-1-24 16_57_7 (1).png

Code:

/**
 * Get Simple Widget Settings Display
 * @method getSimpleWidgetDisplay
 * @param  string           $id The widget id. This is the key of the 'list' array in getWidgetList
 * @param  string           $uuid The UUID of the widget
 * @return array               Array of information
 */
public function getSimpleWidgetSettingsDisplay($id, $uuid) {
    $displayvars = array();
    return array(
        'title' => _("Follow Me"),
        'html' => $this->load_view(__DIR__.'/views/widget.php',$displayvars)
    );
}

Array:

Key

Type

Required

Notes

title

string

Yes

Not Used at this time but still required (will be soon)

html

string

Yes

The widget HTML

getUserSettingsDisplay

Get user settings display. When the modal has finished loading Developing for UCP 14+#post-body.simplewidgetsettings is triggered with a widget_id of 'settings' and a widget_type_id of null

The return array is nested so that you can have multiple views from one module

Generates:

 

image2017-2-15 13_37_41.png

Code:

/**
 * Display a Tab in the user settings modal
 * @method getUserSettingsDisplay
 * @return array               Array of information
 */
function getUserSettingsDisplay() {
    return array(
        array(
            "rawname" => "zulu",
            "name" => _("Zulu"),
            "html" => $this->load_view(__DIR__.'/views/zulu.php',$vars)
        )
    );
}

Array:

Key

Type

Required

Notes

rawname

string

Yes

module rawname

name

string

Yes

The Tab's Title

html

string

Yes

HTML for the tab

poll (PHP)

Used to send and get information to/from the UCP interface. On a 5 second interval. This would be the PHP side. Make sure to utilize the javascript side as well

/**
 * Poll for information
 * @method poll
 * @param array     $data     Data from Javascript prepoll function (if any)
 * @return mixed              Data you'd like to send back to the javascript for this module
 */
function poll($data) {
    return array("data" => "goes here");
}

getChatHistory

Returns an array of chat history to load when a new chat window is created in UCP

Code:
/**
 * Get Chat History
 * @method getChatHistory
 * @param string      $from       The 'from' set by UCP.addChat javascript function
 * @param string      $to         The 'to' set by UCP.addChat javascript function
 * @param boolean     $newWindow  Is this a new window? (true or false)
 * @return array                  Array of information
 */
function getChatHistory($from, $to, $newWindow) {
    $final = array();
    $final['messages'][] = array(
        'id' => 'msgid',
        'from' => 'from',
        'message' => $body,
        'date' => '1517696178',
        'direction' => 'in'
    );
    reutrn array('messages' => $final, 'lastMessage' => $final['messages'][0]);
}

Javascript Module Methods

All of these methods should be in your module's javascript class definition, class definitions should be <modulerawname>C. Thus "findmefollow" turns into "FindmefollowC":

var FindmefollowC = UCPMC.extend({
    init: function(){
         
    },
    displayWidget: function(widget_id,dashboard_id) {

All of these methods will only execute for the widget's parent module.

Method

Description

Developing for UCP 14+#addSimpleWidget

Triggered when a module has been added to the sidebar

Developing for UCP 14+#displayWidget

Triggered when a module's widget is displayed

Developing for UCP 14+#displaySimpleWidget

Triggered when a module's simple display finishes

Developing for UCP 14+#displayWidgetSettings

Triggered when a module's widget settings are displayed

Developing for UCP 14+#displaySimpleWidgetSettings

Triggered when a module's simple widget settings are displayed

Developing for UCP 14+#showDashboard

Triggered when a dashboard has finished loading

Developing for UCP 14+#resize

Triggered when a module's widget is resized

Developing for UCP 14+#windowState

Triggered when the browser window is hidden (Values: hidden, visible)

Developing for UCP 14+#prepoll

Used to generate data to send to the PHP poll function for this module

poll

Used to process data sent from the PHP poll function for this module

addSimpleWidget

This method is executed when the side bar widget has been added to the side bar. 

/**
 * Add Simple Widget
 * @method addSimpleWidget
 * @param  {string}      widget_id    The widget ID on the dashboard
 */
addSimpleWidget: function(widget_id) {

The widget id is UUID string. You can find your widget by running:

$(".custom-widget[data-widget_id='"+widget_id+"']")

displayWidget

This method is executed when the side bar widget has finished loading. 

/**
 * Display Widget
 * @method displayWidget
 * @param  {string}      widget_id    The widget ID on the dashboard
 * @param  {string}      dashboard_id The dashboard ID
 */
displayWidget: function(widget_id,dashboard_id) {

The widget id is UUID string. You can find your widget by running:

$(".grid-stack-item[data-id='"+widget_id+"']")

displaySimpleWidget

This method is executed after the side bar widget has been clicked and the window has fully extended has finished loading. 

/**
 * Display Side Bar Widget
 * @method displaySimpleWidget
 * @param  {string}            widget_id The widget id in the sidebar
 */
displaySimpleWidget: function(widget_id) {

The widget ID is a UUID string. You can find your side bar widget by running:

$(".widget-extra-menu[data-id="+widget_id+"]")

displayWidgetSettings

This method is executed when the settings window has finished loading.

/**
 * Display Widget Settings
 * @method displayWidgetSettings
 * @param  {string}      widget_id    The widget ID on the dashboard
 * @param  {string}      dashboard_id The dashboard ID
 */
displayWidgetSettings: function(widget_id, dashboard_id) {

The widget ID is a UUID string. You can find the widget settings by running:

$("#widget_settings .widget-settings-content")

displaySimpleWidgetSettings

This method is executed when the settings window has finished loading.

/**
 * Display Simple Widget Settings
 * @method displaySimpleWidgetSettings
 * @param  {string}      widget_id    The widget ID on the sidebar
 */
displaySimpleWidgetSettings: function(widget_id) {

The widget ID is a UUID string. You can find the widget settings by running:

$("#widget_settings .widget-settings-content")

showDashboard

This method is executed when the dashboard has finished loading

/**
 * When the dashboard is displayed and has finished loading
 * @method showDashboard
 * @param  {string}            dashboard_id The dashboard id
 */
showDashboard: function(dashboard_id) {

resize

The method is executed when the widget has been resized

/**
 * Resize Widget
 * @method resize
 * @param  {string}      widget_id    The widget ID on the dashboard
 * @param  {string}      dashboard_id The dashboard ID
 */
resize: function(widget_id,dashboard_id) {

The widget id is a UUID string. You can find your widget by running:

$(".grid-stack-item[data-id='"+widget_id+"']")

windowState

The method is executed when the tab in the browser (Or the browser itself) is brought into focus or out of focus

/**
 * Window State
 * @method windowState
 * @param  {string}      state    The window state. Can be "hidden" or "visible"
 */
windowState: function(state) {

prepoll

This method is used to populate data to send to the PHP poll function for this module

/**
 * Pre Poll (Before the poll)
 * @method prepoll
 * @return  {mixed}      Data to send back to the PHP poll function for this module
 */
prepoll: function() {

poll (Javascript)

This method is used to process data returned from the PHP poll function for this module

/**
 * Poll
 * @method prepoll
 * @param  {mixed}      data    Data returned from the PHP poll function for this module
 */
poll: function(data) {

UCP Javascript Events

Event

Description

Developing for UCP 14+#post-body.simplewidget

Triggered when the simple widget display finishes loading

Developing for UCP 14+#post-body.widgets

Triggered when all widgets on the active dashboard finish loading

Developing for UCP 14+#post-body.widget-added

Triggered when a single widget is added

Developing for UCP 14+#post-body.widget-removed

Triggered when a single widget is removed

Developing for UCP 14+#post-body.widgetsettings

Triggered when the module settings window finishes loading

Developing for UCP 14+#post-body.simplewidgetsettings

Triggered when the simple settings window finishes loading

Developing for UCP 14+#logIn

Triggered when the user is "logged in" (after a login or a refresh if already logged in)

Developing for UCP 14+#logOut

Triggered when the user clicks log out

Developing for UCP 14+#chatWindowAdded

Triggered when a chat window has been added to the screen

Developing for UCP 14+#chatWindowRemoved

Triggered when a chat window has been removed from the screen

post-body.simplewidget

This trigger is executed after the side bar widget has been clicked and the window has fully extended has finished loading. 

$(document).on("post-body.simplewidget", function(event, widget_id) {
});

The widget ID is a UUID string. You can find your side bar widget by running:

$(".widget-extra-menu[data-id="+widget_id+"]")

post-body.widgets

This trigger is executed after all widgets have finished loading on a dashboard.

$(document).on("post-body.widgets", function(event, dashboard_id) {
});

post-body.widget-added

This trigger is executed after the widget has finished loading after being added on a dashboard.

$(document).on("post-body.widgets", function(event, widget_id, dashboard_id) {
});

The widget id is a UUID string. You can find your widget by running:

$(".grid-stack-item[data-id='"+widget_id+"']")

post-body.widget-removed

This trigger is executed after a widget has been removed from a dashboard

$(document).on("post-body.widget-removed", function(event, widget, dashboard_id) {
});

The widget is the jquery object of the element before it was deleted

post-body.widgetsettings

This trigger is executed after the html has finished loading inside a settings modal

$(document).on("post-body.widgetsettings", function(event, widget_id, dashboard_id) {
});

The widget id is a UUID string. You can find your widget by running:

$(".grid-stack-item[data-id='"+widget_id+"']")

post-body.simplewidgetsettings

This trigger is executed after the html has finished loading inside a settings modal

$(document).on("post-body.simplewidgetsettings", function(event, widget_id) {
});

The widget id is a UUID string. You can find your widget by running:

$(".widget-extra-menu[data-id='"+widget_id+"']")

logIn

This trigger is executed when the user logs in

$(document).on("logIn", function(event) {
});

logOut

This trigger is executed when the user logs out

$(document).on("logOut", function(event) {
});

chatWindowAdded

This is triggered when a new chat window has been added to the page

/**
 * @param {object} event     Jquery event object
 * @param {string} windowId  The chat window ID
 * @param {string} module    The chat window rawmodule name
 * @param {object} object    The chat window jquery object
 */
$(document).on("chatWindowAdded", function(event, windowId, module, object) {
});

chatWindowRemoved

This is triggered when a chat window has been removed from the page

/**
 * @param {object} event     Jquery event object
 * @param {string} windowId  The chat window ID
 */
$(document).on("chatWindowRemoved", function(event, windowId) {
 
});

Getting logged in User Information

To get logged in user information anywhere in your PHP script just run $this->user. All of the logged in user's information will be in this property as an array:

$this->user

Available Libraries

Javascript

PHP

Styling/Library Options

Bootstrap Tables

All available options can also be set through HTML5 data tags: http://bootstrap-table.wenzhixin.net.cn/documentation/

<table data-toggle="table">
image2017-1-17 18_2_0.png

Bootstrap Toggle

For checkboxes we recommend styling them using bootstrap toggle which is included

All available options can also be set through HTML5 data tags: http://www.bootstraptoggle.com/

<input type="checkbox" name="dndenable" data-toggle="toggle" data-on="<?php echo _("Enabled")?>" data-off="<?php echo _("Disabled")?>">
image2017-6-8_23-24-47.png

Bootstrap Select

We recommend using this method over multiselect because the dropdown will not get stuck inside of modal boxes or containers as the dropdown is attached to the entire document body.

All available options can also be set through HTML5 data tags: https://silviomoreto.github.io/bootstrap-select/options/

<select data-toggle="select" data-size="auto">
image2017-1-17 17_58_39.png

Bootstrap Multiselect

This is not a recommended way to show dropdowns in UCP but it is supported for legacy reasons. Options can not be set through HTML5 data tags and the drop down can get stuck in containers (see image below)

<select data-toggle="multiselect">
image2017-1-17 18_1_0.png

UCP Javascript Dialogs

Alerts

Alert styling uses http://getbootstrap.com/components/#alerts for colors. Using the global UCP.showAlert function is preferred over the standard javascript alert().

The ID for the global Alert Box is "alert_modal". You can use this in conjunction with bootstrap modal events or methods: http://getbootstrap.com/javascript/#modals

The callback function is executed when the window is displayed.

image2017-1-17 18_3_4.png

Function:

/**
 * Show Alert Modal Box
 * @method showAlert
 * @param  {string}  message       The HTML to show
 * @param  {string}  type          The alert info type
 * @param  {function}  callback_func Callback function when the alert is shown
 */
UCP.showAlert
function (message, type, callback_func)

Example:

UCP.showAlert('This is a danger Alert!','danger',function() {
    console.log("Alert is shown!");
});


Confirms

Confirm styling uses http://getbootstrap.com/components/#alerts for colors. Using the global UCP.showConfirm function is preferred over the standard javascript confirm().

The ID for the global Alert Box is "confirm_modal". You can use this in conjunction with bootstrap modal events or methods: http://getbootstrap.com/javascript/#modals

The callback function is only executed if the user hits accept

image2017-1-17 18_4_26.png

Function:

/**
 * Show Confirmation Modal Box
 * @method showConfirm
 * @param  {string}    html          The HTML to show
 * @param  {string}    type          The alert info type
 * @param  {function}    callback_func Callback function when the user presses accept
 */
UCP.showConfirm
function (html, type, callback_func) {

Example:

UCP.showConfirm('This is an info confirm','info',function() {
    console.log("User hit Accept");
});

Dialogs

The ID for the global Dialog Box is "globalModal". You can use this in conjunction with bootstrap modal events or methods: http://getbootstrap.com/javascript/#modals

 

image2017-1-24 15_48_12.png

Function:

/**
 * Show a global dialog box
 * @method showDialog
 * @param  {string}   title    The HTML title of the modal
 * @param  {string}   content  The HTML content of the modal
 * @param  {string}   footer   The HTML footer of the modal
 * @param  {Function} callback Callback function when the modal is displayed
 */
UCP.showDialog
function (title, content, footer, callback) {

Example:

UCP.showDialog("My Title","My Content","<button class='btn btn-primary'>My Button</button>",function() {
    console.log("Modal box is shown!");
});

UCP Javascript Date Methods

Additionally there are some new methods provided by UCP for date conversions from PHP timestamps to the user's date formats that have been set either in UCP or in the Administration panel. These methods should be used whenever parsing and displaying time/date based data is need

UCP.humanDiff

>UCP.humanDiff(1483573115)
"9 minutes ago"

UCP.dateTimeFormatter

>UCP.dateTimeFormatter(1483573115)
"Wed, Jan 4, 2017 3:38 PM"

UCP.timeFormatter

>UCP.timeFormatter(1483573115)
"3:38 PM"

UCP.dateFormatter

>UCP.dateFormatter(1483573115)
"1/4/2017"

UCP.durationFormatter

UCP.durationFormatter(45)
"45 seconds"

UCP Javascript Date/Time Specific Variables

>timezone
"America/Los_Angeles"
>language
"en_US"
>dateformat
"l"
>timeformat
"LT"
>datetimeformat
"llll"

UCP Javascript Utility Functions

UCP.parseUrl

Needs Documentation

UCP.callModuleByMethod

Needs Documentation

UCP.callModulesByMethod

Needs Documentation

UCP.validMethod

/**
 * Check if method exists in a module
 * @method validMethod
 * @param  {string}   title    The HTML title of the modal
 * @param  {string}   content  The HTML content of the modal
 * @return {boolean}  True if method exists
 */
UCP.validMethod
function (module, method) {

UCP Javascript Chat Functions Methods

UCP.addChat

Brings up a chat window, calling this method multiple times will add additional chat messages to the window utilizing the function UCP.addChatMessage

image2018-2-3_14-1-55.png

Function:

/**
 * Adds a chat window
 * @method addChat
 * @param  {module}   module         The module rawname that added the chat window (Ex Xmpp or Sms)
 * @param  {string}   id             The chat window ID, This should be unique!
 * @param  {string}   icon           Class name to be used for the chat window icon (usually in the form of 'fa fa-icon' from font awesome, can be custom css reference)
 * @param  {string}   from           Who the message is from
 * @param  {string}   to             Who the message is to
 * @param  {string}   title          Title of the chat window
 * @param  {string}   msgid          The message id (This should be unique inside of this window!)
 * @param  {string}   message        The body of the message
 * @param  {function} callback       Function that is called back once the window has loaded (no parameters)
 * @param  {boolean}  html           True Treat 'message' as HTML, if false then HTML will be stripped from the message
 * @param  {enum}     direction      The direction of the message, should be 'in' or 'out'
 */
UCP.addChat
addChat: function(module, id, icon, from, to, title, msgid, message, callback, html, direction) {

Example:

UCP.addChat('Rawmodule', 'windowid', 'fa fa-bus', 'from', 'to', 'title', 'msgid-1', 'This is an out message<b>bolded!</b>', function() {
    console.log("Chat Window has loaded!")
}, true, 'out')

UCP.addChatMessage

Adds a single message to an already existing chat window. The window must already exist utilizing UCP.addChat

image2018-2-3_14-4-29.png

/**
 * Adds a chat message to an already existing chat window (see UCP.addChat)
 * @method addChat
 * @param  {string}   id         The chat window ID, This should be unique!
 * @param  {string}   message    The body of the message
 * @param  {string}   msgid      The message id (This should be unique inside of this window!)
 * @param  {boolean}  newmsg     Colorize the chat window's title box to show there are new messages
 * @param  {boolean}  htmlv      True Treat 'message' as HTML, if false then HTML will be stripped from the message
 * @param  {enum}     direction  The direction of the message, should be 'in' or 'out'
 */
UCP.addChatMessage
function(id, msgid, message, newmsg, htmlV, direction) {

Example:

UCP.addChatMessage('windowid', 'msgid-2', 'This is an in message <b>bolded!</b>', true, true, 'in')

UCP.removeChat

Remove the chat window from display

/**
 * Removes a chat window
 * @method removeChat
 * @param  {string}   id         The chat window ID, This should be unique!
 */
UCP.removeChat
function(id) {

UCP PHP Chat functions

Code Snippets

  • No labels