How to handle file download errors with angularjs? - javascript

Downloading files looks very trivial and there are many working solutions. Working until the server says 401 UNAUTHORIZED. My requirements are rather natural:
In no case the current window may be replaced.
I don't want to open a new window, as it makes no sense.
In case of an error, some message must be shown to the user.
I tried to use an iframe as a target of the link and hoped to be notified in case of an error. I see I was naive.
I could imagine to place some script in the iframe which would notify main page onunload. It look a bit complicated so I'm asking first instead.
I could ask the server about the outcome. This would surely work, somehow. But it's complicated too, as there's timing problem and the session is expired, so I'd have to circumvent this.

You can use a Blob object to trigger file download from javascript. This is introduced with the File API and still in Working Draft state. So you'll have limited browser-support. As of 2015, you have wide browser support for Blob URLs and Blob Constructor.
<div ng-controller="appController" ng-app="app">
<a ng-href="{{ fileUrl }}" download="file.txt">download</a>
</div>
var app = angular.module('app', []);
// Angular prepends "unsafe" tag in ng-href to prevent XSS
// so we need to sanitize this
app.config(['$compileProvider', function ($compileProvider) {
// for Angular 1.0.x and 1.1.x, you should use urlSanitizationWhitelist
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|blob|ftp):/);
}]);
app.controller('appController', function ($scope, $window) {
// Creating a Blob with our data for download
// this will parse the URL in ng-href such as: blob:http...
var data = 'some data here...',
blob = new Blob([data], { type: 'text/plain' }),
url = $window.URL || $window.webkitURL;
$scope.fileUrl = url.createObjectURL(blob);
});
See a demo fiddle here.
There are some libraries to extend browser support for this such as Blob.js
You should also check out FileSaver.js which also falls back to data:URI.

You could handle HTTP errors without it interrupting your controller / service logic by making use of HTTP interceptors. When an error happens, handle it in an interceptor and then broadcast the error to an error handler, which could display an error to the user.
$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
return {
'request': function(config) {
// same as above
},
'response': function(response) {
// same as above
}
};
});

Related

How to add custom header for accessing a page URL and then performing test automation on that page, i am using a webdriverio,mocha,nodejs framework

I have a web page where the functionality changes when a custom header "headerKey": "headervalue" is being set through a chrome extension. While I can do this manually I want to do it through the code in order to do test automation.
Note:-
Please help with the approaches.
There is no functionality in webdriver to perform this operation.
I have used modheader but it is not working.
getModHeaderExtension() {
const filename = path.join(__dirname, "Modify.crx");
console.log(filename);
const stream = fs.readFileSync(filename);
return new Buffer(stream).toString('base64');
}
I know some time passed since the question was added, although it took me some time to figure it out by myself so maybe someone will make use of it.
Add #wdio/devtools-service to your dev dependencies (this should have the same major version as your other #wdio libraries, otherwise you can get some problems)
You need to add devtools to services in wdio.conf.ts:
export const config = {
[...]
services: ['chromedriver', 'devtools']
[...]
}
Now add you can add headers in you test method or wherever you need to:
const encodedCredentials = base64.encode(`${appConfig.basicAuthDevUser.userName}:${appConfig.basicAuthDevUser.password}`);
browser.cdp('Network', 'setExtraHTTPHeaders', {
headers: {
Authorization: `Basic ${encodedCredentials}`,
},
});
More on using devtools with wdio:
https://webdriver.io/docs/devtools-service/
More on chrome specific capabilites:
https://chromedevtools.github.io/devtools-protocol/tot/Network/
I was able to find the solution to pass a custom header while opening my webpage.
I am using ChromeDevTools service which is supported by webdriverio v5, using its method
browser.cdp(domain, command, parameters);
For the domain, command, and parameters please visit devtools protocol below:-
https://chromedevtools.github.io/devtools-protocol/

MEAN.js - How to redirect after Push function

