Below my sample code
function getResponseData()
{
var service = googleOAuth();
var attachmentURL = https://sites.google.com/feeds/content/domainname/sitename?kind=attachment&start-index=1&max-results=500
if(service.hasAccess())
{
var dataResponse;
try
{
dataResponse = UrlFetchApp.fetch(attachmentURL, {method: 'get',headers: {Authorization: 'Bearer ' + service.getAccessToken()}});
}
catch(e)
{
Logger.log(e);
}
}
function googleOAuth()
{
var service = OAuth2.createService('sites')
.setAuthorizationBaseUrl('')
.setTokenUrl('').setClientId('').setClientSecret('').setProjectKey('').setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getScriptProperties()).setScope('')
.setParam('login_hint', Session.getActiveUser().getEmail()).setParam('access_type', 'offline')
.setParam('approval_prompt', 'force');
return service;
}
function authCallback(request)
{
var driveService = googleOAuth();
var isAuthorized = driveService.handleCallback(request);
if(isAuthorized)
{
return HtmlService.createHtmlOutput('Success! You can close this tab.');
}
else
{
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
I have attached the google sites page 1500 documents and but get the lastest attachment document below 1000. how to get all documents. please help me.I have get the 996 document only retrieve. how to another document retrieve.please help me.
I created a google site of my own, uploaded 1500 files and wrote google apps script (similar to yours) to replicate this issue. As it turns out, I too am not able to retrieve files after the first 1000. This is happening even if I change the start-index and max-results. The only reason I can think of is that this is an undocumented limitation. There must be a limit of 1000 for the number of attachments. I might be wrong.
The limits for attachments to a Google Site are mentioned in this link - Storage and file limits
Related
I'm trying to list all my google script files using the Google Drive File API as documented here. However I always get back an empty [] list. I think my token and my scopes are fine as I'm getting back this from Google OAuth2:
{ "access_token": "xxxxx", "expires_in": 3600, "refresh_token": "yyyyy",
"scope": "https://www.googleapis.com/auth/drive.scripts https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive",
"token_type": "Bearer"
}
But then when I issue the query using mimeType filtering (I just want to get google app scripts list):
mimeType = 'application/vnd.google-apps.script'
I'm only getting back an empty items list ([]), even if I've just created a google script inside a Google Sheet:
{ "kind": "drive#fileList", "etag": "....", "selfLink": "https://www.googleapis.com/drive/v2/files?q=mimeType+%3D+'application/vnd.google-apps.script'", "incompleteSearch": false, "items": []}
I'm guessing that app-script means something else than google script code which is part of a Google Sheet... Any help appreciated :)
About retrieving the file list of projects of the container-bound script type and exporting each script from the project, please check the following answer.
Retrieve file list of projects of container-bound script type
Unfortunately, the file list of projects of the container-bound script type cannot be retrieved yet, while the file list of projects of the standalone type can be retrieved using drive.files.list of Drive API.
This has been reported as a future request. https://issuetracker.google.com/issues/111149037
Export scripts in a project
When you want to export each script from a project of the containter-bound script type and the standalone type, you can do it using Google Apps Script API.
This might become a GAS sample script for your situation. here.
Also you can do it using a GAS library like this.
A Patchwork Quilt of a Work Around to get a list of Standalone and Bound Project Ids
I posted this here because I ran across this page several times during the last couple of days and in case there is someone else who would really like to get a complete list of their projects and doesn't mind having to jump through a few hoops to get it then this might be useful to them.
I found a way to get all of the my Apps Script Project File Ids. It's not pretty but but I got all 1393 of them bound and standalone. I went into Google Developer Hub and noticed that my project id's could be found in an attribute name data-script-id="project id" and by continuing to page down to the bottom of the list I was able to get the page to keep seeking more pages until there were none left. Then I went into Chrome developers tools and I found the div that contained all the divs with the above attribute and for me this one was named <div class="uBvHbd" and I copied the entire div and tried pasting it into an ascii file on my Google Account but I found that to be a problem so I opened up my copy of UltraEdit and pasted it in there. I played around with the regex for a while and spent the day retraining myself on the UltraEdit Document Model and their version of the Javascript Engine and developed the following routines which allowed me to build a complete list of projectids.
UltraEdits Scripting Language:
function getDocumentPaths() {
UltraEdit.outputWindow.write('File Paths:');
for(var i=0;i<UltraEdit.document.length;i++) {
UltraEdit.outputWindow.write('[' + i + ']: ' + UltraEdit.document[i].path);
}
}
function getFileNames() {
var nA=[];
var fnToIdx={fnA:[]};
for(var i=0;i<UltraEdit.document.length;i++) {
var p=UltraEdit.document[i].path;
var pA=p.split(/\\/);
var fn=pA[pA.length-1].split('.')[0];
fnToIdx.fnA.push(fn);
fnToIdx[fn]=i;
}
UltraEdit.outputWindow.write('FileNames: \r\n' + fnToIdx.fnA.join('\r\n'));
return fnToIdx;
}
function getScriptIdsIndex() {
for(var i=0;i<UltraEdit.document.length;i++) {
if(UltraEdit.document[i].isName('ScriptIds')) {
return i;
}
}
}
function getFileIndexByName(name) {
var name=name||'ScriptIds';
if(name) {
for(var i=0;i<UltraEdit.document.length;i++) {
if(UltraEdit.document[i].isName(name)) {
return i;
}
}
}else{
UltraEdit.messageBox("Invalid or Missing Inputs at getFileIndexByName().","Alert")
}
}
function getAllFileIndices(obj) {
UltraEdit.outputWindow.write('File Indices:');
var fnIndicesA=[]
for(var j=0;j<obj.fnA.length;j++) {
fnIndicesA.push({name:obj.fnA[j] , index:obj[obj.fnA[j]]})
}
var_dump(fnIndicesA);
}
function updateWorkingTabs(index) {
UltraEdit.outputWindow.write('Index: ' + index);
UltraEdit.document[index].selectAll();
UltraEdit.document[index].copy();
var workingTabsA=[];
for(var i=0;i<UltraEdit.document.length;i++) {
if(UltraEdit.document[i].path.slice(0,-1)=='Edit') {
UltraEdit.document[i].paste();
workingTabsA.push(i);
}
}
return workingTabsA;
}
function findAllIds() {
var fnToIndex=getFileNames();
UltraEdit.document[fnToIndex['ScriptIds']].selectAll();
UltraEdit.document[fnToIndex['ScriptIds']].copy();
var s=UltraEdit.clipboardContent;
var re=/data-script-id="[^"]+"/g;
var matchA=s.match(re);
UltraEdit.document[fnToIndex['FileIds']].selectAll();
UltraEdit.document[fnToIndex['FileIds']].cut();
for(var i=0;i<matchA.length;i++) {
UltraEdit.document[fnToIndex['FileIds']].write(matchA[i].slice(16,-1) + '\r\n');
}
}
function removeDuplicates() {
var fnToIndex=getFileNames();
UltraEdit.document[fnToIndex['FileIds']].selectAll();
UltraEdit.document[fnToIndex['FileIds']].copy();
var fnA=UltraEdit.clipboardContent.split('\r\n');
if(!fnA[fnA.length-1]) {
fnA.pop();
}
var uA=[];
for(var i=0;i<fnA.length;i++) {
if(uA.indexOf(fnA[i])==-1) {
uA.push(fnA[i]);
}
}
var s='';
for(var i=0;i<uA.length;i++) {
if(i>0){
s+='\r\n';
}
s+=uA[i];
}
UltraEdit.document[fnToIndex['FileIds']].selectAll();
UltraEdit.document[fnToIndex['FileIds']].cut();
UltraEdit.document[fnToIndex['FileIds']].write(s);
}
UltraEdit.outputWindow.clear();
//UltraEdit.open('E:\\Projects\\ScriptIds\\ScriptIds.txt');
//var ScriptIds_idx=getScriptIdsIndex();
//var wtA=updateWorkingTabs(ScriptIds_idx);
//findAllIds()
removeDuplicates();
The input file was 24 MB and the output list was 81KB and it took the script running on my laptop less then 5 seconds to strip out the id's and remove duplicates (didn't find any either).
I ran these two routines in Google Apps Script to get script info from Google Apps Script API so that I could figure out which were Standalone and which were container bound. And for the container bound I was able to get the Container Filename and id.
function getInfo(projectId) {
var projectId=projectId||pid;
var rObj={'ProjectId':projectId};
var params = {muteHttpExceptions:true,headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()}};
try {
var url=Utilities.formatString('https://script.googleapis.com/v1/projects/%s',projectId);
}
catch(e) {
return rObj['Error']=e;
}
var resp=UrlFetchApp.fetch(url,params);
var data=JSON.parse(resp.getContentText());
if(data.hasOwnProperty('parentId')) {
var pFile=DriveApp.getFileById(data.parentId)
var parentName=pFile.getName();
var pfldrA=[];
var pFolders=pFile.getParents();
while(pFolders.hasNext()) {
var folder=pFolders.next();
pfldrA.push({id:folder.getId(),name:folder.getName()});
}
rObj['ProjectName']=data.title;
rObj['ParentName']=parentName;
rObj['ParentId']=data.parentId;
rObj['ParentFolders']='';
rObj['ParentFolderIds']='';
//var html=Utilities.formatString('<br /><b>Project Name:</b>%s<br /><b>ParentName:</b> %s<br /><b>ParentId:</b> %s',data.title,parentName,data.parentId);
//html+=Utilities.formatString('<br /><b>Parent Folders:</b>');
for(var i=0;i<pfldrA.length;i++) {
if(i>0) {
rObj.ParentFolders+=', ';
rObj.ParentFolderIds+=', ';
}
//html+=Utilities.formatString('<br /><b>Name:</b> %s <b>Id:</b> %s',pfldrA[i].name,pfldrA[i].id);
rObj.ParentFolders+=pfldrA[i].name;
rObj.ParentFolderIds+=pfldrA[i].id;
}
}else{
rObj['ProjectName']=data.title;
//var html=Utilities.formatString('<br /><b>StandAlone Project</b><br /><b>Project Name:</b>%s<br /><b>ProjectId:</b>%s',data.title,projectId);
}
//Logger.log(data);
//var userInterface=HtmlService.createHtmlOutput(html);
//SpreadsheetApp.getUi().showModelessDialog(userInterface, "Project Info");
return rObj;
}
function updateProjects() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Projects');
var idx={hA:[],hr:1}
idx.hA=sh.getRange(1,1,1,sh.getLastColumn()).getValues()[0];
idx.hA.forEach(function(e,i){idx[e]=i;});
var lr=sh.getLastRow();
var sr=getColumnHeight(1,sh,ss) + 1;
var cnt=25;
setGlobal('count',cnt);
if(lr-sr+1<cnt) {
cnt=lr-sr+1;
}
if(sr>=lr) {
setGlobal('count',0);
return;
}
//Item ProjectId ProjectName ParentId ParentName ParentFolders ParentFolderIds
var rg=sh.getRange(sr,1,cnt,sh.getLastColumn());
var vA=rg.getValues();
for(var i=0;i<vA.length;i++) {
if(!vA[i][idx.Item] && vA[i][idx.ProjectId]) {
var pObj=getInfo(vA[i][idx.ProjectId]);
if(!pObj.hasOwnProperty('Error') && pObj.hasOwnProperty('ParentId')) {
vA[i][idx.Item]=i+sr;
vA[i][idx.ProjectName]=pObj.ProjectName;
vA[i][idx.ParentId]=pObj.ParentId;
vA[i][idx.ParentName]=pObj.ParentName;
vA[i][idx.ParentFolders]=pObj.ParentFolders;
vA[i][idx.ParentFolderIds]=pObj.ParentFolderIds;
}else if(!pObj.hasOwnProperty('Error') && !pObj.hasOwnProperty('ParentId')){
vA[i][idx.Item]=i+sr;
vA[i][idx.ProjectName]=pObj.ProjectName;
vA[i][idx.ParentName]='Standalone Project';
}else{
vA[i][idxItem]=i+sr;
vA[i][idx.ProjectName]=(pObj.hasOwnProperty('Error'))?pObj.Error:"Unknown Problem";
}
Utilities.sleep(1000);
}
}
rg.setValues(vA);
}
I'll come back later and put some comments in the code. It's not pretty but I now have a list of all my projects and where to find them and the names of their containers if they have one. I have to thank #Tanaike for the Apps Script library. It really helped me to figure out how access the Apps Script API which turned out to be a lot less difficult than I thought it would be.
A Glimpse at a portion of the final list:
I've got a Google Apps script I've been working on all week. I have 6 saved versions going back as far as 9/18. I can go to "Manage Versions" and see all of them but I can't restore any of them.
When I got to "See Revision History" it only goes back to 9/21.
One thing to note, the majority of this project is in a .html file, not a .gs file. Is there any hope to get at the code in the previous version?
The only way to get the code out of previous saved versions is with the Apps Script API. The code editor doesn't have a way to load a previous saved version.
See:
https://developers.google.com/apps-script/api/reference/rest/v1/projects/getContent
A version number can be designated. If no version number is designated, then the head version is returned.
You'll need to use UrlFetchApp.fetch(url) if you want to use the Apps Script API from Apps Script.
function getAppsScriptContent() {
var accessTkn,id,options,projectID,response,url;
id = "";//Enter the project id here
url = "https://script.googleapis.com/v1/projects/{scriptId}/content";
url = url.replace("{scriptId}", id);
if (versionNumber) {
url = url + '?versionNumber=' + versionNumber;
}
accessTkn = ScriptApp.getOAuthToken();
options = {};
options.muteHttpExceptions = true;//Make sure this is always set
options.method = "GET";
options.headers = {
'Authorization': 'Bearer ' + accessTkn
}
response = UrlFetchApp.fetch(url,options);
if (!response || response .getResponseCode() !== 200) {
//Your error handling
}
fileContent = response.getContentText();
try{
fileContent = JSON.parse(fileContent);
files = fileContent.files;
}catch(e){
//Error handling
}
}
I'm trying to make an inbox-listener element to automatically pull message content with a specific subject line into a Google Spreadsheet for reporting. I'm hoping this GitHub Project will do the trick, and I've made the Google Apps Script project that should handle it. Part of the process is to verify the project ownership by publishing it as draft on the Chrome web store. According to Google's documentation, that should be under the publish function, but I don't see it there, or anywhere in the script editor. Could anyone tell me what I'm doing wrong, or if this feature is disabled?
Here is what the menu looks like in my IDE.
Adapted from a OP's comment to the accepted answer
The script is definitely not contained in a Google Doc or Spreadsheet, but it is in a workspace tied to a Google Site. Interestingly, I've tried following the link in the KENdi's answer to edit Apps Scripts under my work account, and I'm told I don't have >permission to generate app scripts on that level. I'll send an email to my Admin to see if I can elevate privileges so I can publish through this channel.
GAS Code
main.gs
<meta name="google-site-verification" content="eA0WbBgImGB_wcsnSADjvwnCBaNyrSifyyxuNhHSXf8" />
var PROJECTID = 'api-project-...';
var WEBHOOK_URL = 'My custom project URL'
function doPost(e){
var postBody = JSON.parse(e.postData.getDataAsString());
var messageData = Utilities.newBlob(Utilities.base64Decode(postBody.message.data)).getDataAsString();
var ss = SpreadsheetApp.openById('...').getSheetByName("Log");
ss.appendRow([new Date(), messageData, JSON.stringify(postBody,undefined,2)])
return 200;
}
function setupPubSub(){
var newTopic = CreateTopic("mailTrigger");
newTopic.setIamPolicy(addGmailPolicy());
Logger.log(newTopic.getName());
var newSub = CreateSubscription("mailTrigger",newTopic.getName(),WEBHOOK_URL);
}
function disEnrollEmail(email){
var email = email || "me";
var res = UrlFetchApp.fetch("https://www.googleapis.com/gmail/v1/users/"+email+"/stop",{method:"POST",headers:{authorization:"Bearer "+ScriptApp.getOAuthToken()}});
Logger.log(res.getContentText());
}
function enrollEmail(email){
var email = email || "me";
PubSubApp.setTokenService(getTokenService())
var topicName = PubSubApp.PublishingApp(PROJECTID).getTopicName("mailTrigger")
Logger.log(watchEmail(topicName,{labelIds:["INBOX"], email:email}));
}
helper.gs
function addGmailPolicy(Policy){
return PubSubApp.policyBuilder()
[(Policy)?"editPolicy":"newPolicy"](Policy)
.addPublisher("SERVICEACCOUNT", 'gmail-api-push#system.gserviceaccount.com')
.getPolicy();
}
function addDomainSubs(Domain,Policy){
return PubSubApp.policyBuilder()
[(Policy)?"editPolicy":"newPolicy"](Policy)
.addPublisher("DOMAIN", Domain)
.getPolicy();
}
function getSubscriptionPolicy(){
return PubSubApp.policyBuilder()
.newPolicy()
.addSubscriber("DOMAIN","ccsknights.org")
}
function watchEmail(fullTopicName,watchOptions){
var options = {email:"me",token:ScriptApp.getOAuthToken(),labelIds:[]};
for(var option in watchOptions){
if(option in options){
options[option] = watchOptions[option];
}
}
Logger.log(options);
var url = "https://www.googleapis.com/gmail/v1/users/"+options.email+"/watch"
var payload = {
topicName: fullTopicName,
labelIds: options.labelIds
}
var params = {
method:"POST",
contentType: "application/json",
payload: JSON.stringify(payload),
headers:{Authorization: "Bearer "+ options.token
},
muteHttpExceptions:true
}
var results = UrlFetchApp.fetch(url, params);
if(results.getResponseCode() != 200){
throw new Error(results.getContentText())
}else{
return JSON.parse(results.getContentText());
}
}
function CreateTopic(topicName) {
var topic;
PubSubApp.setTokenService(getTokenService());
var pubservice = PubSubApp.PublishingApp(PROJECTID);
try{topic = pubservice.newTopic(topicName)}
catch(e){topic = pubservice.getTopic(topicName);}
return topic;
}
function CreateSubscription(subscriptionName,topicName,webhookUrl){
var sub;
PubSubApp.setTokenService(getTokenService());
var subService = PubSubApp.SubscriptionApp(PROJECTID);
try{sub = subService.newSubscription(subscriptionName,topicName,webhookUrl)}
catch(e){sub = subService.getSubscription(subscriptionName,topicName,webhookUrl)}
return sub;
}
function getTokenService(){
var jsonKey = JSON.parse(PropertiesService.getScriptProperties().getProperty("jsonKey"));
var privateKey = jsonKey.private_key;
var serviceAccountEmail = jsonKey.client_email;
var sa = GSApp.init(privateKey, ['https://www.googleapis.com/auth/pubsub'], serviceAccountEmail);
sa.addUser(serviceAccountEmail)
.requestToken();
return sa.tokenService(serviceAccountEmail);
}
function requestGmailScope_(){GmailApp.getAliases()}
The "Register in Chrome Web Store" option is only available in the standalone scripts.
Meaning, if this script is bound to a Google Sheets, Docs, Forms or scripts appear among your files in Google Drive, then this "Register in Chrome Web Store" is not available.
You can verify it by checking the publish in this "https://www.google.com/script/start/". This one will have an option of "Register in Chrome Web Store" in the publish.
While by checking the script editor of a spreadsheet, created in your Drive, the "Register in Chrome Web Store" will not be found in the Publish section.
Hope this information helps you.
It turns out I couldn't build Apps Scripts under my work account because my browser was defaulting to my personal Gmail account. Resetting the browser and connecting as my work account first fixed the problem. For future reference, you have to make your scripts under Script.Google.com in order for them to be shareable via the web store. You can bypass an authentication problems like this by resetting your browser history or running Chrome in incognito mode if you have to.
Great catch, KENDi!
Trying to understand if I'm doing something wrong here.
I'm trying to unsubscribe from a calendar using Apps Scripts, but it won't let me, I receive the error
Calendar test.user#cadillacfairview.com cannot be unsubscribed from. Try deleting instead.
Code:
function anotherTest(){
var cal = CalendarApp.getCalendarById('test.user#cadillacfairview.com');
cal.unsubscribeFromCalendar();
}
I don't own the calendar, but I do have rights to it.
I tried issuing the cal.deleteCalendar() but it said "action not allowed".
I ran in to the exact same problem. It seems like the Google Apps Script Calendar service produces those errors because it's trying to delete the actual calendar, rather than just unsubscribe from it.
My working solution involves using the CalendarList: delete method of the Google Calendar API instead of the Apps Script Calendar Service.
That deletes an entry from your list of calendars in the Google Calendar sidebar, which is the same thing as "unsubscribing". Also, it does not simply "hide" it from the list, it removes it altogether.
const calendarList = CalendarApp.getAllCalendars();
for (var i = 0; i < calendarList.length; i++) {
const calendar = calendarList[i];
// calendar.unsubscribeFromCalendar(); // results in "...cannot be unsubscribed from. Try deleting instead"
// calendar.deleteCalendar(); // results in "...Action not allowed"
Logger.log(`will remove calendar: ${calendar.getName()}`);
const url = `https://www.googleapis.com/calendar/v3/users/me/calendarList/${encodeURIComponent(calendar.getId())}`;
try {
const response = UrlFetchApp.fetch(url, {
headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
method: 'DELETE',
muteHttpExceptions: false
});
const responseStatusCode = response.getResponseCode();
Logger.log(`responseStatusCode: ${responseStatusCode}`);
if (responseStatusCode === 200) {
Logger.log('removed calendar');
}
} catch(e) {
Logger.log(`failed to remove calendar: ${e.message}`);
}
}
I have a fairly basic spreadsheet that uses some Google Scripts to accomplish various tasks. I was trying to cleanup the interface for the end user, and decided to implement the Google Picker. Originally the user had to manually import a CSV into the spreadsheet. The new goal here is to select the CSV via the Google Picker, upload it, import it, then delete it. I already have all the code working to import it and delete it. I just worked up the code for the picker, and it seems to work fine. However, and I think I'm just missing something small, how do I pass the File ID back from the Picker.html to my Google Scripts in order to continue my process?
If it helps, I'm using the basic callback provided in the Google documentation right now. I'm assuming this is where the change will be made. Just not sure what to do.
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
var url = doc[google.picker.Document.URL];
var title = doc[google.picker.Document.NAME];
document.getElementById('result').innerHTML =
'<b>You chose:</b><br>Name: ' + title + '<br>ID: ' + id;
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
}
}
This should probably work:
In your pickerCallback(data) function:
if (data.action == google.picker.Action.PICKED) {
var fileId = data.docs[0].id;
google.script.run
.withSuccessHandler(useData) // this will call the google apps script function in your Code.gs file
.doSomething(fileId); // this is a function in your JavaScript section where you will do something with the code you got from your apps script function
}
function useData(data) {
// do something with the data
}
In Code.gs, create a function to handle the input from the picker:
function doSomething(fileId) {
// do an operation in Drive with the fileId
var file = DriveApp.getFileById(fileId);
var fileName = file.getName();
return fileName;
}
First of all, open the chrome developer console when you are running this so you can see any errors that happen client side (when the picker is active). You can also use console.log to report any variable values in the Chrome console.
secondly, the call to the server works asynchronously, so it means that in your code, you'll get your message 'script was run', when it fact it hasn't yet. All that's happened is that google.script.run has asked for your server side function to execute.
That's why you have withSuccessHandler and withFailureHandler.
so you should do
google.script.run
.withSuccessHandler (function (response) {
document.getElementById('result').innerHTML = 'it worked'
})
.withFailureHandler (function (err) {
document.getElementById('result').innerHTML = err;
})
.justatest (fileId);
and back in the server script
function justatest(fileId) {
Logger.log (fileId);
}
If you then go back and look in the script log file, you should see the fileId.