How to copy and paste specific information in a same doc using Google App Script? - google-apps-script

I'm very new using GAS what I'm trying to do is to copy some information that is already on the bottom of my doc to anywhere I want in my docs, this should work just by copying the information and paste it at the place I desire, but I want it to be done with Google App Script because it's a daily task and it's easier to do it with a function, instead of copying and pasting manually. Searching on how to do this, I found a lot of information about how to do it on Spreadsheets, but I needed it to be done on Google Docs. How can I do that?
If someone can guide me or send me a link to another similar question that would be very helpful, I don't know where to start.
This is what I have until now, I get all the data of the current doc and set it again to the page, the code gives me problems because it deletes my other information, also it selects all the doc's information. I want to select a piece of specific information and don't copy the content style.
function copyPasteInfo() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var notesText = body.getText();
body.appendPageBreak();
body.setText(notesText);
}
Link to the doc document
https://docs.google.com/document/d/1s2TCspXbjvHVurwhIWSdwJ_hMcZIoLTKj4FAB82nmhM/edit
Video example of how what i want to do
https://www.screencast.com/t/UmEon8Fm0lPe
Picture of the information i'm trying to copy and paste to the bottom of my doc

If I correctly understood your question, this code will help you to achieve your goal.
let body = DocumentApp.getActiveDocument().getBody();
function moveTable() {
// Get the last table and the previous table found in your Doc
const [previousTable, bottomTable] = getDesireTables();
// Make a copy of your last table
const bottomTableCopy = bottomTable.copy();
// Get the previous table's index
const previousTableIndex = body.getChildIndex(previousTable);
// Insert the last table's copy under the previous table in your Doc
body.insertTable(previousTableIndex + 1, bottomTableCopy);
// Remove the original last table
body.removeChild(bottomTable);
}
function getDesireTables(){
const tablesArr = body.getTables().slice(-3);
// Get the parent element type to check if it's a cell
const parentELementType = tablesArr[tablesArr.length - 1].getParent().getType();
if(parentELementType === DocumentApp.ElementType.TABLE_CELL){
// If there's a table inside a table, return this
return tablesArr.slice(0, 2);
}
else{
return tablesArr.slice(-2);
}
}
What I did was to get the last two tables in the Doc, then I made a copy of the last one and with the index of the previous one, I inserted it under the previous one.
Edit
I noticed you had a table inside a table. therefore I added the getDesireTables function. Which it will check if your bottom table has a table inside.
Docs
These are the docs I used to help you:
getTables().
copy().
insertTable(childIndex, table).

Related

Display image in HTML page served by Apps Script

