John Simpson <jms1@jms1.net>
2016-06-23
Last updated 2020-02-10 - updated this page with the actual markdown.cgi
script which is being used on the site.
This documents how to host Markdown files directly on an Apache web server, running on CentOS 6.
The software which actually renders Markdown into HTML is a Ruby "gem" called redcarpet. This is available from the EPEL repo.
The EPEL repository contains a collection of Fedora RPMs, packaged for use with RedHat Enterprise (or CentOS.)
If the server doesn't already have the EPEL repo installed, install it by running one of the following commands:
CentOS/RHEL 7: rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
CentOS/RHEL 6: rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
CentOS/RHEL 5: rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-5.noarch.rpm
Install the rubygem-redcarpet
package. If they aren't already installed, this will install ruby
and ruby-libs
(from the CentOS base repo) as well.
yum install rubygem-redcarpet
We will be creating a CGI script which uses redcarpet
to perform the rendering. This script needs to be in a location which is accessible from the web site. On my own server I created a /markdown/
directory in the root of the web site, like so:
mkdir -m 0755 /var/www/website/markdown
markdown.cgi
In the new directory, create markdown.cgi
. It should be owned by root, with permissions 0755
, and the following contents:
#!/usr/bin/ruby
#
# markdown.cgi
# John Simpson <jms1@jms1.net> 2016-06-23
#
# 2016-06-28 jms1 - added header with source download link at the top of the
# rendered page
#
# 2016-07-07 jms1 - tweaked the format of the source link
#
# 2018-07-18 jms1 - Removed the "topmenu" stuff. I've gotta be honest, I'm
# not sure what I was thinking there.
#
###############################################################################
#
# Copyright (C) 2016-2018 John Simpson.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, either version 2
# or version 3, as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# or visit http://www.gnu.org/licenses/gpl.txt
#
###############################################################################
require 'redcarpet'
renderer = Redcarpet::Render::HTML.new(
:with_toc_data => true ,
:xhtml => true
)
markdown = Redcarpet::Markdown.new(
renderer ,
:autolink => true ,
:strikethrough => true ,
:superscript => true ,
:underline => true ,
:highlight => true ,
:fenced_code_blocks => true ,
:tables => true
)
###############################################################################
# Figure out what we're doing
infile = ENV['PATH_TRANSLATED'] # full disk path to what was requested
root = ENV['DOCUMENT_ROOT'] # web site's document root
########################################
# If the request had "/src" at the end, remove it
# and set a flag to show source instead of rendered HTML
if ( infile.match( /\/src$/ ) ) then
show_src = true
infile = infile.gsub( /\/src$/ , '' )
else
show_src = false
end
########################################
# Other bits of text we'll probably need
my_url = infile.gsub( /^#{root}/ , '' )
title = infile.gsub( /^.*\// , '' )
########################################
# Read the file from the disk
text = File.read( infile )
###############################################################################
#
# Output
if ( show_src ) then
########################################
# Send the plain text to the browser
print "Content-Type: text/plain\n\n#{text}"
else
########################################
# Send the rendered output to the browser
md = markdown.render( text )
print( <<-EOF )
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<title>#{title}</title>
<link rel='stylesheet' href='/markdown/markdown.css'>
<style type='text/css'>
<!--
.srclink
{
float : right ;
font-size : 70% ;
}
-->
</style>
</head>
<body>
<div class='srclink'><a href='#{my_url}/src'>[src]</a></div>
#{md}
</body>
</html>
EOF
end
markdown.css
This is the CSS stylesheet which controls the visual appearnance of the rendered HTML.
The stylesheet I use is below. It's a modified version of one of the samples that came with Marked2 (which I highly recommend as a way to preview changes, as well as render PDFs of finished documents.)
body {
-webkit-font-smoothing: antialiased ;
font: normal .8764em/1.5em Arial,Verdana,sans-serif ;
}
html>body {
font-size: 13px ;
}
li {
font-size: 110% ;
}
li li {
font-size: 100% ;
}
li p {
font-size: 100% ;
margin: .5em 0 ;
}
h1 {
color: #000 ;
font-size: 2.2857em ;
line-height: .6563em ;
margin: .6563em 0 ;
border-top: 3px solid ;
}
h2 {
color: #111 ;
font-size: 1.7143em ;
line-height: .875em ;
margin: .875em 0 ;
border-top: 1px solid ;
}
h3 {
color: #111 ;
font-size: 1.5em ;
line-height: 1em ;
margin: 1em 0 ;
}
h4 {
color: #111 ;
font-size: 1.2857em ;
line-height: 1.1667em ;
margin: 1.1667em 0 ;
}
h5 {
color: #111 ;
font-size: 1.15em ;
line-height: 1.3em ;
margin: 1.3em 0 ;
}
h6 {
font-size: 1em ;
line-height: 1.5em ;
margin: 1.5em 0 ;
}
body,p,td,div {
color: #111 ;
font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif ;
word-wrap: break-word ;
}
h1,h2,h3,h4,h5,h6 {
line-height: 1.5em ;
}
a {
-webkit-transition: color .2s ease-in-out ;
-moz-transition: color .2s ease-in-out ;
-o-transition: color .2s ease-in-out ;
-ms-transition: color .2s ease-in-out ;
transition: color .2s ease-in-out ;
color: #0d6ea1 ;
text-decoration: none ;
}
a:hover {
color: #3593d9 ;
}
.footnote {
color: #0d6ea1 ;
font-size: .8em ;
vertical-align: super ;
}
#wrapper img {
max-width: 100% ;
height: auto ;
}
dd {
margin-bottom: 1em ;
}
li > p:first-child {
margin: 0 ;
}
ul ul,ul ol {
margin-bottom: .4em ;
}
caption,col,colgroup,table,tbody,td,tfoot,th,thead,tr {
border-spacing: 0 ;
}
table {
border: 1px solid rgba(0,0,0,0.25) ;
border-collapse: collapse ;
display: table ;
empty-cells: hide ;
margin: -1px 0 23px ;
padding: 0 ;
table-layout: fixed ;
}
caption {
display: table-caption ;
font-weight: 700 ;
}
col {
display: table-column ;
}
colgroup {
display: table-column-group ;
}
tbody {
display: table-row-group ;
}
tfoot {
display: table-footer-group ;
}
thead {
display: table-header-group ;
}
td,th {
display: table-cell ;
}
tr {
display: table-row ;
}
table th,table td {
font-size: 1.1em ;
line-height: 23px ;
padding: 0 1em ;
}
table thead {
background: rgba(0,0,0,0.15) ;
border: 1px solid rgba(0,0,0,0.15) ;
border-bottom: 1px solid rgba(0,0,0,0.2) ;
}
table tbody {
background: rgba(0,0,0,0.05) ;
}
table tfoot {
background: rgba(0,0,0,0.15) ;
border: 1px solid rgba(0,0,0,0.15) ;
border-top: 1px solid rgba(0,0,0,0.2) ;
}
figure {
display: inline-block ;
position: relative ;
margin: 1em 0 2em ;
}
figcaption {
font-style: italic ;
text-align: center ;
background: white ;
color: #666 ;
position: absolute ;
left: 0 ;
bottom: -24px ;
width: 98% ;
padding: 1% ;
-webkit-transition: all .2s ease-in-out ;
-moz-transition: all .2s ease-in-out ;
-o-transition: all .2s ease-in-out ;
-ms-transition: all .2s ease-in-out ;
transition: all .2s ease-in-out ;
}
figure:hover>figcaption {
/* background: rgba(0,0,0,1) */
}
.poetry pre {
display: block ;
font-family: Georgia, Garamond, serif !important ;
font-size: 110% !important ;
font-style: italic ;
line-height: 1.6em ;
margin-left: 1em ;
}
.poetry pre code {
font-family: Georgia, Garamond, serif !important ;
}
blockquote p {
font-size: 110% ;
font-style: italic ;
line-height: 1.6em ;
}
sup,sub,a.footnote {
font-size: 1.4ex ;
height: 0 ;
line-height: 1 ;
position: relative ;
vertical-align: super ;
}
sub {
vertical-align: sub ;
top: -1px ;
}
p,h5 {
font-size: 1.1429em ;
line-height: 1.3125em ;
margin: 1.3125em 0 ;
}
dt,th {
font-weight: 700 ;
}
pre {
border: 1px solid ;
padding: 2px ;
}
table tr:nth-child(odd),table th:nth-child(odd),table td:nth-child(odd) {
background: rgba(255,255,255,0.06) ;
}
table tr:nth-child(even),table td:nth-child(even) {
background: rgba(200,200,200,0.25) ;
}
@media print {
body {
overflow: auto ;
}
img,pre,blockquote,table,figure,p {
page-break-inside: avoid ;
}
#wrapper {
background: #fff ;
color: #303030 ;
font-size: 85% ;
padding: 10px ;
position: relative ;
text-indent: 0 ;
}
}
@media screen {
.inverted #wrapper,.inverted {
background: rgba(37, 42, 42, 1) ;
}
.inverted hr {
border-color: rgba(51, 63, 64, 1) !important ;
}
.inverted p,.inverted td,.inverted li,.inverted h1,.inverted h2,.inverted h3,.inverted h4,.inverted h5,.inverted h6,.inverted pre,.inverted code,.inverted th,.inverted .math,.inverted caption,.inverted dd,.inverted dt {
color: #eee !important ;
}
.inverted table tr:nth-child(odd),.inverted table th:nth-child(odd),.inverted table td:nth-child(odd) {
background: none ;
}
.inverted a {
color: rgba(172, 209, 213, 1) ;
}
#wrapper {
padding: 20px ;
}
::selection {
background: rgba(157, 193, 200,.5) ;
}
h1::selection {
background-color: rgba(45, 156, 208, .3) ;
}
h2::selection {
background-color: rgba(90, 182, 224, .3) ;
}
h3::selection,h4::selection,h5::selection,h6::selection,li::selection,ol::selection {
background-color: rgba(133, 201, 232, .3) ;
}
code::selection {
background-color: rgba(0,0,0,.7) ;
color: #eee ;
}
code span::selection {
background-color: rgba(0,0,0,.7) !important ;
color: #eee !important ;
}
a::selection {
background-color: rgba(255, 230, 102,.2) ;
}
.inverted a::selection {
background-color: rgba(255, 230, 102,.6) ;
}
td::selection,th::selection,caption::selection {
background-color: rgba(180, 237, 95, .5) ;
}
}
In the DocumentRoot's .htaccess
file, add the following:
Action markdown /markdown/markdown.cgi
AddHandler markdown .md
DirectoryIndex index.cgi index.shtml index.html index.md
For the DirectoryIndex
line, the idea is to add index.md
to the list.
I'm not sure if there's a higher-level Allow
option that needs to be set in order for these directives to be processed, I normally use AllowOverride all
for web sites, especially for sites where authoring access is limited.