Starting with 10.10, Apple started to also support JavaScript for Automation instead of AppleScript but I really have a hard time to understand their JavaScript syntax.
E.g. let's take this easy AppleScript:
tell application "Mail"
set seletedMails to selection
repeat with aMail in seletedMails
set aSubject to subject of aMail
display dialog aSubject
end repeat
end tell
All it does is displaying a dialog with the subject of every selected e-mail in the Mail application. Can't get much simpler than that, can it? Should be easy to do that in JavaScript, right? So here's my code
function main ( ) {
var Mail = Application('com.apple.mail')
Mail.includeStandardAdditions = true
var selectedMails = Mail.selection
for (var aMail of selectedMails) {
Mail.displayDialog(aMail.subject)
}
}
main()
Result:
Error -1700: Can't convert types.
Okay... what did I do wrong? I cannot call selectedMails.length either, same error. selectedMails doesn't seem to be an array at all. Oh, wait, when I do this var selectedMails = Mail.selections (note the plural form!), then I can do selectedMails.length, but it always gives me 0, regardless how many mails I have selected. And when I do this (note, also uses the plural form):
function main ( ) {
var Mail = Application('com.apple.mail')
Mail.includeStandardAdditions = true
var selectedMails = Mail.selections
for (var aMail of selectedMails) {
Mail.displayDialog(aMail.subject)
}
}
main()
It only says:
Error -2700: Script error.
What extremely helpful error messages we get here, not!
You were very close. 😉
Generally, with JXA, you need to use a parenthesis at the end of a command to GET the values. So, you needed:
Mail.selection()
aMail.subject()
Here's the complete script, with my changes:
'use strict';
var app = Application.currentApplication()
app.includeStandardAdditions = true
var Mail = Application('com.apple.mail')
var selectedMails = Mail.selection();
for (var aMail of selectedMails) {
app.displayDialog(aMail.subject());
}
I also added the var app = Application.currentApplication(), as this is considered the best practice by many, and used by Apple in all of their documents.
See Introduction to JavaScript for Automation Release Notes
Related
I implemented Application Insigths in the frontend applciation and I want to disable/enable it based on a variable that can change over the lifetime of the applications. (e.g. user declined Application Insights consent => Disable Telemetry)
What I tried is:
appInsights.appInsights.config.disableTelemetry = true
however if I try to enable it back setting disableTelemetry =false this is not working anymore.
Is anything else that I need to make to persist this change or is there another way of doing this?
You could use a telemetry filter for that:
var filteringFunction = (envelope) => {
if (condition) {
return false; // Do not send telemetry
}
return true; // Do send the telemetry
};
Register the filter like this:
appInsights.addTelemetryInitializer(filteringFunction);
While Peter's answer is correct, I have different approach where instead of using telemetry filters we can stop the application insights object itself from starting to log to app insights.
Here in the following code based on the value of the variable a it will start the app insight service.
So, we will run appInsights.start(); only for a particular for value of variable a.
import { createRequire } from "module";
const require = createRequire(import.meta.url);
let appInsights = require('applicationinsights');
appInsights.setup("<Your connection String>")
.setAutoCollectConsole(true, true);
var a = 10 ;
if(a==10)
{appInsights.start();}
console.log("Hello World ");
Here I am running the code twice but with different value of variable a.
Here in application insights one log appear.
I have a script to create contacts in my database from the contents of my Google Sheet. It first verifies the contact doesn't exist in my database, then adds the contact. I have thousands of contacts, so to reduce the number of contacts in the existing contacts cache, I filter my contact list by state.
var leadsCache = [];
function createContact(leads){
var leadState = '';
for(var i=0; i<leads.length; i++){
if(leads[i].state != leadState){
leadState = leads[i].state;
populateLeadsCache(leadState);
}
var existingLead = leadsCache[leads[i].email];
if(existingLead === undefined){
var leadId = createNewLead(leads[i]);
}
}
}
This works as expected, until I get to a lead with a new state. The code hangs here:
leadState = leads[i].state;
I don't get an error message. I can set the var to empty like this: leadState = '', but I cannot set the value to something else.
In stepping through the code, I can see that leads[i].state has a new string value.
Why can't I change the value? What is the best way to accomplish my desired results?
UPDATE
I wish there was a better error reporting system for Apps Script. Turns out I had an issue in populateLeadsCache (continuous loop) but the system appeared stuck on leadState = leads[i].state;.
Anyone know how to improve the error reporting in Apps Script?
I am answering the question so it can be closed, but leaving the question here in case someone else has a similar issue that isn't really the issue.
Apps Script does not have robust error reporting tools and the debugger failed to highlight the true issue in another part of the code.
When getting a timeout message in Apps Script be aware that it may not have anything to do with where the code appears to break.
For me, the next line of code populateLeadsCache had an issue if the selected state had too many contacts, which resulted in a continuous loop.
I'm working on a Google Docs Add-On based on Google's Quickstart tutorial. I'm trying to change the workflow of the Add On in the tutorial to append a new page and then insert a translation on that new page rather than the line-by-line workflow.
I have a script working in single documents but I'm having a hard time moving it to the Add On architecture. I think it's something to do with passing selections from client-side JS to the server-side script doing the translation.
Here's the translate script
function translate(origin, dest, savePrefs) {
Logger.log('Starting the script');
if (savePrefs == true) {
var userProperties = PropertiesService.getUserProperties();
userProperties.setProperty('originLang', origin);
userProperties.setProperty('destLang', dest);
Logger.log(origin,dest);
}
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
// Add a page break for the translated material.
body.appendPageBreak();
// Get the number of elements in the document
var elements = body.getNumChildren();
Logger.log('Got the page elements');
// Use the number to loop through each element in the document.
for( var i=0;i<elements;i++) {
var element = body.getChild(i).copy();
var type = element.getType();
Logger.log('Element Types were successful. Starting tests.');
// Test each type for a child element and run script based on the result
// Images are nested in a paragraph as a child, so the second `if` makes
// sure there is no image present before moving to the next paragraph.
if( type == DocumentApp.ElementType.PARAGRAPH ){
if(element.asParagraph().getNumChildren() != 0 && element.asParagraph().getChild(0).getType() == DocumentApp.ElementType.INLINE_IMAGE) {
var img = element.asParagraph().getChild(0).asInlineImage().getBlob();
body.appendImage(img);
} else if(element.asParagraph().getNumChildren() !=0 && element.asParagraph().getChild(0).getType() == DocumentApp.ElementType.INLINE_DRAWING) {
var drawing = element.asParagraph().copy();
body.appendParagraph(drawing);
} else {
var text = element.asParagraph().getText();
Logger.log(text);
var spn = LanguageApp.translate(text, origin, dest);
body.appendParagraph(spn);
}
} else if(type == DocumentApp.ElementType.TABLE) {
element.asTable().copy();
body.appendTable(element);
} else if(type == DocumentApp.ElementType.LIST_ITEM) {
var list = element.asListItem().getText();
body.appendListItem(LanguageApp.translate(list, origin, dest));
}
}
The client-side JS is:
$(function() {
$('#run-translation').click(loadPreferences);
google.script.run(runTranslation)
});
function runTranslation() {
this.disabled = true;
var origin = $('input[name=origin]:checked').val();
var dest = $('input[name=dest]:checked').val();
var savePrefs = $('#save-prefs').is(':checked');
google.script.run
.runTranslation(origin, dest, savePrefs);
}
If I hard-code the languages to use in translation into the server-side script, it works. But as soon as I try to use variables from the radio buttons, it doesn't run. I don't see any errors in the console and I can't run scripts from the editor to check the logs. How can I debug this code?
Calling server-side functions from client Javascript
You've got a minor syntax error with google.run:
google.script.run(runTranslation)
It should be:
google.script.run
.withFailureHander(failFunc) // Optional
.withSuccessHander(succFunc) // Optional
.serverFunction(optionalParams...);
The two Handler methods assign callback functions from your client-side JavaScript to be invoked in the case of success or failure of the server-side function. In both cases, the client-side function is provided as the only parameter.
The server-side function you want to communicate with is presented as if it is a method itself, with optional parameters to be passed across the divide.
The simplest case for you is:
google.script.run
.translate(origin, dest, savePrefs);
(You had runTranslation in your posted code, but the server-side function is named translate()... I assume that's the right one.)
Now, this might will not take care of all your problems, so you wisely asked about debugging...
Debugging asynchronous client / server code in Google Apps Script
The provided debug environment isn't enough for debugging this sort of client / server exchange. There are also weaknesses in the Debugger for use with asynchronous execution - you can read more about that in Not seeing logs from onEdit trigger.
The simplest tool to get you debugging in this case would be to write logs to a spreadsheet
/**
* Write message and timestamp to log spreadsheet.
* From: https://stackoverflow.com/a/32212124/1677912
*/
function myLog( message ) {
var ss = SpreadsheetApp.openById( logSpreadsheetId );
var logSheet = ss.getSheetByName("Log") || ss.insertSheet("Log");
logSheet.appendRow([ new Date(), message );
}
You can call this from your client-side Javascript thusly:
google.script.run.myLog( "CLIENT: " + message );
That's a basic approach, but you can extend it more through use of utility functions and the BetterLog library. See more about that in my blog entry Did you know? (You can log to a spreadsheet from client JavaScript!)
I'm a beginner at this, so this error might be on account of faulty coding, but this is why I'm here! lol.
I have written a Sheets function that (in theory) would go through all the files in a particular folder and find all the instances of a particular word and then return the number of instances of that word. Here is the code I wrote:
function commentCount(name) {
var files = DocsList.getFolderById('FOLDER ID GOES HERE').getFiles();
var counter = 0;
for(i in files) {
var doc = DocumentApp.openById(files[i].getId());
var text = doc.getText();
text = text.replace( /\./g, "" );
var textArray = text.split(" ");
for(w in textArray){
if(textArray[w] == name){
counter++;
}
}
}
return counter;
}
When I call the function in Sheets, an error reads - Error: You do not have permission to call getFolderById (line 3, file "commentCount")
I've tried using getFolder("Folder name"), and getFolder(path), and the same error occurs. It seems that the DocList functions are not working correctly.
Not sure what the issue is because everything seems fine when I debug the function.
I won't be able to figure out if the rest of my code is sound until I figure out this error. Any help would be greatly appreciated!
Phil Bozak clarified that formulas in Spreadsheets that call scripts functions do not get full permissions, which makes it impossible to use getFolderByID function in this case.
I am using code in an aspx page (javascript) that is displayed using the sharepoint 2010 UI framework dialog functions.
However, it throws an error. I can't get at the exact details. But here's the code
function DoReject(rejectype) {
rejecttype = rejectype;
this.clientContext = new SP.ClientContext.get_current();
var targetList = clientContext.get_web().get_lists().getByTitle('Applications');
var qs =window.location.search.substring(1);
var arrs = qs.substring(0,qs.indexOf('&',0)).replace('arr=','').split(',');
for (var i = 0; i < arrs.length;i++) {
k = arrs[i];
if (k != null && k != '') {
try {
this.applicant = targetList.getItemById(k);
applicant.set_item('ApplicationStatus', 'REJECTED');
applicant.update();
clientContext.executeQueryAsync(Function.createDelegate(this, this.doNothingReject), Function.createDelegate(this, this.rejectError));
this.applicant = targetList.getItemById(k);
clientContext.load(applicant, 'EMail', 'CrisDBID', 'ApplyJobTitle', 'JobRef', 'BrandId');
clientContext.executeQueryAsync(Function.createDelegate(this, this.DoRejectSuccess), Function.createDelegate(this, this.rejectError2));
}
catch (e) {
alert(e);
}
}
}
}
Note i haven't included the success / error methods, as they are superfluous in this. And the ids return correctly - they are passed into the query string. So the variable k is definitely the id of the list item.
In the error event rejectError, I use the signature
rejectError(e)
Does anyone know how to get the error details from the parameter / whats wrong with the code?
is it possible to call list operations on a page that isn't the native list page using the javascript object model?
thanks in advance
M
What browser do you get the error in? Is the error consistent between browsers? I would recommend that you debug the javascript and look at the variables at the line before it fails to see what's going on.
On a side note the first snippet you pasted looks like a standard train-wreck so if the code fails there it's likely one of these are undefined (if clientContext, get_web() or get_lists() does not return a value). I don't know anything about Sharepoint UI framework so "list operation" etc doesn't say much to me.