Prevent Browser Caching of UI5 Application Resources - caching

We have an SAPUI5 App deployed on SAP PO. The problem is that whenever we do changes and deploy the new version of our application, the changes are not reflected and we need to do a Hard Reload and Clear browser Cache to fetch new changes.
This is causing a lot of issues as we cannot ask clients to clear cache after every change.
Below are the unsuccessful methods we tried so far:
Enabling "resources/sap-ui-cachebuster/sap-ui-core.js" in SAPUI5 bootstrap.
Using 'Application Cache buster' for application resource ( using sap-ui-cachebuster-info.json)
Setting HTML header to keep no cache:
<meta http-equiv='cache-control' content='no-cache, no-store, must-revalidate'>
<meta http-equiv='Expires' content='-1'>
<meta http-equiv='Pragma' content='no-cache'>
Clear cookies with below code:
document.cookie.split(";").forEach(function(c) {
document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
});
None of the above solutions have worked so far. This is what we see in Networks tab of Chrome:
NOTE: Application is deployed on SAP PO 7.4 ( JAVA Stack)

We had the same issue than you on SAP MII and I have spent months with several OSS Calls for SAP to provide an acceptable solution.
They did so in the SP3 of SAP MII (we haven't updated yet but I hope their correction is right), but this will not apply in your case as you're on SAP PO but it's still a Java Stack.
So I think you should open an OSS Call, recommending to SAP to consult SAP Notes:
2463286 - Issues when reloading JavaScript files
2459768 - Force browsers to reload modified resource files
They will probably redirect you to the following stack overflow topic:
http://stackoverflow.com/questions/118884/how-to-force-browser-to-reload-cached-css-js-files
But this is only a work around, SAP web server on Java stack doesn't seem to be working correctly and they have to provide a correction.
Hope this will help you.
EDIT
Hi,
Here is an update, there is a work around that we sometime use.
We have a URL parameter which is used to identify if a reload of the page is needed.
See below a JS snippet that we embed the index.html page of the SAPUI5 app.
Hope this will help you.
<script>
window.onload = function () {
version = undefined;
fCheckVersionMatch = false;
onInit();
};
/***************************************************************************
* Function launch when we start the application it test
* - if the Reload parameters is set in the url
* - if we are loading an hold application with a false reload value
****************************************************************************/
var onInit = function() {
checkParamReload();
};
/***************************************************************************
* Check in the url if there is the reload value and if this value is less
* than the difference with the date now => avoid when using favorite link
* to load a previous version with an incorrect time stamp
****************************************************************************/
var checkParamReload = function() {
var sUrlParameters = window.top.document.location.search;
var regexReload = /(\?|&)reload=([^&]*)/;
var aReload = sUrlParameters.match(regexReload);
var nTime = aReload == null ? NaN : parseInt(aReload[2]);
if ( isNaN(nTime) || Math.abs(Date.now() - nTime) > 60000 ) {
// In case no reload tag is present or it's value is more than 1 minute ago, reload page
reloadPage(true); // True means force reload => reset retry count.
}
};
/***************************************************************************
* Reload page and make sure the reload param is updated.
* If force reload is used, retry count is resetted, otherwise it is
* it is incremented up to a limit, which - in case it is reached - stops
* the reload process and instead display an error message.
****************************************************************************/
var reloadPage = function (bForce) {
var retries = 0;
var oLocation = window.top.document.location;
var sSearch = oLocation.search;
sSearch = queryReplace(sSearch, "reload", _ => Date.now());
if (bForce) {
sSearch = queryReplace(sSearch, "retry", _ => 0);
} else {
sSearch = queryReplace(sSearch, "retry", function (n) {
if (isNaN(parseInt(n))) {
return 0;
} else {
retries = parseInt(n);
return retries + 1;
}
});
}
if (retries < 10) {
// Reload Page
window.top.document.location.replace(oLocation.origin + oLocation.pathname + sSearch + oLocation.hash);
} else {
// Display error
document.getElementById('Error').style.display = "block";
}
};
var queryReplace = function (sQuery, sAttribute, fnReplacement) {
// Match the attribute with the value
var matcher = new RegExp(`(\\?|&)${ sAttribute }=([^&]*)`);
var sNewQuery = sQuery.length < 2 ? `?${ sAttribute }=` : sQuery;
if (sNewQuery.search(matcher) < 0) {
// If we could not match, we add the attribute at the end
sNewQuery += "&" + sAttribute + "=" + fnReplacement("");
} else {
sNewQuery = sNewQuery.replace(matcher, (_, delim, oldVal) => delim + sAttribute + "=" + fnReplacement(oldVal));
}
return sNewQuery;
}
</script>

Related

GAS sends multiple slack notifications

I'd like to send notifications to slack using Google Apps Scripts.
It's supposed to happen when any change is made on Gsheet but sometimes the program sends multiple notifications even after only a single change.
I guess my code has any fault or the triggers cause any problem - because I apply this function to more than one sheet.
But it doesn't happen "always", so is it an error caused by GAS specification?
My code is as below. Any idea or advice will be appreciated.
function slackNotification(sheetname,slack_channel){
Logger.log(sheetname)
var mySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname);
var myActiveRange = mySheet.getActiveRange();
var lastRow = mySheet.getLastRow();
var pdfColumn = PDF_column_n
var checkColumn = slack_column_n
Logger.log(lastRow)
var unslacked = [];
for (var i=1; i <= lastRow; i++){
var cell = mySheet.getRange(i, checkColumn);
if(cell.isBlank()){
Logger.log(i + "is not yet notified");
unslacked.push(i);
}
}
Logger.log("unslcked yet"+unslacked)
for (var i=unslacked[0]; i <= unslacked[unslacked.length - 1]; i++){
var pdfCell = mySheet.getRange(extention_column+i)
var checkSlack = mySheet.getRange(slack_column+i)
Logger.log(checkSlack.isBlank())
Logger.log(pdfCell.getValue())
if (pdfCell.getValue() == "pdf" && checkSlack.isBlank() == true)
{
var requestId = mySheet.getRange(request_column+i).getValue();
var clientId = mySheet.getRange(client_column+i).getValue();
var claimId = mySheet.getRange(claim_column+i).getValue();
var groupId = mySheet.getRange(group_column+i).getValue();
Logger.log(groupId)
if (branches.includes(groupId)){
var message = "\nrequestId:" + requestId + "\nclientId:" + clientId + "\nclaimId:" + claimId
var postUrl = slack_channel
var username = 'botbot';
var icon = ':hatching_chick:';
var jsonData =
{
"username" : username,
"icon_emoji": icon,
"text" : message,
link_names: 1
};
var payload = JSON.stringify(jsonData);
var options =
{
"method" : "post",
"contentType" : "application/json",
"payload" : payload
};
UrlFetchApp.fetch(postUrl, options);
checkSlack.setValue(true);
}
}
}
}
Apps Script triggers are bound project-based, not script filed based
If you have multiple functions with the same name in your project - there is no way to know to which one the trigger becomes bound.
Most likely your 5 onChange triggers are all bound to the same function and thus the function is executed multiple times (sending multiple Slack notifications as a result)
It does not make a difference if the functions are located within the same script file or within different script files within the same project
Splitting up your code into different code files is only for visibility/structure purposes, for the trigger the functions are still located all within the same project
Solution
Remove from your project all existing triggers that are bound to functions with not unique names
Give to all functions in your project (not just script file) unique names and save all script files
Install new triggers either programmatically or manually
Now you can be sure that each trigger is bound to a different function
Mind that if the content of your functions is the same (e.g. containing request to send Slack notifications), this content will be executed the same amount of times as the number of respective triggers in your project, so if the content of your functions is identical, you actually need to keep only one function bound to one trigger and remove all the functions of identic content.

