Splunk Search

Navigation Menu URLLoader?

rhayle
Path Finder

The navigation menu at the top would be so much better if it could transmit the context (index and host) for the new page to have its filters pre-populated.

Is this possible or is the navigation menu just static and fixed?

In reply to the below answer, this is the approach I used with Splunk 5.0

    <!-- Copyright (C) 2010-2013 Sideview LLC.  All Rights Reserved. -->
<view autoCancelInterval="90" isVisible="True" onunloadCancelJobs="true" template="dashboard.html" isSticky="False">
  <label>customBehavior 2 addSelectionToAllLinksOnPage example</label>
  <module name="AccountBar" layoutPanel="appHeader" />
  <module name="AppBar" layoutPanel="appHeader" />
  <module name="SideviewUtils" layoutPanel="appHeader">
    <param name="customJavascript">sideview_utils/custom_behavior_to_rewrite_links.js</param>
  </module>

  <module name="Message" layoutPanel="messaging">
    <param name="filter">*</param>
    <param name="maxSize">2</param>
    <param name="clearOnJobDispatch">False</param>
  </module>

  <module name="HTML" layoutPanel="viewHeader">
    <param name="html"><![CDATA[
    <h1>Testcase for Link-rewriting customBehavior</h1>
    ]]></param>
  </module>


  <module name="HTML" layoutPanel="panel_row1_col1">
    <param name="html"><![CDATA[
    <p>
      This page, along with the customBehavior defined in application.js,  shows an example of a customBehavior implementation where certain key-value pairs from the selected form elements get dynamically rewritten into ALL of the app navigation links.   By this I mean that when you change the Pulldown value,   and then navigate to any view in the app navigation menu above,   
    </p>
    ]]></param>
  </module>

  <module name="URLLoader" layoutPanel="panel_row1_col1" autoRun="True">
    <param name="keepURLUpdated">True</param>

    <module name="Pulldown">
      <param name="name">test1</param>
      <param name="label">test1</param>

      <param name="staticOptions">
        <list>
          <param name="label">fooLabel</param>
          <param name="value">fooValue</param>
        </list>
        <list>
          <param name="label">barLabel</param>
          <param name="value">barValue</param>
        </list>
        <list>
          <param name="label">bazLabel</param>
          <param name="value">bazValue</param>
        </list>
      </param>

      <module name="CustomBehavior">
        <param name="customBehavior">addSelectionToAllLinksOnPage</param>
      </module>

      <module name="HTML">
        <param name="html"><![CDATA[

         <p>
         selected value is:<br>
          $test1$
         </p>

         Note that the customBehavior will only rewrite links that are in the app navigation menus. So barring some modification to the JS, the following links will NOT get the special href-rewriting behavior. <br>

         <a href="testcases_for_link_rewrite_custom_behavior">link1</a><br>
         <a href="/app/sideview_utils/testcases_for_link_rewrite_custom_behavior">link2</a>

        ]]></param>
      </module>
    </module>
    <module name="Pulldown">
      <param name="name">host</param>
      <param name="label">host</param>

      <param name="staticOptions">
        <list>
          <param name="label">abc1012361</param>
          <param name="value">abc1012361</param>
        </list>
        <list>
          <param name="label">abc1012362</param>
          <param name="value">abc1012362</param>
        </list>
      </param>

      <module name="CustomBehavior">
        <param name="customBehavior">addHostToAllLinksOnPage</param>
      </module>
    </module>
  </module>
</view>

