How insert,update,delete value of an XML element with Extendscript scriptui - javascript

I am learning extendscript for scripting adobe illustrator.It is very difficult for me to write xml related parsing.
My problem statement is given below:- "I have taken three text boxes namely name,city and country."name" is a unique key.when i click ok button the data must be saved in xml if name does not exist else update the previous name with out creating duplicate.All the names in xml file are displayed in list box.The date of particular name could be deleted by remove button.which will remove data in selected item of list box.
The code i tried to do is:-
var myWindow = new Window ("dialog", "Form");
var txt1 = myWindow.add ("edittext");//name unique
var txt2 = myWindow.add ("edittext");//city
var txt3 = myWindow.add ("edittext");//country
var btn=myWindow.add ("button", undefined, "OK");
btn.onClick = function () {
for (i=0;i<numberofnamesinxml;i++)//coding required
{
if(txt1.text!=xmlname[i]) // to verify name not there since it is like primary key
xmlFile.open("a");
xmlFile.write(root.toXMLString());
xmlFile.copy ('C:/data.xml');
xmlFile.close();
//myList refresh
}
}
var myList = myWindow.add ("listbox");
for (i=0;i<numberofnamesinxml;i++)//coding required
{
config='C:/data.xml';
config.open("r");
data= xmlname[i] //here i need to set data to
config.close();
myList.add ("item", data);
}
var btn1=myWindow.add ("button", undefined, "remove");
btn1.onClick = function () {
myList.remove (myList1.selection[i]);
//xml data having this list name must be removed
}
myWindow.show ();
Please kindly help me.

This should not be considered a full answer. I still post it because it might help finding one.
This is what I tried to write as an answer. The read/write part works but the checking of an element exists fails.
if the child is not an exact match, xml.contains(xml) will not return true.
var path = '~/Desktop/test.xml';
var xmlfile = File(path);
if (!xmlfile.exists) {
$.writeln('xml file does not exist. Creating new one');
xmlfile = new File(path);
xmlfile.open('w');
xmlfile.write('<Root></Root>');
xmlfile.close();
}
xmlfile.open('r');
xmlcontent = xmlfile.read();
xmlfile.close();
//$.writeln(xmlcontent);
var xml = new XML(xmlcontent);
// here the problems start
// the check is only working for known elements
var child = new XML('<name data="bob"></name>');
if (xml.contains(child)) {
child.#data = 'jim';
$.writeln('A child called "name" exists. Update');
xml.replace('name', child);
} else {
$.writeln('no child called "name" exists. Append');
child.#data = 'bob';
xml.appendChild(child);
}
xmlfile.open('w');
xmlfile.write(xml.toXMLString());
xmlfile.close();
My real answer is:
Use JSON instead.

Related

Why won't the makeCopy portion of my script execute properly in Google Apps Script?

I am a very novice coder and am trying to accomplish the following using a Google Form:
Rename file uploaded by user based on name defined by combination of form fields
Create a copy of the uploaded file to a specific folder in GDrive, based on answer to particular form question
So far, I have managed to get Part 1 working, but Part 2 doesn't seem to function properly (no error message, just no action). Anyone able to guide me where I'm going wrong?
function fileRename() {
var form = FormApp.getActiveForm()
// returns the total number of form submissions
var length=form.getResponses().length;
//retrieve fileID of document uploaded by user in Question 6 of the form (i.e. Index 5)
var id=form.getResponses()[length-1].getItemResponses()[5].getResponse();
//getResponses()[length-1] retrieves the last form response, accounting for the fact that the first index is zero and hte last length-1
//gets the form answers used to concatenate the file name
var fileUploadEntity=form.getResponses()[length-1].getItemResponses()[0].getResponse();
var fileUploadDate=form.getResponses()[length-1].getItemResponses()[3].getResponse();
var fileUploadType=form.getResponses()[length-1].getItemResponses()[1].getResponse();
//accesses the uploaded file
var file=DriveApp.getFileById(id);
var name = file.getName();
//changes the file name
var name = fileUploadEntity+'_'+fileUploadDate+'_'+fileUploadType
file.setName(name);
//creates a copy and saves it to the relevant regional shared drive depending on which array the entity belongs to, using its four-letter identifier
var APAC = ["WRAU", "WRNZ", "WRSG", "WRMY", "WRHK"];
var NORAM = ["WRCA", "WRCC", "WRCW", "WRUS"];
var MEA = ["WRKE", "WRUG", "WRSO", "WRSA", "WRRW", "WRTZ", "WRZW"];
var LATAM = ["WRMX"];
var EEA = ["WRBE", "WRUK"];
var folderAPAC = DriveApp.getFolderById('1IKIDSEEGHf802WaF4l4ntN9uiUO5jJpa');
var folderNORAM = DriveApp.getFolderById('1BitldN3Uw7453wxnnI1X5PUmbmTiQn5O');
var folderMEA = DriveApp.getFolderById('18tWR1C-mdO7moAtktOHJsvXjx_V0kdg0');
var folderLATAM = DriveApp.getFolderById('1cG0iPocn3KyXK8XgaxnZNWVU-HKJ97dX');
var folderEEA = DriveApp.getFolderById('1N8tB8AjMkR7gRarcwd4NYmry_wh0WVkY');
if (fileUploadEntity.indexOf(APAC)>-1) {
file.makeCopy(name, folderAPAC);
}
else if (fileUploadEntity.indexOf(NORAM)>-1) {
file.makeCopy(name, folderNORAM);
}
else if (fileUploadEntity.indexOf(LATAM)>-1) {
file.makeCopy(name, folderLATAM);
}
else if (fileUploadEntity.indexOf(MEA)>-1) {
file.makeCopy(name, folderMEA);
}
else if (fileUploadEntity.indexOf(EEA)>-1) {
file.makeCopy(name, folderEEA);
}
}
You code is using indexOf the wrong way.
Instead of
fileUploadEntity.indexOf(APAC)
try
APAC.indexOf(fileUploadEntity)
Do the same or the other places where indexOf is used
Reference
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf

