Move a row from sheet to sheet with comments - google-apps-script

I have the below script in place (1st script implemented 1 year ago) to move a row form 1 sheet to another (within a google sheet/workbook). The problem is that is does not take the comments with it. Does anyone know how I can include any unresolved comments in the transition from sheet to sheet?
var objSettings = {
sheets : ['CHECK', 'STAGE2', 'READY', 'SCHEDULED', 'ARCHIVE', 'WASTE'],
shActive : [1, 1, 1, 1, 1, 1],
column : 1,} function onEdit(e) {try{var sh = e.range.getSheet();if (e.range.getNumRows() == 1 && e.range.getNumColumns() == 1 && e.range.getColumn() == objSettings.column) {
var sheetName = '';
for (var i = 0; i < objSettings.sheets.length; i++) {
if (objSettings.sheets[i] == sh.getName().trim().toUpperCase()) {
if (objSettings.shActive[i] == 1) {
Logger.log(('' + e.value).trim().toUpperCase() );
Logger.log(sh.getName().trim().toUpperCase());
if (('' + e.value).trim().toUpperCase() != sh.getName().trim().toUpperCase()) {
var shDest = e.source.getSheetByName(e.value);
if (!(shDest)) {
var shDest = e.source.getSheetByName(('' + e.value).trim().toProperCase());
}
if (!(shDest)) {
var shDest = e.source.getSheetByName(('' + e.value).trim().toLowerCase());
}
if (!(shDest)) {
var shDest = e.source.getSheetByName(('' + e.value).trim().toUpperCase());
}
Logger.log(shDest);
if (shDest) {
var currRow = e.range.getRow();
var dataDest = shDest.getDataRange().getValues();
for (var j = 3; j < dataDest.length; j++) {
if (('' + dataDest[j][objSettings.column - 1]).length == 0) {
break;
}
}
var lastRow = j;
for (var k = 0; k < objSettings.sheets.length; k++) {
if (('' + e.value).trim().toUpperCase() == objSettings.sheets[k]) {
var type = objSettings.shActive[k];
break;
}
}
var dataR = sh.getRange(currRow, 1, 1, sh.getLastColumn()).getValues();
if (type == 1) {
shDest.getRange(lastRow + 1, 1, 1, dataR[0].length).setValues(dataR);
} else {
var dataR0 = dataR[0];
dataR0.shift();
shDest.getRange(lastRow + 1, 1, 1, dataR[0].length).setValues([dataR0]);
}
sh.deleteRow(currRow);
Logger.log(currRow);
Logger.log(lastRow + 1);
Logger.log(type);
sh.getParent().toast('The row has been moved!', 'Info');
}
}
}
break;
}
}
}}catch(e){
SpreadsheetApp.getUi().alert(e); } }
Alternatively, if you think there is a better more stable way of accomplishing this, please do let me know.

Use moveTo() to cut and paste ranges with comments.
Comments are a part of the Drive API.
Enable the Advanced Drive Service to use Drive.Comments.{}
Then you can check the status property for "open" || "resolved"
function moveComments () {
var ss = SpreadsheetApp.getActive();
var range_one = ss.getSheets()[0].getRange("A10");
var range_two = ss.getSheets()[1].getRange("A10");
// Get a list of all comments in the workbook
var workbook_comments = Drive.Comments.list(ss.getId());
/*
var comment_cell_value = workbook_comments.items[0].context.value;
var check_status = workbook_comments.items[0].status; // "open" || "resolved"
*/
// Move the range with comments to another sheet.range
range_one.moveTo(range_two);
}
See also Manage Comments and Discussions

Related

New answer to old question about Google Sheets/Calendar

