Back

How I Generate This Webpage

I generate this site from a directory of markdown files using a script written in R. The code for this and some instructions are given below.

Set up your directory.

Your directory should have a folder called “markdown”. This folder must contain one folder for each category of page you want on the index page of the site. I have, for instance “papers” and “teaching” as subfolders. Each subfolder must have one markdown file per page you want on the site. The markdown files should start with a single blank line and then a header # Title. This header will be used as the title of the page on the index.

Pull the code from “Background Code” into a .R file in your directory and save it as “background.R”. You might also want to put a styleshee (css) file in the directory as well. I keep mine in a /style subdirectory. The content of my stylesheet is also provided below.

Running the code.

To generate your site, run the following code. Make sure to replace the path in the directory variable with the path to the directory you are setting up your site in. Also make sure to update the page title! You might also need to update the path to your stylesheet if you have placed it somewhere other than a /style subdirectory or called it something other than style.css.

directory="c://webpage"
setwd(directory)
source("background.R")
style = paste(c(directory,"//style/style.css"),collapse="")
pageTitle = "Greg Leo"
makePage(directory,style,pageTitle)

After you run this code, your site will be built in the /static directory. Enjoy!

Background Code

library(knitr)
library(fs)
library(stringr)
library(dplyr)
library(stringi)
library(magrittr)

list.dirs <- function(path = ".",
                      pattern = NULL,
                      all.dirs = FALSE,
                      full.names = FALSE,
                      ignore.case = FALSE) {
  # use full.names=TRUE to pass to file.info
  all <- list.files(path,
                    pattern,
                    all.dirs,
                    full.names = TRUE,
                    recursive = FALSE,
                    ignore.case)
  dirs <- all[file.info(all)$isdir]
  # determine whether to return full names or just dir names
  if (isTRUE(full.names))
    return(dirs)
  else
    return(basename(dirs))
}

#Get Folders in Markdown Directory
getFolders <- function(directory) {
  markdownDirectory = paste(c(directory, "//markdown"), collapse = "")
  folders = list.dirs(markdownDirectory)
}

#Get Files in Markdown Directory Folder
getFiles <- function(folder, directory) {
  markdownDirectory = paste(c(directory, "//markdown"), collapse = "")
  staticDirectory = paste(c(directory, "//static"), collapse = "")
  currentDirectoryMarkdown = paste(c(markdownDirectory, "//", folder), collapse =
                                     "")
  currentDirectory = paste(c(staticDirectory, "//", folder), collapse =
                             "")
  temp = list.files(currentDirectoryMarkdown, pattern = "*.md$")
  temp
}

#Extract Page Title from File
getTitle <- function(file) {
  pageTitle <- str_extract(readChar(file, 500), regex("# .*"))
  pageTitle <- substring(pageTitle, 3)
  pageTitle
}


#Extract Page Title from File
getPriority <- function(file) {
  priority <- str_extract(readChar(file, 10), regex("[0-9]{1,10}"))
  priority
}


#Add a "back" link to each page.
addBack <- function(fileInfo){
  content <- fileInfo$content
  content <- paste("[Back](../index.html)  \n\n\n", content, collapse = "")
  fileInfo$content <- content
  fileInfo
}

#Setup the Static Folder
makeStaticFolder <- function(folder, directory) {
  staticDirectory = paste(c(directory, "//static"), collapse = "")
  currentDirectory = paste(c(staticDirectory, "//", folder), collapse =
                             "")
  dir.create(currentDirectory)
}
setupStatic <- function(directory, Ffolders) {
  sapply(folders, makeStaticFolder, directory = directory)
}

#Setup the Temp Folder
setupTemp <- function(directory) {
  tempDirectory = paste(c(directory, "//temp"), collapse = "")
  dir.create(tempDirectory)
}

#Get Full Path to Markdown File
getMarkdownPath <- function(file, folder, directory) {
  paste(c(directory, "//markdown//", folder, "//", file), collapse = "")
}

#Get Full Path to Static File
getStaticPath <- function(file, folder, directory) {
  paste(c(
    directory,
    "//static//",
    folder,
    "//",
    substr(file, 1, nchar(file) - 2),
    "html"
  ),
  collapse = "")
}

#Make a Link to the Static File in Markdown Format
makeLink <- function(fileInfo) {
  paste(c(
    "[",
    fileInfo$title,
    "](",
    fileInfo$folder,
    "/",
    substr(fileInfo$fileName, 1, nchar(fileInfo$fileName) - 2),
    "html)\n"
  ),
  collapse = "")
}

#Create the Index Page
makeIndex <- function(fileData,pageTitle) {
  indexText <- c(paste("# ",pageTitle,sep=""))
  folders <- fileData %>% pull(folder) %>% unique
  for(currentFolder in folders){
    indexText <- c(indexText,paste("## ",toupper(substring(currentFolder,3)),sep=""))
    subsetFiles <- fileData %>% filter(folder==currentFolder) %>% arrange(desc(priority))
    for(i in 1:dim(subsetFiles)[1]){

      indexText <- c(indexText,makeLink(subsetFiles[i,]))
    }
  }
  writeLines(indexText, paste(directory, "//markdown//index.md", sep = ""))
  knit2html(
    paste(directory, "//markdown//index.md", sep = ""),
    paste(directory, "//static//index.md", sep = ""),
    stylesheet = style
  )
}