why my Local Storage show me "object HTMLSpanElement"?

Hello everybody I am relatively new to coding. I have a local storage project in object oriented javascript for reservation data and to display them in a reservation summary.I am currently blocked, I manage to recover the various values ​​with "getItem", but when I want to display them on my html it displays "object HTMLSpanElement".
the only solution I found is to add ".textcontent" at the end of my variables but it doesn't work.
this is my code
class information{
constructor(){
this.StationName = document.getElementById("StationName");
this.name = document.getElementById("name");
this.firstname = document.getElementById("firstname");
this.adresse = document.getElementById("adresse");
this.PlacesTotal = document.getElementById("PlacesTotal");
this.nbrVeloDispo = document.getElementById("nbrVeloDispo");
};
initstorage(){
var StationName = document.getElementById("StationName").textContent;
var name = document.getElementById("name").value;
var firstname = document.getElementById("firstname").value
var adresse = document.getElementById("adresse").textContent;
var PlacesTotal = document.getElementById("PlacesTotal").textContent;
var nbrVeloDispo = document.getElementById("nbrVeloDispo").textContent;
};
initreservation(){
var StationName = localStorage.getItem("StationName");
var name = localStorage.getItem("name");
var firstname = localStorage.getItem("firstname");
var adresse = localStorage.getItem("adresse");
var PlacesTotal = localStorage.getItem("PlacesTotal");
var nbrVeloDispo = localStorage.getItem("nbrVeloDispo");
};
reserver(){
document.getElementById("signature").style.display = "block";
document.getElementById("panneaureservation").style.display = "none";
};
};
document.getElementById("reservation1").innerHTML = StationName;
const stockage = new information();
document.getElementById("reserve").onclick = function() { stockage.initstorage(); stockage.initreservation(); stockage.reserver(); }
Thank you for taking your time to help me.
When you call localStorage.setItem() you are attempting to store a reference to the DOM element itself instead of the content of that element. Since localStorage only stores strings, the object implicitly gets its .toString() method called on it and a string representation of the object's type and element name is what gets stored.
Here's an example of what's happening (described above):
console.log(document.querySelector("div").toString());
<div>My div element</div>
Once that happens, you can't extract the reference later because all you have is the string name of the object.
You didn't show that code, but that's what needs to be updated. It should store the string value that you will want to get later. Something like this:
localStorage.setItem("StationName", document.getElementById("StationName").textContent);
localStorage.setItem("name", document.getElementById("name").value);
localStorage.setItem("firstname", document.getElementById("firstname").value);
localStorage.setItem("adresse", document.getElementById("adresse").textContent);
localStorage.setItem("PlacesTotal", document.getElementById("PlacesTotal").textContent);
localStorage.setItem("nbrVeloDispo", document.getElementById("nbrVeloDispo").textContent);
Where you set the .textContent of non-form field DOM elements and the .value of form field DOM elements. And, of course, you don't want this code to run against form field data until the form has been completed.

