Adding custom menus to Google docs

Using Google Apps Script, but unfortunately not in Google apps.

Google apps menu

I’ve been using Google Docs more because at work it’s great for collaboration, and also, for shopping lists and notes to myself, I can easily edit the same documents from my phone, tablet, and laptop. I found out that it’s pretty easy to add menus that perform custom functions, so I created a few menu choices… and then found out that they weren’t available on my phone or tablet. Still, it’s good to know how easy it is to automate a few things.

Extending Google Docs is a good introduction to getting started. Picking Script Editor from the Tools menu puts you into this editor with an empty function waiting for you to fill it in or, more likely, to replace it with code you copied from web pages such as “Extending Google Docs.” Google Apps Script is basically Javascript, and I had an easy time searching for any code that I wanted to plug in.

For example, when writing a note about something, I sometimes want to add a date-time stamp to show exactly when I made a particular note, because if it’s ongoing research it’s easier to see my progress leading up to where I left off. (I’ve had my .emacs file set up to let me add this with Alt+D for years.) To add a timestamp menu choice to Google Docs, I replaced the blank function in the script editor with menu code based on what you see in Custom Menus in Google Apps, and then I added a line to insert the current date and time at the cursor using the format “Sun Mar 13 2016 10:40:33 GMT-0400 (EDT).” I’d prefer the terser ISO 8601 format, and I found a function to convert it, but the function wants to know what time zone you’re in, and the simpler Date() function that creates the more verbose form already knows.

When I read something on my tablet and I’m taking notes, I often paste blocks of text into a Google docs document. To remember which parts are large verbatim blocks of someone else’s writing, I enclose them in <blockquote></blockquote> tags. My second new menu item inserts this string and then moves the cursor between those tags so that if I have something in my copy-paste buffer I can just paste it right there. The “utilities” menu that I added also demonstrates how to add a menu separator and a submenu that pops up a message box.

The code is all shown below. If I want to share these features across multiple documents, to be honest, the simplest way I’ve found is to paste this code into the script editor for each of the other documents. This is not, if I may string together some buzzwords, a scalable code maintenance solution.

These are known as “bound” scripts because they’re bound to specific documents. You can also create standalone scripts, which I hoped would be a way to store shared code that could be referenced from multiple documents, but you actually run them independently of the documents to perform tasks that are not tied to any specific document such as, in the example on that page, searching Google Drive for documents meeting certain conditions.

If you have a script that adds choices to a document and you want to use it from multiple documents, you must publish it. As the Publishing an Add-on web page says,

Publishing add-ons allows them to be used by other users in their own documents. Public add-ons require a review before publication, although if you are a member of a private Google Apps domain, you can publish just for users within your domain without a review. You can also publish an add-on for domain-wide installation, which lets a domain admins find [sic], authorize and install your add-on on behalf of all users within their domain.

There’s even an add-on store with offerings available from some recognizable brand names.

I never did find a way to create a single script that I could share among my own documents without going through some approval process. In an even greater disappointment, I found that the menu I created was not available when editing that same document on my phone or tablet, which was much of the point of creating them. In other words, this part of Google Apps script doesn’t work with Google apps.

Still, skimming the Apps Script Reference for available methods to call when customizing for Google Docs, spreadsheets, calendars, and more shows that there’s a lot to play with, and I didn’t even try a standalone script. If this ever works on phones and tablets, I will definitely be digging back into the reference material again.

function onOpen() {
  var ui = DocumentApp.getUi();
  // Or DocumentApp or FormApp.
  ui.createMenu('utilities')
      .addItem('timestamp', 'insertTimestamp')
      .addItem('blockquote', 'insertBqTags')
     .addSeparator()
      .addSubMenu(ui.createMenu('Sub-menu')
          .addItem('Second item', 'menuItem2'))
      .addToUi();
}


function insertTimestamp() {
  DocumentApp.getUi() ; 
  var doc = DocumentApp.getActiveDocument(); 
  var body = doc.getBody();
  // The following gives me ISO format, which I prefer, but unlike Date(), 
  // needs to be told the time zone 
  // var timestamp = Utilities.formatDate(new Date(), "EDT", "yyyy-MM-dd'T'HH:mm:ss"); 
  var timestamp = new Date();
  // https://developers.google.com/apps-script/reference/document/document#getcursor
  // has error-checking code for the following that would make it more robust.
  var cursor = DocumentApp.getActiveDocument().getCursor();
  var element = cursor.insertText(timestamp);
}


function insertBqTags() {
  DocumentApp.getUi() ;
  var doc = DocumentApp.getActiveDocument(); 
  var body = doc.getBody();
  var cursor = DocumentApp.getActiveDocument().getCursor();
  var insertedText = cursor.insertText("<blockquote></blockquote>");
  var position = doc.newPosition(insertedText, 12);
  doc.setCursor(position); 
}


function menuItem2() {
  DocumentApp.getUi() // Or DocumentApp or FormApp.
     .alert('You clicked the second menu item!');
}