I found this old question about synchronizing Google Sheets and Google Calendar and tried to download the template and then edit it accordingly.
When I try to run the script it prompts this error ReferenceError: calenderId is not defined.
Since I can only see one line, 9, where to to enter the calendarId I can not make the script work.
Did I miss a line in my example? I am aware that I need to write my cal-id in line 9.
// Script to synchronize a calendar to a spreadsheet and vice versa.
//
// See https://github.com/Davepar/gcalendarsync for instructions on setting this up.
//
// Set this value to match your calendar!!!
// Calendar ID can be found in the "Calendar Address" section of the Calendar Settings.
//var calendarId = '<your-calendar-id>#group.calendar.google.com';
var calendarId = 'Her er min kalender-adresse, naturligvis';
// Set the beginning and end dates that should be synced. beginDate can be set to Date() to use
// today. The numbers are year, month, date, where month is 0 for Jan through 11 for Dec.
var beginDate = new Date(1970, 0, 1); // Default to Jan 1, 1970
var endDate = new Date(2500, 0, 1); // Default to Jan 1, 2500
// Date format to use in the spreadsheet.
var dateFormat = 'M/d/yyyy H:mm';
var titleRowMap = {
'title': 'Title',
'description': 'Description',
'location': 'Location',
'starttime': 'Start Time',
'endtime': 'End Time',
'guests': 'Guests',
'color': 'Color',
'id': 'Id'
};
var titleRowKeys = ['title', 'description', 'location', 'starttime', 'endtime', 'guests', 'color', 'id'];
var requiredFields = ['id', 'title', 'starttime', 'endtime'];
// This controls whether email invites are sent to guests when the event is created in the
// calendar. Note that any changes to the event will cause email invites to be resent.
var SEND_EMAIL_INVITES = false;
// Setting this to true will silently skip rows that have a blank start and end time
// instead of popping up an error dialog.
var SKIP_BLANK_ROWS = false;
// Updating too many events in a short time period triggers an error. These values
// were successfully used for deleting and adding 240 events. Values in milliseconds.
var THROTTLE_SLEEP_TIME = 200;
var MAX_RUN_TIME = 5.75 * 60 * 1000;
// Special flag value. Don't change.
var EVENT_DIFFS_WITH_GUESTS = 999;
// Adds the custom menu to the active spreadsheet.
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{
name: "Update from Calendar",
functionName: "syncFromCalendar"
}, {
name: "Update to Calendar",
functionName: "syncToCalendar"
}
];
spreadsheet.addMenu('Calendar Sync', menuEntries);
}
// Creates a mapping array between spreadsheet column and event field name
function createIdxMap(row) {
var idxMap = [];
for (var idx = 0; idx < row.length; idx++) {
var fieldFromHdr = row[idx];
for (var titleKey in titleRowMap) {
if (titleRowMap[titleKey] == fieldFromHdr) {
idxMap.push(titleKey);
break;
}
}
if (idxMap.length <= idx) {
// Header field not in map, so add null
idxMap.push(null);
}
}
return idxMap;
}
// Converts a spreadsheet row into an object containing event-related fields
function reformatEvent(row, idxMap, keysToAdd) {
var reformatted = row.reduce(function(event, value, idx) {
if (idxMap[idx] != null) {
event[idxMap[idx]] = value;
}
return event;
}, {});
for (var k in keysToAdd) {
reformatted[keysToAdd[k]] = '';
}
return reformatted;
}
// Converts a calendar event to a psuedo-sheet event.
function convertCalEvent(calEvent) {
convertedEvent = {
'id': calEvent.getId(),
'title': calEvent.getTitle(),
'description': calEvent.getDescription(),
'location': calEvent.getLocation(),
'guests': calEvent.getGuestList().map(function(x) {return x.getEmail();}).join(','),
'color': calEvent.getColor()
};
if (calEvent.isAllDayEvent()) {
convertedEvent.starttime = calEvent.getAllDayStartDate();
var endtime = calEvent.getAllDayEndDate();
if (endtime - convertedEvent.starttime === 24 * 3600 * 1000) {
convertedEvent.endtime = '';
} else {
convertedEvent.endtime = endtime;
if (endtime.getHours() === 0 && endtime.getMinutes() == 0) {
convertedEvent.endtime.setSeconds(endtime.getSeconds() - 1);
}
}
} else {
convertedEvent.starttime = calEvent.getStartTime();
convertedEvent.endtime = calEvent.getEndTime();
}
return convertedEvent;
}
// Converts calendar event into spreadsheet data row
function calEventToSheet(calEvent, idxMap, dataRow) {
convertedEvent = convertCalEvent(calEvent);
for (var idx = 0; idx < idxMap.length; idx++) {
if (idxMap[idx] !== null) {
dataRow[idx] = convertedEvent[idxMap[idx]];
}
}
}
// Returns empty string or time in milliseconds for Date object
function getEndTime(ev) {
return ev.endtime === '' ? '' : ev.endtime.getTime();
}
// Determines the number of field differences between a calendar event and
// a spreadsheet event
function eventDifferences(convertedCalEvent, sev) {
var eventDiffs = 0 + (convertedCalEvent.title !== sev.title) +
(convertedCalEvent.description !== sev.description) +
(convertedCalEvent.location !== sev.location) +
(convertedCalEvent.starttime.toString() !== sev.starttime.toString()) +
(getEndTime(convertedCalEvent) !== getEndTime(sev)) +
(convertedCalEvent.guests !== sev.guests) +
(convertedCalEvent.color !== ('' + sev.color));
if (eventDiffs > 0 && convertedCalEvent.guests) {
// Use a special flag value if an event changed, but it has guests.
eventDiffs = EVENT_DIFFS_WITH_GUESTS;
}
return eventDiffs;
}
// Determine whether required fields are missing
function areRequiredFieldsMissing(idxMap) {
return requiredFields.some(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Returns list of fields that aren't in spreadsheet
function missingFields(idxMap) {
return titleRowKeys.filter(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Set up formats and hide ID column for empty spreadsheet
function setUpSheet(sheet, fieldKeys) {
sheet.getRange(1, fieldKeys.indexOf('starttime') + 1, 999).setNumberFormat(dateFormat);
sheet.getRange(1, fieldKeys.indexOf('endtime') + 1, 999).setNumberFormat(dateFormat);
sheet.hideColumns(fieldKeys.indexOf('id') + 1);
}
// Display error alert
function errorAlert(msg, evt, ridx) {
var ui = SpreadsheetApp.getUi();
if (evt) {
ui.alert('Skipping row: ' + msg + ' in event "' + evt.title + '", row ' + (ridx + 1));
} else {
ui.alert(msg);
}
}
// Updates a calendar event from a sheet event.
function updateEvent(calEvent, convertedCalEvent, sheetEvent){
var numChanges = 0;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (convertedCalEvent.starttime.toString() !== sheetEvent.starttime.toString() ||
getEndTime(convertedCalEvent) !== getEndTime(sheetEvent)) {
if (sheetEvent.endtime === '') {
calEvent.setAllDayDate(sheetEvent.starttime);
} else {
calEvent.setTime(sheetEvent.starttime, sheetEvent.endtime);
}
numChanges++;
}
if (convertedCalEvent.title !== sheetEvent.title) {
calEvent.setTitle(sheetEvent.title);
numChanges++;
}
if (convertedCalEvent.description !== sheetEvent.description) {
calEvent.setDescription(sheetEvent.description);
numChanges++;
}
if (convertedCalEvent.location !== sheetEvent.location) {
calEvent.setLocation(sheetEvent.location);
numChanges++;
}
if (convertedCalEvent.color !== ('' + sheetEvent.color)) {
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
calEvent.setColor('' + sheetEvent.color);
numChanges++;
}
}
if (convertedCalEvent.guests !== sheetEvent.guests) {
var guestCal = calEvent.getGuestList().map(function (x) {
return {
email: x.getEmail(),
added: false
};
});
var sheetGuests = sheetEvent.guests || '';
var guests = sheetGuests.split(',').map(function (x) {
return x ? x.trim() : '';
});
// Check guests that are already invited.
for (var gIx = 0; gIx < guestCal.length; gIx++) {
var index = guests.indexOf(guestCal[gIx].email);
if (index >= 0) {
guestCal[gIx].added = true;
guests.splice(index, 1);
}
}
guests.forEach(function (guest) {
if (guest) {
calEvent.addGuest(guest);
numChanges++;
}
});
guestCal.forEach(function (guest) {
if (!guest.added) {
calEvent.removeGuest(guest.email);
numChanges++;
}
});
}
// Throttle updates.
Utilities.sleep(THROTTLE_SLEEP_TIME * numChanges);
return numChanges;
}
// Synchronize from calendar to spreadsheet.
function syncFromCalendar() {
console.info('Starting sync from calendar');
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
var calEvents = calendar.getEvents(beginDate, endDate);
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var eventFound = new Array(data.length);
// Check if spreadsheet is empty and add a title row
var titleRow = [];
for (var idx = 0; idx < titleRowKeys.length; idx++) {
titleRow.push(titleRowMap[titleRowKeys[idx]]);
}
if (data.length < 1) {
data.push(titleRow);
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
if (data.length == 1 && data[0].length == 1 && data[0][0] === '') {
data[0] = titleRow;
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
// Map spreadsheet headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
// Array of IDs in the spreadsheet
var sheetEventIds = data.slice(1).map(function(row) {return row[idIdx];});
// Loop through calendar events
for (var cidx = 0; cidx < calEvents.length; cidx++) {
var calEvent = calEvents[cidx];
var calEventId = calEvent.getId();
var ridx = sheetEventIds.indexOf(calEventId) + 1;
if (ridx < 1) {
// Event not found, create it
ridx = data.length;
var newRow = [];
var rowSize = idxMap.length;
while (rowSize--) newRow.push('');
data.push(newRow);
} else {
eventFound[ridx] = true;
}
// Update event in spreadsheet data
calEventToSheet(calEvent, idxMap, data[ridx]);
}
// Remove any data rows not found in the calendar
var rowsDeleted = 0;
for (var idx = eventFound.length - 1; idx > 0; idx--) {
//event doesn't exists and has an event id
if (!eventFound[idx] && sheetEventIds[idx - 1]) {
data.splice(idx, 1);
rowsDeleted++;
}
}
// Save spreadsheet changes
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
if (rowsDeleted > 0) {
sheet.deleteRows(data.length + 1, rowsDeleted);
}
}
// Synchronize from spreadsheet to calendar.
function syncToCalendar() {
console.info('Starting sync to calendar');
var scriptStart = Date.now();
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
if (!calendar) {
errorAlert('Cannot find calendar. Check instructions for set up.');
}
var calEvents = calendar.getEvents(beginDate, endDate);
var calEventIds = calEvents.map(function(val) {return val.getId();});
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
if (data.length < 2) {
errorAlert('Spreadsheet must have a title row and at least one data row');
return;
}
// Map headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
var idRange = range.offset(0, idIdx, data.length, 1);
var idData = idRange.getValues()
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
var keysToAdd = missingFields(idxMap);
// Loop through spreadsheet rows
var numAdded = 0;
var numUpdates = 0;
var eventsAdded = false;
for (var ridx = 1; ridx < data.length; ridx++) {
var sheetEvent = reformatEvent(data[ridx], idxMap, keysToAdd);
// If enabled, skip rows with blank/invalid start and end times
if (SKIP_BLANK_ROWS && !(sheetEvent.starttime instanceof Date) &&
!(sheetEvent.endtime instanceof Date)) {
continue;
}
// Do some error checking first
if (!sheetEvent.title) {
errorAlert('must have title', sheetEvent, ridx);
continue;
}
if (!(sheetEvent.starttime instanceof Date)) {
errorAlert('start time must be a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime !== '') {
if (!(sheetEvent.endtime instanceof Date)) {
errorAlert('end time must be empty or a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime < sheetEvent.starttime) {
errorAlert('end time must be after start time for event', sheetEvent, ridx);
continue;
}
}
// Ignore events outside of the begin/end range desired.
if (sheetEvent.starttime > endDate) {
continue;
}
if (sheetEvent.endtime === '') {
if (sheetEvent.starttime < beginDate) {
continue;
}
} else {
if (sheetEvent.endtime < beginDate) {
continue;
}
}
// Determine if spreadsheet event is already in calendar and matches
var addEvent = true;
if (sheetEvent.id) {
var eventIdx = calEventIds.indexOf(sheetEvent.id);
if (eventIdx >= 0) {
calEventIds[eventIdx] = null; // Prevents removing event below
addEvent = false;
var calEvent = calEvents[eventIdx];
var convertedCalEvent = convertCalEvent(calEvent);
var eventDiffs = eventDifferences(convertedCalEvent, sheetEvent);
if (eventDiffs > 0) {
// When there are only 1 or 2 event differences, it's quicker to
// update the event. For more event diffs, delete and re-add the event. The one
// exception is if the event has guests (eventDiffs=99). We don't
// want to force guests to re-confirm, so go through the slow update
// process instead.
if (eventDiffs < 3 && eventDiffs !== EVENT_DIFFS_WITH_GUESTS) {
numUpdates += updateEvent(calEvent, convertedCalEvent, sheetEvent);
} else {
addEvent = true;
calEventIds[eventIdx] = sheetEvent.id;
}
}
}
}
console.info('%d updates, time: %d msecs', numUpdates, Date.now() - scriptStart);
if (addEvent) {
var newEvent;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (sheetEvent.endtime === '') {
newEvent = calendar.createAllDayEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent);
} else {
newEvent = calendar.createEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent.endtime, sheetEvent);
}
// Put event ID back into spreadsheet
idData[ridx][0] = newEvent.getId();
eventsAdded = true;
// Set event color
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
newEvent.setColor('' + sheetEvent.color);
}
// Throttle updates.
numAdded++;
Utilities.sleep(THROTTLE_SLEEP_TIME);
if (numAdded % 10 === 0) {
console.info('%d events added, time: %d msecs', numAdded, Date.now() - scriptStart);
}
}
// If the script is getting close to timing out, save the event IDs added so far to avoid lots
// of duplicate events.
if ((Date.now() - scriptStart) > MAX_RUN_TIME) {
idRange.setValues(idData);
}
}
// Save spreadsheet changes
if (eventsAdded) {
idRange.setValues(idData);
}
// Remove any calendar events not found in the spreadsheet
var numToRemove = calEventIds.reduce(function(prevVal, curVal) {
if (curVal !== null) {
prevVal++;
}
return prevVal;
}, 0);
if (numToRemove > 0) {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Delete ' + numToRemove + ' calendar event(s) not found in spreadsheet?',
ui.ButtonSet.YES_NO);
if (response == ui.Button.YES) {
var numRemoved = 0;
calEventIds.forEach(function(id, idx) {
if (id != null) {
calEvents[idx].deleteEvent();
Utilities.sleep(THROTTLE_SLEEP_TIME);
numRemoved++;
if (numRemoved % 10 === 0) {
console.info('%d events removed, time: %d msecs', numRemoved, Date.now() - scriptStart);
}
}
});
}
}
}
// Set up a trigger to automatically update the calendar when the spreadsheet is
// modified. See the instructions for how to use this.
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('syncToCalendar')
.forSpreadsheet(ss)
.onEdit()
.create();
}
// Delete the trigger. Use this to stop automatically updating the calendar.
function deleteTrigger() {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
for (var idx = 0; idx < allTriggers.length; idx++) {
if (allTriggers[idx].getHandlerFunction() === 'syncToCalendar') {
ScriptApp.deleteTrigger(allTriggers[idx]);
}
}
}
The ReferenceError: calenderId is not defined error message you are getting is due to the fact that you are declaring calenderId instead of calendarId and you are using calendarId all over your code.
So in order to fix your error you might want to check the line at which you are declaring the variable and declare it with the appropriate name.

Lock/Protect cells in a whole Google spreadsheet containing a specific text value

I have the following script (from this answer) which inserts a specific text value to a range of cells based on the date on the second column (B) in the same row.....
function onEdit(e) {
if (e.range.getColumn() == 2) {
//User edited the date column
if (typeof e.range.getValue() === typeof new Date()) {
//Value of edit was a date
endColumns(e.range.getRow(), e.range.getValue());
} else if (e.range.getValue() === "" || e.range.getValue() === null) {
var sheets = SpreadsheetApp.getActiveSheet();
// Delete all "ENDED" values:
var resetRange = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1, 1, sheets.getMaxColumns() - e.range.getColumn());
var resetValues = resetRange.getValues()[0];
for(var i = 0; i < resetValues.length; i++) {
if(resetValues[i] == "ENDED") {
var resetCell = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1 + i);
resetCell.clearContent();
}
}
}
}
}
function endColumns(rowNum, limitDate) {
var sheets = SpreadsheetApp.getActiveSheet();
var colOffset = 3; //Offset to account for your row Headers
var dateHeader = sheets.getRange(1, colOffset, 1, sheets.getMaxColumns() - colOffset);
var availableDates = dateHeader.getValues()[0];
var foundCol = 0;
for (var i = 0; i < availableDates.length; i++) {
if (availableDates[i] >= limitDate) {
break;
}
foundCol++;
}
var rewriteCells = sheets.getRange(rowNum, foundCol + colOffset, 1, sheets.getMaxColumns() - (foundCol + colOffset - 1));
//Add your formatting and text below:
rewriteCells.setValue("ENDED");
// Clear past dates if new date is later:
var beforeDate = sheets.getRange(rowNum, colOffset, 1, foundCol);
var beforeValues = beforeDate.getValues()[0];
for(var i = 0; i < beforeValues.length; i++) {
if(beforeValues[i] == "ENDED") {
sheets.getRange(rowNum, colOffset + i).clearContent();
}
}
// Clear all cells that are "white" (no header)
for (var i = 0; i < availableDates.length; i++) {
if (availableDates[i] === "" || availableDates[i] === null) {
sheets.getRange(rowNum, colOffset+i).clear();
}
}
}
I want these cells, containing that specific text value, to be lock/protect from edit/delete by any other user except me (which i am the owner of the file). If the date in second column (B) is deleted then the above script clears the text values and the cells should be available for editing from all users.
I've try many codes snippets to do this with no luck and the closest approach that i found is the bellow script, but it locks only one column and all the cells in that column. Not only those with a specific value...
function onEdit(event)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var CellRow = SpreadsheetApp.getActiveRange().getRow();
var CellColumn = SpreadsheetApp.getActiveRange().getColumn();
if (CellColumn == 7){
sheet.getRange(CellRow, CellColumn).setValue("ENDED");
Browser.msgBox("YOU CANNOT ADD DATA HERE!");
}
}
Any idea how can i lock only the cells containing that specific text value ("ENDED") and unlock/un-protect them when that value it's cleared?.
The workflow would be as following:
Incorporate a function that when run, protects all cells of your range with the content "ENDED"
Modify your existing onEdit() function by adding additional code that will check if the content of a cell in column B has been deleted
Below is a sample:
function myStartFunction(){
var sheets = SpreadsheetApp.getActive().getActiveSheet();
//adapt according to your datarange
var myRange=sheets.getRange(1,3,sheets.getLastRow(),sheets.getLastColumn());
var values=myRange.getValues();
for(var i=0;i<values.length;i++){
for(var j=0;j<values[0].length;j++){
if(values[i][j]=="ENDED"){
var cell=myRange.getCell(i+1,j+1);
var protection = cell.protect();
protection.removeEditors(protection.getEditors());
}
}
}
}
function onEdit(e){
//YOUR EXISTING CODE
if (e.range.getColumn() == 2) {
//User edited the date column
if (typeof e.range.getValue() === typeof new Date()) {
//Value of edit was a date
endColumns(e.range.getRow(), e.range.getValue());
} else if (e.range.getValue() === "" || e.range.getValue() === null) {
var sheets = SpreadsheetApp.getActiveSheet();
// Delete all "ENDED" values:
var resetRange = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1, 1, sheets.getMaxColumns() - e.range.getColumn());
var resetValues = resetRange.getValues()[0];
for(var i = 0; i < resetValues.length; i++) {
if(resetValues[i] == "ENDED") {
var resetCell = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1 + i);
resetCell.clearContent();
}
}
}
}
//HERE COMES THE ADDITIONAL PART
if(e.range.getColumn() == 2 && e.oldValue != "" && e.range.getValue() == "") {
var row=e.range.getRow();
var sheets = SpreadsheetApp.getActiveSheet();
var protections=sheets.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.getRange().getRow()==row) {
protection.remove();
}
}
}
}
UPDATE
To implement protection changes on each edit, you can modify the code as following:
var sheets = SpreadsheetApp.getActiveSheet();
function Open(){
var sheets = SpreadsheetApp.getActive().getActiveSheet();
//adapt according to your datarange
var myRange=sheets.getRange(3,3,sheets.getLastRow(),sheets.getLastColumn());
var values=myRange.getValues();
for(var i=0;i<values.length;i++){
for(var j=0;j<values[0].length;j++){
if(values[i][j]=="ENDED"){
var cell=myRange.getCell(i+1,j+1);
var protection = cell.protect();
protection.removeEditors(protection.getEditors());
}
}
}
}
function onEdit(e) {
if (e.range.getColumn() == 2) {
//User edited the date column
if (typeof e.range.getValue() === typeof new Date()) {
//Value of edit was a date
endColumns(e.range.getRow(), e.range.getValue());
// wait until the function endColumns() finished
SpreadsheetApp.flush();
var myRange=sheets.getRange(e.range.getRow(),3,1,sheets.getLastColumn());
var values=myRange.getValues();
for(var j=0;j<values[0].length;j++){
if(values[0][j]=="ENDED"){
var cell=myRange.getCell(1,j+1);
var protection = cell.protect();
protection.removeEditors(protection.getEditors());
}
}
} else if (e.range.getValue() === "" || e.range.getValue() === null) {
// Delete all "ENDED" values:
var resetRange = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1, 1, sheets.getMaxColumns() - e.range.getColumn());
var resetValues = resetRange.getValues()[0];
for(var i = 0; i < resetValues.length; i++) {
if(resetValues[i] == "ENDED") {
var resetCell = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1 + i);
resetCell.clearContent();
}
}
Logger.log('date removed');
var row=e.range.getRow();
var protections=sheets.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.getRange().getRow()==row) {
protection.remove();
}
}
}
}
}
function endColumns(rowNum, limitDate) {
...
}

