Various tweaks

This commit is contained in:
Noah 2018-04-11 18:59:30 -07:00
parent 709e1f9577
commit b7d1661fcd
7 changed files with 216 additions and 169 deletions

View File

@ -50,7 +50,7 @@ def main(tumblr, root, scheme):
} }
log.info("Blog title: %s", SiteSettings["site"]["title"]) log.info("Blog title: %s", SiteSettings["site"]["title"])
log.info("Description: %s", SiteSettings["site"]["description"]) log.info("Description: %s", SiteSettings["site"]["description"])
write_db(root, "app/settings", SiteSettings) # write_db(root, "app/settings", SiteSettings)
posts_total = r["posts-total"] # n posts_total = r["posts-total"] # n
log.info("Total Posts: %d", posts_total) log.info("Total Posts: %d", posts_total)
@ -63,6 +63,9 @@ def main(tumblr, root, scheme):
POST_ID = 0 POST_ID = 0
# Unique Tumblr post IDs so no duplicates.
tumblr_posts = set()
while posts_start >= 0: while posts_start >= 0:
log.info("GET PAGE start=%d num=%d\n", posts_start, per_page) log.info("GET PAGE start=%d num=%d\n", posts_start, per_page)
r = api_get(API_ROOT, start=posts_start, num=per_page) r = api_get(API_ROOT, start=posts_start, num=per_page)
@ -80,6 +83,10 @@ def main(tumblr, root, scheme):
timestamp = datetime.fromtimestamp(post["unix-timestamp"]) timestamp = datetime.fromtimestamp(post["unix-timestamp"])
timestamp = timestamp.strftime("%Y-%m-%dT%H:%M:%SZ") timestamp = timestamp.strftime("%Y-%m-%dT%H:%M:%SZ")
if post["id"] in tumblr_posts:
continue
tumblr_posts.add(post["id"])
slug = post.get("slug") slug = post.get("slug")
if not slug: if not slug:
slug = re.sub(r'.*{}/'.format(tumblr), '', post.get("url-with-slug")) slug = re.sub(r'.*{}/'.format(tumblr), '', post.get("url-with-slug"))
@ -209,12 +216,15 @@ def _download_images(root, images):
for i, url in enumerate(images): for i, url in enumerate(images):
log.info("DOWNLOAD: %s", url) log.info("DOWNLOAD: %s", url)
filename = url.rsplit("/", 1)[-1] filename = url.rsplit("/", 1)[-1]
filename = "static/photos/"+filename
if os.path.isfile(os.path.join(root, filename)):
result.append("/"+filename)
continue
r = requests.get(url) r = requests.get(url)
if r.ok: if r.ok:
filename = "static/photos/"+filename
if os.path.isfile(os.path.join(root, filename)):
continue
with open(os.path.join(root, filename), "wb") as fh: with open(os.path.join(root, filename), "wb") as fh:
fh.write(r.content) fh.write(r.content)
result.append("/"+filename) result.append("/"+filename)

View File

