Storing / Printing / Emailing / Scheduling CUSTOM REPORTS

(imported topic written by jnmoore91)

This topic builds off the ideas in this topic: http://forum.bigfix.com/viewtopic.php?pid=13888#p13888

Greetings all,

I figured I’d share part of my work around for enabling these BES web reports features for custom reports until there is official method to enable these features.

Before I begin, I should clarify in my mind there are two types of custom reports:

static

and

dynamic

. A static report doesn’t contain any (or little) javascript. It’s relevance queries do not change (unless you change it in the config report link), and if you e-mail or print it, it looks the same as if you simply view it. A dynamic report relies heavily on javascript. Typically the user selects some options in a form on the first page, presses submit, and on the second page, the content based on the user’s parameters is dynamically generated. Dynamic reports emulate / appear very similar to the default reports that come with the Web Report product, however, if you try to print / export / email a Dynamic report, you pretty much get garbage.

The reason for the reliance of JavaScript is that in BES Web Reports, there is no server side scripting. Everything must be done in the browser. This could change if the BES team implements a new feature to the relevance language that allows embedded variables in relevance and embedded POST / GET variables in relevnance.

ie <?relevance names of bes fixlets whose (id of it as string is $POST) ?> or even <?relevance names of bes fixlets whose($POST) ?> where $POST could be id of it is 345 OR id of it is 654

. If BigFix implemented this, my reliance on JavaScript in Custom Web Reports would fall 90%.

However, as it stands (at least for v7.1 and v7.2), since you have to rely heavily on JavaScript, you cannot email / print / export / store a dynamic custom report. The work around I’ve developed relies on the Store Report method described in the URL above. While my dynamic custom report generates the content in JS, I am also building a static report and saving it in a JS variable. Then, when the user wishes to email/print/export/store, I prompt the user to store the report (and then use the method described in the link above). After the user stores the report, I fetch the ID of the newly created report, and display a view / export / email / print option to the user.

HTML code:

<!-- COPY REPORTS SECTION --> <div id=
"copyReport_divID" style=
"width: 450px; position: absolute; visibility: hidden; left:25%; top: 100px; z-index:9"> <div style=
"background-color: #285386; color: white; padding:5px"><b>Store Report</b> Prompt</div> <div style=
"background-color: white; border-left: 5px solid #285386; border-right: 5px solid #285386;padding:5px"> <div id=
"copyReport_blankContainerID"> </div> <div id=
"copyReport_promptContainerID"> <i>Fill in the following information and then press 
"Store Report"</i> <form onSubmit=
"return false"><!-- never submit b/c we use AJAX --> <table> <tr><td><b>Report Name:</b><td><input type=
"text" name=
"reportName" id=
"storeReport_name" size=
"40" maxlength=
"100" /><br/>( max size: 100 characters ) <tr><td> </td></tr> <tr><td><b>Description:</b><td><textarea name=description rows=7 cols=30 maxlength=1000><?relevance html 
"<" ?>/textarea><br/>( max size: 1000 characters ) <tr><td> </td></tr> <tr><td><b>Category:</b><td><input type=
"text" name=
"category" size=
"40" value=
"Stored Fixlet Report"> <tr><td> </td></tr> <tr><td><b>Visibility:   </b><td><input type=
"radio" name=
"public" value=
"1" checked id=
"storeReport_public" /><label for=
"storeReport_public">Public</label><br/> <input type=
"radio" name=
"public" value=
"0" id=
"storeReport_private" /><label for=
"storeReport_private">Private</label> <tr><td> </td></tr> </table> <textarea name=data id=storeReport_data readonly style=
"position: absolute; visibility:hidden"><?relevance html 
"<" ?>/textarea> <input type=
"button" name=
"submitButton" value=
"Store Report" onClick=
"validateAndSaveStoreReport(this.form);" /> <input type=
"button" value=
"Cancel" onClick=
"hideStoreReport();" /> </form> <i>After this, you wil be presented with a menu to view/export/print/email</i> </div> </div> </div>

BES Web Reports bug:

