Skip to content

How to Create a Widget v3

Jon Crain edited this page Dec 1, 2018 · 1 revision

To create a widget, you'll need to have some knowledge of how to compose a database query and how to render the results in html using the php scripting language.

Getting the Data

We'll use an example of a running count of WiFi states. For this we will use the following sql query:

SELECT COUNT(CASE WHEN state = 'running' THEN 1 END) AS connected,
COUNT(CASE WHEN state = 'init' THEN 1 END) AS on_not_connected,
COUNT(CASE WHEN state = 'sharing' THEN 1 END) AS sharing,
COUNT(CASE WHEN state = 'unknown' THEN 1 END) AS unknown,
COUNT(CASE WHEN state = 'off' THEN 1 END) AS off
FROM wifi

You can test this query against your munkireport database to see if it gives you the desired results. We want the computer_name, the pending installs and the serial_number (which we'll use for a link to the detail page).

Once you have created the query with the data that you want, you should create a function in the module_model.php to retrieve the data.

module_model.php:

public function get_wifi_state()
{
    $sql = "SELECT COUNT(CASE WHEN state = 'running' THEN 1 END) AS connected,
            COUNT(CASE WHEN state = 'init' THEN 1 END) AS on_not_connected,
            COUNT(CASE WHEN state = 'sharing' THEN 1 END) AS sharing,
            COUNT(CASE WHEN state = 'unknown' THEN 1 END) AS unknown,
            COUNT(CASE WHEN state = 'off' THEN 1 END) AS off
            FROM wifi
            LEFT JOIN reportdata USING(serial_number)
            ".get_machine_group_filter();
    return current($this->query($sql));
}

Note that we also join to reportdata so that if a Machine Group filter is being used, the widget will only report for the filtered machines.

Finally, we want to make this information available to our pages so we need to add another function to our module_controller.php.

module_controller.php:

public function get_wifi_state()
{
    $obj = new View();

    if (! $this->authorized()) {
        $obj->view('json', array('msg' => array('error' => 'Not authenticated')));
        return;
    }
    
    $wifi = new wifi_model;
    $obj->view('json', array('msg' =>$wifi->get_wifi_state()));
}

This function is creating a view for the data (you can see this raw data by loading http://munkireport/index.php?/module/name_of_module/name_of_function). It will look something like this:

{"connected":"209","on_not_connected":"3","sharing":"2","unknown":"0","off":"48"}

The View

The html

Layout is done with bootstrap, which is a web framework. Widgets are made with a couple of nested divs:

<div class="col-lg-4 col-md-6">
	<div class="panel panel-default" id="wifi-state-widget">
		<div class="panel-heading" data-container="body">
			<h3 class="panel-title"><i class="fa fa-wifi"></i>
			    <span data-i18n="wifi.state"></span>
                <list-link data-url="/show/listing/wifi/wifi"></list-link>
			</h3>
		</div>
		<div class="panel-body text-center"></div>
	</div><!-- /panel -->
</div><!-- /col -->
  • The first div determines the width of the widget. class="col-lg-4" means that the widget takes up 4 columns (out of 12) on a large screen.
  • The next div creates a panel (see https://getbootstrap.com/docs/3.3/components/#panels)
  • Inside the panel there's a heading and a body
  • the fa fa-wifi refers to the icon for wifi. Munkireport comes with the great icon font Font Awesome
  • <span data-i18n="wifi.state"></span> is used instead of hard coding the title for internationalization. See also Localizing. This will pull the title from the modules locales/en.proj json data. Please consider utilizing this for all UI text.

The script

The data is then loaded and parsed into the widget via javascript:

<script>
$(document).on('appUpdate', function(e, lang) {

    $.getJSON( appUrl + '/module/wifi/get_wifi_state', function( data ) {

    	if(data.error){
    		//alert(data.error);
    		return;
    	}
		
		var panel = $('#wifi-state-widget div.panel-body'),
			baseUrl = appUrl + '/show/listing/wifi/wifi';
		panel.empty();
		
		// Set statuses
		if(data.unknown != "0"){
		panel.append(' <a href="'+baseUrl+'#unknown" class="btn btn-info"><span class="bigger-150">'+data.unknown+'</span><br>'+i18n.t('unknown')+'</a>');
		}
		if(data.off != "0"){
			panel.append(' <a href="'+baseUrl+'#off" class="btn btn-danger"><span class="bigger-150">'+data.off+'</span><br>&nbsp;&nbsp;'+i18n.t('off')+'&nbsp;&nbsp;</a>');
		}
		if(data.on_not_connected != "0"){
			panel.append(' <a href="'+baseUrl+'#init" class="btn btn-warning"><span class="bigger-150">'+data.on_not_connected+'</span><br>&nbsp;&nbsp;'+i18n.t('on')+'&nbsp;&nbsp;</a>');
		}
		if(data.connected != "0"){
			panel.append(' <a href="'+baseUrl+'#running" class="btn btn-success"><span class="bigger-150">'+data.connected+'</span><br>'+i18n.t('connected')+'</a>');
		}
		if(data.sharing != "0"){
			panel.append(' <a href="'+baseUrl+'#sharing" class="btn btn-info"><span class="bigger-150">'+data.sharing+'</span><br>'+i18n.t('wifi.sharing')+'</a>');
		}
    });
});
</script>

This may seem like a lot, but is fairly straight forward. Compare a few modules and you will see very similar structure in all of them. Let breakdown one of these blocks:

		if(data.connected != "0"){
			panel.append(' <a href="'+baseUrl+'#running" class="btn btn-success"><span class="bigger-150">'+data.connected+'</span><br>'+i18n.t('connected')+'</a>');
		}
  • It is first checking to see whether to display a block for connected.
  • It is then appending the panel to include the information
  • <a href="'+baseUrl+'#running" class="btn btn-success"> is creating the link for the block.
  • <span class="bigger-150"> is creating the style of the block.
  • data.connected is the actual data (as seen on the http://munkireport/index.php?/module/name_of_module/name_of_function page)
  • i18n.t('connected') is referencing the internationalization of connected.

That's our widget.

Clone this wiki locally