Adding rows to a sqlite database using a loop (Phonegap) - javascript

I have created a little Phonegap application that gets news data from an Ajax call via XML. This works fine, but I would like to store the data in a database table to also allow offline reading of the news.
So when the Ajax callback loops through the data, I fill a global news object with it and then call a function to check if the data is already stored in the database. If not, it should be inserted into the database news table.
The problem is that the in the transaction to store the news in the table, it seems like my news Object is not present anymore because I get the message:
Uncaught TypeError: Cannot read property 'title' of undefined in ...
So how can I make sure that this work? Here is the code of the part where I select the news and want to check if it's already there:
// Check if a news from the internet already exists in the database; If not, insert it
function checkNewsInDB(){
db.transaction(function(tx){
tx.executeSql("SELECT * FROM NEWS", [], checkSuccess, dbErrorCB);
}, dbErrorCB, dbSuccessCB);
}
// Result Callback from the News Check
function checkSuccess(ctx, result){
var len = result.rows.length;
var found = false;
for(var n = 0; n < newsContainer.length; n++){
for(var r = 0; r < len; r++){
if(result.rows.item(r).n_title == newsContainer[n].title
&& result.rows.item(r).n_pubdate == newsContainer[n].pubdate){
found = r;
}
}
if(found == false){
db.transaction(function(tx){
tx.executeSql("INSERT INTO NEWS (n_title, n_link, n_creator, n_pubdate, n_description) VALUES (?,?,?,?,?)", [newsContainer[n].title, newsContainer[n].link, newsContainer[n].creator, newsContainer[n].pubdate, newsContainer[n].description], insertSuccess, dbErrorCB);
}, dbErrorCB, dbSuccessCB);
} else {
found = false;
}
}
}
The newsContainer IS filled with a few rows of data, I have checked that. I would be very happy if somebody could help me understand why this does not work.
Thanks in advance!
Greetings,
Bernd

db.transaction is asynchronous - by the time executeSql actually runs, n has already been incremented to the end of the loop.
Rather than creating a new transaction for each item, try moving the loop inside the transaction function.

Thanks for the answer. Here is the code that works for all people who have the same problem:
// Check if a news from the internet already exists in the database; If not, insert it
function checkNewsInDB(){
db.transaction(function(tx){
tx.executeSql("SELECT * FROM NEWS", [], checkSuccess, dbErrorCB);
}, dbErrorCB, dbSuccessCB);
}
// Result Callback from the News Check
function checkSuccess(ctx, result){
var len = result.rows.length;
var found = false;
for(var n = 0; n < newsContainer.length; n++){
for(var r = 0; r < len; r++){
if(result.rows.item(r).n_title == newsContainer[n].title
&& result.rows.item(r).n_pubdate == newsContainer[n].pubdate){
found = r;
}
}
if(found == false){
var title = newsContainer[n].title;
var link = newsContainer[n].link;
var creator = newsContainer[n].creator;
var pubdate = newsContainer[n].pubdate;
var description = newsContainer[n].description;
ctx.executeSql("INSERT INTO NEWS (n_title, n_link, n_creator, n_pubdate, n_description) VALUES (?,?,?,?,?)",
[title, link, creator, pubdate, description], insertSuccess, dbErrorCB);
} else {
found = false;
}
}
}

Related

Array returning empty in Javascript

