dinsdag 9 november 2021

Sharing Chrome bookmarks but not all of them with #GoogleAppsScript

It is possible to export Chrome bookmarks  to a html file with the name bookmarks_dd-mm-yyyy.html:

  • select the three vertical points at the top right 
  • select bookmarks
  • select bookmark manager
  • select the three vertical points at the top right
  • select export bookmarks

The Chrome bookmarks can be imported somewhere else via the html file.

I do want to share bookmarks but not all of them. Only bookmarks stored in some specific  bookmark folders. That is possible [what not ;-)] with Google Apps Script.

Bookmark folder names need to be unique. 

It took some time before I understood that 'new lines' [\n] had to be added.

function bookmarx2Share(){

var bookmarkFolders = [["parentFolder_1","childFolder_11","childFolder_12","childFolder_13"],["parentFolder_2","childFolder_21","childFolder_22","childFolder_23"]];

var html = "";
html += '<!DOCTYPE NETSCAPE-Bookmark-file-1>\n';
html += '<!-- This is an automatically generated file.\n';
html += 'It will be read and overwritten.\n';
html += 'DO NOT EDIT! -->\n';
html += '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">\n';
html += '<TITLE>Bookmarks</TITLE>\n';
html += '<H1>Bookmarks</H1>\n';

  html += '<DL><p>\n';
  

  for (var b=0;b< bookmarkFolders.length;b++){

    var bookmarkFolder = bookmarkFolders[b]; 

    html += '<DT><H3 ADD_DATE="" LAST_MODIFIED=">' + bookmarkFolder[0] + '</H3>\n';
    html += '<DL><p>\n'

    for (var c=1;c< bookmarkFolder.length;c++){

      var bookmarkSubFolder = bookmarkFolder[c]; 

      if(bookmarkSubFolder !== ""){

        var bookmarxPerSubFolder = bookmarxPerFolder_(bookmarkSubFolder); 

        html += '<DT><H3 ADD_DATE="" LAST_MODIFIED=">' + bookmarkSubFolder + '</H3>\n';
        html += '<DL><p>\n';

          for(var d=0;d<bookmarxPerSubFolder.length;d++){

            var bookmark = bookmarxPerSubFolder[d];

            html += '<DT><A HREF="' + bookmark[1] +  '" ADD_DATE="" ICON="">' +  bookmark[0] +  '</A>\n';

          }
        html += '</DL><p>\n';
        
      }
    }
  html += '</DL><p>\n';
  }
  
  html += '</DL><p>\n';
  
var bookmarxFile = DriveApp.createFile('Bookmarx',html,"text/html");

var emails = ContactsApp.getContactGroup("BOOKMARK_CONTACTS").getContacts().map(function(contact){return contact.getEmails()[0].getAddress()});

GmailApp.sendEmail(emails,"Bookmarx shared with you","",{attachments: [bookmarxFile.getAs(MimeType.HTML)]})

}



