Customize left navigation bar

(imported topic written by jnmoore91)

Hi All,

For the Store, Export, Print, and E-mail functions on the left navigation bar, is there a way to control these in a custom report?

For example, for my custom report, if you try to e-mail it, the setup page of the custom report is sent. Is there a way to change this to be a different page of my report? Perhaps by manipulating the parameters in the URL?

Also for the e-mail, does it replace the

For Store, it looks like it just makes a copy of the HTML. Is there a way to make the “Store” function a little bit more useful?

–Jerroyd

(imported comment written by jnmoore91)

bump

If it isn’t possible to customize the left nav bar, is there a way to prepopulate a blank report?

(imported comment written by BenKus)

Sorry, Jerroyd…

Since the custom reports are very free-form (arbitrary HTML, Flex, javascript, etc.), the web reports doesn’t know how to properly render/export them in the way the built-in reports work… Also, there isn’t a way to pass arguments in the url because the web reports wouldn’t know how to pass them to the custom report in the current engine (other than the argument of which report to run)…

Ben

(imported comment written by jnmoore91)

Thanks Ben,

Is there a way to disable those links then?

Also, is there a way to pre-populate a blank report (as a possible work around to this)?

(imported comment written by BenKus)

There isn’t any way to disable the links that I can think of… sorry…

Ben

(imported comment written by jnmoore91)

dang, what about pre-populating a blank report (through a GET or a POST request)?

(imported comment written by jessewk)

Hi Jerroyd,

You can hijack the navigation bar links using some javascript hackery. There’s an example in my comment #3 here:

http://forum.bigfix.com/viewtopic.php?id=444

Check out the EnableExportToCSVLink() function.

This is obviously very hacky, but it will work at least on a particular web reports version.

I’m not sure what you’re looking for when you say “pre-populate a blank report”. Can you explain further?

Jesse

(imported comment written by jnmoore91)

Hi jessewk,

Thanks for the resource. It’s so brilliantly simple!

As to pre-populate a blank report, in order to duplicate the “Store Report” function, and have it behave the way I want it to, what I would like to do is pass in a dynamically created custom report into a blank report (either through a GET or a POST method).

Here is the data / user flow I envision:

  1. User clicks on my custom report

  2. User chooses prerequisites, filters, properties, etc

  3. User then views the data, wishes to save all of his or her prequisites, filters, etc, so he or she clicks on store report

  4. When the user clicks on “Store Report,” HTML & Relevance Code has been generated (by me) as a new custom report to duplicate the page the user is seeing. This new custom report is passed into a blank report. All the user has to do is click “Create Report,” and his or her prequisites on my custom report has been saved in a new custom report.

I’d like to use the same method to E-Mail report, so I’m also stripping out all the javascript as well.

Do you think it’s possible?

Thanks again for the Enable Export ToCSVLink()!

(imported comment written by jessewk)

I see.

I’ve done that in the past but it’s complicated and very hacky. You have to recreate the ‘store report’ UI on your page (I did it in Flex), write a variable containing the configuration parameters to the current report HTML, do some trickery to grab the ReportID and AdminToken, and then construct a post.

I’ve gone ahead and posted what is, I believe, all the functions you need. You’ll still need to fill in the code that shows the ‘Save Report’ UI and also add code that looks for your saved parameters onload and applies them to the report.

Obviously this is completely unsupported, liable to break, and probably a bad idea… but I was pretty enamored with it at the time. YMMV.

Jesse

function init()
{
// hijack the webreports store link
try
{
var actionsDiv = document.getElementById(‘wr_reportactions’);
var actionLinks = actionsDiv.getElementsByTagName(‘a’);
var wrLinkID = (typeof SAVED_REPORT != “undefined”)? ‘wrlink3’ : ‘wrlink2’ ;

for (var i = 0; i < actionLinks.length; i++)
{
if ((actionLinks+.href).match(wrLinkID))
{
var theStoreLink = actionLinks+;
theStoreLink.href = ‘javascript:showSaveUI()’;
break;
}
}
}
catch (E) {};
}

