Hi I am trying to get all documents library only created by the logged users. With the following code I get also libraries which was not created from a user. Thank you.
function GetAllLibraries() {
var listCollection = lists.getEnumerator();
while (listCollection.moveNext()) {
var listName = listCollection.get_current().get_title('Title');
document.getElementById('leftDiv').innerHTML += "<b>" + listName + "<b/>" + "<br />";
}
}
Since you are utilizing SharePoint JavaScript API (a.k.a JSOM) it is a bit tricky since SP.List object does not expose Author property to determine who created this object. But the good news that Author property could be extracted from SP.List.schemaXml property as demonstrated below
Here is a complete example how to retrieve lists created by current user
var ctx = SP.ClientContext.get_current();
var allLists = ctx.get_web().get_lists();
var currentUser = ctx.get_web().get_currentUser();
ctx.load(allLists,'Include(SchemaXml)');
ctx.load(currentUser);
ctx.executeQueryAsync(
function(){
var lists = allLists.get_data().filter(function(list){
var listProperties = schemaXml2Json(list.get_schemaXml());
var listAuthorId = parseInt(listProperties.Author);
return listAuthorId == currentUser.get_id();
});
console.log("The amount of lists created by current user: " + lists.length);
},
logError);
}
function schemaXml2Json(schemaXml)
{
var jsonObject = {};
var schemaXmlDoc = $.parseXML(schemaXml);
$(schemaXmlDoc).find('List').each(function() {
$.each(this.attributes, function(i, attr){
jsonObject[attr.name] = attr.value;
});
});
return jsonObject;
}
function logError(sender,args){
console.log(args.get_message());
}
If you want to know who created list or library, you need to get property SPList.Author. As i know, you can't get it by JSOM.
My advice for you is to develop your own http hanlder with logic on server-side and call it by ajax. For example, you pass arguments into handler like web url (_spPageContextInfo.webAbsoluteUrl), current user login or id (_spPageContextInfo.userId), and in handler iterate lists on web, compare current user and list creator. Finally, return needed lists info.
Or just develop web part and do the same: iterate lists and compare it with SPContext.Current.Web.CurrentUser
UPDATE:
Example of c# code. You can put it in your web part or event handler. In this code we iterate all lists on SPWeb and save lists title created by current user.
private void GetLists()
{
using (SPSite site = new SPSite("{site_url}"))
{
using (SPWeb web = site.OpenWeb())
{
SPListCollection listCol = web.Lists;
List<string> currentUserLists = new List<string>();
foreach(SPList list in listCol)
{
if (list.Author.ID == SPContext.Current.Web.CurrentUser.ID)
{
currentUserLists.Add(list.Title);
}
}
}
}
}
Related
I'm trying to create a database with "users" and their data in it. Strangely it doesn't put() new variables in it when I try to for the third time. To do all this I create a local database dblocal and replicate this DB to the remote db called dbremote. At first I create a document with one variable.
function newuser() {
if (window.document.consent_form.consent_to_share.value) {
var id = "p" + Date.now() + "-" + Math.floor(Math.random() * 10000);
var dblocal = new PouchDB(id);
var consenttoshare = window.document.consent_form.consent_to_share.value;
document.cookie = id;
var dbremote = 'http://localhost:5984/experiment';
dblocal.put({
_id: id,
consent: consenttoshare
});
dblocal.replicate.to(dbremote, {live: true});
}
}
This all worked well, in another js file I'm trying to add a variable to the same document by executing the following function putdb(). Im doing this in the following way (as said in their documentation is the right way):
function putdb () {
if (document.cookie){
var id = document.cookie;
var loggedin = "True";
var dblocal = new PouchDB(id);
dblocal.get(id).then(function (doc) {
doc.loggedin = loggedin;
return dblocal.put(doc);
}).then(function () {
return dblocal.get(id);
}).then(function (doc) {
console.log(doc);
var dbremote = 'http://localhost:5984/experiment';
dblocal.replicate.to(dbremote, {live: true});
});
}
}
This succesfully added the variable loggedin to the document as I wanted. However upon trying to add information to this document for the third time (again in another js file), nothing happens. I used exactly the same approach as before but only use different variables.
function putdb (checked) {
if (document.cookie) {
var id = document.cookie;
var checkedlist = [];
for (i = 0; i < checked; i++) {
checkedlist.push($("input[type=checkbox]:checked")[i].value)
}
var playlistname = document.getElementById("playlistname").value;
var dblocal = new PouchDB(id);
dblocal.get(id).then(function (doc) {
doc.checkedlist = checkedlist;
doc.playlistname = playlistname;
return dblocal.put(doc);
}).then(function () {
return dblocal.get(id);
}).then(function (doc) {
console.log(doc);
var dbremote = 'http://localhost:5984/experiment';
dblocal.replicate.to(dbremote, {live: true});
});
}
}
I checked all variables, they are correct.
I tried plain text variables.
The script does run.
I tried to add information to the document the way I did the first time.
None of all this seems to add another variable to the document as I wanted in the last function. I think it has to do with the way pouchDB works which I don't know. help is much appreciated!
There are a number of problems in your code that results in bad usage of PouchDB, and may lead to problems.
First of all, it does not make a lot of sense to give your document the same id as the name of your database. Assuming you want a one database per user approach, there are two approaches you can follow.
Multiple document approach
You can instead make multiple documents within the same database with different id's. For instance, your 'consent' information may be stored like this:
var id = "p" + Date.now() + "-" + Math.floor(Math.random() * 10000);
let dblocal = new PouchDB(id);
document.cookie = id;
let dbremote = 'http://localhost:5984/experiment';
dblocal.put({
_id: "consent",
consent: window.document.consent_form.consent_to_share.value
});
dblocal.replicate.to(dbremote, {live: true});
While your playlist information is stored like this:
dblocal.put({
_id: "playlist",
name: playlistname,
itemsChecked: checkedlist
});
Single-document approach
The second option is to store a single document containing all the information you want to store that is associated to a user. In this approach you will want to fetch the existing document and update it when there is new information. Assuming you named your document global-state (i.e. replace "consent" in the first code snippet with "global-state"), the following code will update a document:
dblocal.get("global-state").then((doc)=>{
doc.loggedIn = true; // or change any other information you want
return dblocal.put(doc);
}).then((response)=>{
//handle response
}).catch((err)=>{
console.log(err);
});
Furthermore, you should only call the
dblocal.replicate.to(dbremote, {live: true});
function once because the 'live' option specifies that future changes will automatically be replicated to the remote database.
this is a kind of embarrassing question but I'm stuck.
My background is managed code and I never learned JavaScript but yet I want to implement a tiny project.
The script is running on SharePoint 2010, queries items from a custom list using the JavaScript Object Model and populates a Google chart or table respectively.
With the help of MSDN and Google Developer I was able to query data from one list and visualize it.
However, I'm unable to transfer the concept to query multiple lists, combine result sets and finally pass to Google API.
In my code I created a chain of callbacks like showChart->loadListData->drawChart. This proves to be bad design since it's inflexible and cannot be extended. All API methods are asynchronous and don't have return values but expect method names to call once finished. This is what get's me stuck and where I lack knowledge.
I'm very happy for every comment and answer, also I can provide actual source code if requested. Thank you in advance, Toby
UPDATE as asked for by #Utkanos:
var listItems;
$(document).ready(function() {
ExecuteOrDelayUntilScriptLoaded(loadChartData, "sp.js");
});
function loadChartData() {
var camlQuery = SP.CamlQuery.createAllItemsQuery();
camlQuery.set_viewXml("<View><Query><Where><Eq><FieldRef Name='Year'/><Value Type='Text'>2015</Value></Eq></Where></Query></View>");
loadListData('CustomList', camlQuery, drawChart, readListItemFailed);
}
function loadListData(listTitle, camlQuery, onSuccess, onFail) {
context = SP.ClientContext.get_current();
var list = context.get_web().get_lists().getByTitle(listTitle);
var listItems = list.getItems(camlQuery);
context.load(listItems);
context.executeQueryAsync(function(sender, args){onSuccess(listItems);}, onFail);
}
function drawDpOverviewChart(listItems) {
var data;
var enumerator = listItems.getEnumerator();
data = new google.visualization.DataTable();
data.addColumn('string', 'Column1');
data.addColumn('number', 'Column2');
var listItem;
while (enumerator.moveNext()) {
listItem = enumerator.get_current();
data.addRow([listItem.get_item('Title'), Math.round(listItem.get_item('Balance')/10000)/100]);
}
var options = {'title':'Pretty Chart'};
var chart = new google.visualization.PieChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
function readListItemFailed(sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
if using SP 2010 on a typical .aspx page, you have some tools available,
such as MicrosoftAjax.js and _spPageContextInfo
using the REST API, you can join lists on lookup fields and include fields from both lists in one query
following is an example url for a call to rest...
'/_vti_bin/ListData.svc/MI_Projects?$expand=ModifiedBy&$filter=ModifiedBy/Id eq 738'
this call actually "joins" the list MI_Projects to the UserInformationList by "expanding" ModifiedBy
so when the data returns, you can access any of the user info fields, i.e.
row.ModifiedBy.Name
this can be done with lookup fields on custom lists as well
to make the call, you can use the Sys.Net.WebRequest class from MicrosoftAjax
this class also allows you to pass variables to the callback
see following snippet...
function makeCall() {
// Sys.Net.WebRequest is from MicrosoftAjax.js
var webRequest = new Sys.Net.WebRequest();
webRequest.get_headers()['Cache-Control'] = 'no-cache';
webRequest.get_headers()['Accept'] = 'application/json';
webRequest.get_headers()['Content-Type'] = 'application/json';
webRequest.set_url(_spPageContextInfo.webServerRelativeUrl + '/_vti_bin/ListData.svc/MI_Projects?$expand=ModifiedBy&$filter=ModifiedBy/Id%20eq%20738');
// use the 'user context' to pass variables you want available in the callback
webRequest.set_userContext({
Title: 'variable to pass to completed callback'
});
webRequest.add_completed(restComplete);
webRequest.invoke();
}
// the first argument of callback is the Sys.Net.WebRequestExecutor class
function restComplete(executor, eventArgs) {
if (executor.get_responseAvailable()) {
if (executor.get_statusCode() === 200) {
// get variable passed via user context
var variablePassed = executor.get_webRequest().get_userContext().Title;
// i.e. -- build google table
// add rows received from rest (forEach is from MicrosoftAjax.js)
// list results array = executor.get_object().d.results
Array.forEach(executor.get_object().d.results, function (row) {
data.addRow(row.Title, row.Id, row.ModifiedBy.Name);
}, this);
}
}
}
In my Sharepoint 2010 Web Part, I've got this Javascript:
function getListItemID(username, payeename, oList) {
var arrayListEnum = oList.getEnumerator();
...which is called by this:
function upsertPostTravelListItemTravelerInfo1() {
var clientContext = SP.ClientContext.get_current();
var oList =
clientContext.get_web().get_lists().getByTitle('PostTravelFormFields');
this.website = clientContext.get_web();
currentUser = website.get_currentUser();
var itemCreateInfo = new SP.ListItemCreationInformation();
this.oListItem = oList.addItem(itemCreateInfo);
var travelersEmail = $('traveleremail').val();
/* If this is an update, the call to getListItemID() will return a val; otherwise (an insert), get from newly instantiated ListItem. */
listId = getListItemID(currentUser, travelersEmail, oList);
I got the basis for this code from here.
But got the err listed above ("Uncaught TypeError: oList.getEnumerator is not a function");
One answer said I needed to add this:
<script type="text/javascript" src="/_layouts/15/sp.js" ></script>
...which I changed from "15" to "14" as that is the folder/version we're using.
That not only didn't work, but was unrecognized. I then found a clue here, namely to add this:
$(document).ready(function () { ExecuteOrDelayUntilScriptLoaded(CustomAction, "sp.js"); });
...but that only an error prior to the one already shown, namely, "Uncaught ReferenceError: CustomAction is not defined"
So what's the scoop? What is required to getEnumerator(), or otherwise retreive the ID val I need?
Here is the full code to that method, to show what I'm trying to accomplish, and how:
function getListItemID(username, payeename, oList) {
var arrayListEnum = oList.getEnumerator();
while (arrayListEnum.moveNext()) {
var listItem = arrayListEnum.get_current();
if (listItem.get_item("ptli_formPreparedBy") === username &&
listItem.get_item("ptli_TravelersEmail") === payeename &&
listItem.get_item("ptli_formCompleted") == false) {
return listItem.get_id();
}
}
return '';
}
UPDATE
When I tried this (first and third lines are new):
<SharePoint:ScriptLinkID="ScriptLink1" Name="SP.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false"></SharePoint:ScriptLink>
<script type="text/javascript">
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);
...which was inspired by a cat here, I got, "System.Web.HttpParseException was unhandled by user code
Message=The server tag is not well formed."
Personally, I don't think Sharepoint is very well formed. But that's (right) beside the point (no pun intended).
Problem 1: You're calling getEnumerator on a list instead of a list item collection
getEnumerator() can only be called on a list item collection (not on a List object), and only after it's been populated with items by running clientContext.executeQueryAsync()
Problem 2: You need to call executeQueryAsync to populate the list item collection
When using the SharePoint JavaScript client object model, your code needs to be broken up into two parts: the first part specifies what you want to get, and involves you loading queries and commands into an SPClientContext object; the second part lets you manipulate the results of the query to SharePoint, and runs as an asynchronous callback of the query execution.
Create your context, specify which lists you want to access, etc.
Run clientContext.executeQueryAsync() (where clientContext is an SP.ClientContext object), and pass in delegate functions to run on success or failure
In your "onSuccess" delegate function, you can work with the results of the commands you loaded up in step 1
Problem 3: You won't be able to return values directly from an asynchronously executing function
Because step 3 above runs asynchronously, you can't get a return value from it. Any logic that depends on the results that you get in step 3 needs to be moved forward in the execution chain, using function delegation and callbacks.
Problem 4: Inefficient filtering of list items
This is really more of a design flaw than a show-stopping problem, but instead of having your code return every item in the list, and then using JavaScript to enumerate through the results to see if the item you want is in there, you should tell SharePoint what filter options you want before it even executes the query. Then it'll only give you items that match your query.
Use a CAML query for this; CAML (Collaborative Application Markup Language) is an XML-based query language that SharePoint uses extensively. There are plenty of resources and tools for composing CAML queries, and you can even steal the CAML query from a SharePoint list view web part if you've already created a view that matches your query.
Example of how to query a SharePoint list using JavaScript CSOM
Here's an example using parts of your code:
/*
ExecuteOrDelayUntilScriptLoaded(yourcode,"sp.js") makes sure
your code doesn't run until SP.js (the SharePoint JavaScript CSOM)
has been loaded
*/
ExecuteOrDelayUntilScriptLoaded(function(){
var payeename = $('traveleremail').val();
var clientContext = SP.ClientContext.get_current();
var oList = clientContext.get_web().get_lists().getByTitle('PostTravelFormFields');
/* Use a CAML query to filter your results */
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name=\'ptli_TravelersEmail\' /><Value Type=\'Text\'>'+payeename+'</Value></Eq></Where></Query></View>');
/* get the list item collection from the list */
var oListItems = oList.getItems(camlQuery);
/* tell SharePoint to load the list items */
clientContext.load(oListItems);
/* execute the query to get the loaded items */
clientContext.executeQueryAsync(
/* onSuccess Function */
Function.createDelegate(this,function(){
/*
now that the query has run, you can get an enumerator
from your list item collection
*/
var arrayListEnum = oListItems.getEnumerator();
var ids = [];
while(arrayListEnum.moveNext()){
var listItem = arrayListItem.get_current();
ids.push(listItem.get_id());
}
alert(ids.length > 0 ? "IDs of matching items: " + ids : "No matching items found!");
}),
/*onFailure Function*/
Function.createDelegate(this,function(sender,args){
alert("Whoops: " + args.get_message() + " " + args.get_stackTrace());
})
);
},"sp.js");
The CAML query in the example code only filters on the ptli_TravelersEmail column; you'd need to add some <And> elements to capture the other two filter conditions you want.
This is what finally worked for me, thanks to Thriggle:
function setListItemID(username, payeename) {
var clientContext = new SP.ClientContext.get_current();
var oList = clientContext.get_web().get_lists().getByTitle('PostTravelFormFields');
/* Use a CAML query to filter your results */
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><Eq><FieldRef Name=\'ptli_TravelersEmail\' /><Value Type=\'Text\'>' + payeename + '</Value></Eq></Where></Query></View>');
/* get the list item collection from the list */
var oListItems = oList.getItems(camlQuery);
/* tell SharePoint to load the list items */
clientContext.load(oListItems);
/* execute the query to get the loaded items */
clientContext.executeQueryAsync(
/* onSuccess Function */
Function.createDelegate(this, function () {
/*
now that the query has run, you can get an enumerator
from your list item collection
*/
var arrayListEnum = oListItems.getEnumerator();
var ids = [];
while (arrayListEnum.moveNext()) {
var listItem = arrayListItem.get_current();
ids.push(listItem.get_id());
}
if (ids.length > 0) {
listId = ids[0];
}
else {
listId = '';
}
}),
/*onFailure Function*/
Function.createDelegate(this, function (sender, args) {
alert("Whoops: " + args.get_message() + " " + args.get_stackTrace());
})
);
}
I'm trying to figure of the best--or really any working way-- to store key/value pairs in a SharePoint hosted app. The pairs need to:
Be loaded on start up, if the settings exist, otherwise use defaults.
Be created on demand--i.e. a user can add a new setting in the UI, then I use that setting elsewhere in the code to make changes. For example a use a custom string of text as a list name instead of the app's default setting.
I've tried using the PropertyBag, but get an Access Denied error when trying to write to it.
I've also tried to use a list but had problems getting that technique to work correctly.
Does anyone have a suggestion of a good method and how it would be done. I'd be happy to revisit the techniques I've already attempted, if those are the best ways.
Keep in mind that this question should be restricted to things that work with a SharePoint-hosted app. That means that C#, and server-side code are out.
Here's the solution I ended up using--storing settings in a list in the hostweb of the app.
It's made up of a few functions seen below.
CreateSettingsList:
Create makes an ordinary list with the fields Title and Value, which I use to store a setting name and a value to be associated with it. This is called in the document ready function to ensure that a list has been created, and if one already has, it goes on and tries to read from it. If a list didn't exist before, I call a function to initialize default variable values in the list.
//___________________________________Create settings list________________________________________
function createSettingsList()
{
// Create a SharePoint list with the name that the user specifies.
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
var listCreationInfo = new SP.ListCreationInformation();
//title the list
listCreationInfo.set_title("PTOAppSettings");
//set the base type of the list
listCreationInfo.set_templateType(100); //generic list
listCreationInfo.set_description("A list for custom app settings. If you have uninstalled the Paid Time Off App with no intention of reinstalling, this list can be safely deleted.");
var lists = hostweb.get_lists();
//use the creation info to create the list
var newList = lists.add(listCreationInfo);
var fieldCollection = newList.get_fields();
//add extra fields (columns) to the list & any other info needed.
fieldCollection.addFieldAsXml('<Field Type="Text" DisplayName="Value" Name="Value" />', true, SP.AddFieldOptions.AddToDefaultContentType);
newList.update();
currentContext.load(fieldCollection);
currentContext.load(newList);
currentContext.executeQueryAsync(onSettingsListCreationSuccess, onSettingsListCreationFail);
}
function onSettingsListCreationSuccess(){
//All is well.
initializeSettings();
}
function onSettingsListCreationFail(sender, args) {
//alert("We didn't create the list. Here's why: " + args.get_message());
//If a list already exists, we expect the creation to fail, and instead try to read from the existing one.
getSetting("VAR_CCEmail");
}
Initialize:
Initialize creates new list items for the variables that I may be storing in the future. I set their value to "" or null if they're not being used.
//___________________________________Initialize setting(s)________________________________________
function initializeSettings()
{
//Get info to access host web
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
//Get list in host web
var lstObject = hostweb.get_lists().getByTitle("PTOAppSettings");
//Prepare an object to add a new list item.
var listItemCreationInfo = new SP.ListItemCreationInformation();
var newItem = lstObject.addItem(listItemCreationInfo);
//Create item. You should repeat this for all the settings you want to track.
newItem.set_item('Title', "VAR_CCEmail");
newItem.set_item('Value', "");
//Write this new item to the list
newItem.update();
currentContext.executeQueryAsync(onListItemSuccess, onListItemFailure);
function onListItemSuccess() {
//Load customizations, if any exist
getSetting("VAR_CCEmail");
}
function onListItemFailure(sender, args) {
bootbox.dialog({
title: "Something went wrong!",
message: "We were unable to initialize the app settings! Here's what we know about the problem: " + args.get_message() + '\n' + args.get_stackTrace(),
buttons: {
success:{
label: "Ok"
}
}
});
}
}
Set:
Set is a fairly straightforward function that accepts a setting name and a value and allows you to update the value stored in a given variable.
//___________________________________Set setting________________________________________
function setSetting(setting, value){
//Get info to access host web
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
//Get list in host web
var list = hostweb.get_lists().getByTitle("PTOAppSettings");
//A caml query get the appropriate setting
var queryXml = "<View><Query><Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + setting + "</Value></Eq></Where></Query></View>"
var query = new SP.CamlQuery();
query.set_viewXml(queryXml);
var items = list.getItems(query);
currentContext.load(items);
currentContext.executeQueryAsync(onListItemSuccess, onListItemFailure);
function onListItemSuccess() {
//looking up a specific setting should only return one item in the array.
var item = items.getItemAtIndex(0);
//update the value for the item.
item.set_item("Value", value);
item.update();
}
function onListItemFailure(sender, args) {
bootbox.dialog({
title: "Something went wrong!",
message: "We were unable to set app settings! Here's what we know about the problem: " + args.get_message() + '\n' + args.get_stackTrace(),
buttons: {
success:{
label: "Ok"
}
}
});
}
}
Get:
Get reads the list, finds the setting that you specified, and then determines if the Value associated with that setting "" or null or if it is an actual value. If it is an actual value, I write the value to the global variable that the program uses to do things with that setting.
//___________________________________Get setting________________________________________
function getSetting(setting) {
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
var list = hostweb.get_lists().getByTitle("PTOAppSettings");
//A caml query to get manager name for the record where user is equal to current user.
var queryXml = "<View><Query><Where><Eq><FieldRef Name='Title' /><Value Type='Text'>" + setting + "</Value></Eq></Where></Query></View>"
var query = new SP.CamlQuery();
query.set_viewXml(queryXml);
var items = list.getItems(query);
currentContext.load(items);
currentContext.executeQueryAsync(
function() //on success.
{
//get first (and only) item.
var item = items.getItemAtIndex(0);
var value = item.get_item("Value");
//If the property is empty it hasn't been set.
if (value === "" || value === null){
//Return null to the appropriate global variable. Not all of the settings in this switch are implemented in the program yet, but may be later.
switch(setting) {
case "VAR_PaidTimeOff":
paidTimeOffListName = "";
break;
case "VAR_Contacts":
contactsListName = "";
break;
case "VAR_CCEmail":
carbonCopyEmail = "";
break;
}
}
else
{
//Return the value. Not all of the settings in this switch are implemented in the program yet, but may be later.
switch(setting) {
case "VAR_PaidTimeOff":
paidTimeOffListName = value;
break;
case "VAR_Contacts":
contactsListName = value;
break;
case "VAR_CCEmail":
carbonCopyEmail = value;
break;
}
}
},
function(sender,args){
bootbox.dialog({
title: "Something went wrong!",
message: "We were unable to get app settings! Here's what we know about the problem: " + args.get_message() + '\n' + args.get_stackTrace(),
buttons: {
success:{
label: "Ok"
}
}
});
});
}
This could be expanded to include other functions to do other special tasks, for example you could make a "createSetting" function that would allow you to add new settings on the fly (one of the requirements I mentioned in my initial question). In my case, initializing a set group of settings fulfilled my needs, but other may want a way to write more.
I want to update a div with a list of anchors that I generate from a local database in chrome. It's pretty simple stuff, but as soon as I try to add the data to the main.js file via a callback everything suddenly becomes undefined. Or the array length is set to 0. ( When it's really 18. )
Initially, I tried to install it into a new array and pass it back that way.
Is there a setting that I need to specify in the chrome manifest.json in order to allow for communication with the database API? I've checked, but all I've been able to find was 'unlimited storage'
The code is as follows:
window.main = {};
window.main.classes = {};
(function(awe){
awe.Data = function(opts){
opts = opts || new Object();
return this.init(opts);
};
awe.Data.prototype = {
init:function(opts){
var self = this;
self.modified = true;
var db = self.db = openDatabase("buddy","1.0","LocalDatabase",200000);
db.transaction(function(tx){
tx.executeSql("CREATE TABLE IF NOT EXISTS listing ( name TEXT UNIQUE, url TEXT UNIQUE)",[],function(tx,rs){
$.each(window.rr,function(index,item){
var i = "INSERT INTO listing (name,url)VALUES('"+item.name+"','"+item.url+"')";
tx.executeSql(i,[],null,null);
});
},function(tx,error){
});
});
self._load()
return this;
},
add:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("INSERT INTO listing (name,url)VALUES(?,?)",[item.name,item.url],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',error)
})
});
self._load()
},
remove:function(item){
var self = this;
self.modified = true;
self.db.transaction(function(tx){
tx.executeSql("DELETE FROM listing where name='"+item.name+"'",[],function(tx,rs){
//console.log('success',tx,rs)
},function(tx,error){
//console.log('error',tx,error);
});
});
self._load()
},
_load:function(callback){
var self = this;
if(!self.modified)
return;
self.data = new Array();
self.db.transaction(function(tx){
tx.executeSql('SELECT name,url FROM listing',[],function(tx,rs){
console.log(callback)
for(var i = 0; i<rs.rows.length;i++)
{
callback(rs.rows.item(i).name,rs.rows.item(i).url)
// var row = rs.rows.item(i)
// var n = new Object()
// n['name'] = row['name'];
// n['url'] = row['url'];
}
},function(tx,error){
//console.log('error',tx,error)
})
})
self.modified = false
},
all:function(cb){
this._load(cb)
},
toString:function(){
return 'main.Database'
}
}
})(window.main.classes);
And the code to update the list.
this.database.all(function(name,url){
console.log('name','url')
console.log(name,url)
var data = []
$.each(data,function(index,item){
try{
var node = $('<div > '+item.name + '</div>');
self.content.append(node);
node.unbind();
node.bind('click',function(evt){
var t = $(evt.target).attr('href');
chrome.tabs.create({
"url":t
},function(evt){
self._tab_index = evt.index
});
});
}catch(e){
console.log(e)
}
})
});
From looking at your code above, I notice you are executing "self._load()" at the end of each function in your API. The HTML5 SQL Database is asynchronous, you can never guarantee the result. In this case, I would assume the result will always be 0 or random because it will be a race condition.
I have done something similar in my fb-exporter extension, feel free to see how I have done it https://github.com/mohamedmansour/fb-exporter/blob/master/js/database.js
To solve a problem like this, did you check the Web Inspector and see if any errors occurs in the background page. I assume this is all in a background page eh? Try to see if any error occurs, if not, I believe your encountering a race condition. Just move the load within the callback and it should properly call the load.
Regarding your first question with the unlimited storage manifest attribute, you don't need it for this case, that shouldn't be the issue. The limit of web databases is 5MB (last I recall, it might have changed), if your using a lot of data manipulation, then you use that attribute.
Just make sure you can guarantee the this.database.all is running after the database has been initialized.