Im new to MEAN.js and im trying to figure out how to redirect a user after posting a comment to an article.
The function called is addComment and adds the comment fine after pressing submit. What i need to know is how to redirect similar to the way it redirects after posting an article.
addComment() Pushing to a nested Array
$scope.addComment = function () {
var article = $scope.article;
$scope.article.comment.push({details: this.details, created: Date.now(), user: user._id});
$scope.update(false);
this.details = '';
console.log(article._id);
$location.path('articles/' + article._id);
console.log(article._id);
};
I need to use the $location.path('articles/' + article._id); properly if the push was successful.
Thanks in advance.
Try injecting $window and run a lower level location api with $window.location.href = 'path';
also it may matter that you want to keep your data in sync so try $location.path('/someNewPath').replace();
also take a look at your $locationProvider service settings
$location service configuration To configure the $location service,
retrieve the $locationProvider and set the parameters as follows:
html5Mode(mode): {boolean|Object} true or enabled:true - see HTML5
mode false or enabled:false - see Hashbang mode requireBase:true - see
Relative links default: enabled:false
hashPrefix(prefix): {string} prefix used for Hashbang URLs (used in
Hashbang mode or in legacy browser in Html5 mode) default: ""
More information about the $location service in the Angular docs
https://docs.angularjs.org/guide/$location

Executing a script via AJAX on Firefox OS device

My question regards the Apps CSP https://developer.mozilla.org/en-US/Apps/CSP
Here it says that all the remote script, inline script, javascript URIs, and other security issues won't work on a Firefox OS app.
So, I tried to download a script that is necessary for my app (Flurry and Ad service) and neither would work on the device. The way I made the call was with AJAX, that way I would avoid the remote and inline scripting that both scripts ment. In the simulator works perfectly, but on the device the ads never show and the Flurry session never starts.
Here is the part of my code where I make the AJAX call for Flurry:
$.ajax({
url: 'https://cdn.flurry.com/js/flurry.js',
dataType: "script",
xhrFields: {
mozSystem: true
},
success: function(msg){
console && console.log("Script de Flurry: luego de la descarga en AJAX "+msg);
flurryLibrary = true;
FlurryAgent.startSession("7ZFX9Z4CVT66KJBVP7CF");
},
error:function(object,status,errortxt){
console && console.log("The script wasn't downloaded as text. The error:" +errortxt);
flurryLibrary = false;
},
always: function(object,status,errortxt){
console && console.log("The script may or may not be downloaded or executed. The error could be:" +errortxt);
}
});
In my app I use the systemXHR permission and make the calls for other websites using this line:
request = new XMLHttpRequest({ mozSystem: true });
Wich is the same as using the xhrFields{mozSystem:true} in the AJAX call.
I believe it's not a cross domain problem because in the rest of my app I make calls for xml files that are not in my domain, and the calls are returned succesfully.
So, my question is, can a Firefox OS app execute scripts that are downloaded via AJAX? Is there a way to get around this problem?
Thank you for your time.
PS: I forgot to add that my app is privileged, just in case you ask
I believe that is a security feature and the short answer to your question would be NO. To quote the CSP doc that you linked to yourself:
You cannot point a at a remote JavaScript file. This means that all JS files that you reference must be included in your app's package.
If you load a JS file using ajax from a remote server, that JS is not included in your app package. You should be careful to obey CSP restrictions. It is possible to get many things working in the simulator or even the phone while developing without fully complying to CSP, but that does not mean it is OK. When you submit your app in future to any credible marketplace (such as Firefox Marketplace), it will be reviewed carefully to make sure it does not violate CSP restrictions. As a general rule of thumb, I would say any attempt at dynamically evaluating JS code will be a security risk and most likely banned by CSP regulations.
First, I'll point out that your two examples are not equivalent.
$.ajax({
xhrFields: {
mozSystem: true
},
});
Is the same as
request = new XMLHttpRequest();
request.mozSystem = true;
which is not the same as
request = new XMLHttpRequest({ mozSystem: true });
Instead, we can follow the advice in the linked bug report and run the following at application load time:
$.ajaxSetup( {
xhr: function() {
return new window.XMLHttpRequest( {
mozSystem: true
} );
}
} );
This alone should fix your problem. However, if it doesn't work, then the next workaround here is to fetch the script resource as plain text and then load that text content as a script.
However, inline scripts and data: URLs are off-limits for privileged Firefox OS apps. We might still accomplish this goal through a blob: URL, however:
window.URL = window.URL || window.webkitURL;
var request = new XMLHttpRequest({ mozSystem: true });
request.open("GET", "https://cdn.flurry.com/js/flurry.js");
// when the Ajax request resolves, load content into a <script> tag
request.addEventListener("load", function() {
// make a new blob whose content is the script
var blob = new Blob([request.textContent], {type: 'text/javascript'});
var script = document.createElement('script');
script.src = window.URL.createObjectURL(blob);
// after the script finishes, do something else
script.addEventListener("load", function() {
flurryLibrary = true;
FlurryAgent.startSession("7ZFX9Z4CVT66KJBVP7CF");
});
document.body.appendChild(script);
});
However, if the script itself does something not allowed by the CSP, then you're definitely out of luck.
You must use mozSystem and mozAnon properties, example:
var xMLHttpRequest = new XMLHttpRequest({
mozAnon: true,
mozSystem: true
});
Its a shame this is a problem, I was hoping on getting loadScript working, as firefoxOS is an environment, and in my app all the application code is HTML5 and local, the current rule is all the scripts need to be loaded in memory in one shot, unless you url load a full page, which means you can not have a persisten wrapper around the site, and ajax inthe pages with assosiated scripts when needed. you would have thought that firefox would have enabled local lazy load for scripts at least. works in chrome, but not in firefox.