GSCRIPT - How to cache HTML page content?

I'm kinda stuck with something here.
I've a HTML template that i'm loading, and within this page there's a lot of JavaScript going on.
I'm trying to accelerate the operation by caching the template with the onOpen() of my Google Sheet. I can't figure how to cache my HTML page CalForm.html (from my internal Google Sheet scripts).
Here's what I have for now:
Creating the cache
function CacheCreate() {
CacheService.getScriptCache().put('CalCache', 'CalForm');
Browser.msgBox("done");
}
Get the cache
var evalSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Evaluation');
var row = evalSheet.getActiveCell().getRow();
var CalCache2 = CacheService.getScriptCache().get('CalCache');
Browser.msgBox(CacheService.getScriptCache().get('CalCache'))
initialize(row);
//var cache = CacheService.getScriptCache();
//var cache2 = cache.get('rss-feed-contents');
//Browser.msgBox(cache.get('rss-feed-contents'));
var html = HtmlService
.createTemplateFromFile(CalCache2)
.evaluate()
.setWidth(1200)
.setHeight(560)
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
SpreadsheetApp.getUi().showModalDialog(html, 'Calculatrice');
Thanks for your help!
First you'll want to get the HTML from your "internal Google Sheet scripts", the following code will get the HTML (in string format), given that you have a File in your Script called "template.html".
var template = HtmlService.createHtmlOutputFromFile('template').getContent();
I run a check to see if the data is already in the cache...
function getObjectsFromCache(key, objects, flush)
{
Logger.log("Running: getObjectsFromCache(" + key + ", objects)");
var cache = CacheService.getScriptCache();
var cached = cache.get(key);
flush = false; // 1st run 45.33, 2nd run 46.475
//flush = true;// 65.818 seconds
if (cached != null && flush != true)
{
Logger.log("\tEXISTING DATA -> ");
cached = cache.get(key);
}
else
{
Logger.log("\tNEW DATA -> ");
//Logger.log("\tJSON.stringify(objects): " + JSON.stringify(objects) + ", length: " + objects.length);
//If you're working with spreadsheet objects, or array data, you'll want to put the data in the cache as a string, and then reformat the data to it's original format, when it is returned.
//cache.put(key, JSON.stringify(objects), 1500); // cache for 25 minutes
//In your case, the HTML does not need to be stringified.
cache.put(key, objects, 1500); // cache for 25 minutes
cached = objects;
}
return cached;
}
I commented-out the JSON.Stringify(objects), because in my original code I use another function called formatCachedData(cache, key) to return the different types of data - a multi-dimensional array (spreadsheet data), or Google user data from AdminDirectory.Users.list({...}).