How to document merge a parent record and all of its child records

I'm creating a document merge (mail merge) from Google App Maker to a Google Document template. I can do so successfully when merging one single record, but how do you merge several records into the one document?
I have an purchase_orders parent record which has several purchase_order_line_items child records but I can't seem to get all of these records into a single document merge.
A similar question (Document Merge with Google App Maker) was asked by by Johan W with a comprehensive answer by Markus Malessa and Pavel Shkleinik (thank you!). However, it only caters for cases when you are merging one single record.
I have tried to build on their answer by using a second for loop to get the data of all associated child records. The script runs but only seems to merge the first child record; not all of them.
Here is an example of the server-side code I've tried to use:
function Export(key, key2) {
// Get the parent record by its key, which was passed by the first parameter above
var record = app.models.Purchase_Orders.getRecord(key);
// Get the first child record by its key, which was passed by the second parameter above
var childRecord = app.models.Purchase_Order_Line_Items.getRecord(key2);
// Get the Google Document which will be used as a template for this merge
var templateId = '1Xbt8camqHJYrhBnx0a6G2-RvTvybqU0PclHifcdiLLA';
//Set the filename of the new merge document to be created
var filename = 'Document for Customer ' + new Date();
//Make a copy of the template to use as the merge document
var copyFile = DriveApp.getFileById(templateId).makeCopy(filename);
//Get the Google Docs ID of the newly created merge document
var copyDoc = DocumentApp.openById(copyFile.getId());
var copyBody = copyDoc.getBody();
// Replace the field names in the template with the field data from the parent record
var fields = app.metadata.models.Purchase_Orders.fields;
for (var i in fields) {
console.log(i);
var text = '<<' + fields[i].name + '>>';
var data = record[fields[i].name];
if (data !== null) {
copyBody.replaceText(text, data);
} else {
// do nothing
}
}
// Replace the field names in the template with the field data from the child records
childFields = app.metadata.models.Purchase_Order_Line_Items.fields;
for (i in childFields) {
console.log(i);
var childtext = '<<' + childFields[i].name + '>>';
var childdata = childRecord[childFields[i].name];
if (childdata !== null) {
copyBody.replaceText(childtext, childdata);
} else {
// do nothing
}
}
}
How can I improve my code so that all associated child records are merged into a single document?
How can I set up my Google Document template to cater for any number of child records?
Rather than passing in the child record key via a second parameter, I would suggest just passing in the parent key and then changing your function as follows:
function Export(key) {
// Get the parent record by its key, which was passed by the first parameter above
var record = app.models.Purchase_Orders.getRecord(key);
// Get the first child record by its key, which was passed by the second parameter above
var childRecords = record.Purchase_Order_Line_Items;
// Get the Google Document which will be used as a template for this merge
var templateId = '1Xbt8camqHJYrhBnx0a6G2-RvTvybqU0PclHifcdiLLA';
//Set the filename of the new merge document to be created
var filename = 'Document for Customer ' + new Date();
//Make a copy of the template to use as the merge document
var copyFile = DriveApp.getFileById(templateId).makeCopy(filename);
//Get the Google Docs ID of the newly created merge document
var copyDoc = DocumentApp.openById(copyFile.getId());
var copyBody = copyDoc.getBody();
// Replace the field names in the template with the field data from the parent record
var fields = app.metadata.models.Purchase_Orders.fields;
for (var i in fields) {
console.log(i);
var text = '<<' + fields[i].name + '>>';
var data = record[fields[i].name];
if (data !== null) {
copyBody.replaceText(text, data);
} else {
// do nothing
}
}
// Replace the field names in the template with the field data from the child records
var childFields = app.metadata.models.Purchase_Order_Line_Items.fields;
var table = [];
var tableheader = [];
for (i in childFields) {
console.log(i);
tableheader.push(childFields[i].displayName);
}
table.push(tableheader);
for (i in childRecords) {
var data = [];
for (var j in childFields) {
data.push(childRecords[i][childFields[j].name]);
}
table.push(data);
}
copyBody.appendTable(table);
The table building is based on a 2D array and the documentation is here https://developers.google.com/apps-script/reference/document/table. But you will also need to remove your prebuilt table in favor of just appending a table instead. This way you are not dependent on the quantity of child records being fixed like they currently are in your document template. Also, the variable for childRecords may or may not work, I have not tested this since I am unsure if prefetch works in conjunction with .getRecord(key). This may require some additional testing but hopefully this will provide enough guidance.
Thought I would add this as an alternative. Lets say you keep your table but remove all the rows with exception for the header row then you could still use DocumentApp service to add your rows to the table like so:
var tableheaderfieldnames = ['Quantity_for_PO', 'Inventory_Item.id', 'Unit_Price']; //set a fixed table header with the field names, uncertain if the table header for the related inventory item will work or not
var table = copyBody.getTables()[0];
for (i in childRecords) {
var row = table.appendRow();
for (var j in tableheaderfieldnames) {
row.appendTableCell(childRecords[i][tableheaderfieldnames[j]]);
}
}
Keep in mind that AM does not allow you to use FK references, so for your inventory item that appears to use a fk field you may need to tinker around with setting the proper name reference for when you are trying to fill in the item in your table.

Firebase checking if child exists, pull and update data if exists javascript

I am trying to count the number of times a certain key word (city) is searched. I have stored the data so for each new input I want to see if the child exists and if so pull the numSearched child and add 1. I can see if it exists but I can add on.
My database is a simple
file:///Users/paulamactavish/Downloads/practicaltravel-63ff1-export.json
var cityToCheck = city;
var cityRef = firebase.database().ref('travelPlans');
cityRef.once('child_added', function(snapshot) {
snapshot.forEach(function(child) {
if (snapshot.val().city==cityToCheck) {
var myJSON = JSON.stringify(snapshot);
var citySearch = JSON.parse(myJSON)
numSearchCounter = (citySearch.numSearched);
numSearchCounter++
child.ref().update({
numSearched = numSearchCounter
});
};
});
});
I have looked over lots of other responses but I cant seem to make it work as a whole. Thanks in advance!

Get all items in NotesXSPDocument

In my Notes Database, I perform an audit when the document is saved. Pretty easy in LotusScript. I grab the original document (oDoc) from the server, then in the document I modified (mDoc), I do a Forall loop that gets the names of each item; forall item in mDoc.items. Grab the same item from oDoc, execute a function with the new item as an argument that will run down a case statement that will see if its a field we care about. if so, I update a set of list values in the document with "When", "Who", "What field", and the "New Value".
I'm doing this in a server side script. In trying this, I discovered a couple of interesting things;
currentDocument is the NotesXSPDocument that contains everything that was just changed.
currentDocument.getDocument() contains the pre-change values. It also returns a NotesDocument which has the "items" field that I can run through.
Thing is, I need something similar in the NotesXSPDocument. Is there a way in an iterative loop to grab the names and values of all items from there?
Here's the broken code. (Currently it's walking through the NotesDocument items, but those are the old values. I'd rather walk down the XSP document items)
function FInvoice_beginAudit() {
var original_doc:NotesDocument = currentDocument.getDocument();
var oItem:NotesItem;
var oItems:java.util.Vector = original_doc.getItems();
var iterator = oItems.iterator();
while (iterator.hasNext()) {
var oItem:NotesItem = iterator.next();
item = currentDocument.getItemValue(oItem.getName());
if (oItem == undefined) {
var MasterItem = ScreenAudit(doc,item,True)
if (MasterItem) { return true }
} else {
if (item.getValueString() != oItem.getValueString()) {
var MasterItem = ScreenAudit(doc,Item,True);
if (MasterItem) { return true }
}
}
}
}
You can get both versions of a document after submit - the original and the one with changed/new values:
original: var original_doc:NotesDocument = currentDocument.getDocument();
changed: var changed_doc:NotesDocument = currentDocument.getDocument(true);
This way you can compare the items for changes.
But, there is a pitfall: after assigning "changed_doc" to currentDocument.getDocument(true) the "original_doc" has the changed values too because both variables point to the same document. That's why we have to copy all items from currentDocument.getDocument() to a new temporary document first and only after get the changed values with currentDocument.getDocument(true). As an alternative you could read the original document from server like you do in LotusScript.
This is a code for detecting changed items as a starting point:
var original_doc:NotesDocument = database.createDocument();
currentDocument.getDocument().copyAllItems(original_doc, true);
var changed_doc:NotesDocument = currentDocument.getDocument(true);
var oItems:java.util.Vector = original_doc.getItems();
var iterator = oItems.iterator();
while (iterator.hasNext()) {
var oItem:NotesItem = iterator.next();
var itemName = oItem.getName();
var cItem:NotesItem = changed_doc.getFirstItem(itemName);
if (cItem.getText() !== oItem.getText()) {
print("changed: " + itemName);
}
oItem.recycle();
cItem.recycle();
}
original_doc.remove(true);
original_doc.recycle();

Categories