Good afternoon all;
I have a google spreadsheet which is populated by a Qualtrics form, where the user can upload a photo.
Using Apps Script, I have created a CRUD database that is accessed via a google sites page (html form). All is working great, but I'd like to display the photo that the user uploaded in their record; the URL to the image is saved in a column on the spreadhsheet.
The thing is, I'm not a very worthy 'coder' and I can't figure out how to make this happen.
I'm accessing my data like this: Note that I have removed a good portion of the code to save space.
Code.gs
function getFormValues(formObject) {
if (formObject.responseID && checkID(formObject.responseID)) {
var values = [[formObject.responseID.toString(),
formObject.permitNumber,
formObject.dateOfIssue,
formObject.photoLink, <<<this is where the URL would be pulled in. Column Q in the spreadsheet.`
DataTable.html
function populateForm(records){
document.getElementById('responseID').value = records[0][0];
document.getElementById('permitNumber').value = records[0][1];
document.getElementById('photoLink').value = records[0][16];
...I just don't know what to do after this point...
I did some tests, and I have an option for you to use Google Apps Script and the formula IMAGE.
Since I didn't have much information on your setup, like where the files are uploaded, which is the format of the URL, etc. I made a test environment using Google Forms, the files uploaded to Google Drive, and Apps Script.
With the formula:
=IMAGE("URL", [mode], [height], [width])
For the test, I use mode 4, which allows you to specify the size of the image. You can use either of the modes:
1 resizes the image to fit inside the cell, maintaining the aspect ratio.
2 stretches or compresses the image to fit inside the cell, ignoring the aspect ratio.
3 leaves the image at its original size, which may cause cropping.
4 allows the specification of a custom size.
The height and width needs to be in pixels.
When you use a Google Form to upload an image, you get an URL with the following format:
https://drive.google.com/open?id=[ID_OF_THE_FILE]
There is a trick to display images from Google Drive into Google Sheets using the image formula. You need the URL with the format: (I got this idea from this post)
https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]
So the script that I created modifies the URL in the column of the image from:
https://drive.google.com/open?id=[ID_OF_THE_FILE]
To:
=IMAGE("https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]", 4, 60, 60)
And place it in the cell in the column. However, you can place it in the same column as the current URL too.
Here is the sample code:
function testImage() {
// Identifies the sheet link to the Google Sheet and the tab you will be working with
// you can also use const sheet = SpreadsheetApp.getActiveSpreadsheet()
// .getSheetId("[ID_OF_THE_SHEET]");
// if the Script is not bound to the sheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
// Gets the range and the values where the URLs of the images are located.
// in my example there are in column B
let range_1 = sheet.getRange("B2:B").getValues().flat();
// Filters the rows without values or empty
range_1 = range_1.filter((element) => {return (element !== '')});
// Start of the loop to run on each row with URLs
for (let i=0; i< range_1.length ; i++){
let row = range_1[i];
// For this sample, I replace the URL with the word "Done" to prevent duplicates later on in the Script
// This can be skipped or replaced place, in a different location.
if (row != "Done"){
// Replace the URL from https://drive.google.com/open?id=[ID_OF_THE_FILE]
// to =IMAGE("https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]
let temp_imageURL = row.toString().replace('https://drive.google.com/open?id=',
'=IMAGE("https://lh3.googleusercontent.com/d/');
// However, the process is not complete yet, we need to complete the formula
// so we added the last part ",4,60,60) to the URL
// making it "https://lh3.googleusercontent.com/d/[ID_OF_THE_FILE]",4,60,60)
let imageURL = temp_imageURL+'",4,60,60)';
// place the complete formula in the current iteration, row 3 (which is row C)
sheet.getRange(i+2,3).setValue(imageURL);
// replace the current URL in Row 2 (which is row B) with the word "Done"
sheet.getRange(i+2,2).setValue("Done");
}
}
}
It looks like:
Reference:
IMAGE
Replace

Canonical way to reference Elements

Is there a canonical way to set an ID—or other searchable, persistent data attribute—on Elements in Google Docs, such that I can easily refer back to it later? I'm looking for something equivalent to getElementById in javascript. Almost all examples I've seen, including Google's own docs, seem to reference objects by searching for text strings or inserting new strings.
I've found one reference in the NamedRanges class to a getId function, but I can't find any place to set that ID. I do see the setAttributes function on Elements but that seems to apply only for pre-defined attribute types. I haven't tested that, though.
In case it's relevant: my interest is in automatically creating a document from a Google Sheet and populating based on the current values in the sheet. I'd like to assign specific Elements individual IDs so I can easily retrieve the Element and replace the text if the values in the sheet change later on.
Turns out that this is possible using NamedRanges, I just didn't read carefully enough.
Note: All the following examples are working off this Google doc. You can make a copy and select "Script Editor" from the Tools menu to see the code.
You can assign named ranges pretty easily using Apps Script. The below code looks through the doc for [[TITLE]] and [[ABSTRACT]] and assigns named ranges to those chunks. Note that in the aforelinked doc I put them in a table to avoid issues with partial ranges.
function assignNamedRanges() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const placeholders = ["title", "abstract"];
placeholders.forEach(p => {
const rangeBuilder = doc.newRange();
const text = body.findText("[[" + p.toUpperCase() + "]]");
rangeBuilder.addElement(text.getElement());
doc.addNamedRange(p, rangeBuilder.build());
});
}
Once you assigned them, you can update the range to something else in a separate function:
function updateNamedRanges() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const title = doc.getNamedRanges("title")[0];
const abstract = doc.getNamedRanges("abstract")[0];
title.getRange().getRangeElements()[0].getElement().asText().setText("Bob");
abstract.getRange().getRangeElements()[0].getElement().asText().setText("I like pancakes");
}
Note that NamedRanges are persistent, and the multiple NamedRange instances can have the same name. This means that if you run the first function four times, you'll have eight named ranges. You can make a convenience function to clear all those out pretty easily:
function clearNamedRanges() {
DocumentApp.getActiveDocument().getNamedRanges().forEach(r => {
r.remove();
})
}
So I been checking the documentation about elements for Google Docs in AppScript and it seems that some of them can be modified but not as freely as it looks as noted in the documentation:
Elements shown in bold can be inserted; non-bold elements can only be manipulated in place.
I tried checking with setAttributes as you mentioned however the attributes itself can only be from a document elements like: TEXT, PARAGRAPH, TABLE, ETC, this elements can't receive an ID as there is not method to insert an specific ID as you are requiring, most of the values that can be inserted are specific element attributes like: Font size, Font family, etc.

How to exclude table cells paragraphs from getParagraphs() in DocumentApp, apps script

I need to get all the paragraphs from a google document. For that, I am using body.getParagraphs().
The problem is, in any document, if any table exists, the body.getParagraphs() method takes all the table cells as a paragraph and returns as a paragraph. Now what I want is to get all the paragraphs only, from a document and exclude the table and table cells from that. How can I achieve this?
Thanks!
I believe your goal as follows.
You want to retrieve the paragraphs except for the table cell using Google Apps Script.
In this case, how about checking the element type? When this is reflected to a script, it becomes as follows.
Sample script:
Please copy and paste the following script to the script editor of Google Document.
function myFunction() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
const paragraphs = body.getParagraphs();
const res = paragraphs.filter(p => p.getParent().getType() != DocumentApp.ElementType.TABLE_CELL);
// do something
}
In this sample script, the values of paragraphs include the paragraphs of table cell.
By filtering the element type, res can have only the paragraphs except for the table cell.
References:
getParent() of Class Paragraph
getType()

Problem, merging cell in a table document, google apps script [duplicate]

I've writen this function (thanks, #Mogsdad) to merge cells in a table in a text google document, like this:
function onOpen() {
// Add a menu with some items, some separators, and a sub-menu.
DocumentApp.getUi().createMenu('Sample')
.addItem('merge cells of a table', 'mergeCells')
.addToUi();
}
function mergeCells() {
var body = DocumentApp.getActiveDocument().getBody();
for (var p= 0; p< body.getNumChildren(); p++) {
var child = body.getChild(p);
if (child.getType() == DocumentApp.ElementType.TABLE){
// Assume we've already located our table
var table = child;
var tableRow = table.getChild(2); // gets third row
var tableCell = tableRow.getChild(1); // gets second cell in row
tableCell.merge(); // Merges seconde cell with first cell.
}
}
}
But when I run the code, I got this weird result (very different of the expected, with the merged cell with the same dimensions of the table):
Is there a way to fix it? (merged cell with the same dimensions)
[edit to address updates in Google Docs]
This currently not possible. You can know if a cell is merged by looking calling getColSpan and getRowSpan but there are no setter methods.
Please star the following issue to be notified by updates regarding this.
The merge function you found is not specific to table cells, it is there to merge any element with a previous sibling of the same type, joining their content.
[original answer]
If you were expecting to have a merged cell that, like what you can do in a spreadsheet, that is not possible. Simply because that's not possible in Google Documents (at least not yet). Therefore the API cannot do this (it can only do things that are also possible manually).
This merge function is not specific to table cells as you probably imagined. It is working as designed.
You can do this by a workaround. Add a drawing and add a table in this drawing document. In this document te option 'merge cell's' is a possibility if you select 2 cells and press the right mouse button. See this youtube video for a tutorial
Use the Advanced Documents Service, batchUpdate and mergeTableCells.
function mergeCells() {
const documentId = 'DOCUMENT_ID';
const resource = {
requests: [
{
"mergeTableCells": {
"tableRange": {
"tableCellLocation": {
"tableStartLocation": {
"index": 2
},
"rowIndex": 0,
"columnIndex": 0
},
"rowSpan": 1,
"columnSpan": 2
}
}
}
]
}
Docs.Documents.batchUpdate(resource, documentId);
}
Resources
https://developers.google.com/apps-script/guides/services/advanced#enabling_advanced_services
MergeTableCellsRequest
One workaround is to embed tables within tables instead of merging cells. That way you can still programmatically add or remove cells/rows without ruining the tables. If you set the cell padding to 0, any stray paragraphs to 1pt font and remove any cell borders and you can achieve almost the same effect without any merged cells.
For example, I've got a cell with 4 columns, where I want the last column to be merged as as a single cell. I also want to be able to add or remove rows with Apps Script.
Table embedded within a table:
This way I can add or remove rows from the embedded table on the left and the right cell will remain "merged". You will have trouble getting the cell borders to line up, but if you can do without them you can still get it looking nice, like this:
And without borders:
Here is how to solve the merged cell in tables of a MS document when converting to a Google document: The idea is to go back to the MS word document and remove the merged cells and then copy and paste it or convert MS word to Google document. In this way we can get easy conversion of tables in MS documents to Google documents. But, within Google documents the cell can't be merged.

How to insert and center a Table in Google Docs with Google Script

I am trying to create a table and have it end up being centered in the document body of a Google DocumentApp using Google Script.
As of now, I create Tables using a For Loop and they are created fine, they Just will not center.
I have tried the Following:
Attempt 1:
var insertedTable = updateBody.insertTable(numberOfTables, objectivesTables[g]);
insertedTable.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
RESULT: This makes one LEFT aligned table then stalls and times out. (I am using a library that I have created and I am calling this function from another Script so I do not get an error message)
Attempt 2:
var style = {};
style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER;
updateBody.insertTable(numberOfTables, objectivesTables[g]).setAttributes(style);
RESULT: All Tables Are created but are Left Aligned.
I have seen this done with Text, Paragraphs, and Images but I cannot figure out how to accomplish this with a Table so any help would be graciously received.
A nasty solution might be to put a placeholder in the doc, center it manually and then replace it with a table.