function bookmarxPerFolder_(folder) {

  var html = "bookmarks_dd-mm-yyyy.html"; 
  
  var folderPlus = ">" + folder + "<"; 
   
  var bookmarxArray =[];
  var files = DriveApp.getFilesByName(html);

  while(files.hasNext()){

    var file = files.next();
  
    var bookmarx = file.getBlob().getDataAsString().split(folderPlus)[1].split("</DL><p>")[0].split(/<DT><A HREF="/);
        
    for(var b=1;b<bookmarx.length;b++){

     var bookmark = bookmarx[b];

     var textBookmark = bookmark.split(/<\/A>/)[0].split(/">/)[1].trim();
     var urlBookmark = bookmark.split(/"/)[0].trim();
                    
     bookmarxArray.push([textBookmark,urlBookmark])
    }
  } 
  
  return bookmarxArray.sort()
}

zaterdag 30 januari 2021

Travel advice #GoogleAppsScript

 


I would like to send clients a travel advice (url) but I don't know how to do that with the Maps Service.

Luckely the website of the 'Nederlandse Spoorwegen' offers a possibility to do what I want to do (with some Google Apps Script). 

function travelAdvice(arriveTime,labelOrigin,labelDestination){

var geoOrigin = geoCodeAddress(labelOrigin);
var geoDestination = geoCodeAddress(labelDestination);

var urlTravelAdvice = "https://www.ns.nl/reisplanner/#/?"
urlTravelAdvice += "vertrek=" + geoOrigin;
urlTravelAdvice += "&vertrektype=geo&vertreklabel=" + labelOrigin;
urlTravelAdvice += "&aankomst=" + geoDestination;
urlTravelAdvice += "&aankomsttype=geo&aankomstlabel="+ labelDestination;
urlTravelAdvice += "&type=aankomst&tijd=" + arriveTime; //yyyy-MM-ddThh:mm

return travelAdvice;
}

function geoCodeAddress(address){
/*
https://developers.google.com/apps-script/reference/maps/geocoder#geocode(String)
*/
  if(address !== ""){
  var response = Maps.newGeocoder().geocode(address);
    for (var i = 0; i < response.results.length; i++) {
      var result = response.results[i];
    }
  }
return result.geometry.location.lat + "," +result.geometry.location.lng;
}

Updated: 31-01-2021

Dynamic dependent dropdown data validation lists #GoogleAppsScript

Recently I revisited 'my' script for labeling court decisions with dynamic dependent dropdown data validation lists.

The new script is a little bit less slow than the old one.

function onEdit(){
 
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var data = ss.getSheetByName("Skelet").getDataRange().getValues();

dataValidation_(sheet,data,1);
dataValidation_(sheet,data,2);
dataValidation_(sheet,data,3);
dataValidation_(sheet,data,4);
dataValidation_(sheet,data,5);
}


function dataValidation_(sheet,data,dropDownLevel){
 
var dropDowns = 6;
var columnsBeforeDropDowns = 6;
var columnNumber = dropDownLevel + columnsBeforeDropDowns;
 
/*
See documentation for the difference between  
*/
var currentCell = sheet.getCurrentCell();
 
  if(currentCell.getColumn() == columnNumber && currentCell.getRow()>1){
   
/*
The validation in the cell on the right side of the current cell is dependent of the content of the current cell.
If the content of the current cell changes the content of the cell*S* on the right side of the current cell and their validations have to be cleared before the first cell on the right side of the current cell gets its new content and validation.
*/
   sheet.getRange(currentCell.getRow(),columnNumber+1,1,dropDowns).clearContent().clearDataValidations();
   
/*
The content of the current cell en the content of the cell*S* on the left side of the currect cell (if any) are used to filter the 'data' in the sheet '****' in order to find the validation for the cell on the right side of the current cell.
First (do not ask me why) the content of the current cell is used to filter the data.
Next the content of the cell*S* on the left side of the current cell (if any) are used to filter the data.
Look out: mind the difference between the index of a row and the number of the dropDownLevel
Look out: the content of the last dropDownLevel is not needed to find a validation
*/
   var filteredData = data.filter(function(row){return row[dropDownLevel-1] == currentCell.offset(0, 0).getValue()})
   for(var d=0;d<dropDownLevel-1;d++){
     filteredData.filter(function(row){return row[d] == currentCell.offset(0,d-dropDownLevel+1).getValue()})
   }

/*
The result of filtering the data are rows with more than one element.
What is needed is the content of the elements on the right side of the element with the content of the current cell
*/

   var dropDownArray = filteredData.map(function(row){return row[dropDownLevel]});
   
     if(dropDownArray[0]){
     var validationRule = SpreadsheetApp.newDataValidation().requireValueInList(dropDownArray).build();
     currentCell.offset(0,1).setDataValidation(validationRule);
   }  
 }
}