How to configure SonarQube search for Names/Keys that do NOT contains strings

I have some searches for projects in SonarQube that are like this...
Date > "2014-01-01"
Name Contains > "ProjectBear"
What I would like is
Date > "2014-01-01"
Name Contains > "ProjectBear"
Key DOES NOT Contains > "customerA"
Is this possible in the free/standard SonarQube?
I've not got an "Proper" answer for this, but I have created a GreaseMonkey script to hide the modules I dont need to care about at the moment.
// ==UserScript==
// #name My Fancy New Userscript
// #namespace http://use.i.E.your.homepage/
// #version 0.1
// #description enter something useful
// #match http://10.187.72.999:9000/measures/filter/4?display=list
// #copyright 2012+, You
// ==/UserScript==
function addJQuery(callback) {
var script = document.createElement("script");
script.setAttribute("src", "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js");
script.addEventListener('load', function() {
var script = document.createElement("script");
script.textContent = "window.jQ=jQuery.noConflict(true);(" + callback.toString() + ")();";
document.body.appendChild(script);
}, false);
document.body.appendChild(script);
}
// the guts of this userscript
function main() {
// Note, jQ replaces $ to avoid conflicts.
jQ('a[href$="28555"]').parent().parent().remove(); //projectA
jQ('a[href$="20094"]').parent().parent().remove(); //projectB
jQ('a[href$="21139"]').parent().parent().remove(); //projectC
// alert("There are " + jQ('a').length + " links on this page.");
}
// load jQuery and execute the main function
addJQuery(main);
No, it's not possible.
By the way there's only one unique version of SonarQube that is open source.

Filtering a loaded kml file in OpenLayers

