initial commit

This commit is contained in:
mischief 2014-03-25 00:58:46 -07:00
commit 78310b7fb1
18 changed files with 893 additions and 0 deletions

2
.hgignore Normal file
View file

@ -0,0 +1,2 @@
gowerc

363
main.go Normal file
View file

@ -0,0 +1,363 @@
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"github.com/russross/blackfriday"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
var (
listen = flag.String("l", ":8080", "set http listener to [ip]:port")
root = flag.String("root", "werc", "werc webroot")
)
type WercConfig struct {
MasterSite string
Title string
Subtitle string
}
type WercPage struct {
Title string // <head> title
Menu []MenuEntry // menu entries
Content template.HTML // inner page content
Config WercConfig // site-specific config
}
type MenuEntry struct {
Name string
Path string
This bool
Sub []MenuEntry
}
type Werc struct {
root string
conf WercConfig
tmpl *template.Template
}
func New(root string) *Werc {
w := new(Werc)
w.root = root
w.tmpl = template.Must(template.ParseGlob(root + "/lib/*.html"))
b, err := ioutil.ReadFile(root + "/etc/config.json")
if err != nil {
log.Printf("no config.json in %s", root)
return nil
}
err = json.Unmarshal(b, &w.conf)
if err != nil {
log.Printf("%s: %s", root+"/config.json", err)
return nil
}
return w
}
func cleanname(s string) string {
if s == "_werc" {
return ""
}
if strings.HasPrefix(s, "index") {
return ""
}
switch s {
case "sitemap.txt", "sitemap.gz":
return ""
}
for _, suf := range []string{".md", ".txt", ".html"} {
if strings.HasSuffix(s, suf) {
return strings.TrimSuffix(s, suf)
}
}
return s
}
func ptitle(s string) string {
s = strings.TrimSuffix(s, "/")
if idx := strings.LastIndex(s, "index"); idx != -1 {
s = s[:idx-1]
}
_, file := filepath.Split(s)
for _, suf := range []string{".md", ".txt", ".html"} {
if strings.HasSuffix(file, suf) {
return strings.TrimSuffix(file, suf)
}
}
return file
}
func (werc *Werc) genmenu(site, dir string) []MenuEntry {
var dirs []string
var root []MenuEntry
base := werc.root + "/sites/" + site
spl := strings.Split(strings.TrimPrefix(filepath.Clean(dir), "/"), string(filepath.Separator))
_, current := filepath.Split(dir)
if current != "" {
spl = spl[:len(spl)-1]
}
//log.Printf("base %s path %s spl %+v", base, dir, spl)
dirs = append(dirs, "/")
for i := range spl {
path := "/" + filepath.Join(spl[:i+1]...)
dirs = append(dirs, path)
}
//log.Printf("dirs %v", dirs)
var last []MenuEntry
for i := range dirs {
var sub []MenuEntry
b := filepath.Join(base, dirs[i])
fi, _ := ioutil.ReadDir(b)
for _, f := range fi {
newname, ok := okmenu(b, f)
if !ok {
continue
}
me := MenuEntry{Name: newname, Path: filepath.Join(dirs[i], newname)}
if f.Mode().IsDir() {
me.Path = me.Path + "/"
me.Name = me.Name + "/"
}
// if browing a file, mark it as current
if me.Name == current {
me.This = true
}
//log.Printf("me %+v", me)
sub = append(sub, me)
}
if dirs[i] == "/" {
root = sub
last = root
} else {
// mark directories as currently being browsed
for l, v := range last {
_, file := filepath.Split(dirs[i])
if v.Name == file+"/" {
last[l].This = true
last[l].Sub = sub
last = sub
}
}
}
}
return root
}
func (werc *Werc) WercCommon(w http.ResponseWriter, r *http.Request, page *WercPage) {
site := r.URL.Host
if site == "" {
site = werc.conf.MasterSite
}
path := r.URL.Path
page.Menu = werc.genmenu(site, path)
conf := werc.root + "/sites/" + site + "/_werc/config.json"
b, err := ioutil.ReadFile(conf)
if err != nil {
log.Printf("%s: %s", conf, err)
}
err = json.Unmarshal(b, &page.Config)
if err != nil {
log.Printf("%s: %s", conf, err)
}
//log.Printf("root %+v", page.Menu)
if err := werc.tmpl.ExecuteTemplate(w, "base.html", page); err != nil {
log.Printf("%s: %s", r.URL, err)
}
}
// returns true if a path name is ok to show in the navigation
func okmenu(base string, fi os.FileInfo) (string, bool) {
if strings.HasPrefix(fi.Name(), ".") {
return "", false
}
if fi.Name() == "_werc" {
return "", false
}
if strings.HasPrefix(fi.Name(), "index.") {
return "", false
}
if strings.Contains(fi.Name(), "sitemap.") {
return "", false
}
if fi.Mode().IsDir() {
return fi.Name(), true
}
for _, suf := range []string{".md", ".txt", ".html"} {
if strings.HasSuffix(fi.Name(), suf) {
return strings.TrimSuffix(fi.Name(), suf), true
}
}
return "", false
}
func (werc *Werc) WercDir(w http.ResponseWriter, r *http.Request, dir string) {
type DirEntry struct {
Name string
Fi os.FileInfo
}
type DirData struct {
Title string
Entries []DirEntry
}
var data DirData
data.Title = r.URL.Path
buf := new(bytes.Buffer)
fi, err := ioutil.ReadDir(dir)
if err != nil {
http.Error(w, fmt.Sprintf("%s", err), 500)
return
}
for _, f := range fi {
if name := cleanname(f.Name()); name != "" {
e := DirEntry{Name: name, Fi: f}
data.Entries = append(data.Entries, e)
}
}
werc.tmpl.ExecuteTemplate(buf, "directory.html", data)
werc.WercCommon(w, r, &WercPage{Title: ptitle(dir), Content: template.HTML(buf.String())})
}
func (werc *Werc) WercMd(w http.ResponseWriter, r *http.Request, path string) {
b, err := ioutil.ReadFile(path)
if err != nil {
http.Error(w, fmt.Sprintf("%s", err), 404)
return
}
md := blackfriday.MarkdownBasic(b)
werc.WercCommon(w, r, &WercPage{Title: ptitle(path), Content: template.HTML(string(md))})
}
func (werc *Werc) WercHTML(w http.ResponseWriter, r *http.Request, path string) {
b, err := ioutil.ReadFile(path)
if err != nil {
http.Error(w, fmt.Sprintf("%s", err), 404)
return
}
werc.WercCommon(w, r, &WercPage{Title: ptitle(path), Content: template.HTML(string(b))})
}
func (werc *Werc) WercTXT(w http.ResponseWriter, r *http.Request, path string) {
b, err := ioutil.ReadFile(path)
if err != nil {
http.Error(w, fmt.Sprintf("%s", err), 404)
return
}
buf := new(bytes.Buffer)
werc.tmpl.ExecuteTemplate(buf, "text.html", string(b))
werc.WercCommon(w, r, &WercPage{Title: ptitle(path), Content: template.HTML(buf.String())})
}
func (werc *Werc) Werc404(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
}
func (werc *Werc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("%s", r.URL)
site := r.URL.Host
if site == "" {
site = werc.conf.MasterSite
}
path := r.URL.Path
base := werc.root + "/sites/" + site
if strings.HasSuffix(path, "/index") {
http.Redirect(w, r, strings.TrimSuffix(path, "/index"), http.StatusMovedPermanently)
return
}
if !strings.HasSuffix(path, "/") {
if st, err := os.Stat(base + path); err == nil {
if st.Mode().IsDir() {
http.Redirect(w, r, path+"/", http.StatusMovedPermanently)
return
}
}
}
// various format handling
sufferring := map[string]func(w http.ResponseWriter, r *http.Request, path string){
"md": werc.WercMd,
"html": werc.WercHTML,
"txt": werc.WercTXT,
}
for suf, handler := range sufferring {
f := base
if strings.HasSuffix(path, "/") {
f = filepath.Join(f, path, "index."+suf)
} else {
f = filepath.Join(f, path+"."+suf)
}
if _, err := os.Stat(f); err == nil {
log.Printf("%s %s", suf, f)
handler(w, r, f)
return
}
}
if st, err := os.Stat(base + path); err == nil {
if st.Mode().IsDir() {
// directory handling
log.Printf("d %s", base+path)
werc.WercDir(w, r, base+path)
return
} else {
// plain file handling
log.Printf("f %s", base+path)
http.ServeFile(w, r, base+path)
return
}
}
werc.Werc404(w, r)
}
func main() {
flag.Parse()
w := New(*root)
mux := http.NewServeMux()
mux.Handle("/", w)
mux.Handle("/pub/", http.StripPrefix("/pub/", http.FileServer(http.Dir(filepath.Join(w.root, "pub")))))
s := &http.Server{
Addr: *listen,
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
}

4
werc/etc/config.json Normal file
View file

@ -0,0 +1,4 @@
{
"mastersite": "offblast.org"
}

64
werc/lib/base.html Normal file
View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<link rel="stylesheet" href="/pub/style/style.css" type="text/css" media="screen, handheld" title="default">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="/_werc/pub/style.css" type="text/css" media="screen" title="default">
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!--
% if(! ~ $#meta_description 0)
% echo ' <meta name="description" content="'$"meta_description'">'
% if(! ~ $#meta_keywords 0)
% echo ' <meta name="keywords" content="'$"meta_keywords'">'
% h = `{get_lib_file headers.inc}
% if(! ~ $#h 0)
% cat $h
%($"extraHeaders%)
-->
</head>
<body>
<div id="header">
<div class="superHeader">
{{template "topbar.html"}}
</div>
<div class="midHeader">
<h1 class="headerTitle"><a href="/">{{.Config.Title}}</a><span id="headerSubTitle">{{.Config.Subtitle}}</span></h1>
</div>
<div class="subHeader">
<br>
</div>
</div>
<div id="side-bar">
<div>
{{template "menu.html" .Menu}}
</div>
</div>
<div id="main-copy">
{{.Content}}
</div>
<!--
% run_handlers $handlers_body_head
% run_handler $handler_body_main
% run_handlers $handlers_body_foot
-->
<div id="footer">
{{template "footer.html"}}
</div>
</body>
</html>

7
werc/lib/directory.html Normal file
View file

@ -0,0 +1,7 @@
<h1 class="dir-list-head">{{.Title}}</h1>
<ul class="dir-list">
{{range .Entries}}
<li><a href="{{.Name}}">{{.Name}}</a></li>
{{end}}
</ul>

2
werc/lib/footer.html Normal file
View file

@ -0,0 +1,2 @@
<div class="left"><a href="https://bitbucket.org/mischief/gowerc">Powered by gowerc</a></div>

15
werc/lib/menu.html Normal file
View file

@ -0,0 +1,15 @@
<ul>
{{range .}}
{{if .This}}
<li><a class="thisPage" href="{{.Path}}">» {{.Name}}</a></li>
{{else}}
<li><a href="{{.Path}}"> {{.Name}}</a></li>
{{end}}
{{with $sub := .Sub}}
<li>
{{template "menu.html" $sub}}
</li>
{{end}}
{{end}}
</ul>

4
werc/lib/text.html Normal file
View file

@ -0,0 +1,4 @@
<pre>
{{.}}
</pre>

11
werc/lib/topbar.html Normal file
View file

@ -0,0 +1,11 @@
<div class='left'>
<a href="/sitemap">site map</a><span> | </span>
<a href='http://9.offblast.org/'>9.offblast.org</a><span> | </span>
<a href='https://mindlock.us/'>mindlock.us</a><span> | </span>
<a href='https://github.com/mischief'>github</a><span> | </span>
<a href='https://bitbucket.org/mischief'>bitbucket</a>
</div>
<div class="right">
</div>

0
werc/pub/style/style.css Normal file
View file

View file

@ -0,0 +1,4 @@
{
"title": "offblast.org",
"subtitle": "blast off!"
}

View file

@ -0,0 +1,332 @@
/* General style guidelines:
- All font-size at least 14px (recommended 16px)
- Line-height: 1.5 for paragraph body, 1.1 for header
*/
body {
color: #859900;
background-color: #002b36;
font-family: Helvetica, Verdana, Arial, 'Liberation Sans', FreeSans, sans-serif;
font-size: 84%; /* Enables font size scaling in MSIE */
margin: 0;
padding: 0;
}
/* # Header # */
.superHeader {
color: #859900;
height: 1.6em;
}
.superHeader img { vertical-align: bottom; }
.superHeader a {
color: #268BD2;
background-color: transparent;
font-size: 91%;
margin: 0;
padding: 0 0.5ex 0 0.25ex;
}
a { text-decoration: none; }
a:hover { text-decoration: underline; }
.superHeader div {
position: absolute;
top: 0.40ex;
}
.superHeader .left { left: 0.4em; }
.superHeader .right { right: 0.4em; }
.midHeader {
color: #839496;
/* background-color: #859900;*/
border: solid 0 #859900;
border-width: 2px 0;
}
.headerTitle {
color: #839496;
font-size: 233%;
font-weight: normal;
margin: 0 0 0 4mm;
padding: 0.25ex 0;
}
#headerSubTitle {
font-size: 50%;
font-style: italic;
margin-left: 1em;
}
.headerTitle a { color: #268BD2; }
.headerTitle a:hover { text-decoration: none; }
.subHeader {
display: none;
color: white;
background-color: rgb(0,51,153);
margin: 0;
padding: 1ex 1ex 1ex 1.5mm;
}
.subHeader a {
color: white;
background-color: transparent;
font-weight: bold;
margin: 0;
padding: 0 0.75ex 0 0.5ex;
}
.superHeader .highlight, .subHeader .highlight {
color: rgb(253,160,91);
background-color: transparent;
}
/* # Side # */
#side-bar {
width: 16em;
float: left;
clear: left;
border-right: 1px solid #859900;
}
#side-bar div {
border-bottom: 1px solid #859900;
}
.sideBarTitle {
font-weight: bold;
margin: 0 0 0.5em 2mm;
padding: 1em 0 0 0;
}
#side-bar ul {
list-style-type: none;
list-style-position: outside;
margin: 0;
padding: 0 0 0.3em 0;
}
li ul {
padding-left: 0.6em !important;
}
#side-bar li {
margin: 0;
padding: 0.1ex 0; /* Circumvents a rendering bug (?) in MSIE 6.0 XXX should move to iehacks.css, this causes an ugly gap */
}
#side-bar a {
color: #268BD2;
background-color: transparent;
margin: 0;
padding: 0.25em 1ex 0.25em 2mm;
display: block;
text-transform: capitalize;
font-weight: bold!important;
font-size: 102%;
border-left: #268BD2 solid 0.2em;
}
.thisPage, .thisPage a {
color: #859900 !important;
background-color: white;
padding-left: 5mm;
}
#side-bar a:hover {
color: #839496;
/* background-color: #586e75;
border-left: black solid 0.2em;*/
text-decoration: none;
}
.sideBarText {
line-height: 1.5em;
margin: 0 0 1em 0;
padding: 0 1.5ex 0 2.5mm;
display: block;
}
#side-bar .sideBarText a {
margin: 0;
padding: 0;
display: inline;
}
#side-bar .sideBarText a:hover {
color: rgb(0,102,204);
background-color: transparent;
text-decoration: none;
}
/* # Main Copy # */
#main-copy {
max-width: 70em;
color: #859900;
background-color: transparent;
text-align: justify;
line-height: 1.5em;
margin: 0em 0 0 16em;
padding: 0.5mm 5mm 5mm 5mm;
border-left: 1px solid #859900;
}
#bodyText {
margin: 0 0 0 15.5em;
padding: 2mm 5mm 2mm 5mm;
}
#main-copy p {
margin: 1em 1ex 1em 1ex !important; /* Need !important so troff-generated pages don't look totally squezed */
padding: 0;
}
#main-copy a {
color: #268bd2;
background-color: transparent;
}
#main-copy a:hover {
color: #839496;
}
#main-copy h1, #main-copy h2 {
color: #859900;
background-color: transparent;
font-size: 145.5%;
font-weight: bold;
margin: 2em 0 0 0;
padding: 0.5ex 0 0.5ex 0.6ex;
border-bottom: 2px solid #859900;
}
#main-copy h2 {
font-size: 115.5%;
border-bottom: 1px solid #859900;
}
#main-copy .topOfPage {
color: rgb(0,102,204);
background-color: transparent;
font-size: 91%;
font-weight: bold;
text-decoration: none;
margin: 3ex 1ex 0 0;
padding: 0;
float: right;
}
dl {
margin: 1em 1ex 2em 1ex;
padding: 0;
}
dt {
font-weight: bold;
margin: 0 0 0 0;
padding: 0;
}
dd {
margin: 0 0 2em 2em;
padding: 0;
}
/* # Footer # */
#footer {
color: #839496;
border: solid 0 #859900;
border-width: 2px 0;
padding: 2em;
clear: both;
}
#footer .left {
text-align: left;
line-height: 1.55em;
float: left;
clear: left;
}
#footer .right {
text-align: right;
line-height: 1.45em;
}
#footer a {
color: #268BD2;
background-color: transparent;
}
/* GENERAL */
table {
border: solid 1px black;
}
th {
background-color: #abc;
border: solid 1px black;
text-align: center;
}
td {
background-color: #def;
border: solid 1px black;
}
hr {
border-width: 0px 0px 0.1em 0px;
border-color: black;
}
acronym, .titleTip {
border-bottom: 1px solid #ddd;
cursor: help;
margin: 0;
padding: 0 0 0.4px 0;
}
pre {
margin-left: 2em;
font-size: 1.2em;
}
blockquote {
border-left: 1px solid blue;
font-style: italic;
}
.smallCaps {
font-size: 110%;
font-variant: small-caps;
}
.doNotDisplay { display: none; }
.notify_errors,
.notify_notes,
.notify_success { padding: .8em; margin-bottom: 1em; border: 2px solid #ddd; }
.notify_errors { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
.notify_notes { background: #FFF6BF; color: #514721; border-color: #FFD324; }
.notify_success { background: #E6EFC2; color: #264409; border-color: #C6D880; }
.notify_errors a { color: #8a1f11; }
.notify_notes a { color: #514721; }
.notify_success a { color: #264409; }
/* # Page/Handler specific # */
h1.dir-list-head, ul.dir-list {
text-transform: capitalize;
font-weight: bold;
}
ul.sitemap-list a {
text-transform: capitalize;
}

View file

@ -0,0 +1,67 @@
# Dolentem volucri lugubris
## Alto exercet
[Lorem markdownum rogavit](http://imgur.com/) obsedit **Thebis** in dextera? Qui
cum, cum **nubila fieri** quamquam: non graia reperta inpositos ut de.
if (server_compiler + memory_ipx < typeface(scannerDll)) {
keystroke = personalListserv * uriTextDisplay(dslam, blacklist_png,
primary);
} else {
malwareSms(smmSsl, animated_brouter.up(install_d, macro_nat_isp,
petaflops_system), 319307);
marketingRowTitle(2, network(logic_cmos_shift), middlewareDhcp);
}
sidebar(matrixVirus(wordartAjax), flatPartyDisk.rootDynamicSla(42 -
model_command_wavelength, error_hard_superscalar(snippet, -3),
gnutella_wimax));
unixHardSource.schemaKilobit(logCybersquatterCard(megabyte));
hot.file_operating_source += -2;
## Et Amymonen quoque que inferius artus
In hos parce generoso et quod, Latonam hoc levis hausta me tamen ingrate lumina.
Pia tibi flumen priora solitam colorum ille matutinaeque posse! Ergo servatae
latet mirabere, est vocat nervos ingreditur tam Phinea hac longe.
Post virgo? Cava coepti, liquido agros. Humano ducta inritata India. Porrexit
tibi, in [nitebat variarum tenebat](http://www.wedrinkwater.com/) dixit
testudine me mare per sucosque ut petitos passa temploque spesque, calculus.
## Usum harundine coniugis notae nono
Cura lacrimas; et socio, Troum? Solebat nudaeque amor, auxilio esse; partem
finxit minimamque. Novo toro primaeque et quamvis quae futura simulat, vos posse
tubere. Hominum et meque deum iter: quam cretus, Picus.
1. Igitur quae corpus
2. Permanet ambage et pecudes dumque moenia scelus
3. Dederant cervice Iuppiter
4. Dare externa instrumenta Cipe
5. Humiles ambo deprensa quem mirabile cornua fidesque
6. Urbes suum viderunt
## Vivitur notavit inferior praelata tarde et auguris
Id visura, animi opera: spatium nisi rident vacuas, graves est potuisse irae,
casus arva, sunt. Dixit fratrem cervix! Ordine et iam, cum Bactrius templum,
exhalantur curru cultosque dedit. Cognovit tenderet in naribus perdere **eunt**,
Dryantaque noverit quotiens luserit, summorum concussit spectat crinis
**moriemur titulum aut** luce. Utque lucem nescio, armis dubitanti ramis
cruciata maerent e stridore me vulgata, hospitio in *Perseus*, est femina.
- Agit quasque falsum
- Huius vulgus virtute
- Vota nam
- Pararis nosti
Tua sed ut Telamon pudetque permulcetque Nessus rotae, tremuere. Finite
inornatos nymphis patuit sine occupat nescio; meri inprudens genu equorum usus
hoc inritus. Tecta annos Charaxi aquis caelataeque feror pugnantem satis
attonitum potes. Videntur incipiat totidemque **viri incaluit** locum quo lumina
conatur addidit. Musa illa intortos.
[Lorem markdownum rogavit]: http://imgur.com/
[nitebat variarum tenebat]: http://www.wedrinkwater

View file

@ -0,0 +1,5 @@
welcome to go werc
===================
add directories and files here.

View file

@ -0,0 +1,3 @@
different test page
==========

View file

@ -0,0 +1,2 @@
<h1>HTML!</h1>

View file

@ -0,0 +1,3 @@
THIS IS PLAIN TEXT!
===================

View file

@ -0,0 +1,5 @@
test index
==========
testing 1 2 3