/*
  • THE FOLLOWING IS AN ADVANCED PROTOTYPE AND NOT CORE FUNCTIONALITY.
  • IF YOU DO NOT FULLY UNDERSTAND IT AND FEEL COMFORTABLE WITH BEING
  • RESPONSIBLE FOR ITS PRESENCE ON YOUR PRODUCTION SYSTEMS, DO NOT USE.
  • see "testcases_for_link_rewrite_custom_behavior.xml" in Sideview Utils.
  • Advanced customBehavior and other custom code to implement a feature
  • whereby several views can have one or more pulldowns, where even if
  • the user navigates away from the view to another random view, if the
  • pulldown(s) are present in the new view, the selected value is always
  • passed from the first view to the second. Again, this behavior happens
  • even if the user uses the app navigation bar to switch views.
  • As such the implementation involves actively rewriting the href
  • attributes of the actual link elements in the HTML.
    */
    if (typeof(Sideview)!="undefined") {
    var fieldsToPreserve = ["test1", "host"];
    function rewriteLinks(argDict) {
    // We start by getting a collection of all the link elements within the
    // AppBar module (aka the app navigation menus).
    var linksToRewrite = $("div.AppBar a");

    // depending on version the submenus of the AppBar may not actually be
    // in the AppBar module's container div.
    var splunkVersion = Splunk.util.getConfigValue("VERSION_LABEL");
    
    if (Sideview.utils.compareVersions(splunkVersion,"4.3") > -1) {
        var extraMenuLinks = $("body .outerMenuWrapper .innerMenuWrapper a");
        linksToRewrite = linksToRewrite.add(extraMenuLinks);
    }
    
    linksToRewrite.each(function(i) {
        var a = $(this);
        var href = a.attr("href");
        // dont mess with these links. They might have their own args.
        if (a.text() == "Help" || a.text() == "About" || href.charAt(0)=="#") return true;
    
        // dont mess with these links either. Especially with the patch to
        // get all the links that 4.3 puts at the bottom of the page, we're
        // going to be iterating through links that are from a number of 
        // other modules that have action menus.
        if (href.substring(0,11)=="javascript:") return true;
    
        // nor do we touch links that go to other apps.
        if (href.indexOf("/app/" + Splunk.util.getCurrentApp() + "/")==-1) return true;
    
        var newArgDict = {};
        if (href.indexOf("?")!=-1) {
            // make sure to preserve any existing args (uncommon but important).
            var existingArgDict = Sideview.utils.stringToDict(a.attr("href").substring(href.indexOf("?")+1));
    
            // well not all of them. We're about to write new definitive 
            // values for our "fieldsToPreserve" ones, so we throw the 
            // old values away. 
            for (var j=0,len=fieldsToPreserve.length;j<len;j++) {
                for (var name in existingArgDict) {
                var result = existingArgDict.hasOwnProperty(name);
                    if (result) {
                        if (name == fieldsToPreserve[j] && (argDict[fieldsToPreserve[j]])) {
                            delete existingArgDict[fieldsToPreserve[j]];
                        }
                    }
                }
            }
            $.extend(argDict,existingArgDict);
            // clean all the old qs args off the string.
            href = href.substring(0,href.indexOf("?"));
        }
        href += "?"+Sideview.utils.dictToString(argDict);
        $(this).attr("href",href);
        //console.log("final href: " + href);
    });
    

    };

    Sideview.utils.declareCustomBehavior("addSelectionToAllLinksOnPage", function(customBehaviorModule) {
    customBehaviorModule.onContextChange = function() {
    var context = this.getContext();
    var args ={};
    for (var i=0,len=fieldsToPreserve.length;i<len;i++) {
    var f = fieldsToPreserve[i];
    // get the untemplated value if there is one.
    var value = context.get(f + ".rawValue") || context.get(f);
    if (value) {
    args[f] = value;
    }
    }
    // an option you might also need is to preserve the current
    // timerange as seen from the customBehavior module.
    //var range = context.get("search").getTimeRange();
    //if (range.getEarliestTimeTerms()) args["earliest"] = range.getEarliestTimeTerms();
    //if (range.getLatestTimeTerms()) args["latest"] = range.getLatestTimeTerms();
    rewriteLinks(args);
    }.bind(customBehaviorModule);
    });

    Sideview.utils.declareCustomBehavior("addHostToAllLinksOnPage", function(customBehaviorModule) {
    customBehaviorModule.onContextChange = function() {
    var context = this.getContext();
    var args ={};
    for (var i=0,len=fieldsToPreserve.length;i<len;i++) {
    var f = fieldsToPreserve[i];
    // get the untemplated value if there is one.
    var value = context.get(f + ".rawValue") || context.get(f);
    if (value) {
    args[f] = value;
    }
    }
    // an option you might also need is to preserve the current
    // timerange as seen from the customBehavior module.
    //var range = context.get("search").getTimeRange();
    //if (range.getEarliestTimeTerms()) args["earliest"] = range.getEarliestTimeTerms();
    //if (range.getLatestTimeTerms()) args["latest"] = range.getLatestTimeTerms();
    rewriteLinks(args);
    }.bind(customBehaviorModule);
    });

    // this rewrites the page links initially, so that they're set before even
    // the first context push hits our CustomBehavior. We have to wait until
    // allModulesInHierarchy is fired or else the AppBar's menus wont exist.
    $(document).bind("allModulesInHierarchy", function() {
    var qsDict = Sideview.utils.stringToDict(document.location.search.substring(1));
    rewriteLinks(qsDict);
    });
    }