I'm trying to create an interactive search engine (for finding event tickets) of which one of its features is a visual map that shows related venues using OpenLayers. I have a plethora of venues (3000+) in a kml file that I would like to selectively show a filtered subsection of. Below is the code I have but when I try to run it has a JavaScript error. Running firebug and chrome developer tools makes me think that it is not getting passed the parameters I give because it says that the variables are null. However, I cannot figure out why they are not getting passed. Any insight is greatly appreciated.
var map, drawControls, selectControl, selectedFeature, select;
$('#kml').load('venuesComplete.kml');
kml=$('#kml').html();
function showVenues(state, city, venue){
filterStrategy = new OpenLayers.Strategy.Filter({});
var kmllayer = new OpenLayers.Layer.Vector("KML", {
strategies: [filterStrategy,
new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "venuesComplete.kml",
format: new OpenLayers.Format.KML({
extractStyles: true,
extractAttributes: true
})
})
});
select = new OpenLayers.Control.SelectFeature(kmllayer);
kmllayer.events.on({
"featureselected": onFeatureSelect,
"featureunselected": onFeatureUnselect
});
map.addControl(select);
select.activate();
filter = new OpenLayers.Filter.Comparison({
type: OpenLayers.Filter.Comparison.LIKE,
property: "",
value: ""
});
function clearFilter(){
filterStrategy.setFilter(null);
}
function setFilter(property, value){
filter.value = value;
filter.property = property;
filterStrategy.setFilter(filter);
}
var vector_style = new OpenLayers.Style();
if(venue!=""){
setFilter('name', venue);
}else if(city!=""){
setFilter('description', city);
}else if(state!=""){
setFilter('description', state);
}
map.addLayer(kmllayer);
function onPopupClose(evt) {
select.unselectAll();
}
function onFeatureSelect(event) {
var feature = event.feature;
var selectedFeature = feature;
var popup = new OpenLayers.Popup.FramedCloud("chicken",
feature.geometry.getBounds().getCenterLonLat(),
new OpenLayers.Size(100,100),
"<h2>"+feature.attributes.name + "</h2>" + feature.attributes.description +'<br>'+feature.attributes,
null,
true,
onPopupClose
);
document.getElementById('venueName').value=feature.attributes.name;
document.getElementById("output").innerHTML=event.feature.id;
feature.popup = popup;
map.addPopup(popup);
}
function onFeatureUnselect(event) {
var feature = event.feature;
if(feature.popup) {
map.removePopup(feature.popup);
feature.popup.destroy();
delete feature.popup;
}
}
}
function init() {
map = new OpenLayers.Map('map');
var google_map_layer = new OpenLayers.Layer.Google(
'Google Map Layer',
{type: google.maps.MapTypeId.HYBRID}
);
map.addLayer(google_map_layer);
state="";
state+=document.getElementById('stateProvDesc').value;
city="";
city+=document.getElementById('cityZip').value;
venue="";
venue+=document.getElementById('venueName').value;
showVenues(state,city,'Michie Stadium');
map.addControl(new OpenLayers.Control.LayerSwitcher({}));
map.zoomToMaxExtent();
}
IF I UNDERSTAND CORRECTLY, your kml does not load properly. if this is not the case, please disconsider my answer.
it is very important to check if your kml layer was properly loaded. i have a map that loads multiple dynamic (from php) kml layers and it is not uncommon to have a large layer simply not load. when that happens, the operation is aborted, but, as far as openlayers is concerned, the layer was properly loaded.
so i do 2 things: i check if the amount of loaded data meets the expected number of features in my orginal php kml parser (i use a jquery or ajax call for that) and then, in case there is a discrepancy, i try reloading (since this is a loop, i limit it to 5 attempts, so as not to loop infinitely).
check out some of my code here

XDomainRequest object caching/asynchronous call issue

I have an aspx page on which I am using XDomainRequest object to populate two div(s) with html returned from AJAX response.
I have used Jquery to get the divs and perform "each()" on the retrieved List
var divs = $("div");
divs.each(function(index) {
if (window.XDomainRequest) {
xdr = new XDomainRequest();
if (xdr) {
xdr.onload = function() {
alert("XDR Response - " + xdr.responseText);
var currentDivID = divs[index].attributes["id"].value;
var selectedDiv = $("div[id='" + currentDivID + "']");
if (xdr.responseText == '') selectedDiv.attr("style", "display:none;");
else selectedDiv.append(xdr.responseText);
};
xdr.open("GET", xdrUrl);
try {
xdr.send();
} catch (e) {
alert(e);
}
} else {
alert('Create XDR failed.');
}
} else {
alert('XDR not found on window object .');
}
}
Now, whats happening is , i have two Divs on a page that have different IDs and when this code runs on "$.ready(function(){})" , both requests are asynchronously sent to the server and processed
the result is
1. sometimes the onload get the response for the second div in both div results.
2. IE sents only one request to the server(I am using fiddler to see what requests are sent to server).
Can anybody guide me whats wrong with the code ? As far as I know XDR does not support synchronous calls, and asynchronous calls are giving me wrong results. Any workaround/tip for this problem.
Issue solved by myself when I pointed out a mistake in my code:(.
xdr = new XDomainRequest();
should be
var xdr = new XDomainRequest();
For Point 2 , I added "Cache-Control:no-cache" header in my response and it solved the matter.

Resources