I have two function, one reads the Cell value upon change.
And the second one makes a Request to a URL and gets the response.
function onEdit(e) {
var range = e.range;
var data = range.getValue();
Logger.log(data)
Logger.log(test());
}
function test() {
var url = 'apps.compete.com/sites/wholesalerhinestones.org/trended/UV/?apikey=[YOUR API KEY HERE]';
var response = UrlFetchApp.fetch(url);
Logger.log(response);
}
Problem is that, test() runs successfully and Logs the output to Console but upon Cell change event, cell's value is logged but test() is not called.
One of the issues here is you're not actually calling the function, nor are you returning anything should it be called.
It should look something like this:
function onEdit(e){
var range = e.range;
var data = range.getValue();
Logger.log(data)
var testedFunction = test();
Logger.log(testedFunction);
}
function test(){
var url = 'YOUR-URL-HERE';
var response = UrlFetchApp.fetch(url);
Logger.log('Response code: ' + response.getResponseCode());
return response
}
The second issue that you're having here is that your using a Simple trigger which does not allow you to access services which require authorization.
Instead using an Installable trigger for 'onEdit' might do the trick. (This is what I was using, but I was testing against 'Google.com'). It should look like this:
Related
I am trying to automate the collection of statistics on Youtube videos in google spreadsheets. To do this, I use code in spreadsheets script editor with the getYoutubeViews function, as well as with the GETURL, linkURL functions, and so on.
Here is the getYoutubeViews function sample
function getYoutubeViews(videoId){
var url = "https://www.googleapis.com/youtube/v3/videos?part=statistics&id=" + videoId;
url = url + "&key=mykey";
//Utilities.sleep(Math.random(15000))
var videoListResponse = UrlFetchApp.fetch(url);
var json = JSON.parse(videoListResponse.getContentText());
return json["items"][0]["statistics"]["viewCount"];
}
function GETURL(input) {
var range = SpreadsheetApp.getActiveSheet().getRange(input);
var url = /"(.*?)"/.exec(range.getFormulaR1C1())[1];
return url;
}
I ran into two problems.
The script starts working when the user loads the table. This starts a large number of processes, since the number of videos in the table exceeds 600 pieces. This causes the error: "Service invoked too many times in a short time: exec qps".
But fixing it with Utilities.sleep does not make sense, because there is a second problem. Google’s API key quota of 10,000 points ends after 3-4 hours of work and regular table reloads.
I tried to minimize functions and actions on list, and use Utilities.sleep, to avoid this error:
Service invoked too many times in a short time: exec qps.
Try Utilities.sleep(1000) between calls. (строка 0).
But it seems that this does not help to solve the quota problem.
It seems to me that I can somehow save data in cells, activating functions only when updating data. I tried to use change triggers for these purposes, but either I did it wrong or it didn't help.
The second assumption is that it would be possible to somehow save the previous data, so that there would be some data in the cells even in case of a script error. But I do not know how this can be done.
An approach to avoid using custom functions (which will make all requests each time), is to use an onOpen() trigger to add a menu [1] that when clicked runs the getYoutubeViews() function. This function will make the request and insert the response data (views count) in the spreadsheet. It'll take the videoIds from B column (starting from 2nd row) and set the views count in D column. I put an "If" condition so that only makes the request (an update the values) for the empty views cells.
To manipulate the data on the spreadsheet I used SpreadsheetApp class [2]
function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.
.createMenu('Actions')
.addItem('Add views', 'getYoutubeViews')
.addToUi();
}
function getYoutubeViews(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet2");
var videoIdArray = sheet.getRange(2, 2, sheet.getLastRow()-1, 1).getValues();
var views = sheet.getRange(2, 4, sheet.getLastRow()-1, 1);
for(var i=0; i<videoIdArray.length ; i++) {
var videoId = videoIdArray[i][0];
var viewsCell = sheet.getRange(2 + i, 4);
if(viewsCell.getValue() == "") {
var url = "https://www.googleapis.com/youtube/v3/videos?part=statistics&id=" + videoId;
url = url + "&key=AAIzaSyAUjC5AchndLg9BRIrRBYKLuKf-fFkMC9M";
var options = {
'muteHttpExceptions' : true,
'headers': {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken(),
}
};
var videoListResponse = UrlFetchApp.fetch(url, options);
var json = JSON.parse(videoListResponse.getContentText());
Logger.log(json)
var views = json["items"][0]["statistics"]["viewCount"];
viewsCell.setValue(views);
}
}
}
You can not run the code directly with the onEdit() function because triggers have restrictions [3], among which there's one that says:
They cannot access services that require authorization.
UrlFetchApp.fetch() is a service that requires authorization from the user.
[1] https://developers.google.com/apps-script/guides/menus
[2] https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app
[3] https://developers.google.com/apps-script/guides/triggers/#restrictions
I have this part of code which should get me for example the "title" of the current user in Sharepoint, but everytime it gives me error: Common Language Runtime detected an invalid program.
<script type="text/javascript">
var personProperties;
// Ensure that the SP.UserProfiles.js file is loaded before the custom code runs.
SP.SOD.executeOrDelayUntilScriptLoaded(getUserProperties, 'SP.UserProfiles.js');
function getUserProperties() {
// Replace the placeholder value with the target user's credentials.
// var targetUser = "domainName\\userName";
// Get the current client context and PeopleManager instance.
var clientContext = new SP.ClientContext.get_current();
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
// Get user properties for the target user.
// To get the PersonProperties object for the current user, use the
// getMyProperties method.
personProperties = peopleManager.getMyProperties();
// Load the PersonProperties object and send the request.
clientContext.load(personProperties);
clientContext.executeQueryAsync(onRequestSuccess, onRequestFail);
}
// This function runs if the executeQueryAsync call succeeds.
function onRequestSuccess() {
// Get a property directly from the PersonProperties object.
var messageText = personProperties.get_userProfileProperties()['Title'];
alert(messageText);
}
// This function runs if the executeQueryAsync call fails.
function onRequestFail(sender, args) {
alert(args.get_message());
}
</script>
Do you have any ideas why it is happening?
Thank you for any suggestions.
Tom
You can make use of SP.SOD.executeFunc to load the files in the correct manner.
Try the below code:
SP.SOD.executeFunc('SP.js', 'SP.ClientContext', function() {
// Make sure PeopleManager is available
SP.SOD.executeFunc('userprofile', 'SP.UserProfiles.PeopleManager', function() {
var clientContext = new SP.ClientContext.get_current();
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
// Get user properties for the target user.
// To get the PersonProperties object for the current user, use the
// getMyProperties method.
personProperties = peopleManager.getMyProperties();
// Load the PersonProperties object and send the request.
clientContext.load(personProperties);
clientContext.executeQueryAsync(onRequestSuccess, onRequestFail);
// This function runs if the executeQueryAsync call succeeds.
function onRequestSuccess() {
// Get a property directly from the PersonProperties object.
var messageText = personProperties.get_userProfileProperties()['Title'];
alert(messageText);
}
// This function runs if the executeQueryAsync call fails.
function onRequestFail(sender, args) {
alert(args.get_message());
}
});
});
I created a form using UI Page and am trying to have some fields autopopulated onChange. I have a client script that works for the most part, but the issue arises when certain fields need to be dot-walked in order to be autopopulated. I've read that dot-walking will not work in client scripts for scoped applications and that a GlideAjax code will need to be used instead. I'm not familiar with GlideAjax and Script Includes, can someone help me with transitioning my code?
My current client script looks like this:
function beneficiary_1(){
var usr = g_user.userID;
var related = $('family_member_1').value;
var rec = new GlideRecord('hr_beneficiary');
rec.addQuery('employee',usr);
rec.addQuery('sys_id',related);
rec.query(dataReturned);
}
function dataReturned(rec){
//autopopulate the beneficiary fields pending on the user selection
if(rec.next()) {
$('fm1_ssn').value = rec.ssn;
$('fm1_address').value = rec.beneficiary_contact.address;
$('fm1_email').value = rec.beneficiary_contact.email;
$('fm1_phone').value = rec.beneficiary_contact.mobile_phone;
var dob = rec.date_of_birth;
var arr = dob.split("-");
var date = arr[1] + "/"+ arr[2] + "/" + arr[0] ;
$('fm1_date_of_birth').value = date;
}
}
fm1_address, fm1_email, and fm1_phone do not auto populate because the value is dot walking from the HR_Beneficiary table to the HR_Emergency_Contact table.
How can I transform the above code to GlideAjax format?
I haven't tested this code so you may need to debug it, but hopefully gets you on the right track. However there are a couple of steps for this.
Create a script include that pull the data and send a response to an ajax call.
Call this script include from a client script using GlideAjax.
Handle the AJAX response and populate the form.
This is part of the client script in #2
A couple of good websites to look at for this
GlideAjax documentation for reference
Returning multiple values with GlideAjax
1. Script Include - Here you will create your method to pull the data and respond to an ajax call.
This script include object has the following details
Name: BeneficiaryContact
Parateters:
sysparm_my_userid - user ID of the employee
sysparm_my_relativeid - relative sys_id
Make certain to check "Client callable" in the script include options.
var BeneficiaryContact = Class.create();
BeneficiaryContact.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getContact : function() {
// parameters
var userID = this.getParameter('sysparm_my_userid');
var relativeID = this.getParameter('sysparm_my_relativeid');
// query
var rec = new GlideRecord('hr_beneficiary');
rec.addQuery('employee', userID);
rec.addQuery('sys_id', relativeID);
rec.query();
// build object
var obj = {};
obj.has_value = rec.hasNext(); // set if a record was found
// populate object
if(rec.next()) {
obj.ssn = rec.ssn;
obj.date_of_birth = rec.date_of_birth.toString();
obj.address = rec.beneficiary_contact.address.toString();
obj.email = rec.beneficiary_contact.email.toString();
obj.mobile_phone = rec.beneficiary_contact.mobile_phone.toString();
}
// encode to json
var json = new JSON();
var data = json.encode(obj);
return data;
},
type : "BeneficiaryContact"
});
2. Client Script - Here you will call BeneficiaryContact from #1 with a client script
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
var usr = g_user.userID;
var related = $('family_member_1').value;
var ga = new GlideAjax('BeneficiaryContact'); // call the object
ga.addParam('sysparm_name', 'getContact'); // call the function
ga.addParam('sysparm_my_userid', usr); // pass in userID
ga.addParam('sysparm_my_relativeid', related); // pass in relative sys_id
ga.getXML(populateBeneficiary);
}
3. Handle AJAX response - Deal with the response from #2
This is part of your client script
Here I put in the answer.has_value check as an example, but you may want to remove that until this works and you're done debugging.
function populateBeneficiary(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
answer = answer.evalJSON(); // convert json in to an object
// check if a value was found
if (answer.has_value) {
var dob = answer.date_of_birth;
var arr = dob.split("-");
var date = arr[1] + "/"+ arr[2] + "/" + arr[0];
$('fm1_ssn').value = answer.ssn;
$('fm1_address').value = answer.address;
$('fm1_email').value = answer.email;
$('fm1_phone').value = answer.mobile_phone;
$('fm1_date_of_birth').value = date;
}
else {
g_form.addErrorMessage('A beneficiary was not found.');
}
}
I am trying to execute a javascript function on page load and then do a postback that would call a function in code behind. I am getting time zone data and trying to pass them as arguments to __dopostback. Here is what I have (abbreviated):
in .aspx file
<body onload="GetLocalTime()">
....
function GetLocalTime(){debugger
var d = new Date();
var tzOffset = d.getTimezoneOffset();
var hfOffset = document.getElementById('<%=hfOffset.ClientID%>');
var hfTZName = document.getElementById('<%=hfTZName.ClientID%>');
var tzName = getTimezoneName();
hfOffset.value = tzOffset;
hfTZName.value = tzName;
__doPostBack('TZSessionVars', tzOffset, tzName);
}
In code behind's Page_Load(), if IsPostback is true:
string sTarget = Request["__EVENTTARGET"];
if (sTarget.Equals("TZSessionVars"))
{
string sArg = Request["__EVENTARGUMENT"];
SetTZSessionVars(sArg); // this is where I parse the argument and set session vars
}
When I trace the javascript, when it hits __doPostback, it says:Microsoft JScript runtime error: Object expected
The parameters tzOffset and tzName are OK and have valid values.
You can use the __EVENTARGUMENT as explained in this article.
Doing or Raising Postback using __doPostBack() function from Javascript in Asp.Net
Try passing concatenated values as a single __EVENTARGUMENT and split it at server to get actual arguments
I would like to call a function within a function and have this inner function return an object. I am using a JSONRequest function that I created myself so just assume that the request fetches an array of roles. Here is the code:
(users = function(){
this.getUserRoles = function(){
var params = {};
var json = new JSONRequest(function(data){
rolesObj = data['roles'];
return rolesObj;
}, 'get_roles', params);
}
});
Then I call the following but it returns undefined:
var cls = new users();
alert(cls.getUserRoles().length);
ajax requests are asynchronous so you're not going to get a return value from them. You do, however, receive a callback when they complete so you can pass your "return" value into a callback.
Here is an example of your code re-written to use a callback:
this.getUserRoles = function(completionCallback){
var params = {};
var json = new JSONRequest(function(data){
rolesObj = data['roles'];
completionCallback(rolesObj);
return rolesObj;
}, 'get_roles', params);
}
and then using it:
var cls = new users();
cls.getUserRoles(function(roles) {
alert(roles.length);
});
The rolesObj is passed into the callback function once the JSON request completes.
Your getUserRoles function doesn't return anything. It invokes an asynchronous JSON request, and the success callback for that request returns something.
There is no way to make an asynchronous request in a function and have the function synchronously return a value from the request. The request will take an unknown amount of time, and the requesting function will return as soon as the request has been sent. The value won't have arrived back from the server yet.