Hi @hallt3
I have a dashboard that serves a purpose almost similar to yours.
Let me try to explain it here.. See if that helps in your case.
For this, I have a used a JS file called download_test.js:
//$("#modal3").append($("<button class=\"btn btn-primary\">Launch Modal</button>").click(function() {
$("#download_file_div").append($("<button id=\"download_file_button\">Download File</button>").click(function() {
// The require will let us tell the browser to load /static/app/MyTest/Modal.js with the name "Modal"
require(['jquery',
'/static/app/MyTest/Modal.js',
'underscore',
'splunkjs/mvc',
'splunkjs/mvc/utils',
'splunkjs/mvc/tokenutils',
'splunkjs/mvc/searchmanager',
'splunkjs/mvc/simplexml/ready!'
], function($,Modal,_, mvc, utils, TokenUtils, SearchManager) {
// Now we initialize the Modal itself
var myModal = new Modal("modal1", {
title: "Alert!!!",
backdrop: 'static',
keyboard: false,
destroyOnHide: true,
type: 'normal'
});
// Create a Search Manager
var exportSearch = new SearchManager({
earliest_time: "-24h",
latest_time: "now",
search: "index=_internal |top 5 sourcetype", //some dummy search to fill up the content of the csv
autostart: false
});
$(myModal.$el).on("hide", function() {})
myModal.body.append($('<p>Do you want to download the session details?</p>'));
myModal.footer.append($('<button>').attr({
type: 'button',
'data-dismiss': 'modal'
}).addClass('btn btn-primary icon-export').on('click', function(_, $, mvc, utils, TokenUtils) {
exportSearch.startSearch();
var exportResults = exportSearch.data("results");
exportResults.on("data", function () {
//var data = exportResults.collection().toJSON();
var data = exportResults.data().fields +"\n";
console.log("d1: "+data);
console.log("exportResults.data().rows: "+exportResults.data().rows);
jQuery.each(exportResults.data().rows, function(row){
data=data+"\n"+exportResults.data().rows[row];
});
console.log("d2: "+data);
// download data as file
var hiddenElement = document.createElement('a');
//hiddenElement.href = 'data:attachment/csv,' + encodeURI(JSON.stringify(data, null, 2));
hiddenElement.href = 'data:attachment/csv,' + encodeURI(data);
hiddenElement.target = '_blank';
hiddenElement.download = 'exportData.csv';
hiddenElement.click();
});
}), $('<button>').attr({
type: 'button',
'data-dismiss': 'modal'
}).addClass('btn btn-primary').text('Never Mind').on('click', function() {
//Some logic for the 'Never Mind' button, if required.. May be the logout option.
var logoutURL = window.location.protocol
+"//"+window.location.hostname
+":8000"
+"/en-US/account/logout"
//window.location=logoutURL;//Redirect the user to logout page if he clicks on 'Never Mind', Uncomment if you want.
}))
myModal.show(); // Launch it!
})
}))
Please note that this uses the same Modal.js that is shipped with splunk (I got it from some other apps, though). I'll still provide the content of that Modal.js, here:
'use strict';
/*console.log("Here's my code path", document.currentScript)*/
//console.log("Trying again", [].slice.call(document.querySelectorAll('script[src]')).pop().src.replace(/.*?static\/app\/([^\/]*).*/, "$1"))
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { return typeof obj; } : function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; };
}();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _setModalMaxHeight(element) {
this.$element = $(element);
this.$content = this.$element.find('.modal-content');
var borderWidth = this.$content.outerHeight() - this.$content.innerHeight();
var dialogMargin = $(window).width() < 768 ? 20 : 60;
var contentHeight = $(window).height() - (dialogMargin + borderWidth);
var headerHeight = this.$element.find('.modal-header').outerHeight() || 0;
var footerHeight = this.$element.find('.modal-footer').outerHeight() || 0;
var maxHeight = contentHeight - (headerHeight + footerHeight);
this.$content.css({
'overflow': 'hidden'
});
this.$element
.find('.modal-body').css({
'max-height': maxHeight,
'overflow-y': 'auto'
});
}
define(['underscore'], function(_) {
return function() {
/**
* A utility wrapper around Bootstrap's modal.
* @param {string|object} id Either an id or a jQuery element that contains the id in its "data-target" attribute
* @param {object} [options] Bootstrap modal options
* @param {boolean|string} [options.backdrop] Whether or not to show a backdrop, or the string "static" to show a backdrop that doesn't close the modal when clicked
* @param {boolean} [options.keyboard] Whether or not the escape key clsoes the modal
* @param {boolean} [options.show=false] Whether or not to show the modal when it's created
* @param {string} [options.type='normal'] Can be 'normal', 'wide', or 'noPadding'
* @param {string} [options.title] The modal's title
* @param {boolean} [options.destroyOnHide=true] Destroy the modal when it's hidden
* @returns {element}
*/
function Modal(id, options) {
var _this = this;
_classCallCheck(this, Modal);
var modalOptions = _.extend({ show: false }, options);
// if "id" is the element that triggers the modal display, extract the actual id from it; otherwise use it as-is
var modalId = id != null && (typeof id === 'undefined' ? 'undefined' : _typeof(id)) === 'object' && id.jquery != null ? id.attr('data-target').slice(1) : id;
var header = $('<div>').addClass('modal-header');
var headerCloseButton = $('<button>').addClass('close').attr({
'type': 'button',
'data-dismiss': 'modal',
'aria-label': 'Close'
}).append($('<span>').attr('aria-hidden', true).text('×'));
this.title = $('<h3>').addClass('modal-title');
this.body = $('<div>').addClass('modal-body');
this.footer = $('<div>').addClass('modal-footer');
// Multiselect can grow large and step over footer causing issues clicking button in footer
this.footer.css('position', 'relative');
this.footer.css('z-index', 1);
/*console.log("Here's my code path 2", document.currentScript)*/
this.$el = $('<div>').addClass('modal hide fade mlts-modal').attr('id', modalId).append($('<div>').addClass('modal-dialog').append($('<div>').addClass('modal-content').append(header.append(headerCloseButton, this.title), this.body, this.footer)));
if (modalOptions.title != null) this.setTitle(modalOptions.title);
if (modalOptions.type === 'wide') this.$el.addClass('modal-wide');
else if (modalOptions.type === 'noPadding') this.$el.addClass('mlts-modal-no-padding');
// remove the modal from the dom after it's hidden
if (modalOptions.destroyOnHide !== false) {
this.$el.on('hidden.bs.modal', function() {
return _this.$el.remove();
});
}
this.$el.on('show.bs.modal', function() {
$(this).show();
_setModalMaxHeight(this);
});
$(window).resize(function() {
if ($('.modal.in').length != 0) {
_setModalMaxHeight($('.modal.in'));
}
});
this.$el.modal(modalOptions);
}
_createClass(Modal, [{
key: 'setTitle',
value: function setTitle(titleText) {
this.title.text(titleText);
}
}, {
key: 'setAlert',
value: function setAlert(alertMessage, alertType) {
if (this.alert == null) {
this.alert = $('<div>').addClass('mlts-modal-alert');
this.body.prepend(this.alert);
}
//Messages.setAlert(this.alert, alertMessage, alertType, undefined, true);
}
}, {
key: 'removeAlert',
value: function removeAlert() {
//Messages.removeAlert(this.alert, true);
}
}, {
key: 'show',
value: function show() {
this.$el.modal('show');
}
}, {
key: 'hide',
value: function hide() {
this.$el.modal('hide');
}
}]);
return Modal;
}();
});
//# sourceURL=Modal.js
Finally, the HTML Code:
<dashboard script="download_test.js">
<label>Download Test</label>
<row id="submit_button">
<panel id="btnPanel">
<html>
<div class="divTable">
<div class="divTableBody">
<div class="divTableRow">
<div class="divTableCell" style="width: 35%;" align="left">
<div id="download_file_div" style="display: table-cell;padding-left: 5px;">
</div>
</div>
<div class="divTableCell" style="width: 65%" align="left"/>
</div>
</div>
</div>
</html>
</panel>
</row>
</dashboard>
This dashboard should populate the Download File Button dynamically, as soon as you open the dashboard. Then while you click on that button, it should bring some modal like below:
Just click on that download icon, and the JS will trigger the search query index=_internal |top 5 sourcetype (you can always change it according to your convenience) and bring up the SaveAs dialog box. The Default filename is exportData.csv. Click on Never Mind button and the modal will disappear. Alternatively, you can also click on the X button on the modal.
Do let me know if you have any queries. That download_test.js file was used for some internal requirements, hence it is not properly formatted. If you face any difficulties understanding it, I can try to explain it.
... View more