I would like to insert a timestamp (date and/or time) into Google Documents. The support documentation () says that there should be a keyboard shortcut, but it does not work in my environment (Win7 + IE9).
Can anyone provide me with a Google Apps script to achieve this?
This works well
In Google Docs : Tools -> Open Script Editor and save this script
function onOpen() {
var ui = DocumentApp.getUi();
// Or FormApp or SpreadsheetApp.
ui.createMenu('Custom Menu')
.addItem('Insert Date', 'insertDate')
.addToUi();
}
function insertDate() {
var cursor = DocumentApp.getActiveDocument().getCursor();
if (cursor) {
// Attempt to insert text at the cursor position. If insertion returns null,
// then the cursor's containing element doesn't allow text insertions.
var d = new Date();
var dd = d.getDate();
dd = pad(dd, 2)
var mm = d.getMonth() + 1; //Months are zero based
mm = pad(mm, 2)
var yyyy = d.getFullYear();
var date = dd + "-" + mm + "-" + yyyy;
var element = cursor.insertText(date);
if (element) {
element.setBold(true);
} else {
DocumentApp.getUi().alert('Cannot insert text at this cursor location.');
}
} else {
DocumentApp.getUi().alert('Cannot find a cursor in the document.');
}
}
function pad (str, max) {
str = str.toString();
return str.length < max ? pad("0" + str, max) : str;
}
Reload the Doc, Accept the permissions.
I am not sure if an add-on falls under the category Google Apps Script you were asking for, bug Text Factory provides the feature to insert a time-stamp.
Here is an edited version (of the one already provided) that prints out a date stamp including the time.
Here is an example of the output: 2:43:21 AM EST 26-03-2014
function onOpen() {
var ui = DocumentApp.getUi();
// Or FormApp or SpreadsheetApp.
ui.createMenu('Insert Date')
.addItem('Insert Date', 'insertDate')
.addToUi();
}
function insertTime() {
var d = new Date();
var timeStamp = d.getTime(); // Number of ms since Jan 1, 1970
// OR:
var currentTime = d.toLocaleTimeString(); // "12:35 PM", for instance
}
function insertDate() {
var cursor = DocumentApp.getActiveDocument().getCursor();
if (cursor) {
// Attempt to insert text at the cursor position. If insertion returns null,
// then the cursor's containing element doesn't allow text insertions.
var d = new Date();
var dd = d.getDate();
dd = pad(dd, 2)
var mm = d.getMonth() + 1; //Months are zero based
mm = pad(mm, 2)
var yyyy = d.getFullYear();
var timeStamp = d.getTime(); // Number of ms since Jan 1, 1970
var currentTime = d.toLocaleTimeString(); // "12:35 PM", for instance
var date = currentTime + " " + dd + "-" + mm + "-" + yyyy;
var element = cursor.insertText(date);
if (element) {
element.setBold(true);
} else {
DocumentApp.getUi().alert('Cannot insert text at this cursor location.');
}
} else {
DocumentApp.getUi().alert('Cannot find a cursor in the document.');
}
}
function pad (str, max) {
str = str.toString();
return str.length < max ? pad("0" + str, max) : str;
}
If you want to get automatically current date after open document, you can add this script:
In Google Docs: Tools -> Open Script Editor and save this script:
/**
* After open document actualize part with text "Generated" to "Generated [actual date]".
*/
function onOpen() {
var body = DocumentApp.getActiveDocument().getBody();
var date = Utilities.formatDate(new Date(), "GMT", "dd.MM.yyyy");
// Clear the text surrounding "Apps Script", with or without text.
body.replaceText("^Generated.*$", "Generated " + date);
}
In the document body you must have text "Generated".
Create a new Sheets document
In A1, put Date: and B1, put the formula NOW(). I named the sheet current-date, but you can name it whatever you want. This document will simply sit on your drive to server as a "date keeper" for all of your Docs documents.
Format Cell as Date
Select cell B1 and choose Number >> Date from the Format menu.
Select Cells and Copy
Select cells A1 and B1, right click, and choose Copy
Paste and Link to Docs Document
Right click in your Docs document where you want to paste the cells and click Paste. Docs should ask you if you want to link these cells to the source document. Select Link to spreadsheet and then click Paste.
Get Rid of the Borders
To get rid of the borders, right click on the inserted cells and select Table Properties.
Now set Table Border to 0pt and click OK.
Final Result
You should end up with something like this. You can drag the edges of the cells to make them bigger or smaller and change the font and text size of the cells as well. If you make the text bigger, the text will wrap inside the cells so you will need to make them wider.
Updating the Date
Now whenever you open your document with the linked cells and the date has changed, you should see this. Click Update and your date will be updated to today's date. There is no need to ever open the current-date spreadsheet!
Enjoy!
For Docs, you are probably out of luck, as there appears to be no hotkey for that, and support for scripting from within Docs is lacking (Spreadsheets would be a different story). Since you're on Windows, you can avail yourself of autohotkey, though. This video, while way too long, shows assigning a global hotkey combo to insert the current date anywhere. With that, you can insert your date/time anywhere you want to while using your Windows system. (You can customize it to be only active in certain applications, such as IE, if you want to get wacky with it)
Because extra key-strokes are being used to insert the date from the menu, my solution is a batch file, sts.cmd, that stores the date-time into the clip-board when called, enabling an easy Windows+R, sts, Ctrl+V to get and paste. If you are adding extra keystrokes to get the date into the doc anyway you might as well just paste it in.
The code is
#ECHO OFF
for /f "tokens=1-12 delims=/:. " %%d in ("%time%") do SET MYTIME= %%d:%%e:%%f
for /f "tokens=1-12 delims=/:. " %%d in ("%date%") do SET MYDATE= %%g-%%e-%%f
SET MYTS=%MYDATE%%MYTIME%
ECHO | SET /p dummyname=%MYTS%|clip
This works for me until GDocs comes up with an embeddable function that will update the display date to the current date each time the doc is opened.
On MacOS, I used Automator.
I created a Service that works with Chrome, takes no input, and runs a shell script and sends the output to the clipboard. The shell script is very basic:
DateTime=`date "+%Y-%m-%d %H:%M"`
echo $DateTime
I could have done without the intermediate variable and just run the date command, but I had the idea that I might want to do extra processing and formatting. Note that if you want to include spaces in your formatting of the date string, the formatting argument must be in quotes. The screen shot uses an underbar instead.
With the script saved, I went to System Settings > Keyboard > Shortcuts, I found my script DateTime2CB.workflow in the Text section and gave it a hotkey. There's no easy way of finding out what hotkeys are in use.
I could have made it for any app (and if I find myself trying to use it other apps I still might do that), but for now it's Chrome only. I could also have had the shell script pipe the output of date to the clipboard, pbcopy on Mac, as above (using "clip"). I've tested both methods and they work.
I added the same script to another Mac and it was set up to insist that the Automator file (name.workflow) be saved in iCloud. in that context, it was not listed in the Keyboard settings. To fix that, I had to reopen the saved iCloud file in Automator using Open > Recent. It asked if I wanted to install the file in Services. A yes answer at that point saved it on my local system and I could find it under Settings > Keyboard > Shorcuts > Services.
I hope this helps other Mac users.
August
Try this:
function insertTimestamp() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cell = ss.getActiveCell();
cell.setValue(new Date());
// sets the cells format to month/day/year.
// Remove if you want time inserted as well
cell.setNumberFormat("MM/dd/yyyy")
}
This works on spreadsheets not sure if you were looking for other docs.
Create a Spreadsheet Today in google docs. Put the date in B1 as Today() Format it in C1 using the function Text(B1,"dddd, mmmm d, yyyy")
Then use the following script (you will need the url of the Today Spreadsheet
function myFunction() {
var doc = DocumentApp.getActiveDocument();
var body1 = doc.getBody();
var style1 = {};
style1[DocumentApp.Attribute.BOLD] = true;
var text1 = doc.editAsText();
body1.appendHorizontalRule();
var wb = SpreadsheetApp.openByUrl('spreadsheet url');
var ss = wb.getSheetByName('Today');
var r = ss.getRange('C1');
var date1 = r.getValues();
var par1 =body1.appendParagraph(date1[0]);
par1.setAttributes(style1);
}
Related
I am familiar with writing conditional formatting rules in Google script.
I have a Google sheet that I have inherited that has been developed over a long period and conditional formatting rules have been manually inserted.
I am looking to copy all of the conditional formatting out of that google sheet using google apps script. The sheet has around 50 columns with many drop downs, and each dropdown, or combination of dropdowns, applies formatting. I think there are over 100 rules.
It would be VERY useful if I could get the conditional formatting out in a format that I could modify as needed and apply other (similar) sheets.
Any suggestions?
Mark
SUGGESTION
Note: We normally do not code for you, but in this case I have a sample script that I can share with you that was derived from the samples of these Spreadsheet App classes listed below:
You can try using these Spreadsheet App classes in Apps Script:
getBooleanCondition()
withCriteria(criteria, args)
ConditionalFormatRuleBuilder
Class Ui
Sample Script
var ui = SpreadsheetApp.getUi();
function onOpen() { //Sets the custom menu
ui.createMenu('Extract Existing Conditional Formatting')
.addItem('Copy Conditonal Formatting', 'checkRangeSelection')
.addToUi();
}
function checkRangeSelection() { //checks the selected range & asks user if he/she wants to copy the range's conditonal formatting values to another sheet range
var rule = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getConditionalFormatRules()[0];
var ranges = rule.getRanges();
var selectedRange = SpreadsheetApp.getActiveSheet().getSelection().getActiveRange().getA1Notation();
var newCriteriaValue;
for (var i = 0; i < ranges.length; i++) {
if (selectedRange == ranges[i].getA1Notation()) {
var response = ui.alert("Selected Range: " + selectedRange +
"\n\nThis range has these criteria:\n" +
"\nTYPE: " + rule.getBooleanCondition().getCriteriaType() +
"\nVALUE: " + rule.getBooleanCondition().getCriteriaValues() + '\n\nDo you want to update it and apply it to a new sheet range?',
ui.ButtonSet.YES_NO);
if (response == ui.Button.YES) {
var valueNew = ui.prompt("Current Criteria Value is: " + rule.getBooleanCondition().getCriteriaValues() + "\n\n Type here to change it:\n");
valueNew.getResponseText() == '' ? newCriteriaValue = rule.getBooleanCondition().getCriteriaValues() : newCriteriaValue = valueNew.getResponseText();
var destSheet = ui.prompt("Type the \"Sheet Name\" where you would like to apply the conditional formatting:\n\n");
var newRange = ui.prompt("Type the \"Range\" (e.g. A1:A100) where you would like to apply the conditional formatting:\n\n");
try {
var destinationSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(destSheet.getResponseText());
var range = destinationSheet.getRange(newRange.getResponseText());
var copiedrule = rule.copy().withCriteria(rule.getBooleanCondition().getCriteriaType(), [newCriteriaValue.toString()]).setRanges([range]).build();
var rules = destinationSheet.getConditionalFormatRules();
rules.push(copiedrule);
destinationSheet.setConditionalFormatRules(rules);
} catch {
ui.alert('Double check Sheet Name and the Range you have used & try again.');
}
} else {
ui.alert('Cancelled');
}
} else {
ui.alert('Selected range \"' + selectedRange + '\" doesn\'t contain any criteria.');
return;
}
}
}
Demonstration
Note: On this demonstration, I have two sample sheets named the CURRENT (the main sheet that contains the conditional formatting rules) & DESTINATION
Save the script as a bound script in your spreadsheet file
Then, run the onOpen function once on the Apps Script editor to load the custom menu.
After that, you will see a custom menu named "Extract Existing
Conditional Formatting" on your spreadsheet as seen below:
You can highlight any range that contains the Conditional
Formatting that you'd like to copy to another sheet tab, then press the custom menu. It will show you the criteria type used and its value, as seen here (this sample was done on the CURRENT sheet) :
If you press the Yes button, you will be prompted to change these details below:
Update the Criteria Value Or you could use the default value by just pressing Ok to go to the next step:
Type the Destination Sheet name
Type the Range (in A1 Notation format) where you want to apply the copied Conditional Formatting
After that, on the DESTINATION sample sheet, the Conditional Formatting has been applied to the range.
So I am making a script to pull the date from a given cell, reformat the date to a mm-dd--yyyy style, and then use that in a "title contains" file search to get a File_ID for referencing on a Vlookup. Unfortunately it is not working, and is returning an error with the formatting date line, but I suspect the problem is elsewhere since that variable works fine in logger. Any help would be appreciated.
function Builder() {
var cell = SpreadsheetApp.getActiveSheet().getRange(4, 1).getValue(); //get the date I want to search for
var final = Utilities.formatDate(cell, "GMT", "MM-dd-yyyy"); //get the date in the right format
var filesource = DriveApp.searchFiles("title contains '" + final + "' and parents in 'File_ID'"); //search for the date in a folder
if(filesource.hasNext() === true){
while(filesource.hasNext()){
var File = filesource.next();
var ID = File.getId();
}
SpreadsheetApp.getSheetbyName(Index).getRange(2, 3).setvalue(ID); //set the value of a certain cell to the string of the file ID I was looking for
}
}
Check that the file is set to the correct locale file -> spreadsheet settings -> general
Since you are using MM-dd-yyy is should be set to United States
Check that the number type of the cell is set to date format -> number -> date
When I do the above it works as expected.
I created a Google Form, linked to a Sheet to capture responses, and added an Apps Script that runs each time the Form is submitted. Ran through a bunch of tests and everything was working fine - form responses fed through, onSubmit function working great. Last night, though, we received a few executions of the script even though the Form was not submitted.
Looking at the Responses page on the Form itself, there were no submissions when I got the notification. Also, this is not a public Form in my organization, and only one other person has the link besides myself. He confirmed he didn't submit the form at the time of the executions.
There are two sheets in the Google Sheet: 1> Form Responses and 2> Data. The data sheet uses a few QUERY functions to pull data from the responses sheet, formatting it differently (e.g. putting hyphens in phone numbers, rendering some fields in upper case, etc.). Also, the data sheet headers are labeled differently than the Form questions (e.g. 'homeAdd1' instead of 'Home Address Line 1'). This is because the script creates a PDF, using the Form responses to replace placeholders ('%homeAdd1%') on a template Google Doc. The script then takes the generated PDF and emails it to the submitter.
Again, everything was working great until yesterday's testing. I didn't realize it at the time, but when my colleague was inputting random values to test the Form, for the Home Address Line 2 he only input a 5-digit ZIP code. It generated a PDF fine, and also emailed it to him, but this caused the QUERY function to render a #VALUE error. The functions look like this:
=QUERY(Responses!L2:S,"SELECT UPPER(L) UPPER(M)...
So when Sheets saw a cell with just 5 digits, it automatically rendered it as a number, and UPPER doesn't work on number values. I (stupidly) didn't think to pre-format all of both sheets as plain text, so this occurred.
Would a #VALUE error on a Google Sheet linked to a Form and an Apps Script cause a misfire of the onSubmit function? This is the only thing I can see that could have possibly caused it, but it doesn't make sense. I've fixed the formatting issue, but I don't know if an erroneous execution could mean some other issue.
With the extra submissions, the script just sent the most recent PDF again and again. Within 20 seconds, it fired 5 times, sending the last PDF that was generated via email each time. Looking at the Stackdriver logs, there's nothing different from when we were testing it earlier yesterday. The console.log and console.info commands work fine, and they all come through listed as having been triggered by the onSubmit function.
Here's the script:
Submit function:
function onSubmit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Data');
var list = ss.getRange("A1:A").getValues();
var row = list.filter(String).length;
var email = ss.getRange(row,2).getValue();
var newResponse = ss.getRange(row,3).getValue();
if (newResponse == 'Generate New') {
newOne(ss,row,email);
} else if (newResponse == 'Upload Completed') {
completed(ss,row,email);
} else {
}
}
Function that was executed:
function newOne(ss,row,email) {
var name = ss.getRange(row,4).getValue();
console.log('Function Start - ' + name);
var newType = ss.getRange(row,6).getValue();
var copyFile = DriveApp.getFileById('[file id]').makeCopy();
var copyDoc = DocumentApp.openById(copyFile.getId());
var copyBody = copyDoc.getActiveSection();
// Replacing variables with values on spreadsheet
console.log('Create file start - ' + name);
var newInfo = ss.getRange(row, 1, 1, 29).getDisplayValues();
var header = ss.getRange(1, 1, 1, 29).getDisplayValues();
for (var i = 1; i <= 5; i++) {
copyBody.replaceText('%' + header[0][i] + '%', newInfo[0][i].toString());
}
var x;
if (newType == 'Office 1') {
x = 6;
} else if (newType == 'Office 2') {
x = 15;
} else {
}
for (var i = x; i <= (x + 8); i++) {
copyBody.replaceText('%' + header[0][i] + '%', newInfo[0][i].toString());
}
copyBody.replaceText('%' + header[0][26] + '%', newInfo[0][26].toString());
// Create the PDF file, rename it, and delete the doc copy
copyDoc.saveAndClose();
var newFile = DriveApp.createFile(copyFile.getAs('application/pdf'));
newFile.setName('New - ' + name + '.pdf');
copyFile.setTrashed(true);
console.log('Create file finished - ' + name);
//Mails PDF to submitter
console.info('Pre-email log for ' + name);
MailApp.sendEmail(email,'Email Subject','', {
noReply: true,
htmlBody: "<body>Hello, and thank you.</body>",
attachments: [newFile]
});
console.info('Email sent for ' + name);
appFile.setTrashed(true);
}
Any insight / help would be appreciated; thanks!
Josh
Spurious unwanted Event Triggers
I've had problems with spurious triggers coming from onFormSubmit event triggers. In my case they were always immediately after a real trigger occurred from a Form Submission. I found that I could identify them because none of my required questions were answered. I discuss it here.
It might be worth your time to capture the e.values array and see if you can find a consistent way to keep them from causing a misfire of your processing function.
As far as I know, onSubmit(e) doesn't work the way you're expecting it to.
I think what you're looking for is an onFormSubmit trigger, try using the following from Class SpreadsheetTriggerBuilder documentation to create a script trigger that executes every time someone submits a response to your linked form:
var sheet = SpreadsheetApp.getActive();
ScriptApp.newTrigger("function name")
.forSpreadsheet(sheet)
.onFormSubmit()
.create();
I have an installation of a spreadsheet and corresponding form where I get frequent duplicate on form submit events, inexplicably. It does not occur in other installations. If this is your situation you can't just check if the event is null because to test it for null you have to have something to test. If it's undefined you will get an error. So first test if it's undefined. Try this code:
`function formSubmitted(e) {
// Deal with the unusual case that this is a bogus event
if ((typeof e === "undefined") || (e == null) || (e.length == 0)) {
Logger.log("formSubmitted() received a bogus or empty event");
return;
}
...`
I'm hoping to make (what I hoped was) a very basic script, where you can type part of a document name into a Google Spreadsheet, and underneath will appear the files in your Drive that have that word in their title.
As an example, I have two files in my Drive called "Rome Adventure" and "London Adventure", and the idea would be that if you typed "Rome", "London", or "Adventure" into a cell, the file titles would appear below.
So far I've got this:
function onEdit(e){
// Gets the edited cell, turns it into string
var activeSheet = e.source.getActiveSheet();
var range = e.range;
var input = range.getCell(1,1).getValue();
var SearchString = 'title contains "' + input + '"';
// Searches Drive for files with titles containing whatever you typed
// and appends titles to the spreadsheet
var result = DriveApp.searchFiles(SearchString);
while (result.hasNext()) {
var file = result.next();
activeSheet.appendRow([file.getName()]);
}
}
But unfortunately nothing is appended, regardless of what I enter. I've tried "Rome", "London", and just about everything else I can think of. Thinking I might have stuffed up something in the first section, I added
range.setNote('Last modified: ' + new Date());
in between the two sections, and that worked. So it's definitely just the DriveApp.searchFiles that I've stuffed up. I thought I might have stuffed up the StringSearch bit, but changing the line to
var result = DriveApp.searchFiles('title contains "Rome"');
still doesn't return anything.
I'm only an enthusiast-level programmer, and this is the first time I've asked a question on here. So forgive me if this is a stupid question, or it's not possible. I'm just at my wit's end trying to get this to work.
Nothing is wrong with your function. It's failing because of an undocumented restriction using the onEdit trigger to search DriveApp. If you run the script as is and then go to View > Execution transcript, you'll see the failure message at the bottom.
You can call the function successfully from a custom menu. Instead of watching for the edited row, use .getActiveCell(). It will search the string inside the selected cell on the spreadsheet. A working example is below.
function onOpen() {
SpreadsheetApp.getUi().createMenu("Search").addItem("Run", "searchFiles").addToUi();
}
function searchFiles() {
var string = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getActiveCell().getValue();
var result = DriveApp.searchFiles('title contains "' + string + '"');
while (result.hasNext()) {
var file = result.next();
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().appendRow([file.getName()]);
}
}
Please see my spreadsheet.
https://docs.google.com/a/zigmens.com/spreadsheet/ccc?key=0AtXr7M_f8JAadG1qclprdXRFb3VuNXV0OXg5bDNpNkE&usp=drive_web#gid=0
I have a menu item, Create/Job Scope that pulls data from the spreadsheet and creates a new document in "my drive" location of google drive. Problem I have, is that it's not including the stye and format that I have in the spreadsheet. I have tried many ways of setting the variable in the script to "bold", for example, but can't seem to get it to work. I am hoping there is an object that I can add to the variable that will set it's style.
For example, in my script, I have a variable called "desc"
var descr = values[n][3] ;
I would like to just create a new variable such as
var descr = values[n][3] ;
var desc_style = descr.setStyle("bold") ;
Here is the script from the spreadsheet but I think it's best if you just update the script in my spreadsheet.
function jobScope() {
var ss = SpreadsheetApp.getActiveSheet();
var values = ss.getDataRange().getValues();
var docTemplate = "19ANrZluvbavWU4Ttgh1z9_DVJgEQ1hrGohd4lQAg7vI";
var job_name = ss.getRange("D4").getValue();
var docName = job_name+' Job Scope ';
var x = 1 ;
while(values[x][0] ^= "") {
++x ;
}
var textToDoc = "" ;
for(n=1;n<x;++n){
var cell = values[n][4] ;
if (cell ^ "0") {
var line_item = values[n][1];
var descr = values[n][3] ;
textToDoc = textToDoc + line_item + " " + descr + "\n\n" ;
}
}
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(docName)
.getId();
var copyDoc = DocumentApp.openById(copyId);
var copyBody = copyDoc.getActiveSection();
copyBody.replaceText('keyScope', textToDoc);
copyBody.replaceText('keyJobName', job_name);
copyDoc.saveAndClose();
}
Have you tried setting the formatting of the keys to your preferred style? If you format the key (ie keyScope) in the document template, then the variable that replaces the key will retain they key's formatting.
//...trimmed previous code
// Get document template, copy it as a new temp doc, and save the docs id
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(docName)
.getId();
//Open the temporary document and set it to a variable
var copyDoc = DocumentApp.openById(copyId);
//Get the documents body section
var copyBody = copyDoc.getActiveSection();
//Replace place holder keys, in our google doc template
copyBody.replaceText('keyline_item', line_item);
copyBody.replaceText('keyDescr', descr);
copyBody.replaceText('keyJobName', job_name);
copyDoc.saveAndClose();
}
Although this is the only answer I know of that answers the original question, what the author is looking for is a way to preserve formatting (bold) in a variable that joins two values from different cells (with different formatting). To the best of my knowledge, it is not possible to carry formatting from spreadsheets to documents, regardless of the number of variables because the keyReplace object is only composed of the raw text (or formula) that occupies the targeted cell or range where .getValues() is used.
If I'm wrong about this, I would appreciate someone correcting me, but since the author combines two adjacent cells into a single variable, and that is the answer he is seeking, I would think that it still wouldn't be possible to do as a single keyObject replacement in a template because two cells with different formatting are being merged into a single object in the script. I know that you can get background and foreground colors, but you have to get those by R1C1 notation. Are there other formatting options that can be captured from spreadsheets?
Either way, I'm going to leave this to the pros, because the only way I could see, from his source files, was to completely rewrite his document template and script, and that is not what the author of the question is looking for. I will leave my script above for people who may want to do something similar in the future.