sammy.js url not found cause of subdirectory

Hi I create a spa with knockout, amplify and sammy.
If I now click an a link like:
#/page?s=About
it links to url.de/subdirectory/#/page?s=About which is right but the console fires following error:
GET url.de/About 404 (Not Found)
because it should be:
url.de/subdirectory/About
My sammy code is:
var app=$.sammy(function () {
// define prexecutes
// update parameters in appModel from request for all routes
this.before('', function() {
//setParameters(this);
});
// authenticate on any page except for login and logout routes
this.before({except: {path:/\/(login|logout|hp)/}}, function() {
});
// actual routes
// home
this.get('#/', function() {
appModel.page("home");
return false;
});
// content
this.get('#/page', function(eventContext) {
content(eventContext);
});
});
app.run('#/');
How do I get sammy not ignoring my subdirectory in which my site is?
You can try with another custom route definition like this
this.get('#/page/:s', function(eventContext) { // where s is parametre
content(eventContext);
});
also use url like this
#/page/About // it will read 'About' as value of parameter 's'
instead of
#/page?s=About
Hope this helps
I ran into a similar situation myself recently. I could not find any configuration options or workarounds that would allow Sammy.js to route to a subdirectory. My solution was to create a virtual host on the server hosting my app with the subdirectory as the subdomain in order to place the app at the document root. In your case, the virtual server would map from url.de/subdir/About to subdir.url.de/About. I realize that this may not be possible for you (since I don't know how much control you have over your hosting environment), but it got me moving again pretty quickly. Hope this helps.
P.S. As a small aside, I just browsed through the Sammy.js source at https://github.com/quirkey/sammy, and it appears that the subdirectory is stripped out (in error) to fix an IE quirk. https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L301 may be the problem.

Chrome extension "Uncaught error: Invalid value" using the redirect rules

I am trying to write an extension that will download audio files when it detects them being requested by chrome. I am basing this project on the code of the two samples "Download_links" and "catifier" provided by Google. Here is what I currently have:
var RequestMatcher = chrome.declarativeWebRequest.RequestMatcher;
var IgnoreRules = chrome.declarativeWebRequest.IgnoreRules;
var RedirectRequest = chrome.declarativeWebRequest.RedirectRequest;
var songFileURL = "http://somefile.mp3";
...
function registerRules() {
var redirectRule = {
priority: 100,
conditions: [
new RequestMatcher({
contentType: ['audio/mp3']
}),
],
actions: [
chrome.downloads.download({url: songFileURL}),
]
};
...
When I load this up and try it out, I get an error: Uncaught Error: Invalid value for argument 1. Property '.0': Value does not match any valid type choices. No matter what I try I cannot figure out what is causing this error. I am fairly to Chrome extensions and JavaScript in general, so I am sure that this is an easy fix, but I cannot figure it out. Any ideas?
I think the problem is that you specify unsupported action. The list of available actions can be found on the chrome.declarativeWebRequest page.
I think you should use chrome.webRequest.onBeforeRequest, onHeadersReceived, or onComplete to trace media links and initiate custom downloads from there, possibly in some deferred way, because I'm not sure downloads will work just from another request handler.
To make a start you may have a look at Google's CatBlock example, or to another related answer or another one. Basically, you need to add appropriate event handler by means of addListener, and in the handler invoke chrome.downloads.download({url: request.url}), where the request is passed to the handler as input parameter.
Which one of events to choose (for example, onBeforeRequest or onComplete) you should decide based on your requirements. As I understand, you don't want to block original request, so it may be useful to wait utill original downloading is completed, and then process it in onComplete handler, so that Chrome would optimize the process by just copying already downloaded file from cache.
As alternative, you can block initial download by returning {cancel: true} from onBeforeRequest handler for every sound file, and then start your download as a single one, possibly with saveAs option involved.

Categories

Resources