Notice that for the < /textarea>, you have to do

<?relevance html "<" ?>/textarea>

, because when you go to “config report” link, Any HTML after your textarea ending tag will become embedded in the page.

JavaScript code (aka “the magic”):

<script language=
"JavaScript"> 
/** * hijack_leftnavbar * @class hijack big fix's left navigation bar & preps for store report * @source http://forum.bigfix.com/viewtopic.php?pid=13888#p13888 * @source http://forum.bigfix.com/viewtopic.php?pid=14344#p14344 */ function hijack_leftnavbar() 
{ 
// hijack the webreports store link 

try 
{ var actionsDiv = document.getElementById(
'wr_reportactions'); var actionLinks = actionsDiv.getElementsByTagName(
'a'); var ConfigReportID = 
'wrlink2'; var StoreReportID  = 
'wrlink3'; var PrintReportID  = 
'wrlink4'; var EmailReportID  = 
'wrlink5'; 

for (var i = 0; i < actionLinks.length; i++) 
{ 

if ((actionLinks+.href).match(StoreReportID))  
//Found Store Report Match 
{ actionLinks+.href = 
'javascript:showSaveUI()'; 
} 

else 

if ((actionLinks+.href).match(PrintReportID))  
//Found Store Report Match 
{ 
//actionLinks+.onclick = "alert('To print this report, you must store this report first, view the newly created report, and click the \"Printable Version\" on that report');"; actionLinks+.href = 
"javascript: alert('To print this report, you must store this report first, view the newly created report, and click the \"Printable Version\" on that report');"; 
} 

else 

if ((actionLinks+.href).match(EmailReportID))  
//Found Store Report Match 
{ 
//actionLinks+.onclick = "alert('To email this report, you must store this report first, view the newly created report, and click the \"Printable Version\" on that report');"; actionLinks+.href = 
"javascript: alert('To email this report, you must store this report first, view the newly created report, and click the \"Printable Version\" on that report');"; 
} 
} 
} 

catch (e) 
{ 

if(e.description) e = e.description alert(
"You will not be able to store web reports.  Please contact your administrator to resolve this problem<br><br>Unable to configure the webreports store link: " + e); 
} 
}   
/** * showSaveUI * @class Displays store report panel for the user * @source http://forum.bigfix.com/viewtopic.php?pid=14344#p14344 */ function showSaveUI() 
{ 

if(window.step == 1) alert(
"You must choose your fixlets and computer properties before you can store your report"); 

else 

if(document.getElementById(
'storeReport_data').value == 
"") alert(
"Please wait until the report is finished loading, so that the HTML for the new report has been generated."); 

else 
{ var prompt, blank; prompt = document.getElementById(
"copyReport_promptContainerID").style; blank  = document.getElementById(
"copyReport_blankContainerID").style; prompt.visibility = 
'visible'; prompt.position = 
'relative'; blank.visibility = 
'hidden'; blank.position = 
'absolute'; document.getElementById(
'copyReport_divID').style.visibility = 
"visible"; 
} 
}   
/** * hideStoreReport * @class Hides the Store Report Panel * @source http://forum.bigfix.com/viewtopic.php?pid=14344#p14344 */ function hideStoreReport() 
{ document.getElementById(
'copyReport_promptContainerID').style.visibility = 
'hidden'; document.getElementById(
'copyReport_blankContainerID').style.visibility = 
'hidden'; document.getElementById(
'copyReport_divID').style.visibility = 
'hidden'; 
}   
/** * validateAndSaveStoreReport * @class Validates user input of theForm & saves a stored report * @require getNewestReportID * @param {Object} theForm the form of the saved report * @source http://forum.bigfix.com/viewtopic.php?pid=13888#p13888 * @source http://forum.bigfix.com/viewtopic.php?pid=14344#p14344 */ function validateAndSaveStoreReport(theForm) 
{ var success, visibility_value; theForm.submitButton.value = 
"Storing Report..."; theForm.submitButton.disabled = 

true; success = 

true; 

if(theForm.reportName.value.length < 1 || theForm.reportName.value.length > 100) 
{ success = 

false; alert(
"Report Name must be at least one character and no longer than one hundred characters"); 
} 

if(theForm.description.value.length > 1000) 
{ success = 

false; alert(
"Report Description cannot be longer than one thousand characters"); 
} visibility_value = ((theForm.public[0].checked) ? 
'1' : 
'0'); 

if(!success) 
{ theForm.submitButton.value = 
"Store Report"; theForm.submitButton.disabled = 

false; 

return; 
} 

else 
{ 
//Save report var http_request = CreateRequest(); var token      = getAdminToken(); 

if ( http_request ) 
{ 
// construct the request var request = 
''; request += 
'page=SaveReport'; request += 
'&oldPage=CustomReport'; request += 
'&SaveReportSubmit=1'; request += 
'&ReportID='            + encodeURIComponent(window.reportID); request += 
'&SaveReportName='      + encodeURIComponent(theForm.reportName.value); request += 
'&ReportDescription='   + encodeURIComponent(theForm.description.value); request += 
'&ReportCategory='      + encodeURIComponent(theForm.category.value); request += 
'&SaveReportPublic='    + encodeURIComponent(visibility_value); request += 
'&ReportData='          + encodeURIComponent(theForm.data.value); request += 
'&AdminToken='          + token; http_request.open( 
'POST', 
'/webreports', 

false ); http_request.setRequestHeader( 
"Content-type", 
"application/x-www-form-urlencoded" ); http_request.send( request ); 

if ( http_request.readyState == 4 && http_request.status == 200 ) 
{ var prompt = document.getElementById(
"copyReport_promptContainerID").style; var blank  = document.getElementById(
"copyReport_blankContainerID"); var newReportID = getNewestReportID(); 
//Small chance that another user created another report! blank.innerHTML = 
"<br /> Successfully stored <b>" + theForm.reportName.value + 
"</b> Report<br />" + 
" With the stored report, you can export, print, email, or schedule the report.<br />" + 
" What would you like to do with the stored report?<br /><br />" + 
'<form method="POST" action="/webreports" class="wr_invisible" name="myCustomPrint"><input type=hidden name="PrintableVersion" value="1"><input type=hidden name="ReportID" value="'+newReportID+
'"><input type=hidden name="page" value="CustomReport"></form>' + 
'<form method="POST" action="/webreports" class="wr_invisible" name="myCustomEmail"><input type=hidden name="ReportID" value="'+newReportID+
'"><input type=hidden name="emailPage" value="CustomReport"><input type=hidden name="page" value="EmailReport"></form>' + 
'<input type=button value="View"   onclick="javascript: window.location=\'/webreports?page=Report&ReportID=' + newReportID +
'\'"> ' + 
'<input type=button value="Export" onclick="javascript: window.location=\'/webreports?page=Report&ReportID=' + newReportID +
'\'&export=1"> ' + 
'<input type=button value="Print"  onclick="javascript:document.forms.myCustomPrint.submit();"> ' + 
'<input type=button value="Email"  onclick="javascript:document.forms.myCustomEmail.submit();"> ' + 
"Cancel".link(
"javascript: hideStoreReport();") + 
"<br />"; prompt.visibility = 
'hidden'; prompt.position = 
'absolute'; blank.style.visibility = 
'visible'; blank.style.position =  
'relative'; 
//window.location = "/webreports?page=StoredReport"; 
} 

else alert(
"Error saving report: Error contacting server(Status code " + http_request.status + 
")"); 
} 

else alert(
"Error saving report: Error creating http request object"); 
} 
}     
/** * prepareStoreReport * @class Allows user to use the store report ability * @param {string} title When viewing all reports, the new stored report will be labelled this * @param {string} report_html The Data / HTML / Relevance / Content of the Report * @source http://forum.bigfix.com/viewtopic.php?pid=14344#p14344 */ function prepareStoreReport(title, report_html) 
{ document.getElementById(
"storeReport_data").value = report_html.replace(/</g, 
'<').replace(/>/g, 
'>'); document.getElementById(
"storeReport_name").value = title.substring(0, 100); 
}   
/** * getAdminToken * @class fetches a token needed to store a report * @require CreateRequest * @source http://forum.bigfix.com/viewtopic.php?pid=13888#p13888 * @return {integer} token The Admin Token required by the Stored Report ability */ function getAdminToken()
{ var    tokenHttpRequest = CreateRequest(); var token = 
''; 

if (tokenHttpRequest)
{ var request = 
''; tokenHttpRequest.open( 
'GET', 
'/webreports?page=SaveReport', 

false ); tokenHttpRequest.setRequestHeader( 
"Content-type", 
"application/x-www-form-urlencoded" ); tokenHttpRequest.send( 

null ); 

if (tokenHttpRequest.readyState == 4 && tokenHttpRequest.status == 200) 
{ var results = (tokenHttpRequest.responseText.match(/<input\s+.*\s+name=
"AdminToken"\s+value=
"(.*?)">/i)); var token = results[1]; 
} 
} 

return token; 
}   
/** * getNewestReportID * @class Parses the Reports Page for the newest Report ID (examines both public and private reports available to the current user * @require CreateRequest * @source http://forum.bigfix.com/viewtopic.php?pid=14344#p14344 * @return {integer} highestID the newest (and highest) report ID available to the user */ function getNewestReportID() 
{ var http_request = CreateRequest(); var highestID = 0; 

if( http_request ) 
{ http_request.open( 
'GET', 
'/webreports?page=StoredReport', 

false ); http_request.setRequestHeader( 
"Content-type", 
"application/x-www-form-urlencoded" ); http_request.send( 

null ); 

if ( http_request.readyState == 4 && http_request.status == 200 ) 
{ var thePage = http_request.responseText; var PubReportsWhere = thePage.indexOf(
"Public Reports"); 

if(PubReportsWhere > -1) 
{ thePage = thePage.substring(PubReportsWhere);   var results = (thePage.match(/<a\s+href=
"\/webreports\?page=Report&ReportID=([0-9]+)"\s+(class=
"wr_menu")?\s*>(.*?)<\/a>/ig)); 

if(results != 

null) 
{ 
//alert(results.join("\n")); var i; var c = results.length; 

for(i=0;i<c;i++) 
{ var tmp = results+.match(/<a\s+href=
"\/webreports\?page=Report&ReportID=([0-9]+)"\s+(class=
"wr_menu")?\s*>(.*?)<\/a>/i); 
//alert(tmp.join("\n:") + "\n\n" + tmp[1] + ":" + tmp[3] + " ? " + highestID); var thisID = Number(tmp[1]); 

if(thisID > highestID) 
{ highestID = thisID; 
//alert(tmp[3]); 
} 
} 
} 

else alert(
"Unable to find report IDs.  This may have been caused by a BES Web Reports Upgrade.  Please contact the tool owner."); 
} 

else alert(
"Unable to find report IDs.  This may have been caused by a BES Web Reports Upgrade.  Please contact the tool owner."); 
} 

else alert(
"Error contacting server(Status code " + http_request.status + 
")"); 
} 

else alert(
"Unable to create http request object"); 

return highestID; 
} </script>

Note to dev team:

Do you have an alternate method to fetch the newly created reportID (maybe in the response of the AJAX request)? The work around I have (getNewestReportID) isn’t the best method to get the newly created reportID…

So I’m sharing this code for those of you who are struggling like me to add some dynamic content to your web report without sacrificing the BES web report functionality. Additionally, I hope this will promote more support for official BES Web Report Abilities (print/store/etc) in dynamic custom reports.

–Jerroyd

(imported comment written by BenKus)

Jerroyd, very nice! If you are interested in applying for our open application engineering positions, let me know. :slight_smile:

Ben

(imported comment written by jnmoore91)

lol. Thanks Ben, I’m flattered. :smiley:

(imported comment written by jessewk)

fyi, consider this an early heads up that this hack will break starting with 8.x

Jesse