Introduction
Google lately added the Google Apps Script functionality in Google Documents and presented some basics on the Google Developers site.
One key feature is the sidebar. This is where the "Quick Parts" could reside. It needs however a bit of coding to do that. See the following example, made by Martin Hawksey: Sidebar
Update 27/07/2013, here's an example I wrote myself:
Code
// global
var app = DocumentApp.getUi();
function onOpen() {
app.createMenu('Quick Parts')
.addItem('Document Property', 'docProperty').addToUi();
}
function docProperty() {
// set variables
var doc = DocumentApp.getActiveDocument();
var fileName = doc.getName(), Id = doc.getId();
var file = DocsList.getFileById(Id), lastUpdated = file.getLastUpdated();
var fileOwner = file.getOwner().getEmail();
// arrays with label and result names
var aNames = ['File name', 'File Id', 'File Owner', 'Last updated by'];
var aResults = [fileName, Id, fileOwner, lastUpdated];
// create Ui
var Ui = UiApp.createApplication().setTitle('Quick Parts').setWidth(450);
var vPanel = Ui.createVerticalPanel().setId('vPanel').setSize(450, 100);
var fTable = Ui.createFlexTable()
.setStyleAttribute('borderCollapse','collapse');
// create labels
for(var i=0, iLen=aNames.length; i<iLen; i++) {
fTable.setWidget(i, 0, Ui.createLabel(aNames[i]));
fTable.setWidget(i, 1, Ui.createLabel(aResults[i]));
}
// add to Ui
vPanel.add(Ui.createLabel().setText("Document Property")
.setStyleAttribute('font-size','175%'));
vPanel.add(fTable);
app.showSidebar(Ui.add(vPanel));
}
Screenshot
Short answer
At this time, variables is not a built-in feature of Google Docs and Google Apps Script, the platform to extend Google Docs, does not include a class or method to handle them.
Alternatives
Alternative 1
One alternative is use a text pattern but you should be sure that it will match only the date that you want to update.
Alternative 2
Another alternative is to use the class NamedRange but bear in mind that
- moving the range will make that it lose its name1.
- replacing text in a named range with multiple elements only works the first time2.
Code:
The following code, intended to be used in a script bound to a Google Document, has two main functions:
- Insert today's date
- Update today's date
For debugging purposes are being used
- date and time, instead of date only.
- custom menus to trigger the main functions.
"Known-issues": The update function replaces the whole paragraph.
To test the code, copy it, then go to your Google Docs, create a new document, click on Tools > Script Editor, select Blank Project, paste the code, save the project, assign a name, run on time to authorize the app, close your doc and open again. A new menu called "Utilities" will be displayed. From there you can call the main functions.
function onOpen() {
// Add a menu with some items, some separators, and a sub-menu.
DocumentApp.getUi().createMenu('Utilities')
.addItem('Insert Today\'s Date', 'insertTodayAtCursor')
.addItem('Update Today\'s Date', 'setTodayNamedRange')
.addToUi();
}
function todayDate(){
return Utilities.formatDate(new Date(), "GMT-5", "yyyy-MM-dd'T'HH:mm:ss'Z'"); // "yyyy-MM-dd"
}
/**
* Inserts the today's date at the current cursor location and create a NamedRange.
*/
function insertTodayAtCursor() {
var str = 'testToday';
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
if (cursor) {
// Attempt to insert today's date at the cursor position. If insertion returns null,
// then the cursor's containing element doesn't allow text insertions.
var date = todayDate();
var element = cursor.insertText(date);
if (element) {
var rangeBuilder = doc.newRange();
rangeBuilder.addElement(element);
return doc.addNamedRange(str, rangeBuilder.build());
} else {
DocumentApp.getUi().alert('Cannot insert text at this cursor location.');
}
} else {
DocumentApp.getUi().alert('Cannot find a cursor in the document.');
}
}
function setTodayNamedRange(){
var str = 'testToday';
var doc = DocumentApp.getActiveDocument();
// Retrieve the named range
var namedRanges = doc.getNamedRanges();
var newRange = doc.newRange();
var date = todayDate();
for(var i=0; i<namedRanges.length; i++){
if(namedRanges[i].getName() == str){
var rangeElement = namedRanges[i].getRange().getRangeElements();
for (var j=0; j<rangeElement.length; j++){
var element = rangeElement[j].getElement().asText().editAsText().setText(date);
newRange.addElement(element);
}
}
}
doc.addNamedRange(str, newRange.build());
}
Below there are some items of different kind (questions, specifications, etc.) that could serve to get inspiration or point to the "right direction" to find a "solution"
Footnotes
Best Answer
You could think of it as a mail-merge field: put merge fields in, and then each time you use the document to generate a contract, just have one row (the current client's details) in the data file. That said Google Documents don't directly support mail merge (yet -but I expect they will sooner or later). However there do appear to be some options - see this help forum article: http://productforums.google.com/forum/#!topic/docs/c6NBVY9xDDE
Another option is just to use a (or several) phrases that you are 100% sure won't appear anywhere else in the document (eg [CUST-NAME] ), and then use Edit > Find and replace > Replace all when you are ready to generate a document for a specific client. This is a little more work each time you set up the document, but easier to set up.