#Add a "back" link to each page.
addBack <- function(fileInfo){
  content <- fileInfo$content
  content <- 
  content <- paste("[Back](../index.html) \r\n", content, collapse = "")
  fileInfo$content <- content
  fileInfo
}

#Create an HTML Page from Markdown File
makeHtml <- function(fileInfo,style,directory) {
  message("Making",fileInfo$title)
  fileInfo <- addBack(fileInfo)
  tempFile <- paste(directory, "//temp//temp",stri_rand_strings(1,1),".md", sep = "")
  writeLines(fileInfo$content, tempFile, sep = "")
  knit2html(tempFile, output=fileInfo$fullStaticPath, stylesheet = style)
  setwd(directory)
}


#Remove Priority String
removePriority <- function(content){
  content <- str_remove(content,"[+][0-9]{1,10}\r\n")
  content
}


#Parse the files and make a dataframe with relevant file info. 
makeFileData <- function(directory){
  fileName=c()
  folder=c()
  fullMarkdownPath=c()
  fullStaticPath=c()
  title=c()
  content=c()
  priority <- c()
  folders <- getFolders(directory)
  for (fileFolder in folders) {
    files <- getFiles(fileFolder, directory)
    for(file in files){
    fileMarkdownPath <- getMarkdownPath(file,fileFolder,directory)
    fileStaticPath <- getStaticPath(file,fileFolder,directory)
    fileTitle <- getTitle(fileMarkdownPath)  
    filePriority <- as.numeric(getPriority(fileMarkdownPath))
    fileContent <- readChar(fileMarkdownPath, file.info(fileMarkdownPath)$size)
    fileContent <- removePriority(fileContent)
    priority <- c(priority,filePriority)
    fileName <- c(fileName,file)
    folder <- c(folder,fileFolder)
    fullMarkdownPath <- c(fullMarkdownPath,fileMarkdownPath)
    fullStaticPath <- c(fullStaticPath,fileStaticPath)
    title <- c(title,fileTitle)
    content <- c(content,fileContent)
    }
  }
  return(data.frame(fileName = fileName,folder = folder,fullMarkdownPath = fullMarkdownPath,fullStaticPath = fullStaticPath,title = title,priority = priority,content = content))
}


#Make sure the directory structure of the site is ok. 
setupSite <- function(directory){
  setwd(directory)
  dir.create(paste(c(directory, "//static//"), collapse = ""))
  folders <- getFolders(directory)
  setupTemp(directory)
  setupStatic(directory, folders)
}


#Put it All Together to Make Page
makePage <- function(directory, style,pageTitle) {
  setupSite(directory)
  fileData <- makeFileData(directory)
  for(i in 1:dim(fileData)[1]){makeHtml(fileData[i,],style = style,directory=directory)}
  makeIndex(fileData,pageTitle)
}

Style Sheet

@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
body {
    background: #2637dd;
    font-family: "Roboto Mono", monospace;
    font-weight: 500;
    color: #fff;
    -webkit-font-smoothing: antialiased;
    font-size: 12pt;
    width: 900px;
    line-height: 16pt;
    padding-left: 5pt;
    padding-right: 5pt;
    padding-top: 5pt;
    margin: auto;
}

main h1 {}

h1 {
    font-family: "Rubik Mono One", sans-serif;
    background: #fff;
    color: #2637dd;
    padding: 0.1em 0.3em;
    font-size: 1.2em;
    text-transform: uppercase;
    display: inline-block;
    margin-top: 15px;
    margin-bottom: 10px;
    font-weight: bold;
    line-height: 20pt;
}

h2 {
    font-family: "Rubik Mono One", sans-serif;
    background: #fff;
    color: #2637dd;
    padding: 0.1em 0.3em;
    font-size: 1em;
    text-transform: uppercase;
    margin-top: 15px;
    margin-bottom: 10px;
    font-weight: bold;
    line-height: 20pt;
}

h3 {
    margin-top: 15px;
    margin-bottom: 5px;
}

h4 {
    margin-top: 15px;
    margin-bottom: 5px;
}

title {
    font-weight: bold;
    line-height: 20pt;
}

p.subheader {
    font-weight: bold;
    color: #593d87;
}

img {
    padding: 3pt;
    float: right;
}

a {
    color: #ffffff;
    color: inherit;
    text-shadow: 1px 1px #2637dd, -1px -1px #2637dd;
    text-decoration: underline;
}

a:link,
a:visited {
    color: #ffffff;
}

pre {
    overflow-x: auto;
    white-space: pre-wrap;
    white-space: -moz-pre-wrap;
    white-space: -pre-wrap;
    white-space: -o-pre-wrap;
    word-wrap: break-word;
    padding: 2px 5px;
    color: #000000;
    background: #fff;
    font-family: inherit;
}

a:hover,
a:active {
    text-decoration: none;
    text-decoration: line-through;
}

p {
    margin: 0px;
}

div.footer {
    font-size: 9pt;
    font-style: italic;
    line-height: 12pt;
    text-align: center;
    padding-top: 30pt;
}