Merge branch 'develop' into chorus

This commit is contained in:
Matt Baer 2019-08-07 10:58:34 -04:00
commit 3cc397ad76
6 changed files with 71 additions and 22 deletions

6
app.go
View File

@ -120,6 +120,8 @@ type Apper interface {
SaveConfig(*config.Config) error
LoadKeys() error
ReqLog(r *http.Request, status int, timeSince time.Duration) string
}
// App returns the App
@ -179,6 +181,10 @@ func (app *App) LoadKeys() error {
return nil
}
func (app *App) ReqLog(r *http.Request, status int, timeSince time.Duration) string {
return fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, timeSince, r.UserAgent())
}
// handleViewHome shows page at root path. Will be the Pad if logged in and the
// catch-all landing page otherwise.
func handleViewHome(app *App, w http.ResponseWriter, r *http.Request) error {

View File

@ -211,6 +211,10 @@ func (c *Collection) DisplayCanonicalURL() string {
}
func (c *Collection) RedirectingCanonicalURL(isRedir bool) string {
if c.hostName == "" {
// If this is true, the human programmers screwed up. So ask for a bug report and fail, fail, fail
log.Error("[PROGRAMMER ERROR] WARNING: Collection.hostName is empty! Federation and many other things will fail! If you're seeing this in the wild, please report this bug and let us know what you were doing just before this: https://github.com/writeas/writefreely/issues/new?template=bug_report.md")
}
if isSingleUser {
return c.hostName + "/"
}

View File

@ -74,7 +74,7 @@ type writestore interface {
GetAnonymousPosts(u *User) (*[]PublicPost, error)
GetUserPosts(u *User) (*[]PublicPost, error)
CreateOwnedPost(post *SubmittedPost, accessToken, collAlias string) (*PublicPost, error)
CreateOwnedPost(post *SubmittedPost, accessToken, collAlias, hostName string) (*PublicPost, error)
CreatePost(userID, collID int64, post *SubmittedPost) (*Post, error)
UpdateOwnedPost(post *AuthenticatedPost, userID int64) error
GetEditablePost(id, editToken string) (*PublicPost, error)
@ -542,7 +542,7 @@ func (db *datastore) GetTemporaryOneTimeAccessToken(userID int64, validSecs int,
return u.String(), nil
}
func (db *datastore) CreateOwnedPost(post *SubmittedPost, accessToken, collAlias string) (*PublicPost, error) {
func (db *datastore) CreateOwnedPost(post *SubmittedPost, accessToken, collAlias, hostName string) (*PublicPost, error) {
var userID, collID int64 = -1, -1
var coll *Collection
var err error
@ -556,6 +556,7 @@ func (db *datastore) CreateOwnedPost(post *SubmittedPost, accessToken, collAlias
if err != nil {
return nil, err
}
coll.hostName = hostName
if coll.OwnerID != userID {
return nil, ErrForbiddenCollection
}

View File

@ -137,7 +137,7 @@ func (h *Handler) User(f userHandlerFunc) http.HandlerFunc {
status = http.StatusInternalServerError
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
u := getUserSession(h.app.App(), r)
@ -175,7 +175,7 @@ func (h *Handler) Admin(f userHandlerFunc) http.HandlerFunc {
status = http.StatusInternalServerError
}
log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()))
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
u := getUserSession(h.app.App(), r)
@ -213,7 +213,7 @@ func (h *Handler) AdminApper(f userApperHandlerFunc) http.HandlerFunc {
status = http.StatusInternalServerError
}
log.Info(fmt.Sprintf("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent()))
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
u := getUserSession(h.app.App(), r)
@ -295,7 +295,7 @@ func (h *Handler) UserAll(web bool, f userHandlerFunc, a authFunc) http.HandlerF
status = 500
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
u, err := a(h.app.App(), r)
@ -381,7 +381,7 @@ func (h *Handler) WebErrors(f handlerFunc, ul UserLevelFunc) http.HandlerFunc {
status = 500
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
var session *sessions.Session
@ -440,7 +440,7 @@ func (h *Handler) CollectionPostOrStatic(w http.ResponseWriter, r *http.Request)
start := time.Now()
status := 200
defer func() {
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
// Serve static file
@ -472,7 +472,7 @@ func (h *Handler) Web(f handlerFunc, ul UserLevelFunc) http.HandlerFunc {
status = 500
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
if ul(h.app.App().cfg) != UserLevelNoneType {
@ -530,7 +530,7 @@ func (h *Handler) All(f handlerFunc) http.HandlerFunc {
status = 500
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
// TODO: do any needed authentication
@ -562,7 +562,7 @@ func (h *Handler) AllReader(f handlerFunc) http.HandlerFunc {
status = 500
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
if h.app.App().cfg.App.Private {
@ -619,7 +619,7 @@ func (h *Handler) Download(f dataHandlerFunc, ul UserLevelFunc) http.HandlerFunc
status = 500
}
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
data, filename, err := f(h.app.App(), w, r)
@ -682,7 +682,7 @@ func (h *Handler) Redirect(url string, ul UserLevelFunc) http.HandlerFunc {
status = sendRedirect(w, http.StatusFound, url)
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
return nil
}())
@ -721,6 +721,10 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er
return
} else if err.Status == http.StatusNotFound {
w.WriteHeader(err.Status)
if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
// This is a fediverse request; simply return the header
return
}
h.errors.NotFound.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
return
} else if err.Status == http.StatusInternalServerError {
@ -799,7 +803,7 @@ func (h *Handler) LogHandlerFunc(f http.HandlerFunc) http.HandlerFunc {
}
// TODO: log actual status code returned
log.Info("\"%s %s\" %d %s \"%s\"", r.Method, r.RequestURI, status, time.Since(start), r.UserAgent())
log.Info(h.app.ReqLog(r, status, time.Since(start)))
}()
if h.app.App().cfg.App.Private {

View File

@ -557,7 +557,7 @@ func newPost(app *App, w http.ResponseWriter, r *http.Request) error {
var coll *Collection
var err error
if accessToken != "" {
newPost, err = app.db.CreateOwnedPost(p, accessToken, collAlias)
newPost, err = app.db.CreateOwnedPost(p, accessToken, collAlias, app.cfg.App.Host)
} else {
//return ErrNotLoggedIn
// TODO: verify user is logged in
@ -1300,14 +1300,32 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
coll.Owner = owner
}
postFound := true
p, err := app.db.GetPost(slug, coll.ID)
if err != nil {
if err == ErrCollectionPageNotFound && slug == "feed" {
// User tried to access blog feed without a trailing slash, and
// there's no post with a slug "feed"
return impart.HTTPError{http.StatusFound, c.CanonicalURL() + "/feed/"}
if err == ErrCollectionPageNotFound {
postFound = false
if slug == "feed" {
// User tried to access blog feed without a trailing slash, and
// there's no post with a slug "feed"
return impart.HTTPError{http.StatusFound, c.CanonicalURL() + "/feed/"}
}
po := &Post{
Slug: null.NewString(slug, true),
Font: "norm",
Language: zero.NewString("en", true),
RTL: zero.NewBool(false, true),
Content: `<p class="msg">This page is missing.</p>
Are you sure it was ever here?`,
}
pp := po.processPost()
p = &pp
} else {
return err
}
return err
}
p.IsOwner = owner != nil && p.OwnerID.Valid && owner.ID == p.OwnerID.Int64
p.Collection = coll
@ -1329,11 +1347,20 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
contentType = "text/markdown"
}
w.Header().Set("Content-Type", fmt.Sprintf("%s; charset=utf-8", contentType))
if !postFound {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Post not found.")
// TODO: return error instead, so status is correctly reflected in logs
return nil
}
if isMarkdown && p.Title.String != "" {
fmt.Fprintf(w, "# %s\n\n", p.Title.String)
}
fmt.Fprint(w, p.Content)
} else if strings.Contains(r.Header.Get("Accept"), "application/activity+json") {
if !postFound {
return ErrCollectionPageNotFound
}
p.extractData()
ap := p.ActivityObject(app.cfg)
ap.Context = []interface{}{activitystreams.Namespace}
@ -1350,6 +1377,7 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
IsPinned bool
IsCustomDomain bool
PinnedPosts *[]PublicPost
IsFound bool
IsAdmin bool
CanInvite bool
}{
@ -1357,12 +1385,16 @@ func viewCollectionPost(app *App, w http.ResponseWriter, r *http.Request) error
StaticPage: pageForReq(app, r),
IsOwner: cr.isCollOwner,
IsCustomDomain: cr.isCustomDomain,
IsFound: postFound,
}
tp.IsAdmin = u != nil && u.IsAdmin()
tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin)
tp.PinnedPosts, _ = app.db.GetPinnedPosts(coll)
tp.IsPinned = len(*tp.PinnedPosts) > 0 && PostsContains(tp.PinnedPosts, p)
if !postFound {
w.WriteHeader(http.StatusNotFound)
}
postTmpl := "collection-post"
if app.cfg.App.Chorus {
postTmpl = "chorus-collection-post"

View File

@ -8,6 +8,7 @@
<link rel="stylesheet" type="text/css" href="/css/write.css" />
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{{ if .IsFound }}
<link rel="canonical" href="{{.CanonicalURL}}" />
<meta name="generator" content="WriteFreely">
<meta name="title" content="{{.PlainDisplayTitle}} {{localhtml "title dash" .Language.String}} {{if .Collection.Title}}{{.Collection.Title}}{{else}}{{.Collection.Alias}}{{end}}">
@ -29,6 +30,7 @@
<meta property="og:updated_time" content="{{.Created8601}}" />
{{range .Images}}<meta property="og:image" content="{{.}}" />{{else}}<meta property="og:image" content="{{.Collection.AvatarURL}}">{{end}}
<meta property="article:published_time" content="{{.Created8601}}">
{{ end }}
{{if .Collection.StyleSheet}}<style type="text/css">{{.Collection.StyleSheetDisplay}}</style>{{end}}
{{if .Collection.RenderMathJax}}
@ -50,14 +52,14 @@
{{if .PinnedPosts}}
{{range .PinnedPosts}}<a class="pinned{{if eq .Slug.String $.Slug.String}} selected{{end}}" href="{{if not $.SingleUser}}/{{$.Collection.Alias}}/{{.Slug.String}}{{else}}{{.CanonicalURL}}{{end}}">{{.PlainDisplayTitle}}</a>{{end}}
{{end}}
{{ if .IsOwner }}<span class="views" dir="ltr"><strong>{{largeNumFmt .Views}}</strong> {{pluralize "view" "views" .Views}}</span>
{{ if and .IsOwner .IsFound }}<span class="views" dir="ltr"><strong>{{largeNumFmt .Views}}</strong> {{pluralize "view" "views" .Views}}</span>
<a class="xtra-feature" href="/{{if not .SingleUser}}{{.Collection.Alias}}/{{end}}{{.Slug.String}}/edit" dir="{{.Direction}}">Edit</a>
{{if .IsPinned}}<a class="xtra-feature unpin" href="/{{.Collection.Alias}}/{{.Slug.String}}/unpin" dir="{{.Direction}}" onclick="unpinPost(event, '{{.ID}}')">Unpin</a>{{end}}
{{ end }}
</nav>
</header>
<article id="post-body" class="{{.Font}} h-entry">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name">{{.FormattedDisplayTitle}}</h2>{{end}}<div class="e-content">{{.HTMLContent}}</div></article>
<article id="post-body" class="{{.Font}} h-entry {{if not .IsFound}}error-page{{end}}">{{if .IsScheduled}}<p class="badge">Scheduled</p>{{end}}{{if .Title.String}}<h2 id="title" class="p-name">{{.FormattedDisplayTitle}}</h2>{{end}}<div class="e-content">{{.HTMLContent}}</div></article>
{{ if .Collection.ShowFooterBranding }}
<footer dir="ltr"><hr><nav><p style="font-size: 0.9em">{{localhtml "published with write.as" .Language.String}}</p></nav></footer>