@ -10,16 +10,24 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/kirsle/blog/internal/forms" "github.com/kirsle/blog/internal/forms"
"github.com/kirsle/blog/internal/log"
"github.com/kirsle/blog/internal/mail" "github.com/kirsle/blog/internal/mail"
"github.com/kirsle/blog/internal/markdown" "github.com/kirsle/blog/internal/markdown"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/internal/render" "github.com/kirsle/blog/internal/render"
"github.com/kirsle/blog/internal/responses" "github.com/kirsle/blog/internal/responses"
"github.com/kirsle/blog/models/settings"
) )
// Register attaches the contact URL to the app. // Register attaches the contact URL to the app.
func Register(r *mux.Router) { func Register(r *mux.Router) {
r.HandleFunc("/contact", func(w http.ResponseWriter, r *http.Request) { r.HandleFunc("/contact", func(w http.ResponseWriter, r *http.Request) {
// Allow ?next= to redirect to other local pages.
nextURL := r.FormValue("next")
if nextURL != "" && nextURL[0] != '/' {
log.Error("/contact?next=: URL must be a local page beginning with /")
nextURL = ""
}
form := &forms.Contact{} form := &forms.Contact{}
v := map[string]interface{}{ v := map[string]interface{}{
"Form": form, "Form": form,
@ -42,6 +50,15 @@ func Register(r *mux.Router) {
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
form.ParseForm(r) form.ParseForm(r)
if err = form.Validate(); err != nil { if err = form.Validate(); err != nil {
// If they're not from the /contact front-end, redirect them
// with the flash.
if len(nextURL) > 0 {
responses.FlashAndRedirect(w, r, nextURL, err.Error())
return
}
// Otherwise flash and let the /contact page render to retain
// their form fields so far.
responses.Flash(w, r, err.Error()) responses.Flash(w, r, err.Error())
} else { } else {
go mail.SendEmail(mail.Email{ go mail.SendEmail(mail.Email{
@ -72,7 +89,13 @@ func Register(r *mux.Router) {
)) ))
fh.Close() fh.Close()
} }
responses.FlashAndRedirect(w, r, "/contact", "Your message has been sent.")
if len(nextURL) > 0 {
responses.FlashAndRedirect(w, r, nextURL, "Your message has been sent.")
} else {
responses.FlashAndRedirect(w, r, "/contact", "Your message has been sent.")
}
return
} }
} }

View File

@ -6,10 +6,10 @@ import (
"time" "time"
"github.com/gorilla/feeds" "github.com/gorilla/feeds"
"github.com/kirsle/blog/internal/responses"
"github.com/kirsle/blog/models/posts" "github.com/kirsle/blog/models/posts"
"github.com/kirsle/blog/models/settings" "github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users" "github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/internal/responses"
) )
func feedHandler(w http.ResponseWriter, r *http.Request) { func feedHandler(w http.ResponseWriter, r *http.Request) {
@ -46,7 +46,7 @@ func feedHandler(w http.ResponseWriter, r *http.Request) {
Description: post.Body + suffix, Description: post.Body + suffix,
Created: p.Created, Created: p.Created,
}) })
if i >= 5 { if i == 9 { // 10 -1
break break
} }
} }

View File

@ -8,11 +8,11 @@ import (
"strconv" "strconv"
"github.com/kirsle/blog/internal/log" "github.com/kirsle/blog/internal/log"
"github.com/kirsle/blog/internal/render"
"github.com/kirsle/blog/internal/types"
"github.com/kirsle/blog/models/comments" "github.com/kirsle/blog/models/comments"
"github.com/kirsle/blog/models/posts" "github.com/kirsle/blog/models/posts"
"github.com/kirsle/blog/models/users" "github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/internal/render"
"github.com/kirsle/blog/internal/types"
) )
// partialIndex renders and returns the blog index partial. // partialIndex renders and returns the blog index partial.
@ -28,10 +28,16 @@ func partialIndex(r *http.Request, tag, privacy string) template.HTML {
if page <= 0 { if page <= 0 {
page = 1 page = 1
} }
perPage := 5 // TODO: configurable perPage := 10 // TODO: configurable
offset := (page - 1) * perPage offset := (page - 1) * perPage
stop := offset + perPage stop := offset + perPage
// Calculate page total.
var pages = int(len(pool) / perPage)
if pages == 0 {
pages = 1
}
// Handle pagination. // Handle pagination.
var previousPage, nextPage int var previousPage, nextPage int
if page > 1 { if page > 1 {
@ -48,7 +54,7 @@ func partialIndex(r *http.Request, tag, privacy string) template.HTML {
var view []PostMeta var view []PostMeta
for i := offset; i < stop; i++ { for i := offset; i < stop; i++ {
if i >= len(pool) { if i >= len(pool) {
continue break
} }
post, err := posts.Load(pool[i].ID) post, err := posts.Load(pool[i].ID)
if err != nil { if err != nil {
@ -81,6 +87,8 @@ func partialIndex(r *http.Request, tag, privacy string) template.HTML {
v := map[string]interface{}{ v := map[string]interface{}{
"PreviousPage": previousPage, "PreviousPage": previousPage,
"NextPage": nextPage, "NextPage": nextPage,
"Page": page,
"Pages": pages,
"View": view, "View": view,
} }
render.Template(&output, r, "blog/index.partial", v) render.Template(&output, r, "blog/index.partial", v)

View File

@ -10,10 +10,10 @@ import (
"github.com/kirsle/blog/internal/log" "github.com/kirsle/blog/internal/log"
"github.com/kirsle/blog/internal/middleware" "github.com/kirsle/blog/internal/middleware"
"github.com/kirsle/blog/internal/middleware/auth" "github.com/kirsle/blog/internal/middleware/auth"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/internal/sessions" "github.com/kirsle/blog/internal/sessions"
"github.com/kirsle/blog/internal/types" "github.com/kirsle/blog/internal/types"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users"
) )
// Vars is an interface to implement by the templates to pass their own custom // Vars is an interface to implement by the templates to pass their own custom
@ -23,6 +23,7 @@ type vars struct {
// Global, "constant" template variables. // Global, "constant" template variables.
SetupNeeded bool SetupNeeded bool
Title string Title string
Description string
Path string Path string
TemplatePath string // actual template file on disk TemplatePath string // actual template file on disk
LoggedIn bool LoggedIn bool
@ -62,6 +63,7 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
Request: r, Request: r,
RequestTime: r.Context().Value(types.StartTimeKey).(time.Time), RequestTime: r.Context().Value(types.StartTimeKey).(time.Time),
Title: s.Site.Title, Title: s.Site.Title,
Description: s.Site.Description,
Path: r.URL.Path, Path: r.URL.Path,
Data: data, Data: data,

View File

@ -50,7 +50,7 @@
<div class="bluez-header"> <div class="bluez-header">
<div class="container"> <div class="container">
<h1 class="bluez-title">{{ .Title }}</h1> <h1 class="bluez-title">{{ .Title }}</h1>
<p class="lead bluez-description">Just another web blog.</p> <p class="lead bluez-description">{{ .Description }}</p>
</div> </div>
</div> </div>

View File

@ -1,169 +1,173 @@
{{ define "title" }}Website Settings{{ end }} {{ define "title" }}Website Settings{{ end }}
{{ define "content" }} {{ define "content" }}
<form action="/admin/settings" method="POST"> <div class="card">
<input type="hidden" name="_csrf" value="{{ .CSRF }}"> <div class="card-body">
<form action="/admin/settings" method="POST">
<input type="hidden" name="_csrf" value="{{ .CSRF }}">
{{ with .Data.s }} {{ with .Data.s }}
<h3>The Basics</h3> <h3>The Basics</h3>
<div class="form-group"> <div class="form-group">
<label for="title">Title</label> <label for="title">Title</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="title" name="title"
value="{{ .Site.Title }}" value="{{ .Site.Title }}"
placeholder="Website Title"> placeholder="Website Title">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="title">Description</label> <label for="title">Description</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="description" name="description"
value="{{ .Site.Description }}" value="{{ .Site.Description }}"
placeholder="Just another web blog."> placeholder="Just another web blog.">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="admin-email">Admin Email</label> <label for="admin-email">Admin Email</label>
<small class="text-muted">For getting notifications about comments, etc.</small> <small class="text-muted">For getting notifications about comments, etc.</small>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="admin-email" name="admin-email"
value="{{ .Site.AdminEmail }}" value="{{ .Site.AdminEmail }}"
placeholder="name@domain.com"> placeholder="name@domain.com">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="admin-email">URL Root</label> <label for="admin-email">URL Root</label>
<small class="text-muted d-block"> <small class="text-muted d-block">
The base absolute URL to your website. This is used to generate The base absolute URL to your website. This is used to generate
emails such as comment notifications. If not provided, these emails such as comment notifications. If not provided, these
emails will not be sent. emails will not be sent.
</small> </small>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="url" name="url"
value="{{ .Site.URL }}" value="{{ .Site.URL }}"
placeholder="https://www.example.com/"> placeholder="https://www.example.com/">
</div> </div>
<h3>Redis Cache</h3> <h3>Redis Cache</h3>
<p> <p>
Using a <a href="https://redis.io/" target="_blank">Redis</a> cache can Using a <a href="https://redis.io/" target="_blank">Redis</a> cache can
boost the performance of the JSON database by caching documents in boost the performance of the JSON database by caching documents in
memory instead of always reading from disk. memory instead of always reading from disk.
</p> </p>
<div class="form-check"> <div class="form-check">
<label class="form-check-label"> <label class="form-check-label">
<input type="checkbox" <input type="checkbox"
class="form-check-input" class="form-check-input"
name="redis-enabled" name="redis-enabled"
value="true" value="true"
{{ if .Redis.Enabled }}checked{{ end }}> {{ if .Redis.Enabled }}checked{{ end }}>
Enable Redis Enable Redis
</label> </label>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="redis-host">Redis Host</label> <label for="redis-host">Redis Host</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="redis-host" name="redis-host"
value="{{ .Redis.Host }}" value="{{ .Redis.Host }}"
placeholder="localhost"> placeholder="localhost">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="redis-port">Port</label> <label for="redis-port">Port</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="redis-port" name="redis-port"
value="{{ .Redis.Port }}" value="{{ .Redis.Port }}"
placeholder="6379"> placeholder="6379">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="redis-db">DB Number</label> <label for="redis-db">DB Number</label>
<small class="text-muted">0-15</small> <small class="text-muted">0-15</small>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="redis-db" name="redis-db"
value="{{ .Redis.DB }}" value="{{ .Redis.DB }}"
placeholder="0"> placeholder="0">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="redis-prefix">Key Prefix</label> <label for="redis-prefix">Key Prefix</label>
<small class="text-muted">(optional)</small> <small class="text-muted">(optional)</small>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="redis-prefix" name="redis-prefix"
value="{{ .Redis.Prefix }}" value="{{ .Redis.Prefix }}"
placeholder="blog:"> placeholder="blog:">
</div> </div>
<h3>Email Settings</h3> <h3>Email Settings</h3>
<div class="form-check"> <div class="form-check">
<label class="form-check-label"> <label class="form-check-label">
<input type="checkbox" <input type="checkbox"
class="form-check-input" class="form-check-input"
name="mail-enabled" name="mail-enabled"
value="true" value="true"
{{ if .Mail.Enabled }}checked{{ end }}> {{ if .Mail.Enabled }}checked{{ end }}>
Enable email to be sent by this site Enable email to be sent by this site
</label> </label>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail-sender">Sender Address</label> <label for="mail-sender">Sender Address</label>
<input type="email" <input type="email"
name="mail-sender" name="mail-sender"
id="mail-sender" id="mail-sender"
class="form-control" class="form-control"
value="{{ .Mail.Sender }}" value="{{ .Mail.Sender }}"
placeholder="no-reply@example.com"> placeholder="no-reply@example.com">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail-host">SMTP Host</label> <label for="mail-host">SMTP Host</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="mail-host" name="mail-host"
id="mail-host" id="mail-host"
value="{{ .Mail.Host }}" value="{{ .Mail.Host }}"
placeholder="localhost"> placeholder="localhost">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail-port">SMTP Port</label> <label for="mail-port">SMTP Port</label>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="mail-port" name="mail-port"
id="mail-port" id="mail-port"
value="{{ .Mail.Port }}" value="{{ .Mail.Port }}"
placeholder="25"> placeholder="25">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail-username">SMTP Username</label> <label for="mail-username">SMTP Username</label>
<small class="text-muted">(optional)</small> <small class="text-muted">(optional)</small>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="mail-username" name="mail-username"
value="{{ .Mail.Username }}" value="{{ .Mail.Username }}"
placeholder=""> placeholder="">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail-password">SMTP Password</label> <label for="mail-password">SMTP Password</label>
<small class="text-muted">(optional)</small> <small class="text-muted">(optional)</small>
<input type="text" <input type="text"
class="form-control" class="form-control"
name="mail-password" name="mail-password"
value="{{ .Mail.Password }}" value="{{ .Mail.Password }}"
placeholder=""> placeholder="">
</div> </div>
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-primary">Save Settings</button> <button type="submit" class="btn btn-primary">Save Settings</button>
<a href="/admin" class="btn btn-secondary">Cancel</a> <a href="/admin" class="btn btn-secondary">Cancel</a>
</div>
{{ end }}
</form>
</div> </div>
{{ end }} </div>
</form>
{{ end }} {{ end }}