sideview
SplunkTrust
SplunkTrust

If you have a relatively recent 2.6 or later build of Sideview Utils, there is actually a hidden view demonstrating this exact thing.

navigate manually to:

/en-US/app/sideview_utils/testcases_for_link_rewrite_custom_behavior

The page will talk about how it works a little bit. It basically puts a CustomBehavior module into the page,

<module name="CustomBehavior">
  <param name="customBehavior">addSelectionToAllLinksOnPage</param>
</module>

You put this at a point downstream from all the changes you want to preserve, and then it uses a little custom code which is packaged in application.js, and which looks like:

UPDATE - the code posted earlier was incomplete and I've updated it.

if (typeof(Sideview)!="undefined") {

    /*
     * advanced customBehavior and other custom code to implement a feature 
     * whereby several views can have one or more pulldowns,  where even if 
     * the user navigates away from the view to another random view,  if the 
     * pulldown(s) are present in the new view,  the selected value is always
     * passed from the first view to the second.  Again, this behavior happens
     * even if the user uses the app navigation bar to switch views. 
     * As such the implementation involves actively rewriting the href 
     * attributes of the actual link elements in the HTML.
     */
    var fieldsToPreserve = ["test1", "host"];
    function rewriteLinks(argDict) {
        // We start by getting a collection of all the link elements within the
        // AppBar module (aka the app navigation menus).
        var linksToRewrite  = $("div.AppBar a");

        // depending on version the submenus of the AppBar may not actually be
        // in the AppBar module's container div.
        var splunkVersion = Splunk.util.getConfigValue("VERSION_LABEL");

        if (Sideview.utils.compareVersions(splunkVersion,"4.3") > -1) {
            var extraMenuLinks = $("body .outerMenuWrapper .innerMenuWrapper a");
            linksToRewrite = linksToRewrite.add(extraMenuLinks);
        }

        linksToRewrite.each(function(i) {
            var a = $(this);
            var href = a.attr("href");
            // dont mess with these links. They might have their own args.
            if (a.text() == "Help" || a.text() == "About" || href.charAt(0)=="#") return true;

            // dont mess with these links either. Especially with the patch to
            // get all the links that 4.3 puts at the bottom of the page, we're
            // going to be iterating through links that are from a number of 
            // other modules that have action menus.
            if (href.substring(0,11)=="javascript:") return true;

            // nor do we touch links that go to other apps.
            if (href.indexOf("/app/" + Splunk.util.getCurrentApp() + "/")==-1) return true;

            var newArgDict = {};
            if (href.indexOf("?")!=-1) {
                // make sure to preserve any existing args (uncommon but important).
                var existingArgDict = Sideview.utils.stringToDict(a.attr("href").substring(href.indexOf("?")+1));

                // well not all of them. We're about to write new definitive 
                // values for our "fieldsToPreserve" ones, so we throw the 
                // old values away. 
                for (var i=0,len=fieldsToPreserve.length;i<len;i++) {
                    delete existingArgDict[fieldsToPreserve[i]];
                }
                $.extend(argDict,existingArgDict);
                // clean all the old qs args off the string.
                href = href.substring(0,href.indexOf("?"));
            }
            href += "?"+Sideview.utils.dictToString(argDict);
            $(this).attr("href",href);
        });

    };


    Sideview.utils.declareCustomBehavior("addSelectionToAllLinksOnPage", function(customBehaviorModule) {
        customBehaviorModule.onContextChange = function() {
            var context = this.getContext();
            var args ={};
            for (var i=0,len=fieldsToPreserve.length;i<len;i++) {
                var f = fieldsToPreserve[i];
                // get the untemplated value if there is one.
                var value = context.get(f + ".rawValue") || context.get(f);
                if (value) {
                    args[f] = value;
                }
            }
            // an option you might also need is to preserve the current 
            // timerange as seen from the customBehavior module.
            //var range = context.get("search").getTimeRange();
            //if (range.getEarliestTimeTerms()) args["earliest"] = range.getEarliestTimeTerms();
            //if (range.getLatestTimeTerms())   args["latest"]   = range.getLatestTimeTerms();
            rewriteLinks(args);
        }.bind(customBehaviorModule);
    });

    // this rewrites the page links initially, so that they're set before even 
    // the first context push hits our CustomBehavior.  We have to wait until
    // allModulesInHierarchy is fired or else the AppBar's menus wont exist.
    $(document).bind("allModulesInHierarchy", function() {
        var qsDict = Sideview.utils.stringToDict(document.location.search.substring(1));
        rewriteLinks(qsDict);
    });
}