This is all still pretty new to me but I am running into an interesting behavior issue when generating an array of numbers in a NodeJS application that handles .nessus result files. First, some details on what I am trying to accomplish. My application generates an array [1234,1223,1222] of entries from an uploaded "results" file that is then used to query a mongodb instance to determine if those entries are currently in the DB. If those entries are not currently in the mongodb instance, it redirects to a page where a user can edit them before being added. If there are no new entries, it goes to a page to generate a report on the entries.
When a file is uploaded, it stores the new entries. In .nessus files, sometimes there is more than one host with entries. That changes the json structure and the function needs to iterate a little differently. The following function is how those entries are stored. This is important as this is where the weird behavior originates (I think)
function parsePluginNumbers(json){
var pluginNumbers = []
//Let's check the number of hosts
var hostLength = json['NessusClientData_v2']['Report'].ReportHost.length
if (hostLength != undefined) {
for (var i = 0; i < hostLength; i++) { //Since there is more than 1, need to iterate over each host to find the findings.
var item_length = json['NessusClientData_v2']['Report'].ReportHost[i].ReportItem.length
for (var t = 0; t < item_length; t++) { //Iterate through each finding on each host
if (json['NessusClientData_v2']['Report'].ReportHost[i].ReportItem[t].risk_factor != 'None') {
var newEntry = json['NessusClientData_v2']['Report'].ReportHost[i].ReportItem[t].pluginID
if (pluginNumbers.indexOf(newEntry) == -1) {
pluginNumbers.push(newEntry)
}
else {
continue
}
} else {
continue
}
}
}
} else {
var item_length = json['NessusClientData_v2']['Report']['ReportHost'].ReportItem.length
for (var t = 0; t < item_length; t++) { //Iterate over findings
if (json['NessusClientData_v2']['Report']['ReportHost'].ReportItem[t].risk_factor != 'None') {
var newEntry = json['NessusClientData_v2']['Report']['ReportHost'].ReportItem[t].pluginID
if (pluginNumbers.indexOf(newEntry) == -1) {
pluginNumbers.push(newEntry)
}
else {
continue
}
} else {
continue
}
}
}
return pluginNumbers
}
Once those plugins are stored. Another function is called to look if those results are in the mongodbinstance. In this function, those plugins are in an array "pluginsTotal".
function queryForNewResultsInANessusFile(pluginsTotal, collectionname, filename){ //function to call mongodb query and send results to parseNewFindings and parseOldFindings.
var db = req.db;
var collection = db.get(collectionname);
collection.find({ 'PluginNumber' : { $in: pluginsTotal }}, 'FindingTitle FindingDescription Remediation Mitigation SeeAlso PluginFamily PluginNumber CVE Risk -_id', function(error, result){
var newPluginArray = parseOutFindingNumbersInMongoDB(result, pluginsTotal);
//IF statements go here with specific redirects as needed to check if there are new values not in the repo
}
During this collection.find call, there is a function parseOutFindingNumbersInMongoDB that is called to determine if there are plugins in the .nessus results file that are not in the repo. It compares the results from collection.find and pluginsTotal (generated from the first function) and returns an array of the new plugins that are not in the repo. The function details are below:
function parseOutFindingNumbersInMongoDB(repoResults, reportPlugins) {
for (var i = 0; i < repoResults.length; i++){
var index = reportPlugins.indexOf(repoResults[i].PluginNumber);
if (index != -1) {
reportPlugins.splice(index, 1);
}
else {
continue
}
}
return reportPlugins
}
Now to my question --- When I upload a .nessus file with more than one host, parseOutFindingNumberInMongoDB always returns empty even though there are new entries. What gives? Is it the way I parse out the numbers to begin with in the parsePluginNumbers function or is because it is called in the collection.find synchronous function (This seems unlikely as if there is one host, it returns the new plugin values as I want)? Any thoughts/ideas/review would be much appreciated as I cannot figure out what is wrong. I have checked the data types within the array before being passed into the functions and they all match up.
It's always returning an empty array because every element in the array matches the condition to be spliced.
You first retrieve elements that have a PluginNumber in pluginTotal array with this filter { $in: pluginsTotal }
collection.find({ 'PluginNumber' : { $in: pluginsTotal }}, 'FindingTitle FindingDescription Remediation Mitigation SeeAlso PluginFamily PluginNumber CVE Risk -_id', function(error, result){
var newPluginArray = parseOutFindingNumbersInMongoDB(result, pluginsTotal);
}
Then you remove all elements that have a PluginNumber in pluginTotal
var index = reportPlugins.indexOf(repoResults[i].PluginNumber);
if (index != -1) {
reportPlugins.splice(index, 1);
}
So the result is always an empty array.

How do I use data out of chrome.storage.get function?

I'm trying to grab data from chrome extension storage, but I can use them only in this function.
var help = new Array();
chrome.storage.local.get(null, function(storage){
//get data from extension storage
help = storage;
console.log(storage);
});
console.log(help); // empty
Result in console:
content.js:1 content script running
content.js:11 []
content.js:8 {/in/%E5%BF%97%E9%B9%8F-%E6%99%8F-013799151/: "link", /in/adam-
isaacs-690506ab/: "link", /in/alex-campbell-brown-832a09a0/: "link",
/in/alex-davies-41513a90/: "link", /in/alex-dunne-688a71a8/: "link", …}
Async function has won. I wrote my code again and now function is called hundreds time, i can not do this in dirrefent way
code:
console.log("content script running");
var cards = document.getElementsByClassName("org-alumni-profile-card");
var searchText = "Connect";
function check(exi, cards) {
chrome.storage.local.get(null, function(storage) {
for (var key in storage) {
if (storage[key] == "link" && key == exi) {
cards.style.opacity = "0.3";
}
}
});
}
for (var i = 0; i < cards.length; i++) {
var ctd = cards[i].getElementsByClassName(
"org-alumni-profile-card__link-text"
);
var msg = cards[i].getElementsByClassName(
"org-alumni-profile-card__messaging-button-shrunk"
);
if (ctd.length < 1 || msg.length > 0) {
cards[i].style.display = "none";
} else {
var exi = cards[i]
.getElementsByClassName("org-alumni-profile-card__full-name-link")[0]
.getAttribute("href");
check(exi, cards[i]);
}
}
SOLUTION of my problem
I wanted to delete this topic, but I can not, so instead of doing that, I'll put here what I've done finally.
The code above is wrong becouse, it was taking a list of links from website and for each from them script was grabbing a data from a storage... Which was stupid of course. I didn't see a solution which was so easy:
Put all your file's code in this function - it grabs data from storage just once.
I'm so sorry for messing up this wonderfull forum with topic like this.
Hope u'll forgive.
help will return undefined because it is referencing a asynchronous function and not the return value of that function. The content from storage looks to be printed on content.js:8, i.e. line 8.

Clearing JS response

I am making a call to an API. The API returns a list of results. When it does so - the response is fed into an object which I then use to iterate through and display them.
Here is the function which does that:
var getAvailability = () => {
if (chosenData.hotel == "") {
showError("Please select a location before booking.");
$timeout(() => LTBNavService.setTab('location'), 50);
return;
}
searchResponse = {};
console.log(searchResponse);
WebAPI.getHotelAvailability(genSearchObject()).then((data) => {
searchResponse = data;
$timeout(() => $('[data-tab-content] .search-btn').first().focus(), 50);
generateRoomTypeObject(searchResponse);
}, (data) => searchResponse.error = data.data.errors[0].error);
};
The Problem:
The old results are still displayed until the new set of results are available. This causes a flicker and a delay which is a bad user experience.
The solution:(which i need help with)
What is the best possible way of handling this problem? Ideally, I would like to reset/clear the search response. As in, the new results are delivered and the old ones are cleared. Is this possible from within the getAvailability function?
What would be the best way to achieve this?
The Solution:
Thanks to #Daniel Beck for his suggestion to call the generateRoomTypeObject function and feed it an empty object - +1'd his comment.
This triggered an undefined error in my generateRoomTypeObject function where i was running a few length checks(makes sense, because the object was empty - so there was nothing to do length checks on).
I handled the error by handling the undefined exception and setting the searchResponse to an empty object.
var generateRoomTypeObject = (searchResponse) => {
var ratePlans = searchResponse.ratePlans,
errors = searchResponse.error,
roomTypes = [],
ignoreBiggerRooms = false;
rawRoomsObjs = [];
if (angular.isUndefined(errors)) {
// Iterate over the rate plan
if(ratePlans === undefined){
//generateRoomTypeObject -- Handle undefined by creating new object
searchResponse = {}
}else{
for (var i = 0; i < ratePlans.length; i++) {
var ratePlan = ratePlans[i],
rooms = ratePlan.rooms;
// Iterate over the rooms and add rooms to room object. Also keep a list of room types.
for (var j = 0; j < rooms.length; j++) {
//Stuff here
}
}
}
}

return from JS function

basic JS question, please go easy on me I'm a newb :)
I pass 2 variables to the findRelatedRecords function which queries other related tables and assembles an Array of Objects, called data. Since findRelatedRecords has so many inner functions, I'm having a hard time getting the data Array out of the function.
As it currently is, I call showWin inside findRelatedRecords, but I'd like to change it so that I can get data Array directly out of findRelatedRecords, and not jump to showWin
function findRelatedRecords(features,evtObj){
//first relationship query to find related branches
var selFeat = features
var featObjId = selFeat[0].attributes.OBJECTID_1
var relatedBranch = new esri.tasks.RelationshipQuery();
relatedBranch.outFields = ["*"];
relatedBranch.relationshipId = 1; //fac -to- Branch
relatedBranch.objectIds = [featObjId];
facSel.queryRelatedFeatures(relatedBranch, function(relatedBranches) {
var branchFound = false;
if(relatedBranches.hasOwnProperty(featObjId) == true){
branchFound = true;
var branchSet = relatedBranches[featObjId]
var cmdBranch = dojo.map(branchSet.features, function(feature){
return feature.attributes;
})
}
//regardless of whether a branch is found or not, we have to run the cmdMain relationship query
//the parent is still fac, no advantage of the parent being branch since cmcMain query has to be run regardless
//fac - branch - cmdMain - cmdSub <--sometimes
//fac - cmdMain - cmdSub <-- sometimes
//second relationship query to find related cmdMains
var relatedQuery = new esri.tasks.RelationshipQuery();
relatedQuery.outFields = ["*"];
relatedQuery.relationshipId = 0; //fac -to- cmdMain
relatedQuery.objectIds = [featObjId];
//rather then listen for "OnSelectionComplete" we are using the queryRelatedFeatures callback function
facSel.queryRelatedFeatures(relatedQuery, function(relatedRecords) {
var data = []
//if any cmdMain records were found, relatedRecords object will have a property = to the OBJECTID of the clicked feature
//i.e. if cmdMain records are found, true will be returned; and continue with finding cmdSub records
if(relatedRecords.hasOwnProperty(featObjId) == true){
var fset = relatedRecords[featObjId]
var cmdMain = dojo.map(fset.features, function(feature) {
return feature.attributes;
})
//we need to fill an array with the objectids of the returned cmdMain records
//the length of this list == total number of mainCmd records returned for the clicked facility
objs = []
for (var k in cmdMain){
var o = cmdMain[k];
objs.push(o.OBJECTID)
}
//third relationship query to find records related to cmdMain (cmdSub)
var subQuery = new esri.tasks.RelationshipQuery();
subQuery.outFields = ["*"];
subQuery.relationshipId = 2;
subQuery.objectIds = [objs]
subTbl.queryRelatedFeatures(subQuery, function (subRecords){
//subRecords is an object where each property is the objectid of a cmdMain record
//if a cmdRecord objectid is present in subRecords property, cmdMain has sub records
//we no longer need these objectids, so we'll remove them and put the array into cmdsub
var cmdSub = []
for (id in subRecords){
dojo.forEach(subRecords[id].features, function(rec){
cmdSub.push(rec.attributes)
})
}
var j = cmdSub.length;
var p;
var sub_key;
var obj;
if (branchFound == true){
var p1 = "branch";
obj1 = {};
obj1[p1] = [cmdBranch[0].Branches]
data.push(obj1)
}
for (var i=0, iLen = cmdMain.length; i<iLen; i++) {
p = cmdMain[i].ASGMT_Name
obj = {};
obj[p] = [];
sub_key = cmdMain[i].sub_key;
for (var j=0, jLen=cmdSub.length; j<jLen; j++) {
if (cmdSub[j].sub_key == sub_key) {
obj[p].push(cmdSub[j].Long_Name);
}
}
data.push(obj);
}
showWin(data,evtObj) <---this would go away
})
}
//no returned cmdRecords; cmdData not available
else{
p = "No Data Available"
obj = {}
obj[p] = []
data.push(obj)
}
showWin(data,evtObj) <--this would go away
})
})
}
I'd like to have access to data array simply by calling
function findRelatedRecords(feature,evt){
//code pasted above
}
function newfunct(){
var newData = findRelatedRecords(feature,evt)
console.log(newData)
}
is this possible?
thanks!
Edit
Little more explanation.....
I'm connecting an Object event Listener to a Function like so:
function b (input){
dojo.connect(obj, "onQueryRelatedFeaturesComplete", getData);
obj.queryRelatedFeatures(input);
console.log(arr) //<----this doesn't work
}
function getData(relatedFeatData){
var arr = [];
//populate arr
return arr;
}
So when obj.QueryRelatedFeatures() is complete, getData fires; this part works fine, but how to I access arr from function b ?
Post Edit Update:
Due to the way that this event is being hooked up you can't simple return data from it. Returning will just let Dojo call to the next method that is hooked up to onSelectionComplete.
When init runs it is long before findRelatedRecords will ever be executed/fired by the onSelectionComplete event of the well, which is why you were seeing undefined/null values. The only way to work with this sort of system is to either 1) call off to a method like you're already doing or 2) fire off a custom event/message (technically it's still just calling off to a method).
If you want to make this method easier to work with you should refactor/extract snippets of it to make it a smaller function but contained in many functions. Also, changing it to have only one exit point at the end of the findRelatedRecords method will help. The function defined inside of subTbl.queryRelatedFeatures() would be a great place to start.
Sorry, you're kind of limited by what Dojo gives you in this case.
Pre Edit Answer:
Just return your data out of it. Everywhere where there is a showWin call just use this return.
return {
data: data,
evtObj: evtObj
}
Then your newfunct would look like this.
function newfunct(){
var newData = findRelatedRecords(feature,evt);
console.log(newData);
console.log(newData.data);
console.log(newData.evtObj);
}
If you only need that "data" object, then change your return to just return data;.
Also, start using semicolons to terminate statements.

How to loop through me/friends and assign each user it's picture from {USERID}/picture from FB.api

Using javascript I get list of facebook friends though it only returns name and id now, but I need to get the picture of each user. I try to loop through the response and then try to call the api to get picture, but due to it's async call I can't associate the returned picture with the index of the friend in the array. *this is kinda a problem that I've had with asynchronous programming in general, is there a standard pattern for this?
Example.
FB.api('me/friends', function(response) {
if(response.error == null){
var friendsSale = response.data;
var len = friendsSale.length;
for(var x=0; x<len; x++){
FB.api(friendsSale[x].id+'/picture', function(response) {
//x no longer is the same x as the initial call, and I can't pass in the orignal array object into the FB.api function to return as part of the response... or can I?
friendsSale[x].pictureUrl = response;
});
}
}
//Then how do I know when I have all the pictures set so I can then set datamodle with the complete friend array?
m.friends(friendsSale);
}
});
Yes, there is a pattern for this: a Closure
...
var len = friendsSale.length;
for (var i = 0; i < len; i++) {
(function() {
var j = i;
FB.api(friendsSale[i].id+'/picture', function(response) {
friendsSale[j].pictureUrl = response;
});
})();
}
To know when all all calls have returned you can simply keep a counter of returned calls, e.g.
...
var len = friendsSale.length;
var returnedCallsCounter = 0;
for (var i = 0; i < len; i++) {
(function() {
var j = i;
FB.api(friendsSale[i].id+'/picture', function(response) {
friendsSale[j].pictureUrl = response;
// Track number of returned calls
returnedCallsCounter++;
// Check if all calls have returned
if (returnedCallsCounter == len) {
m.friends(friendsSale);
}
});
})();
}
Simple solution for you :
All you have to do is query this :
https://graph.facebook.com/user_id/picture
and you will get the users profile picture. For example :
Querying https://graph.facebook.com/4/picture (with no access token BTW - try it in chrome pron incognito mode) :
<img src="https://graph.facebook.com/4/picture">
will yeild this smiling face :
Now you know Marks fbid :P

Categories

Resources