mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-18 01:57:02 +00:00
Added partial templates
mailbox/list now renders
This commit is contained in:
@@ -34,6 +34,9 @@ theme=integral
|
|||||||
# Path to the selected themes template files
|
# Path to the selected themes template files
|
||||||
template.dir=%(install.dir)s/themes/%(theme)s/templates
|
template.dir=%(install.dir)s/themes/%(theme)s/templates
|
||||||
|
|
||||||
|
# Should we cache parsed templates (set to false during theme dev)
|
||||||
|
template.cache=false
|
||||||
|
|
||||||
# Path to the selected themes public (static) files
|
# Path to the selected themes public (static) files
|
||||||
public.dir=%(install.dir)s/themes/%(theme)s/public
|
public.dir=%(install.dir)s/themes/%(theme)s/public
|
||||||
|
|
||||||
|
|||||||
17
config.go
17
config.go
@@ -17,10 +17,11 @@ type SmtpConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebConfig struct {
|
type WebConfig struct {
|
||||||
Ip4address net.IP
|
Ip4address net.IP
|
||||||
Ip4port int
|
Ip4port int
|
||||||
TemplateDir string
|
TemplateDir string
|
||||||
PublicDir string
|
TemplateCache bool
|
||||||
|
PublicDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
var smtpConfig *SmtpConfig
|
var smtpConfig *SmtpConfig
|
||||||
@@ -69,6 +70,7 @@ func LoadConfig(filename string) error {
|
|||||||
requireOption(messages, "web", "ip4.address")
|
requireOption(messages, "web", "ip4.address")
|
||||||
requireOption(messages, "web", "ip4.port")
|
requireOption(messages, "web", "ip4.port")
|
||||||
requireOption(messages, "web", "template.dir")
|
requireOption(messages, "web", "template.dir")
|
||||||
|
requireOption(messages, "web", "template.cache")
|
||||||
requireOption(messages, "web", "public.dir")
|
requireOption(messages, "web", "public.dir")
|
||||||
requireOption(messages, "datastore", "path")
|
requireOption(messages, "datastore", "path")
|
||||||
if messages.Len() > 0 {
|
if messages.Len() > 0 {
|
||||||
@@ -158,6 +160,13 @@ func parseWebConfig() error {
|
|||||||
}
|
}
|
||||||
webConfig.TemplateDir = str
|
webConfig.TemplateDir = str
|
||||||
|
|
||||||
|
option = "[web]template.cache"
|
||||||
|
flag, err := Config.Bool("web", "template.cache")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse %v: %v", option, err)
|
||||||
|
}
|
||||||
|
webConfig.TemplateCache = flag
|
||||||
|
|
||||||
option = "[web]public.dir"
|
option = "[web]public.dir"
|
||||||
str, err = Config.String("web", "public.dir")
|
str, err = Config.String("web", "public.dir")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ a:hover {
|
|||||||
|
|
||||||
#content {
|
#content {
|
||||||
width: 928px;
|
width: 928px;
|
||||||
|
min-height: 300px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +267,6 @@ a:hover {
|
|||||||
|
|
||||||
#emailContent {
|
#emailContent {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
min-height: 300px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#emailHeader {
|
#emailHeader {
|
||||||
|
|||||||
@@ -4,42 +4,50 @@ Design by Free CSS Templates
|
|||||||
http://www.freecsstemplates.org
|
http://www.freecsstemplates.org
|
||||||
Released for free under a Creative Commons Attribution 2.5 License
|
Released for free under a Creative Commons Attribution 2.5 License
|
||||||
-->
|
-->
|
||||||
{{define "col1menu"}}{{/* Used inside #content div */}}
|
|
||||||
<div id="colOne">
|
|
||||||
<div id="logo">
|
|
||||||
<h1><a href="#">inbucket</a></h1>
|
|
||||||
<h2>email testing service</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<title>{{template "title" .}}</title>
|
<title>{{template "title" .}}</title>
|
||||||
<link href="/public/main.css" rel="stylesheet" type="text/css" />
|
<link href="/public/main.css" rel="stylesheet" type="text/css" />
|
||||||
<link rel="shortcut icon" type="image/png" href="/public/images/favicon.png">
|
<link rel="shortcut icon" type="image/png" href="/public/images/favicon.png">
|
||||||
<script src="/public/jquery-1.5.2.min.js" type="text/javascript" charset="utf-8"></script>
|
<script src="/public/jquery-1.5.2.min.js" type="text/javascript" charset="utf-8"></script>
|
||||||
</head>
|
{{template "script" .}}
|
||||||
<body>
|
</head>
|
||||||
<div id="header">
|
<body>
|
||||||
<ul id="menu">
|
<div id="header">
|
||||||
<li><a href="/" accesskey="1" title="">Home</a></li>
|
<ul id="menu">
|
||||||
<li><a href="/" accesskey="2" title="">About</a></li>
|
<li><a href="/" accesskey="1" title="">Home</a></li>
|
||||||
</ul>
|
<li><a href="/" accesskey="2" title="">About</a></li>
|
||||||
<form id="search" action="/mailbox" method="GET">
|
</ul>
|
||||||
<fieldset>
|
<form id="search" action="{{reverse "MailboxIndex"}}" method="GET">
|
||||||
<input name="name" type="text" id="input1" />
|
<fieldset>
|
||||||
<input name="submit" type="submit" id="input2" value="Go" />
|
<input name="name" type="text" id="input1" />
|
||||||
</fieldset>
|
<input name="submit" type="submit" id="input2" value="Go" />
|
||||||
</form>
|
</fieldset>
|
||||||
</div>
|
</form>
|
||||||
<div id="content">
|
</div>
|
||||||
{{template "content" .}}
|
<div id="content">
|
||||||
</div>
|
<div id="colOne">
|
||||||
<div id="footer">
|
{{template "menu" .}}
|
||||||
<p>Inbucket is an open source project hosted at
|
</div>
|
||||||
<a href="http://github.com/jhillyerd/inbucket/">github</a>.
|
<div id="colTwo">
|
||||||
Design by <a href="http://www.freecsstemplates.org/">FCT</a>.</p>
|
{{with .ctx.Session.Flashes "errors"}}
|
||||||
</div>
|
<div class="errors">
|
||||||
</body>
|
<p>Please fix the following errors and resubmit:<p>
|
||||||
|
<ul>
|
||||||
|
{{range .}}
|
||||||
|
<li>{{.}}</li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{template "content" .}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="footer">
|
||||||
|
<p>Inbucket is an open source project hosted at
|
||||||
|
<a href="http://github.com/jhillyerd/inbucket/">github</a>.
|
||||||
|
Design by <a href="http://www.freecsstemplates.org/">FCT</a>.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{{$title := printf "Inbucket for %v" .name}}
|
{{define "title"}}{{printf "Inbucket for %v" .name}}{{end}}
|
||||||
{{set "title" $title .}}
|
|
||||||
{{template "header.html" .}}
|
|
||||||
{{$name := .name}}
|
{{$name := .name}}
|
||||||
|
|
||||||
|
{{define "script"}}
|
||||||
<script>
|
<script>
|
||||||
function listLoaded() {
|
function listLoaded() {
|
||||||
$('.listEntry').hover(
|
$('.listEntry').hover(
|
||||||
@@ -15,14 +15,14 @@
|
|||||||
function() {
|
function() {
|
||||||
$('.listEntry').removeClass("listEntrySelected")
|
$('.listEntry').removeClass("listEntrySelected")
|
||||||
$(this).addClass("listEntrySelected")
|
$(this).addClass("listEntrySelected")
|
||||||
$('#emailContent').load('/mailbox/show/{{$name}}/' + this.id)
|
$('#emailContent').load('/mailbox/show/{{.name}}/' + this.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
$("#messageList").slideDown()
|
$("#messageList").slideDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadList() {
|
function loadList() {
|
||||||
$('#messageList').load("/mailbox/list/{{$name}}", listLoaded)
|
$('#messageList').load("/mailbox/list/{{.name}}", listLoaded)
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadList() {
|
function reloadList() {
|
||||||
@@ -39,44 +39,41 @@
|
|||||||
$('#emailContent').empty()
|
$('#emailContent').empty()
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/mailbox/delete/{{$name}}/' + id,
|
url: '/mailbox/delete/{{.name}}/' + id,
|
||||||
success: reloadList
|
success: reloadList
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function htmlView(id) {
|
function htmlView(id) {
|
||||||
window.open('/mailbox/html/{{$name}}/' + id, '_blank',
|
window.open('/mailbox/html/{{.name}}/' + id, '_blank',
|
||||||
'width=800,height=600,' +
|
'width=800,height=600,' +
|
||||||
'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes')
|
'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes')
|
||||||
}
|
}
|
||||||
|
|
||||||
function messageSource(id) {
|
function messageSource(id) {
|
||||||
window.open('/mailbox/source/{{$name}}/' + id, '_blank',
|
window.open('/mailbox/source/{{.name}}/' + id, '_blank',
|
||||||
'width=800,height=600,' +
|
'width=800,height=600,' +
|
||||||
'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no')
|
'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no')
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(listInit)
|
$(document).ready(listInit)
|
||||||
</script>
|
</script>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
<div id="colOne">
|
{{define "menu"}}
|
||||||
<div id="logo">
|
<div id="logo">
|
||||||
<h1><a href="#">inbucket</a></h1>
|
<h1><a href="#">inbucket</a></h1>
|
||||||
<h2>mail for {{.name}}</h2>
|
<h2>mail for {{.name}}</h2>
|
||||||
</div>
|
|
||||||
<div class="box" style="text-align:center; padding-bottom:10px;">
|
|
||||||
<a href="javascript:reloadList()">Refresh List</a>
|
|
||||||
</div>
|
|
||||||
<div id="messageList"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="box" style="text-align:center; padding-bottom:10px;">
|
||||||
<div id="colTwo">
|
<a href="javascript:reloadList()">Refresh List</a>
|
||||||
{{template "errors.html" .}}
|
|
||||||
|
|
||||||
<div id="emailContent">
|
|
||||||
<p>Select a message at left, or enter a different username into the box on upper right.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="messageList"></div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{template "footer.html" .}}
|
{{define "content"}}
|
||||||
|
<div id="emailContent">
|
||||||
|
<p>Select a message at left, or enter a different username into the box on upper right.</p>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
<div id="colOne">
|
<div id="logo">
|
||||||
<div id="logo">
|
<h1><a href="/">inbucket</a></h1>
|
||||||
<h1><a href="#">inbucket</a></h1>
|
<h2>email testing service</h2>
|
||||||
<h2>email testing service</h2>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{{define "title"}}Inbucket{{end}}
|
|
||||||
{{define "content"}}
|
|
||||||
{{template "col1menu" .}}
|
|
||||||
<div id="colTwo">
|
|
||||||
<p>Inbucket is an email testing service; it will accept email for any email
|
|
||||||
address and make it available to view without a password.</p>
|
|
||||||
<p>To view email for a particular address, enter the username portion
|
|
||||||
of the address into the box on the upper right and click <em>go</em>.</p>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
19
themes/integral/templates/root/index.html
Normal file
19
themes/integral/templates/root/index.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{define "title"}}Inbucket{{end}}
|
||||||
|
|
||||||
|
{{define "script"}}{{end}}
|
||||||
|
|
||||||
|
{{define "menu"}}
|
||||||
|
<div id="logo">
|
||||||
|
<h1><a href="/">inbucket</a></h1>
|
||||||
|
<h2>email testing service</h2>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<p>Inbucket is an email testing service; it will accept email for any email
|
||||||
|
address and make it available to view without a password.</p>
|
||||||
|
|
||||||
|
<p>To view email for a particular address, enter the username portion
|
||||||
|
of the address into the box on the upper right and click <em>go</em>.</p>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
"github.com/jhillyerd/inbucket"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Session *sessions.Session
|
Vars map[string]string
|
||||||
|
Session *sessions.Session
|
||||||
|
DataStore *inbucket.DataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Close() {
|
func (c *Context) Close() {
|
||||||
@@ -14,9 +18,13 @@ func (c *Context) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewContext(req *http.Request) (*Context, error) {
|
func NewContext(req *http.Request) (*Context, error) {
|
||||||
|
vars := mux.Vars(req)
|
||||||
sess, err := sessionStore.Get(req, "inbucket")
|
sess, err := sessionStore.Get(req, "inbucket")
|
||||||
|
ds := inbucket.NewDataStore()
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
Session: sess,
|
Vars: vars,
|
||||||
|
Session: sess,
|
||||||
|
DataStore: ds,
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ctx, err
|
return ctx, err
|
||||||
|
|||||||
@@ -8,21 +8,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var TemplateFuncs = template.FuncMap{
|
var TemplateFuncs = template.FuncMap{
|
||||||
// Reversable routing function
|
// Reversable routing function (shared with templates)
|
||||||
"reverse": func(name string, things ...interface{}) string {
|
"reverse": reverse,
|
||||||
// Convert the things to strings
|
|
||||||
strs := make([]string, len(things))
|
|
||||||
for i, th := range things {
|
|
||||||
strs[i] = fmt.Sprint(th)
|
|
||||||
}
|
|
||||||
// Grab the route
|
|
||||||
u, err := Router.Get(name).URL(strs...)
|
|
||||||
if err != nil {
|
|
||||||
inbucket.Error("Failed to reverse route: %v", err)
|
|
||||||
return "/ROUTE-ERROR"
|
|
||||||
}
|
|
||||||
return u.Path
|
|
||||||
},
|
|
||||||
// Friendly date & time rendering
|
// Friendly date & time rendering
|
||||||
"friendlyTime": func(t time.Time) template.HTML {
|
"friendlyTime": func(t time.Time) template.HTML {
|
||||||
ty, tm, td := t.Date()
|
ty, tm, td := t.Date()
|
||||||
@@ -33,3 +20,18 @@ var TemplateFuncs = template.FuncMap{
|
|||||||
return template.HTML(t.Format("Mon Jan 2, 2006"))
|
return template.HTML(t.Format("Mon Jan 2, 2006"))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reverse(name string, things ...interface{}) string {
|
||||||
|
// Convert the things to strings
|
||||||
|
strs := make([]string, len(things))
|
||||||
|
for i, th := range things {
|
||||||
|
strs[i] = fmt.Sprint(th)
|
||||||
|
}
|
||||||
|
// Grab the route
|
||||||
|
u, err := Router.Get(name).URL(strs...)
|
||||||
|
if err != nil {
|
||||||
|
inbucket.Error("Failed to reverse route: %v", err)
|
||||||
|
return "/ROUTE-ERROR"
|
||||||
|
}
|
||||||
|
return u.Path
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,52 +1,52 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jhillyerd/inbucket/app/inbucket"
|
"github.com/jhillyerd/inbucket"
|
||||||
"github.com/robfig/revel"
|
"net/http"
|
||||||
"html/template"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mailbox struct {
|
func MailboxIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
*rev.Controller
|
name := req.FormValue("name")
|
||||||
}
|
if len(name) == 0 {
|
||||||
|
ctx.Session.AddFlash("Account name is required", "errors")
|
||||||
func (c Mailbox) Index(name string) rev.Result {
|
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
|
||||||
c.Validation.Required(name).Message("Account name is required")
|
return nil
|
||||||
|
|
||||||
if c.Validation.HasErrors() {
|
|
||||||
c.Validation.Keep()
|
|
||||||
c.FlashParams()
|
|
||||||
return c.Redirect(Application.Index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Render(name)
|
return RenderTemplate("mailbox/index.html", w, map[string]interface{}{
|
||||||
|
"ctx": ctx,
|
||||||
|
"name": name,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Mailbox) List(name string) rev.Result {
|
func MailboxList(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
c.Validation.Required(name).Message("Account name is required")
|
name := ctx.Vars["name"]
|
||||||
|
if len(name) == 0 {
|
||||||
if c.Validation.HasErrors() {
|
ctx.Session.AddFlash("Account name is required", "errors")
|
||||||
c.Validation.Keep()
|
http.Redirect(w, req, reverse("RootIndex"), http.StatusSeeOther)
|
||||||
c.FlashParams()
|
return nil
|
||||||
return c.Redirect(Application.Index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ds := inbucket.NewDataStore()
|
mb, err := ctx.DataStore.MailboxFor(name)
|
||||||
mb, err := ds.MailboxFor(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.RenderError(err)
|
return err
|
||||||
}
|
}
|
||||||
messages, err := mb.GetMessages()
|
messages, err := mb.GetMessages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.RenderError(err)
|
return err
|
||||||
}
|
}
|
||||||
rev.INFO.Printf("Got %v messsages", len(messages))
|
inbucket.Trace("Got %v messsages", len(messages))
|
||||||
|
|
||||||
c.Response.Out.Header().Set("Expires", "-1")
|
return RenderPartial("mailbox/_list.html", w, map[string]interface{}{
|
||||||
return c.Render(name, messages)
|
"ctx": ctx,
|
||||||
|
"name": name,
|
||||||
|
"messages": messages,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func (c Mailbox) Show(name string, id string) rev.Result {
|
func (c Mailbox) Show(name string, id string) rev.Result {
|
||||||
|
func MailboxShow(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
c.Validation.Required(name).Message("Account name is required")
|
c.Validation.Required(name).Message("Account name is required")
|
||||||
c.Validation.Required(id).Message("Message ID is required")
|
c.Validation.Required(id).Message("Message ID is required")
|
||||||
|
|
||||||
@@ -77,6 +77,7 @@ func (c Mailbox) Show(name string, id string) rev.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c Mailbox) Delete(name string, id string) rev.Result {
|
func (c Mailbox) Delete(name string, id string) rev.Result {
|
||||||
|
func MailboxDelete(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
c.Validation.Required(name).Message("Account name is required")
|
c.Validation.Required(name).Message("Account name is required")
|
||||||
c.Validation.Required(id).Message("Message ID is required")
|
c.Validation.Required(id).Message("Message ID is required")
|
||||||
|
|
||||||
@@ -104,6 +105,7 @@ func (c Mailbox) Delete(name string, id string) rev.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c Mailbox) Html(name string, id string) rev.Result {
|
func (c Mailbox) Html(name string, id string) rev.Result {
|
||||||
|
func MailboxHtml(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
c.Validation.Required(name).Message("Account name is required")
|
c.Validation.Required(name).Message("Account name is required")
|
||||||
c.Validation.Required(id).Message("Message ID is required")
|
c.Validation.Required(id).Message("Message ID is required")
|
||||||
|
|
||||||
@@ -135,6 +137,7 @@ func (c Mailbox) Html(name string, id string) rev.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c Mailbox) Source(name string, id string) rev.Result {
|
func (c Mailbox) Source(name string, id string) rev.Result {
|
||||||
|
func MailboxSource(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
c.Validation.Required(name).Message("Account name is required")
|
c.Validation.Required(name).Message("Account name is required")
|
||||||
c.Validation.Required(id).Message("Message ID is required")
|
c.Validation.Required(id).Message("Message ID is required")
|
||||||
|
|
||||||
@@ -161,3 +164,4 @@ func (c Mailbox) Source(name string, id string) rev.Result {
|
|||||||
c.Response.Out.Header().Set("Expires", "-1")
|
c.Response.Out.Header().Set("Expires", "-1")
|
||||||
return c.RenderText(*raw)
|
return c.RenderText(*raw)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@@ -5,5 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func RootIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
func RootIndex(w http.ResponseWriter, req *http.Request, ctx *Context) (err error) {
|
||||||
return T("root-index.html").Execute(w, nil)
|
return RenderTemplate("root/index.html", w, map[string]interface{}{
|
||||||
|
"ctx": ctx,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,17 +31,19 @@ var Router *mux.Router
|
|||||||
var sessionStore sessions.Store
|
var sessionStore sessions.Store
|
||||||
|
|
||||||
func setupRoutes(cfg inbucket.WebConfig) {
|
func setupRoutes(cfg inbucket.WebConfig) {
|
||||||
r := mux.NewRouter()
|
Router = mux.NewRouter()
|
||||||
Router = r
|
|
||||||
inbucket.Info("Theme templates mapped to '%v'", cfg.TemplateDir)
|
inbucket.Info("Theme templates mapped to '%v'", cfg.TemplateDir)
|
||||||
inbucket.Info("Theme static content mapped to '%v'", cfg.PublicDir)
|
inbucket.Info("Theme static content mapped to '%v'", cfg.PublicDir)
|
||||||
|
|
||||||
|
r := Router
|
||||||
// Static content
|
// Static content
|
||||||
r.PathPrefix("/public/").Handler(http.StripPrefix("/public/",
|
r.PathPrefix("/public/").Handler(http.StripPrefix("/public/",
|
||||||
http.FileServer(http.Dir(cfg.PublicDir))))
|
http.FileServer(http.Dir(cfg.PublicDir))))
|
||||||
|
|
||||||
// Root
|
// Root
|
||||||
r.Path("/").Handler(handler(RootIndex))
|
r.Path("/").Handler(handler(RootIndex)).Name("RootIndex").Methods("GET")
|
||||||
|
r.Path("/mailbox").Handler(handler(MailboxIndex)).Name("MailboxIndex").Methods("GET")
|
||||||
|
r.Path("/mailbox/list/{name}").Handler(handler(MailboxList)).Name("MailboxList").Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start() the web server
|
// Start() the web server
|
||||||
|
|||||||
@@ -3,31 +3,81 @@ package web
|
|||||||
import (
|
import (
|
||||||
"github.com/jhillyerd/inbucket"
|
"github.com/jhillyerd/inbucket"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cachedTemplates = map[string]*template.Template{}
|
|
||||||
var cachedMutex sync.Mutex
|
var cachedMutex sync.Mutex
|
||||||
|
var cachedTemplates = map[string]*template.Template{}
|
||||||
|
var cachedPartials = map[string]*template.Template{}
|
||||||
|
|
||||||
func T(name string) *template.Template {
|
// RenderTemplate fetches the named template and renders it to the provided
|
||||||
|
// ResponseWriter.
|
||||||
|
func RenderTemplate(name string, w http.ResponseWriter, data interface{}) error {
|
||||||
|
t, err := ParseTemplate(name, false)
|
||||||
|
if err != nil {
|
||||||
|
inbucket.Error("Error in template '%v': %v", name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Header().Set("Expires", "-1")
|
||||||
|
return t.Execute(w, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderPartial fetches the named template and renders it to the provided
|
||||||
|
// ResponseWriter.
|
||||||
|
func RenderPartial(name string, w http.ResponseWriter, data interface{}) error {
|
||||||
|
t, err := ParseTemplate(name, true)
|
||||||
|
if err != nil {
|
||||||
|
inbucket.Error("Error in template '%v': %v", name, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Header().Set("Expires", "-1")
|
||||||
|
return t.Execute(w, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTemplate loads the requested template along with _base.html, caching
|
||||||
|
// the result (if configured to do so)
|
||||||
|
func ParseTemplate(name string, partial bool) (*template.Template, error) {
|
||||||
cachedMutex.Lock()
|
cachedMutex.Lock()
|
||||||
defer cachedMutex.Unlock()
|
defer cachedMutex.Unlock()
|
||||||
|
|
||||||
if t, ok := cachedTemplates[name]; ok {
|
if t, ok := cachedTemplates[name]; ok {
|
||||||
return t
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
templateDir := inbucket.GetWebConfig().TemplateDir
|
cfg := inbucket.GetWebConfig()
|
||||||
templateFile := filepath.Join(templateDir, name)
|
tempPath := strings.Replace(name, "/", string(filepath.Separator), -1)
|
||||||
inbucket.Trace("Parsing template %v", templateFile)
|
tempFile := filepath.Join(cfg.TemplateDir, tempPath)
|
||||||
|
inbucket.Trace("Parsing template %v", tempFile)
|
||||||
|
|
||||||
t := template.New("_base.html").Funcs(TemplateFuncs)
|
var err error
|
||||||
t = template.Must(t.ParseFiles(
|
var t *template.Template
|
||||||
filepath.Join(templateDir, "_base.html"),
|
if partial {
|
||||||
templateFile,
|
// Need to get basename of file to make it root template w/ funcs
|
||||||
))
|
base := path.Base(name)
|
||||||
cachedTemplates[name] = t
|
t = template.New(base).Funcs(TemplateFuncs)
|
||||||
|
t, err = t.ParseFiles(tempFile)
|
||||||
|
} else {
|
||||||
|
t = template.New("_base.html").Funcs(TemplateFuncs)
|
||||||
|
t, err = t.ParseFiles(filepath.Join(cfg.TemplateDir, "_base.html"), tempFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return t
|
// Allows us to disable caching for theme development
|
||||||
|
if cfg.TemplateCache {
|
||||||
|
if partial {
|
||||||
|
inbucket.Trace("Caching partial %v", name)
|
||||||
|
cachedTemplates[name] = t
|
||||||
|
} else {
|
||||||
|
inbucket.Trace("Caching template %v", name)
|
||||||
|
cachedTemplates[name] = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user