Blog

Custom Print Layouts using SP Widgets

There are lots of ways to create custom print layouts. This is my version using a SP Widget.

In the example below, I am using widget to print out details from an incident. However you can customize the widget anyway you would like. Here are just some of possibilities:

  • Add data from other data tables

  • Add a company logos or other graphics

  • Create custom calculations

  • Make some fields editable for additional print detail

  • Use a REST API call to pull in data from another application

  • Style the fields with CSS

SCREENSHOT

Code

Portal

I like to create a portal to contain all the print layouts

Title: Print
URL suffix: print

Page

Title: Print Record
ID: print_record

Add Custom Print Widget to Page after Widget is created

UI Action

Use a UI Action to run the custom print

Name: Print Record
Table: <your choice>
Action name: printRecord
Client: true
OnClick: printRecord()
Form button: true
Show update: true
Isolate Script: false
Script:
function printRecord() {
		win = window.open("print?id=print_record&table_name="+g_form.getTableName()+"&sys_id="+ g_form.getUniqueValue());
	win.focus();
	return false;
}

Widget: Incident Custom Print

HTML

<div>
  <div class="text-right no-print">
    <button class="btn btn-primary pull-right" type="submit" onclick="javascript: window.print();">
      <div class="fa fa-print"></div>
      <span>Click to Print</span>
    </button>
  </div>
  <table class="borderless">
    <tr>
      <td><h3 class="panel-body" style="cursor: default"> Detail</h3></td>
    </tr>
  </table>
  <div class="panel panel- b" >
    <div class="panel-heading">
      <h4 class="panel-title" style="cursor: default"> - </h4> 
    </div>
    <div class="panel-body">
      <dl class="ticket-fields" ng-if="data.fields.length > 0">
        <dt class= "col-md-6 col-sm-12 col-xs-6 break-word" 
            ng-if="field.value && (field.type != 'decimal' || field.type == 'decimal' && field.value != 0)" 
            ng-repeat-start="field in data.fields"></dt>
        <dd class= "col-md-6 col-sm-12 col-xs-6 break-word" 
            ng-repeat-end ng-switch="field.type" 
            ng-if="field.value && (field.type != 'decimal' || field.type == 'decimal' && field.value != 0)">
          <div ng-switch-when="glide_date_time" title="">
            <sn-time-ago timestamp="::field.value" />
          </div>
          <div ng-switch-default ></div>
        </dd>
      </dl>
      <div ng-if="data.variables.length > 0" ng-init="c.variable_toggle = true">
        <button class="h4 options-btn btn" ng-click="c.variable_toggle = !c.variable_toggle" aria-expanded="" aria-controls="variables-toggle">
          <span style="font-size: 12px;" class="glyphicon" ng-class="c.variable_toggle ? 'glyphicon-chevron-down' : 'glyphicon-chevron-up'"></span>
          $
        </button>

        <div ng-if="c.variable_toggle" id="variables-toggle" aria-hidden="">
          <hr role="presentation">
          <div class="m-b break-word" ng-repeat="variable in data.variables | filter:">
            <label class="m-t-xs m-b-none text-muted"><b></b></label>
            <div ng-if="!variable.multi_row"><span class="pre-wrap"></span></div>
            <div ng-if="variable.multi_row">
              <a href="javascript:void(0)" uib-popover-template="'sp_multirow_vs_summarizer.html'" popover-title=""
                 popover-placement="auto left" popover-append-to-body="true" popover-trigger="outsideClick">${Click to view}</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

CSS

.header {
  font-weight:bold;
  padding: 10px 290px 10px 10px; 
}

.pre-wrap {
  white-space: pre-wrap;
}

.ticket-fields {
  margin-left: -7px;
  margin-right: -8px;
  -webkit-margin-before: 0;
  -webkit-margin-after: 0;
  
  dt {
    //font-weight: normal;
    margin-bottom: 10px;
  }
  dd {
    margin-bottom: 10px;
  }
}

@media print

{    
  .no-print, .no-print *
  {
    display: none !important;
  }
}

table, td {
  border-color:#000;
  border-style:solid;
}

table {
  border-collapse:collapse;
  border-spacing:0;
  border-width:0 0 1px 1px;
  margin-top:14px;
}

td {
  border-width:1px 1px 0 0;
  margin:0;
  padding:4px;
  vertical-align:bottom;
}

table.borderless, table.borderless td {
  border:0px;
  border-style:none;
}

table.borderAroundMe {
  border:1px;
  border-style:solid;

}

td.header {
  background-color:#a7a7a7 !important; 
  color:#fff !important;
  font-weight:bold;

}

br.page-break {
  display:block;
  page-break-before:always;
}

table 
  


tr {
  page-break-inside:avoid;
  page-break-after:auto
}

td {
  page-break-inside:avoid;
  page-break-after:auto
}

Server Script

(function() {
	data.sys_id = $sp.getParameter("sys_id");
	data.table = $sp.getParameter("table_name");
	var gr = new GlideRecord(data.table);
	gr.get(data.sys_id);

	//var gr = $sp.getRecord();
	if (gr == null)
		return;

	data.canRead = gr.canRead();
	if (!data.canRead) 
		return;

	var fields = $sp.getFields(gr, 'number,caller,category,subcategory,service,cmdb_ci,contact_type,state, impact,urgency,priority,assignment_group,assigned_to,problem_id,rfc,caused_by,sys_created_on,sys_updated_on,short_description,description,close_code,close_notes');
	
	//if (gr.getValue("sys_mod_count") > 0)
	//	fields.push($sp.getField(gr, 'sys_updated_on'));

	data.number = $sp.getField(gr, 'number').display_value;
	data.short_description = $sp.getField(gr, 'short_description').display_value;
	data.tableLabel = gr.getLabel();
	data.fields = fields;
	data.variables = new GlobalServiceCatalogUtil().getVariablesForTask(gr, true);

})();