edit google document remotely - google-apps-script

I'm trying to add a feature to my website as follows:
Clicking a button appends text to a Google document.
Obviously I will need to create an Apps Script in the drive. The question is how to trigger the Apps Script from my website. You can assume that I am the owner of the drive/document and so have permissions to edit it in any way I like.
I have looked at these topics:
Workarounds : How to edit native google documents programatcally?item
How to programmatically manipulate native google doc files
It seems they are all actions performed from the drive itself and not triggered remotely.
I have also looked up the Apps Script API in Google but have not found a way to do this. Is it even possible?

Yes, it is possible.
First write an Apps script that changes your desired document. Then deploy it as a web-app running as you that anyone has access, even anonymous. Check out the guides at Apps Script page to see how to write your script as a web-app.
Your script will then have a public url, which you can call from your website and have it run "remotely" normally.

To provide an example of what Henrique suggests, here is a small webapp that adds text to a publicly viewable document I own (it doesn't need to be public except for anyone here to check it works !)
I wrote it using UiApp but you could of course use HTMLService if you prefer...
The app runs as me but is accessible to anyone even anonymous.
// publicly viewable test doc url : https://docs.google.com/document/d/1THzBTURxGr2CdUmcZ7i2zD-RM8I3im2JCSHI3BHlkeM/edit
function doGet(){
var app = UiApp.createApplication().setTitle('docEdit');
var panel = app.createAbsolutePanel().setSize('100%','100%').setStyleAttributes({'padding':'40px','backgroundColor':'lightBlue'});
var text = app.createTextArea().setName('text').setPixelSize(500,300);
var grid = app.createFlexTable().setId('grid');
grid.setText(0,0,'Add your text').setWidget(1,0,text);
var handler = app.createServerHandler('writeText').addCallbackElement(panel);
grid.setWidget(2,0,app.createButton('update document',handler).setId('btn'));
app.add(panel.add(grid));
return app;
}
function writeText(e){
var doc = DocumentApp.openById('1THzBTURxGr2CdUmcZ7i2zD-RM8I3im2JCSHI3BHlkeM');
var now = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),'MMM/dd/yyyy # hh:mm:ss');
var body = doc.getBody();
body.appendParagraph('Append text on '+now+' : '+e.parameter.text);
doc.saveAndClose();
var app = UiApp.getActiveApplication();
var grid = app.getElementById('grid');
grid.setWidget(3,0,app.createHTML('Thanks,<br>Your text has been added to the document'));
app.getElementById('btn').setEnabled(false).setHTML('Button disabled');
return app;
}

Related

Google Picker shows up blank when used in Google Sites

To all Google Apps experts - please help me solve this. I'm rather stuck and I've not found any explanation yet on why this problem exists at all. I've included a live example to demonstrate the problem.
The problem may be view at this location ...
https://sites.google.com/a/growthhq.net/faulty-picker/
A Google Apps script made available in a Google Sites using the Apps Script gadget has stopped functioning recently. The crazy thing is that the app works perfectly using the 'dev' and 'exec' urls for the app directly, but when embedding the app in Sites (with the 'exec' url and Apps Script gadget), and the button is clicked, the form comes up blank.
I know that the UiApp is deprecated (sadly, this had some good attributes) but I don't want to change the rest of the code at this stage.
What can I do to get the Picker to show correctly in sites? Is the referrer inadequately specified? I'm at a total loss.
The activated referrers are:
*.google.com
*.googleusercontent.com
I have extracted (and simplified) the code portions for testing purposes. This working test code follows:
// refer to https://developers.google.com/apps-script/guides/dialogs#file-open_dialogs for setting up OAuth
function doGet()
{
var app = UiApp.createApplication();
var buttonHandler = app.createServerHandler('utilityPicker');
var button = app.createButton('Open Picker', buttonHandler)
app.add(button);
return app;
// This is a dummy to activate
DriveApp.getRootFolder();
}
function utilityPicker(e){
var app = UiApp.getActiveApplication();
var authToken = ScriptApp.getOAuthToken();
Logger.log(authToken);
var docPicker = app
.createDocsListDialog()
.setOAuthToken(authToken)
.setDialogTitle('Select a Google Spreadsheet or Form to be used by this Workspace')
.setMultiSelectEnabled(true)
.addView(UiApp.FileType.FOLDERS)
.showDocsPicker()
;
Logger.log(docPicker);
docPicker.addView(UiApp.FileType.SPREADSHEETS);
var handler = app.createServerHandler('pickerPrimarySpreadsheet');
docPicker.addSelectionHandler(handler);
return app;
}
function pickerPrimarySpreadsheet(e)
{
Logger.log(e.parameter);
}
Please help.
got the same problem,
found some google group forum where some people had to
add
.setOrigin('https://script.google.com')
to the pickerbuilder
still does not solve my problem, if u got a working solution to use on google sites please share

Using Google Scripts to upload without intervention

I have used the below to upload files from my local drive by choosing a file from the UI. I would like to upload a particular file with a static pathway every day. Is there a way to have a script upload a particular file? (Then I could just use triggers to make sure it happened routinely).
function doGet(e) {
var app = UiApp.createApplication().setTitle("Upload CSV to Sheet");
var formContent = app.createVerticalPanel();
formContent.add(app.createFileUpload().setName('thefile'));
formContent.add(app.createSubmitButton());
var form = app.createFormPanel();
form.add(formContent);
app.add(form);
return app;
}
function doPost(e) {
// data returned is a blob for FileUpload widget
var fileBlob = e.parameter.thefile;
var doc = DocsList.createFile(fileBlob);
}'
No, that is not possible (using Apps Script). Apps Script runs on Google servers and therefore do not have access to your local files.
I wrote an article for Steegle a while ago where I explain such Apps Script's aspects. You might want to give it a quick read.
You have to write such program using "traditional" development tools. That is, programs that run in your computer. For example, a Java program using Google Drive API library could do this easily. Then you could schedule it using your OS's scheduling feature (e.g. cron on linux/unix).

How To set permissions to Google Drive files/folder while adding from Google Site

I am new to Google Sites, I have created a test google site, and add some pages files from google drive to the pages.
My Question is: Is it possible to set permissions from a Google Site while adding a file/folder from Google Drive via any Google Apps Script or Google Drive SDK?
I also did some overview about Google Apps Script for adding UI elements/ html elements, and perform some basic authentication using Google Drive SDK. Given this:
Is possible to perform permission actions.
If we upload a file in a file cabinet template can we set any kind of permissions or remove download link button of that file?
Apps Script has a service called Drive. Drive has a class named: DriveApp. DriveApp can create, save and retrieve files. Once you have a reference to a file, you can use other Classes, Properties and Methods.
setSharing method - Google Documentation
Thanks to Your Answer #Sandy Good from you suggestion/idea I able to create a simple script and after creating this script I am sure I will be able to create a complicated script also.
I have created a script that will create a form in in google site's page. this form contain a dropdown list of my google drive files and an input filed in which i will add an email address.
and in the last a submit button.
after pressing submit button , the script work as it will grant permission on a selected to the email given as viewer of the file if he has link.
create a blank script in the Google Site's Apps Script Section.
Add some UI via UI Service
function doGet() {
// create ui element
var app = UiApp.createApplication();
// create form
var form = app.createFormPanel();
// create area or panel
var flow = app.createFlowPanel();
// create list box for files
var element_file = app.createListBox().setId('file_name').setName('file_name');
// add first elemnt
element_file.addItem('Please Select','');
// get drive files
var files = DriveApp.getFiles();
while (files.hasNext()) {
var file = files.next();
element_file.addItem(file.getName(),file.getId());
}
flow.add(element_file);
var element_text=app.createTextBox().setId('email').setName('email');
flow.add(element_text);
flow.add(app.createSubmitButton("Submit"));
form.add(flow);
app.add(form);
return app;
}
after this I handle the data submitted via post
function doPost(e) {
var app = UiApp.getActiveApplication();
var email = e.parameter.email;
var file_name = e.parameter.file_name;
var public_file = DriveApp.getFileById(file_name);
public_file.addViewer(email);
return app;
}
after this you can check the permissions of the specific file in the google drive.
you may also add a success message.
after this go to publish menu of the file select deploy as web app then select the required things. remember please select execute scripts only as me option because you are listing all your files.
then go to the page in which you want to add script just edit/add new page from editor of the page just insert Apps Script.
apologies , its my first time to work in google apps script , if I miss anything or i did not follow any rule etc.
Thanks,

