I'm new to Google Docs scripting, and am following this tutorial in order to create Google docs from a template and a Google sheet. I've written everything as the tutorial specifies - here's the entirety of my code:
function onOpen() {
const menu = SpreadsheetApp.getUi().createMenu('Create Review Forms');
menu.addItem('Create Review Forms', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs() {
const googleDocTemplate = DriveApp.getFileById('1JqwXS_yOo_v2CV5akOf26tQnj-AdB3cvrVmxTn10lZI');
const destinationFolder = DriveApp.getFolderById('1ZFaHXonRQZ-2S5YGj5vCRu_r23vWoVod');
const rows = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getDataRange().getValues();
Logger.log(rows)
rows.forEach(function(row, index){
if (index === 0) return;
if (row[6]) return;
const copy = googleDocTemplate.makeCopy(`${row[1]} ${row[2]} application`, destinationFolder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
body.replaceText('{{Cand Number}}', row[0]);
body.replaceText('{{Resume}}', row[1]);
body.replaceText('{{Essay 1}}', row[2]);
body.replaceText('{{Essay 2}}', row[3]);
body.replaceText('{{Essay 3}}', row[4]);
doc.saveAndClose();
sheet.getDataRange(index + 1, 6).setValue(doc.getUrl());
})
}
It works insofar as a menu option is created as specified in the onOpen function. However, every time I try to run or debug the whole thing, I get this "Unknown error" in the execution log:
This started even after I wrote the first couple of lines in the lower function, but even when I comment them out the Unknown Error persists. I'd appreciate any help anyone can offer!
Related
I'm writing a Chrome extension that will pass data from a webpage to a file and download it. Ultimately, the file ends up in Obsidian for easy note-taking. The extension works fine most of the time, as long as all the fields exist, so I've started introducing some code for when those elements don't exist.
For example, this is the original code:
const ogImage = document.querySelector('meta[property="og:image"]');
const og_image = ogImage.content;
That works fine until the og:image tag doesn't exist in the page. This is the code I came up with to handle the problem:
const ogImage = document.querySelector('meta[property="og:image"]');
if (ogImage != "") {
const og_image = ogImage.content;
console.log(og_image);
} else {
const og_image = "https://---/vvMtZvn.jpg";
console.log(og_image);
};
Depending on the page, the console will output the correct value. However, I get errors if I try to use the value outside of this code. The console in the following block replies with: Uncaught ReferenceError: og_image is not defined
const ogImage = document.querySelector('meta[property="og:image"]');
if (ogImage != "") {
const og_image = ogImage.content;
} else {
const og_image = "https://i.imgur.com/vvMtZvn.jpg";
};
console.log(og_image);
I've been searching but can't find an answer. Maybe I'm asking the wrong question. Any help would be appreciated.
Please try this.
let og_image;
const ogImage = document.querySelector('meta[property="og:image"]');
if (ogImage != null) {
og_image = ogImage.content;
console.log(og_image);
} else {
og_image = "https://---/vvMtZvn.jpg";
console.log(og_image);
};
I've been able to sort out the middle bit (the API seems to be called to just fine) along with the submenu displaying. Originally I thought that just the end part wasn't working but I'm now thinking that the selection part isn't either.
What am I doing wrong with the getSelection() and what do I need to do to insert a link into said selection? (to clarify, not to replace the text with a link, but to insert a link into the text)
//Open trigger to get menu
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Scry', 'serumVisions')
.addToUi();
}
//Installation trigger
function onInstall(e) {
onOpen(e);
}
//I'm not sure if I need to do this but in case; declare var elements first
var elements
// Get selected text (not working)
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
Logger.log(elements);
} else {
var elements = "Lack of selection"
Logger.log("Lack of selection");
}
}
//Test run
// insert here
// Search Function
function searchFunction(nameTag) {
// API call + inserted Value
let URL = "https://api.scryfall.com/cards/named?exact=" + nameTag;
// Grabbing response
let response = UrlFetchApp.fetch(URL, {muteHttpExceptions: true});
let json = response.getContentText();
// Translation
let data = JSON.parse(json);
// Jackpot
let link = data.scryfall_uri;
// Output
Logger.log(link);
}
// Test run
searchFunction("Lightning Bolt");
//Let's hope this works how I think it works
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
// Unsure what class I'm supposed to use, this doesn't
const insertLink = DocumentApp.getActiveDocument().getSelection().newRichTextValue()
.setLinkUrl(linkage);
Logger.log(linkage);
}
For the first part, I tried the getSelection() and getCursor() examples from the Google documentation but they don't seem to work, they all just keep returning null.
For the inserting link bit, I read all those classes from the Spreadsheet section of the documentation, at the time I was unaware but now knowing, I haven't been able to find a version of the same task for Google Docs. Maybe it works but I'm writing it wrong as well, idk.
Modification points:
In your script, the functions of getSelectedText() and searchFunction(nameTag) return no values. I think that this might be the reason for your current issue of they all just keep returning null..
elements of var elements = selection.getRangeElements(); is not text data.
DocumentApp.getActiveDocument().getSelection() has no method of newRichTextValue().
In the case of searchFunction("Lightning Bolt");, when the script is run, this function is always run. Please be careful about this.
When these points are reflected in your script, how about the following modification?
Modified script:
Please remove searchFunction("Lightning Bolt");. And, in this case, var elements is not used. Please be careful about this.
From your script, I guessed that in your situation, you might have wanted to run serumVisions(). And also, I thought that you might have wanted to run the individual function. So, I modified your script as follows.
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
var text = "";
if (selection) {
text = selection.getRangeElements()[0].getElement().asText().getText().trim();
Logger.log(text);
} else {
text = "Lack of selection"
Logger.log("Lack of selection");
}
return text;
}
function searchFunction(nameTag) {
let URL = "https://api.scryfall.com/cards/named?exact=" + encodeURIComponent(nameTag);
let response = UrlFetchApp.fetch(URL, { muteHttpExceptions: true });
let json = response.getContentText();
let data = JSON.parse(json);
let link = data.scryfall_uri;
Logger.log(link);
return link;
}
// Please run this function.
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
if (linkage) {
Logger.log(linkage);
DocumentApp.getActiveDocument().getSelection().getRangeElements()[0].getElement().asText().editAsText().setLinkUrl(linkage);
}
}
When you select the text of "Lightning Bolt" in the Google Document and run the function serumVisions(), the text of Lightning Bolt is retrieved, and the URL like https://scryfall.com/card/2x2/117/lightning-bolt?utm_source=api is retrieved. And, this link is set to the selected text of "Lightning Bolt".
Reference:
getSelection()
Click here for picture Overview of the classes/entities
Hi guys, It could be great if someone could re-code and help me on this.I am new in D365 and JS. Basically, how can I query the parent to case_adjustment from adjustment invoice record using JS. I have provide my current code, please help me to review it. I have try everything but so far no luck. Sorry for my unprofessional picture. But I hope you understand it and could help me to code for this situation.
I have try to enable the debugger and it shows that the code cant run the adjustmentTypeLookup. and thats why it cant pass the value to retrieveRecord .Thank you.
function adjustmentInvoiceApproveAmount(executionContext) {
try {
// Get the form context
const formContext = executionContext.getFormContext();
// Extract attribute values from the form
const adjustmentAmount = formContext.getAttribute("case_adjustmentamount").getValue();
const amountDue = formContext.getAttribute("case_amountdue").getValue();
const adjustmentTypeLookup = formContext.getAttribute("case_adjustmenttype").getValue();
// Exit as adjustmenttype is not set
if (!adjustmentTypeLookup) return;
// Extract the adjustment type record ID from the payment type lookup
const adjustmentTypeId = adjustmentTypeLookup[0].id.substring(1, 37);
//console.log("GUID \"case_adjustmenttype\" = " + adjustmentTypeId + " ; " + typeof adjustmentTypeId);
//console.log(adjustmentTypeId);
// Retrieve a SINGLE case_adjustmenttype based on lookup ID on form
Xrm.WebApi.retrieveRecord("case_adjustmenttype", adjustmentTypeId, "$select=case_name").then(
function success(adjustmentType)
{
// If the payment type is credit notes then check payment amount and resit amount
if (adjustmentType.case_name.toLowerCase() == "Credit notes".toLowerCase())
{
if (adjustmentAmount >= amountDue) {
formContext.getEventArgs().preventDefault();
Xrm.Navigation.openErrorDialog({message:"Payment Amount cannot be more than Resit Amount."})
}
}
//Otherwise do nothing
},
function (error)
{
console.log(error.message);
}
);
}
catch (error)
{
console.log(error);
}
}
If you are struggling with the code of retrieving data using web API, I would suggest checking out "CRM REST Builder".
https://github.com/jlattimer/CRMRESTBuilder
Import the solution and refresh the solutions page in dynamics which will show up the button to start this tool. This tool is awesome at generating code for different scenarios.
I have a Google spreadsheet with links to questionnaires. I wanted to know how to get the questions from each of the questionnaires.
I guess I have to do: at best, use the script editor and iterate on the lines, and at worst, do webscraping.
const puppeteer = require('puppeteer');
function appendString() {
var range = SpreadsheetApp.getActiveSheet().getActiveRange();
var numRows = range.getNumRows();
var numCols = 0;
for (var i = 1; i <= numRows; i++) {
for (var j = 1; j <= numCols; j++) {
var currentValue = range.getCell(i,j).getValue();
await page.goto(currentValue);
const pollFrame = page.frames().find() # From there I have some difficulties
}
}
}
But I get the following error:
SyntaxError: await is only valid in async function (ligne 10, fichier "Code.gs")
Not to mention the async problem or the buttonthat I still have to click, the selection looks like this:
<div class="freebirdFormviewerViewItemsItemItemTitle exportItemTitle freebirdCustomFont" id="i1" role="heading" aria-level="3" aria-describedby="i.desc.310938276">How often did you fly before the Covid-19 epidemic? </div>
But the IDs don't follow a logical numerical order, so I don't know how to extract them automatically.
Then I don't know how to do it. I wonder if it's simpler because they're products from the same supplier.
Here is the equivalent in csv format:
https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link
https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link
https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link
Update
So I tried the anwer kindly posted by Neven Subotic's:
// this array will store forms and their questions
let formAndQuestions = [];
let formIds = ["https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link",
"https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link",
"https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link"]
formIds.forEach( formId => {
const form = FormApp.openById( formId );
// lets get the name
const formName = form.getTitle();
// first we get all items
const allItemsInThisForm = form.getItems();
// then we get filter out anything that is not a questions
const allQuestionsInThisForm = allItemsInThisForm.filter( item => {
return isThisItemAQuestion( item )
});
// now we store them in our object
formAndQuestions.push( {
formId: formId,
formName: formName,
questions: allQuestionsInThisForm
})
});
// this function is used to only get the itemTypes you want
// see reference for more information
function isThisItemAQuestion( item ){
const itemType = item.getType();
const validQuestionItemTypes = [ FormApp.ItemType.TEXT, "add others here" ]
let isValid = false;
validQuestionItemsTypes.forEach( validItemType => {
if( itemType == validItemType ) {
isValid = true;
}
});
return isValid
}
Unfortunately I obtain the following error message with the following details Exception: No item with the given ID could be found, or you do not have permission to access it. (line 9, "const form = FormApp.openById( formId );"). I don't understand. As you can see in the gif, I can open these links, so I should have the permission to access them isn't it?
I also tried Ruben's ideas with:
// this array will store forms and their questions
let formAndQuestions = [];
let formIds = ["https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link"]//,
//"https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link",
//"https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link"]
function scrapeForms(){
formIds.forEach( formId => {
// The code below logs the HTML code of the Google home page.
var response = UrlFetchApp.fetch(formId);
results = response.getElementsByClassName("freebirdFormviewerViewItemsItemItemTitleContainer");
Logger.log(results.getContentText())
});
}
But got back:
TypeError: response.getElementsByClassName is not a function (ligne 13, fichier "Code")
According to What is this Javascript "require"? require is not part of the standard JavaScript an AFAIK it's not supported by Google Apps Script.
By the other hand, the error message can't be easily solved as Google Apps Script Chrome V8 engine doesn't support async functions. Related Is google apps script synchronous?
If you will be using Google Apps Script, and you are the form owner or a form editor, instead of trying to web scraping a Google Form use the Forms Service of Google Apps Script. For this you will need the form ../edit URLs instead of the ../viewform URLs. On the official docs there is a quickstart that might help you https://developers.google.com/apps-script/quickstart/forms.
You could use openByUrl to "open" a form. It will not be actually opened in your web browser, it will be opened on the server side. Then you could use getItems to get all the questions, sections, images, videos, etc.
If you aren't the form owner or a form editor then you should use UrlFetchApp service and somehow parse the web page source code of each form based on the position of the questions. Related question: Google Sheets: How to import the following data?
Also, if the form has several sections you should do a post request to emulate clicking on the next button in order to get the second and following sections. There are more "also if the form has..." but I will stop here as the main part of question was already answered, I think.
You first want to get all the forms, so place those in an array:
const formIds = ["someId", "anotherId", "andSoOn"]
Then, lets use the FormApp to get the form and all items. Items can be of different types, see documentation.
// this array will store forms and their questions
let formAndQuestions = [];
formIds.forEach( formId => {
const form = FormApp.openById( formId );
// lets get the name
const formName = form.getTitle();
// first we get all items
const allItemsInThisForm = form.getItems();
// then we get filter out anything that is not a questions
const allQuestionsInThisForm = allItemsInThisForm.filter( item => {
return isThisItemAQuestion( item )
});
// now we store them in our object
formAndQuestions.push( {
formId: formId,
formName: formName,
questions: allQuestionsInThisForm
}
});
// this function is used to only get the itemTypes you want
// see reference for more information
function isThisItemAQuestion( item ){
const itemType = item.getType();
const validQuestionItemTypes = [ FormApp.ItemType.TEXT, "add others here" ]
let isValid = false;
validQuestionItemsTypes.forEach( validItemType => {
if( itemType == validItemType ) {
isValid = true;
}
});
return isValid
}
Then you can initially log out the results and see what it looks like:
Logger.log( formAndQuestions )
Item Types
first of all i am using a bit modified version of the standard html/js code offered from swagger-ui.
Changes are rather simple, instead of using only a single SwaggerUI element like:
window.swaggerUi = new SwaggerUi({
....
window.swaggerUi.load();
we iterate over them and create lots of swaggerUI elements:
function addSwaggerUI(url, dom_id, host){
window[dom_id] = new SwaggerUi({
....
window[domId].load();
this is our iteration:
var services = [{SERVICE_SPECS}];
var doc_container = document.getElementById('service-doc-container');
var index = 0;
services.forEach(function(serviceSpec) {
var swaggerNode = document.createElement('div');
swaggerNode.id = "swagger-ui-container-" + index;
swaggerNode.className = "swagger-ui-wrap";
var messageNode = document.createElement('div');
messageNode.id = "message-bar-" + index;
messageNode.className = "swagger-ui-wrap";
doc_container.appendChild(swaggerNode);
doc_container.appendChild(messageNode);
log("adding service documentation for " + serviceSpec);
addServiceDocumentation(serviceSpec, swaggerNode.id, "{SERVICE_HOST}");
index++;
});
This worked quiet well until we started using securityDefinitions oAuth:
"securityDefinitions": {
"OauthSecurity": {
"type": "oauth2",
"authorizationUrl": "http://localhost/oauth/dialog",
"flow": "implicit",
"scopes":{
"write:sessions":"kill sessions",
"read:sessions":"get sessions"
}
}
Now i get on each swaggerUI this error:
index.js:24 Uncaught TypeError: Cannot read property 'api' of undefined
Original swaggercode:https://github.com/swagger-api/swagger-ui/tree/master/dist see index.html
Can someone help to fix this?
(Alternative solution: Anyone know of another way to add multiple web apis from different json files on one html page? )
UPDATE:
So i looked at the generated page from swagger.petstore.io and found something called 'authorzie-wrapper'it is possible that this element is missing at my code.