/** we need to have the user provide the report details on this page since we don’t know the report ID to use when submitting the report yet. I originally implemented this in Flex, you will probably want to replace this with code that shows an HTML form.
**/
function showSaveUI()
{
document.getElementById(“mainSWF”).showWebReportsSaveUI(‘saveReportWR’);
}

/** the call back from flex with all the report details supplied. If you implement the ‘save report’ form in HTML instead of Flex, call this after you have verified the inputs supplied by the user **/
function saveReportWR(ReportString , ReportName, ReportDescription, ReportCategory, ReportVisibility)
{
var http_request = CreateRequest();
var token = getAdminToken();
if ( http_request )
{
// construct the report data to post
if (typeof SAVED_REPORT == “undefined”)
{
var ReportData = document.getElementById(“wr_datapage”).innerHTML;
ReportData += ‘\n’;
}
else
{
// if the variable already exists in the HTML, we need to replace it
var ReportData = document.getElementById(“wr_datapage”).innerHTML;
ReportData = ReportData.replace(/(var\s+? SAVED_REPORT\s+?=\s+?")(.*?)\n/i , ‘$1’ + ReportString.replace(/\n/g, ‘’) + ‘";\n’);
}

// construct the request
var request = ‘’;
request += ‘page=SaveReport’;
request += ‘&oldPage=CustomReport’;
request += ‘&SaveReportSubmit=1’;
request += ‘&ReportID=’ + encodeURIComponent(getWRReportID());
request += ‘&SaveReportName=’ + encodeURIComponent(ReportName);
request += ‘&ReportDescription=’ + encodeURIComponent(ReportDescription);
request += ‘&ReportCategory=’ + encodeURIComponent(ReportCategory);
request += ‘&SaveReportPublic=’ + encodeURIComponent(ReportVisibility);
request += ‘&ReportData=’ + encodeURIComponent(ReportData);
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 )
{
window.location = “/webreports?page=StoredReport”;
}

return 'Error contacting server. Status code: ’ + http_request.status;
}
else
{
return ‘Error creating http request object.’;
}
}

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;
}

function getWRReportID()
{
var actionsDiv = document.getElementById(‘wr_reportactions’);
var inputs = actionsDiv.getElementsByTagName(‘input’);
for (var i = 0; i < inputs.length; i++)
{
if ((inputs+.name).match(‘ReportID’))
{
return inputs+.value;
}
}
}

(imported comment written by jnmoore91)

Hi jessewk,

Wow, thanks for the awesome code. It’ll probably take me today and monday to internalize the process, so I’ll probably post follow ups on Tuesday

that and I have a lot of meetings from here until Tuesday morning :frowning:

. So thanks!

feature request

Also, any chance of enabling a pre-populated blank report through a GET or POST request in the next version? Unfortunately, it’d make this excellent work around obsolete, but at least intermediate HTML / JS scriptors like myself could use it and understand it better.

Also, looking through the code, the function “encodeURIComponent()”, appears to be undefined. Is that a JS API or BigFix API?

(imported comment written by jessewk)

encodeURIComponent() is provided by the browser. See:

http://www.w3schools.com/jsref/jsref_obj_global.asp

Concerning the feature request, we have a number of ideas on how to ease the process of creating these reports and hooking into the nav bar. I don’t know where we stand implementing them, but we certainly know it’s a corner of the product that could use some polish.

Jesse

(imported comment written by BenKus)

Warning! This method will likely break whenever we update web reports…

Ben

(imported comment written by jnmoore91)

Hello Ben and jessewk,

I finally got to a point in my web report where I can implement the storing reports section, and I had a question about the showSaveUI():

  1. I don’t see any elements (when I look at the html sources of the web reports) for “mainSWF”, so what / where is this?

  2. You don’t provide a showWebReportsSaveUI() definition with your source code above, so I’m curious about it

  3. it looks like the showWebReportsSaveUI() calls the saveReportWR() function? but then how do I pass in arguments?

