Merge branch 'develop' into chorus
This commit is contained in:
commit
3cc397ad76
6
app.go
6
app.go
|
@ -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 {
|
||||
|
|
|
@ -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 + "/"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
28
handle.go
28
handle.go
|
@ -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 {
|
||||
|
|
44
posts.go
44
posts.go
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue