Email Google Slides As PDF Using Template and Apps Script - google-apps-script

Trying to make a menu item that someone can open a slide, click a button, and it will save the Slide deck as a PDF and email it (using a pre-defined template). I started by working on the scripting for saving the PDF, but I am getting errors. Request help!
function onOpen() {
var ui = SlidesApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('RightTrak Mailer')
.addItem('Save PDF', 'SavePDF')
.addSeparator()
.addToUi();
}
function SavePDF() {
folderString = "1vozH-LotRA0xzZwWNxP-sVRirVvswSTl"
var blob =
DriveApp.getFileById("1zd7BecVSJupY0GOebJCTEQKwR_SDLWOxCeoRfwCL2fQ").getBlob();
var dir = DriveApp.getFolderById("1vozH-LotRA0xzZwWNxP-sVRirVvswSTl");
dir.DriveApp.createFile(blob);
SlidesApp.getUi()
.alert('Saved to ' + folderString + " " + blob.getName())
}

When I Created a test Google Slides and Added your Script the First Error I came Across was:
Exception: No item with the given ID could be found. Possibly because
you have not edited this item or you do not have permission to access
it.
Of course your HardCoded File ID Needed to be changed to mine. I did this with getActivePresnetation(). But there is the possibility you were getting an error becasue you had the wrong id. I also found code to get the Folder ID, but then was curious why you wanted to use the long Folder ID in your Saved Name String shown to the user on the alert. I changed that to the folder Name. But your choice, you can change with the indexing on the item returned by the getFolderInfo function. Shown below is code that gets both the Slide Deck and Folder Ids programmatically instead of having to paste them in. That should fix any errors you might have had with wrong Ids.
Your Line "dir.DriveApp.createFile(blob);" Has one problem, the 'dir' is a type Folder, that has its own method createFile. It does not need the DriveApp between dir and createFile. That line changes to: "dir.createFile(blob);" That is referenced Here: https://developers.google.com/apps-script/reference/drive/folder
I was able to get the following code to work on a Google Slide Deck and have it Save a Copy of the Slide Deck as a PDF.
function onOpen() {
var ui = SlidesApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('RightTrak Mailer')
.addItem('Save PDF', 'SavePDF')
.addSeparator()
.addToUi();
}
function getFolderInfo(inputFile)
{
// found at: https://stackoverflow.com/questions/26674758/google-apps-script-get-folder-name-from-file
var fileParents = inputFile.getParents();
while ( fileParents.hasNext() )
{
var folder = fileParents.next();
folderId = folder.getId();
folderName = folder.getName();
}
return [folderId, folderName];
}
function SavePDF() {
thisSlideDeck = SlidesApp.getActivePresentation();
thisSdId = thisSlideDeck.getId();
thisFile = DriveApp.getFileById(thisSdId);
thisFolderInfo = getFolderInfo(thisFile);
var blob = thisFile.getBlob();
var dir = DriveApp.getFolderById(thisFolderInfo[0]);
dir.createFile(blob);
SlidesApp.getUi().alert('Saved to ' + thisFolderInfo[1] + " " + blob.getName());
}

Related

GoogleJsonResponseException: API call with error: Login Required

I made a script using the advanced service API Google Drive. It's working fine when we stay into the app script GUI :
/** #OnlyCurrentDoc */
function GetListOfDrivesName() {
const ALL_AVALIBLE_DRIVE = Drive.Drives.list();
return ALL_AVALIBLE_DRIVE.items.map(driveData => driveData = driveData.name)
}
However, when it's called from cell into Google Sheet we got an error
Error message is like below : GoogleJsonResponseException: API call to drive.drives.list failed with error: Login Required.
I guess it's some authentification & authorisation to ask before using the sheet.
Unfortuantly I have no idea how to request that ! I read doc but it's sound like it will be asked promptly.
In addition, trigger have been added for this function but it neither worked
Any idea ? Thanks in advance !
Hi #Yuri Khristich you were right, using ui is a good work around , code turn up to work corectly
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
ui.createMenu('[FM Script for G Drive]')
.addItem('test','showAvaillableDrive')
.addToUi();
}
function showAvaillableDrive(){
var ui = SpreadsheetApp.getUi();
var resultat = ui.prompt(GetListOfDrivesName())
}
function GetListOfDrivesName() {
const ALL_AVALIBLE_DRIVE = Drive.Drives.list();
return ALL_AVALIBLE_DRIVE.items.map(driveData => driveData = driveData.name)
}
As suggested by the comments you can build a menu over the Sheet and run it to write down Drive files that I own.
I have this custom Sheet where you can run the "Drive" function over "Adv Menu"
I automatically get the information of the Sheet and get a list of my Drive files:
function onOpen () {
var ui= SpreadsheetApp.getUi();
ui.createMenu('Adv Menu').addItem('Drive', 'getMyFilesFromDrive').addToUi();
}
function getMyFilesFromDrive() {
var myFiles = DriveApp.searchFiles('"me" in owners');
var sheet = SpreadsheetApp.getActive().getSheetByName("Files");
sheet.clear();
var rows = [];
rows.push(["ID", "Name", "Url"]);
while(myFiles.hasNext()) {
var file = myFiles.next();
if(file != null) {
rows.push([file.getId(), file.getName(), file.getUrl()]);
}
}
sheet.getRange(1,1,rows.length,3).setValues(rows);
}
It would also write it directly to my Sheet. Feel free to review it and use it.
You can also Draw a button (Inserting a Google Drawing) over the Sheet and assign a script:
Reference:
https://developers.google.com/apps-script/guides/menus

Creating a view history log every time someone accesses a google doc and/or copies it using apps script

I wrote a script to log who and when opens a google doc. It works fine but the doc is a template for users to fill in so want to ensure whoever needs it will make a copy of the template through apps script.
I know users need edit access for the script to work but in terms of workarounds:
Is there a way for the script to still work if I give them a copy link or a template link?
Does it make more sense to use the google sheet as the base and pull from the google doc ID?
If that doesn't work than:
2) Is there a way to prompt them with a menu window to copy the file but delay it by x seconds on open?
//setups a count for the file
function setup() {
var propertyService = PropertiesService.getDocumentProperties();
propertyService.setProperty('viewCount', '0');
}
//logs the email and date of the user accessing the file
function onOpen(e) {
var count = parseInt(PropertiesService.getDocumentProperties().getProperty('viewCount'))+1;
PropertiesService.getDocumentProperties().setProperty('viewCount', count);
Logger.log(count);
var sheet = SpreadsheetApp.openById([spreadsheet ID]);
var user = Session.getActiveUser().getEmail();
var date = new Date();
sheet.getSheetByName('View Count').appendRow([user,date]);
}
You can add a menu item that creates a copy of the template for the user, logs the copy to the same sheet where you track who opens the file, and prompts the user to open their copy of the file to start working.
Steps
First: add these 3 functions.
The first, copyFile, creates a copy of the active document and names it the same plus the user's email. Then it logs the activity to the same change log, adding the new file's ID to the row. Finally, thanks to this nifty function it pops up a window with a link to the new file so the user can easily open it and start working.
The second, showAnchor, is the function that generates the HTML dialog box which presents the user a link to their new file.
The third, createMenu, adds a new menu item to the Google Docs nav bar, which prompts the user to copy the document. This, of course, triggers the copyFile function.
function copyFile() {
var thisDoc = DocumentApp.getActiveDocument()
var sheet = SpreadsheetApp.openById([spreadsheet ID]);
var user = Session.getActiveUser().getEmail();
var date = new Date();
var newCopy = DriveApp.getFileById(thisDoc.getId()).makeCopy(thisDoc.getName() + " " + user)
sheet.getSheetByName('View Count').appendRow([user,date,newCopy.getId()]);
var url = newCopy.getUrl()
showAnchor('Open Your File',url);
}
function showAnchor(name,url) {
var html = '<html><body>'+name+'</body></html>';
var ui = HtmlService.createHtmlOutput(html)
DocumentApp.getUi().showModelessDialog(ui," ");
}
function createMenu() {
const ui = DocumentApp.getUi();
const menu = ui.createMenu("Copy This Template");
menu.addItem("Copy", "copyFile");
menu.addToUi();
}
Then: add a call to createMenu() at the end of your onOpen script:
function onOpen(e) {
var count = parseInt(PropertiesService.getDocumentProperties().getProperty('viewCount'))+1;
PropertiesService.getDocumentProperties().setProperty('viewCount', count);
Logger.log(count);
var sheet = SpreadsheetApp.openById([spreadsheet ID]);
var user = Session.getActiveUser().getEmail();
var date = new Date();
sheet.getSheetByName('View Count').appendRow([user,date]);
createMenu();
}

Save a Single Sheet in Google Sheets as PDF using Script

I would like to be able to save just the active sheet as a pdf when clicking the save pdf button. Currently my code saves all of the sheets. How should I edit this code?
function onOpen() {
var submenu = [{name:"Save PDF", functionName:"saveAsPDF"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu('Save as PDF', submenu);
}
function saveAsPDF() {
const folderName = `social`;
const fileNamePrefix = SpreadsheetApp.getActiveSheet().getRange('a1').getValue();
const PhasePrefix = SpreadsheetApp.getActiveSheet().getRange('a2').getValue();
var actualSheetName = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getName();
DriveApp.getFoldersByName(folderName)
.next()
.createFile(SpreadsheetApp.getActiveSpreadsheet()
.getBlob()
.getAs(`application/pdf`)
.setName(`${fileNamePrefix} - ${PhasePrefix} - ${Utilities.formatDate(new Date(), `GMT-8`, `yyyy-MM-dd`)}`));
var ui = SpreadsheetApp.getUi()
ui.alert('New PDF file created in ' + folderName )
}
first, be careful as the save as pdf option is going away in December. I'm not sure if this will affect the script function, but you'll get a warning when trying it manually. worth checking out...
(not tested)
save as pdf goes to the folder and saves the file. if you're already in the file, you just want to save one sheet, you probably don't need to go and grab the whole file.
I was going to write this up, but I think this link will do what you want best.
https://xfanatical.com/blog/print-google-sheet-as-pdf-using-apps-script/
Scroll down to the Source Code section.
You're looking for this function 'exportCurrentSheetAsPDF'
function exportCurrentSheetAsPDF() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
var currentSheet = SpreadsheetApp.getActiveSheet()
var blob = _getAsBlob(spreadsheet.getUrl(), currentSheet)
_exportBlob(blob, currentSheet.getName(), spreadsheet)
}
This has the simple excerpt. I do encourage you to go to give them clicks and there's lots of other good stuff there. I think that is probably more than you wanted, but maybe things you didn't know you wanted...

Where would I put the file search contains certain words

I have this and I ran the debug and it did not give me any errors. Where can I put the search function for files with certain words? Any help will be greatly appreciated.
This was one I was trying to use
var filesIterator = folder.searchFiles('title contains "Untitled"');
function MoveFiles() {
var SourceFolder = DriveApp.getFolderById('abcdefghijklmnopqrstuvwxyz');
var SourceFiles = DriveApp.getFolderById('abcdefghijklmnopqrstuvwxyz').getFiles();
var DestFolder = DriveApp.getFolderById('1hbuV93CsdOeHkZWLES8BhXYMrayoKTBr');
while (SourceFiles.hasNext()) {
var file = SourceFiles.next();
DestFolder.addFile(file);
SourceFolder.removeFile(file);
}
}
Find and Move Files
This function will show a prompt where you can enter the string to complete the query 'title contains "the string"'. You also have to provide the id of the folder to search for and the id of the destination folder.
function findAndMoveFiles() {
var SourceFolder = DriveApp.getFolderById('Id');
var DestFolder = DriveApp.getFolderById('Id');
var result = SpreadsheetApp.getUi().prompt('Enter the string to complete the search query');
var qry = Utilities.formatString('title contains "%s"', result.getResponseText());//you just need to enter the string that gets put into %s
var SourceFiles = SourceFolder.searchFiles(qry);
while (SourceFiles.hasNext()) {
var file = SourceFiles.next();
DestFolder.addFile(file);
SourceFolder.removeFile(file);
}
}
Enter the script into a gs file in the script editor. You can run the script from the script editor once it start a dialog prompt will appear on the spreadsheet so make sure to switch back to the spreadsheet at that point.
You can also run it from a menu by entering in the following code:
function onOpen(){
SpreadsheetApp.getUi().createMenu('Search Menu')
.addItem('Find and Move Files', 'findAndMoveFiles')
.addToUi()
}
You will need to save it to the Script Editor and select it and run it one time to get the menu into the spreadsheet. Also it will display in the spreadsheet upon opening the spreadsheet next time.
DriveApp Reference
UI Reference
Also checkout all of the features in the help menu of the Script Editor.

Google Apps Script : Making an A Function Execute on Open

I found a script that will move my spreadsheet to a folder. How can I make it so that the functions runs when the spreadsheet is open. I will be applying this to a template so that everytime the template is opened, the "Copy of " spreadsheet file will automatically be moved to my Packing Lists folder.
Here's the code I have....
function MoveFileTo()
{
var docs=DocsList.find('Save Test');
var doc=docs[0]; /*Since we assume there is only one file with the name "Hello World", we take the index 0 */
var folders = doc.getParents();
var newFolder=DocsList.getFolder('Packing Lists');
doc.addToFolder(newFolder); // This will add the document to its new location.
var docParentFolder=folders[0];
doc.removeFromFolder(docParentFolder); // This will remove the document from its original location.
}
EDIT: I tried changing function MoveFileTo to onOpen() and onEdit() but it doesn't seem to work properly. Is there something I am missing?
Try use driveapp instead of doclist (it's deprecated). the impinball comment is really usefull, you should have a look at it.
Anyway here a working code:
function myFunction() {
var destFolder = DriveApp.getFolderById("FOLDER_ID_WHERE8YOU_WANT_TO_MOVE_YOUR_FILES");
var fileList = DriveApp.searchFiles("mimeType = 'application/vnd.google-apps.spreadsheet' and title contains 'FILE_TITLE'"); // change this search if necessary
var originalFileId="THE_ID_OF_THE_ACTUAL_SPREADSHEET_TO_BE_EXCLUDED_FROM_THE_SCRIPT_TREATMENT";
while(fileList.hasNext()){
var fileToMove = fileList.next();
if(fileToMove.getId()==originalFileId){
continue; // don't do anything if it's the original file
}
var papas = fileToMove.getParents();
destFolder.addFile(fileToMove); // do the job
while(papas.hasNext()){
papas.next().removeFile(fileToMove); // remove the actuals parents (can be more than one)
}
}
}