(imported comment written by jessewk)

You should replace the contents of showSaveUI() with a function that replicates the ‘save report’ UI on the page you would normally see when you click the ‘store report’ link. Basically you need to prompt the user for the following inputs:

Report Name, Description, Category,Visibility (public/private)

After getting those inputs and validating them, you pass the values to saveReportWR().

I didn’t provide code for that b/c the report where I used the hack above was already written in Flex so it made sense to also implement the input UI in Flex. You probably just want to show some HTML input controls.

Jesse

(imported comment written by jnmoore91)

Hey jessewk & Ben,

Looking at the code you provided, I’m noticing a JS var “SAVED_REPORT.” What is this for? Can I set this variable to my code and get the same result?

If so, Ben, will this be compatible with future versions?

Also if this is true, why isn’t this documented better? It could have saved me 3-5 days of hair pulling! :slight_smile:

–Jerroyd

(imported comment written by jessewk)

oh no, this is definitely not something provided by the product. It’s virtually guaranteed to break and there is no documentation. I had just implemented the hack previously and kinda liked it b/c I thought it was clever so I thought I’d share.

Anyway, the SAVED_REPORT variable is written by

you

. It’s used to serialize the settings selected by your users. You’ll want to read the variable when the report is loaded so you can setup the UI to match the user settings and run the report as configured by the user. You also need to write the settings to the variable. This is the ‘ReportString’ argument to the saveReportWR() function. In my implementation I originally used XML, but that was before I knew about JSON. These days I would definitely use JSON.

Hope that helps! Good luck hacking away…

Jesse

(imported comment written by jnmoore91)

Hey jessewk,

Thanks for the clarification. It might not be a bad idea to implement a SAVED_REPORT variable, and when the user clicks store report, just use that for the text. Food for Thought.

FYI,

I had to take the following out:

// construct the report data to post 

if (typeof SAVED_REPORT == 
"undefined") 
{ var ReportData = document.getElementById(
"wr_datapage").innerHTML; ReportData += 
'\n<script type="text/javascript" id="SavedReportScript">\n'; ReportData += 
'var SAVED_REPORT = "'; ReportData += ReportString.replace(/\n/g, 
''); ReportData += 
'";\n</script>'; 
} 

else 
{ 
// if the variable already exists in the HTML, we need to replace it var ReportData = document.getElementById(
"wr_datapage").innerHTML; ReportData = ReportData.replace(/(var\s+? SAVED_REPORT\s+?=\s+?
")(.*?)\n/i , '$1' + ReportString.replace(/\n/g, '') + '";\n
');

Additionally, the following would hijack the “Config Report” link instead of the "Store Report link:

var wrLinkID = (typeof SAVED_REPORT != 
"undefined")? 
'wrlink3' : 
'wrlink2' ;

might be useful for anyone using the BES web reports version 7.1.1.315

feature request

Throughout writing my custom report, I think it would make things A LOT simpler if we can embed POST and GET variables in the static relevance queries. I hate calling EvaluateRelevance(). It makes my page load slower and I have a sneaky feeling that if the relevance between <?relevance ?> are a lot faster. It would also reduce my reliance of JS.

Additionally, Regarding the E-mail & Print Reports buttons, as it stands, users cannot use these buttons on my dynamic report, because they get a bunch of random garbage, however, on the static reports, the reports generated by your hijack the “Store Report” button you provided above, it works. Since my dynamic report is already generating the static reports and stores it in a JS variable, is there anyway to use the JS variable and hijack the e-mail & Print buttons?

–Jerroyd

(imported comment written by jnmoore91)

Hi,

I recently discovered that IE caches AJAX requests (

work around here

), which created a problem in one of my reports, so I decided to review all of my AJAX requests to make sure IE users weren’t going to encounter strange behaviors in my reports. For getAdminToken(), the token generated on the stored report page, is the token constant for the user’s browser session or will it change every time the user visits the stored page?

–Jerroyd

(imported comment written by jessewk)

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

Jesse