Unfortunately in 6.0 they changed the app navigation menu entirely so this customBehavior will only work on 5.0 and earlier. It shouldn't be too hard to update for 6.0 if you need to.

0 Karma

rhayle
Path Finder

Thank you for your response regarding Splunk 6.0, even if it is not the answer I want to hear. 😞

Are there any good sources/examples available (splunk 6.0) for building a custom AppBar module for a novice like myself?

0 Karma

sideview
SplunkTrust
SplunkTrust

I apologize for that - I was going too fast and I saw the test1 and host Pulldowns in your xml and assumed I had posted that. Indeed the example only has one Pulldown. I looked into what it will take to update the customBehavior to work with the AppBar in 6.0 and it's going to be far harder than it was in 4.3 and 5.0. The jury is still out. The new web framework is kind of built to be utterly impenetrable and impervious to any "customization" short of rebuilding the entire AppBar module as a custom module.

0 Karma

rhayle
Path Finder

I'm not following you? I go to /en-US/app/sideview_utils/testcases_for_link_rewrite_custom_behavior of sideview_utils 2.7 and all I see is 1 pulldown called test1. Where can I find the 2 pulldown example?

0 Karma

sideview
SplunkTrust
SplunkTrust

There are two Pulldowns in my example. It definitely works but it definitely does not work in 6.0. I'm sorry it requires an unknown amount of work to modify the code to work in 6.0.

0 Karma

rhayle
Path Finder

Are you sure it works the way you say it works? You only have 1 pulldown in your example and when I try to add a second one, it does not work for me. It will allow you to choose one or the other, but not both. I'm guessing it has to do with the addSelectionToAllLinksOnPage customBehaviorModule.onContextChange and getContext() and so it will only get one, rather than both, but I have no idea how to change this? My problem is that it doesn't seem to work with multiple pulldowns and I need it working with version 6.0, as I am using index and host, rather than test1 and host. Thanks!

0 Karma

sideview
SplunkTrust
SplunkTrust

It works with multiple pulldowns. NOTE: the previous code I posted was incomplete. Sorry about that. As you see now in the updated code, you have to specify the name param of the Pulldowns you want to preserve (here "host" and "test1"). It'll need to be updated for Splunk 6.0 and it might be easy or it might be hard. I just haven't done it yet I'm afraid. But they changed the dom structure and implementation of the menuing system again, enough to break the customBehavior again. The last time this happened was when 4.3 came out, as you can see in the code.

0 Karma

rhayle
Path Finder

Does this work with multiple pulldowns?
If not, what would I need to change?

What would cause it not to work with Splunk 6.0?

0 Karma
Get Updates on the Splunk Community!

Enterprise Security Content Update (ESCU) | New Releases

In September, the Splunk Threat Research Team had two releases of new security content via the Enterprise ...

New in Observability - Improvements to Custom Metrics SLOs, Log Observer Connect & ...

The latest enhancements to the Splunk observability portfolio deliver improved SLO management accuracy, better ...

Improve Data Pipelines Using Splunk Data Management

  Register Now   This Tech Talk will explore the pipeline management offerings Edge Processor and Ingest ...