Script to automatically insert a specific text in cells of a Google spreadsheet, based on a date in another cell, on the same row

I have the following script provided by #ZektorH in another question of mine.
function onEdit(e) {
if (e.range.getColumn() ==2) {
//User edited the date column
if (typeof e.range.getValue() === typeof new Date()) {
//Value of edit was a date
endColumns(e.range.getRow(), e.range.getValue());
} else if (e.range.getValue() === "" || e.range.getValue() === null) {
var sheets = SpreadsheetApp.getActiveSheet();
var resetRange = sheets.getRange(e.range.getRow(), e.range.getColumn()+1, 1, sheets.getMaxColumns()-e.range.getColumn());
resetRange.clear({contentsOnly: true}); //Will delete all text, not only the "ENDED" text.
}
}
}
function endColumns(rowNum, limitDate) {
var sheets = SpreadsheetApp.getActiveSheet();
var colOffset = 3; //Offset to account for your row Headers
var dateHeader = sheets.getRange(1, colOffset, 1, sheets.getMaxColumns()-colOffset);
var availableDates = dateHeader.getValues()[0];
var foundCol = 0;
for (var i=0; i<availableDates.length; i++) {
if (availableDates[i]>=limitDate) {
break;
}
foundCol++;
}
var rewriteCells = sheets.getRange(rowNum, foundCol+colOffset, 1, sheets.getMaxColumns()-(foundCol+colOffset-1));
//Add your formatting and text below:
rewriteCells.setValue("ENDED");
//Clear all cells that are "white" (no header)
for (var i=0; i<availableDates.length; i++) {
if (availableDates[i]==="" || availableDates[i] ===null) {
sheets.getRange(rowNum, colOffset+i).clear();
}
}
}
The script is working correctly after some small edits, but there is still one problem. When you clear the value in the date cell (col B) on a row, the script will clear all values in all cells on that row.
This is an issue for me because many rows have important data in the cells prior to the ENDED date.
Question
What do I need to change in this script so that it will only clear the cells with the ENDED text, after clearing the date cell?
I've reworked your code a bit, basically adding a condition to check whether the value in each cell equals "ENDED", and only deleting data if that's the case:
// Delete all "ENDED" values:
var resetRange = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1, 1, sheets.getMaxColumns() - e.range.getColumn());
var resetValues = resetRange.getValues()[0];
for(var i = 0; i < resetValues.length; i++) {
if(resetValues[i] == "ENDED") {
var resetCell = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1 + i);
resetCell.clearContent();
}
}
And also this, which deletes corresponding "ENDED" values if you edit to a later date:
// Clear past dates if new date is later:
var beforeDate = sheets.getRange(rowNum, colOffset, 1, foundCol);
var beforeValues = beforeDate.getValues()[0];
for(var i = 0; i < beforeValues.length; i++) {
if(beforeValues[i] == "ENDED") {
sheets.getRange(rowNum, colOffset + i).clearContent();
}
}
So, full code would be like this:
function onEdit(e) {
if (e.range.getColumn() == 2) {
//User edited the date column
if (typeof e.range.getValue() === typeof new Date()) {
//Value of edit was a date
endColumns(e.range.getRow(), e.range.getValue());
} else if (e.range.getValue() === "" || e.range.getValue() === null) {
var sheets = SpreadsheetApp.getActiveSheet();
// Delete all "ENDED" values:
var resetRange = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1, 1, sheets.getMaxColumns() - e.range.getColumn());
var resetValues = resetRange.getValues()[0];
for(var i = 0; i < resetValues.length; i++) {
if(resetValues[i] == "ENDED") {
var resetCell = sheets.getRange(e.range.getRow(), e.range.getColumn() + 1 + i);
resetCell.clearContent();
}
}
}
}
}
function endColumns(rowNum, limitDate) {
var sheets = SpreadsheetApp.getActiveSheet();
var colOffset = 3; //Offset to account for your row Headers
var dateHeader = sheets.getRange(1, colOffset, 1, sheets.getMaxColumns() - colOffset);
var availableDates = dateHeader.getValues()[0];
var foundCol = 0;
for (var i = 0; i < availableDates.length; i++) {
if (availableDates[i] >= limitDate) {
break;
}
foundCol++;
}
var rewriteCells = sheets.getRange(rowNum, foundCol + colOffset, 1, sheets.getMaxColumns() - (foundCol + colOffset - 1));
//Add your formatting and text below:
rewriteCells.setValue("ENDED");
// Clear past dates if new date is later:
var beforeDate = sheets.getRange(rowNum, colOffset, 1, foundCol);
var beforeValues = beforeDate.getValues()[0];
for(var i = 0; i < beforeValues.length; i++) {
if(beforeValues[i] == "ENDED") {
sheets.getRange(rowNum, colOffset + i).clearContent();
}
}
// Clear all cells that are "white" (no header)
for (var i = 0; i < availableDates.length; i++) {
if (availableDates[i] === "" || availableDates[i] === null) {
sheets.getRange(rowNum, colOffset+i).clear();
}
}
}
I hope this is of any help to you.