Using UrlFetchApp in a container-bound script to run code in a web app as effective user

In a little over my head. Trying to use UrlFetchApp.fetch(url, options) from a container-bound script to run code in a web app.
The problem: Container-bound doc script can only run as the activeUser. doc script creates a new doc by copying a template. I would like the doc newly created from the template to be stored in a centralized folder owned by the developer. I see two solutions.
I give all domain users view/edit access to the developer's folder.
I create a web app from a standalone script which runs as the effectiveUser (developer) who has access to the folder. In this case the doc script calls the web app using UrlFetchApp passing in the parameters (folder, doc). However, to quite able to figure out how to do this, if possible.
var unitId = DocumentApp.getActiveDocument().getId();
var unit = DriveApp.getFileById(unitId);
var folderId = unit.getDescription() //FoldId for unit is stored in description
var folder = DriveApp.getFolderById(folderId);
var lesson = DriveApp.getFileById(UNIT.LESSON_TEMPLATE_ID).makeCopy('Lesson - ' + result.getResponseText());
folder.addFile(lesson);//Currently I have the folder shared/edit with domain users.
//I would prefer to share/view. However, since the
//container-bound doc script runs only as active user, no
//can do. Is it possible to build a small web app which
//runs as effective user and saves lesson to folder.
showLessonSidebar(folderId);
Any hints out there?
Yes, it is possible to create a web app that runs as effective user.If the developer - effective user has access to folder and template file.
You could pass the folderId and Name of the file to be copied to the WebApp, rather than passing folder and Doc object (passing the folder Object and Doc object as URL parameter may be quite tricky and if content is too large may not be even possible as there are constraints on the possible length of URL)
1) Write a Apps Script
function doGet(e) {
var folderId = e.parameters.folderId;
var copyFileName = e.parameters.name;
var userEmail = e.parameters.email;
var folder = DriveApp.getFolderById(folderId);
var lesson = DriveApp.getFileById(UNIT.LESSON_TEMPLATE_ID).makeCopy(copyFileName);
folder.addFile(lesson);
//share file to domain or share folder with domain - folder.setSharing(....);
file.setSharing(DriveApp.Access.DOMAIN, DriveApp.Permission.VIEW);
}
2) While deploying the Web App, developer can set Execute as me(developer#...) and set "Who has access to Web App" as Anyone.
3) Make sure developer has authorized the web App.
4) Share the Web App Script file with all your domain users.
Also, if you want to pass the content of file for some reason from container-script to Web App, you may consider implementing the logic in doPost() as you would have the ability to pass large data using POST in UrlFetch.
As far as I understand, the only way to use UrlFetch in a bound script, is using an installed trigger. (UrlFetch is not authorized to run when used in a bound script's simple triggers: https://developers.google.com/apps-script/add-ons/lifecycle#authorization_modes.)
An installed trigger always "runs with the authorization of the user who created the trigger, even if another user with edit access opens the spreadsheet". Thus, simply use installed triggers:
function setupTrigger(){
var doc = DocumentApp.getActiveDocument();
ScriptApp.newTrigger('open').forDocument(doc).onOpen().create();
}
This sample code creates an installable trigger for opening the document bound to the script.

Google Gadget API remote-content issue

In my Google Gadget JavaScript, I have an URL to call from Google Apps Script.
function getFileId(postData) {
var params = {};
var url = "https://script.google.com/a/macros/mycompany.com/s/AAABBBCCCDDEEE-FFF-GGG/exec";
params[gadgets.io.RequestParameters.POST_DATA] = postData;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
gadgets.io.makeRequest(url , responseFileId, params);
};
So, when the function responseFileId() has returned obj.text, it actually goes to the mycompany.com login screen. That means it needs authentication from that Gadget I did. Didn't that Google Gadget hold my current domain's Google credential for running the Google Apps Script at "https://script.google.com/a/macros/mycompany.com/s/AAABBBCCCDDEEE-FFF-GGG/exec"?
I tried to run manually with Google Chrome browser, (https://script.google.com/a/macros/mycompany.com/s/AAABBBCCCDDEEE-FFF-GGG/exec) and it returns value without showing up the login screen...
Can someone tells what step has been missing for this? Has this something to do with Google OAuth?
Instead of doing this. I found a better idea => Using tags for remote website.