Set Value of checkbox

I want to uncheck a checkbox if an adjacent cell has a value input in it.
function onEdit(event) {
var eventRange = event.range;
if (eventRange.getColumn() == 3) { // 3 == column C
var columnFRange = SpreadsheetApp.getActiveSheet().getRange(eventRange.getRow(), 6, eventRange.getNumRows(), 6);
var values = columnFRange.getValues();
for (var i = 0; i < values.length; i++) {
values[i][0] = 'FALSE';
}
columnFRange.setValues(values);
}
}
This looks at a sheet called "Testing" and if there is a value in column A it will put "TRUE" in the adjacent cell to the right. I believe this is what you're going for?
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Testing");
var hasValue = sheet.getRange("A:A").getValues();
var value = '';
for(var i = 0; i < hasValue.length; i++){
if(hasValue[i][0].length > 0) {
sheet.getRange("B" + (i + 1)).setValue("TRUE");
}
else {
sheet.getRange("B" + (i + 1)).setValue("");
}
}
}

Script wont work on new sheets/spreadsheets

I have been using this script for months, but now i wanted to add it to another sheets or spreadsheet and its not working, on old sheets where it was added moths ago it works fine.
Script is for hiding empty rows and showing rows that has values.
Maybe someone have any idea what to change in script so that it works again?
function hiderows() {
var ss = SpreadsheetApp.openById('1YoInjxlCeBPZP0HlEW0HSYBkdMCGtRaeJFylCQNXnMM');
sheets = ["Lapa1"]
for (var s = 0; s < sheets.length; s++) {
var sh = ss.getSheetByName(sheets[s]);
var firstRow = 1;
var firstCol = 1;
var data = sh.getDataRange().getValues();
var show = null, map = [], index, flag;
for (var i = firstRow - 1, length = data.length; i < length; i++) {
flag = false;
for (var j = firstCol - 1, width = data[0].length; j < width; j++) {
if (data[i][j] ) {
flag = true;
break;
}
}
if (show === flag) {
map[index].rows++;
} else {
index = i + 1;
show = flag;
map[index] = {rows: 1, show: show};
}
}
for (
var i in map) {
if (map[i].show) {
sh.showRows(i, map[i].rows);
} else {
sh.hideRows(i, map[i].rows);
}
}
}
}