From 95a98234eb9dc3fe893515673543b7b759fb558f Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 9 Aug 2019 14:04:15 -0700 Subject: [PATCH 01/17] fix panic on duplicate remoteuser key this changes handleFetchCollectionInbox to log _all_ errors after attempting to insert an actor in the remoteusers table. previously checking for all errors _except_ duplicate keys would cause a panic if an actor made a request to follow while already having followed. --- activitypub.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activitypub.go b/activitypub.go index 997609d..d47a7ea 100644 --- a/activitypub.go +++ b/activitypub.go @@ -375,11 +375,11 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request // Add follower locally, since it wasn't found before res, err := t.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox) VALUES (?, ?, ?)", fullActor.ID, fullActor.Inbox, fullActor.Endpoints.SharedInbox) if err != nil { - if !app.db.isDuplicateKeyErr(err) { - t.Rollback() - log.Error("Couldn't add new remoteuser in DB: %v\n", err) - return - } + // if duplicate key, res will be nil and panic on + // res.LastInsertId below + t.Rollback() + log.Error("Couldn't add new remoteuser in DB: %v\n", err) + return } followerID, err = res.LastInsertId() From f87371b594ce78fa191c841e0fbf821b4b0839ab Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Wed, 18 Sep 2019 12:39:53 -0700 Subject: [PATCH 02/17] update IsJSON to check for Accept header this changes the helper IsJSON to take a request instead of a string, allowing to check multiple headers. In this case both Content-Type and Accept. --- account.go | 16 ++++++++-------- collections.go | 6 +++--- handle.go | 2 +- posts.go | 4 ++-- request.go | 12 ++++++++---- unregisteredusers.go | 7 ++++--- 6 files changed, 26 insertions(+), 21 deletions(-) diff --git a/account.go b/account.go index 2a66ecf..920fc9d 100644 --- a/account.go +++ b/account.go @@ -85,7 +85,7 @@ func apiSignup(app *App, w http.ResponseWriter, r *http.Request) error { } func signup(app *App, w http.ResponseWriter, r *http.Request) (*AuthUser, error) { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) // Get params var ur userRegistration @@ -120,7 +120,7 @@ func signup(app *App, w http.ResponseWriter, r *http.Request) (*AuthUser, error) } func signupWithRegistration(app *App, signup userRegistration, w http.ResponseWriter, r *http.Request) (*AuthUser, error) { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) // Validate required params (alias) if signup.Alias == "" { @@ -377,7 +377,7 @@ func webLogin(app *App, w http.ResponseWriter, r *http.Request) error { var loginAttemptUsers = sync.Map{} func login(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) oneTimeToken := r.FormValue("with") verbose := r.FormValue("all") == "true" || r.FormValue("verbose") == "1" || r.FormValue("verbose") == "true" || (reqJSON && oneTimeToken != "") @@ -580,7 +580,7 @@ func viewExportOptions(app *App, u *User, w http.ResponseWriter, r *http.Request func viewExportPosts(app *App, w http.ResponseWriter, r *http.Request) ([]byte, string, error) { var filename string var u = &User{} - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) if reqJSON { // Use given Authorization header accessToken := r.Header.Get("Authorization") @@ -662,7 +662,7 @@ func viewExportFull(app *App, w http.ResponseWriter, r *http.Request) ([]byte, s } func viewMeAPI(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) uObj := struct { ID int64 `json:"id,omitempty"` Username string `json:"username,omitempty"` @@ -686,7 +686,7 @@ func viewMeAPI(app *App, w http.ResponseWriter, r *http.Request) error { } func viewMyPostsAPI(app *App, u *User, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) if !reqJSON { return ErrBadRequestedType } @@ -717,7 +717,7 @@ func viewMyPostsAPI(app *App, u *User, w http.ResponseWriter, r *http.Request) e } func viewMyCollectionsAPI(app *App, u *User, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) if !reqJSON { return ErrBadRequestedType } @@ -822,7 +822,7 @@ func viewEditCollection(app *App, u *User, w http.ResponseWriter, r *http.Reques } func updateSettings(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) var s userSettings var u *User diff --git a/collections.go b/collections.go index c095ecb..cdf3d5c 100644 --- a/collections.go +++ b/collections.go @@ -338,7 +338,7 @@ func (c *Collection) RenderMathJax() bool { } func newCollection(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) alias := r.FormValue("alias") title := r.FormValue("title") @@ -454,7 +454,7 @@ func fetchCollection(app *App, w http.ResponseWriter, r *http.Request) error { c.hostName = app.cfg.App.Host // Redirect users who aren't requesting JSON - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) if !reqJSON { return impart.HTTPError{http.StatusFound, c.CanonicalURL()} } @@ -919,7 +919,7 @@ func handleCollectionPostRedirect(app *App, w http.ResponseWriter, r *http.Reque } func existingCollection(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) vars := mux.Vars(r) collAlias := vars["alias"] isWeb := r.FormValue("web") == "1" diff --git a/handle.go b/handle.go index 99c23ae..7e410f5 100644 --- a/handle.go +++ b/handle.go @@ -772,7 +772,7 @@ func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) return } - if IsJSON(r.Header.Get("Content-Type")) { + if IsJSON(r) { impart.WriteError(w, impart.HTTPError{http.StatusInternalServerError, "This is an unhelpful error message for a miscellaneous internal error."}) return } diff --git a/posts.go b/posts.go index 88730f7..1f35eda 100644 --- a/posts.go +++ b/posts.go @@ -472,7 +472,7 @@ func handleViewPost(app *App, w http.ResponseWriter, r *http.Request) error { // /posts?collection={alias} // ? /collections/{alias}/posts func newPost(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) vars := mux.Vars(r) collAlias := vars["alias"] if collAlias == "" { @@ -598,7 +598,7 @@ func newPost(app *App, w http.ResponseWriter, r *http.Request) error { } func existingPost(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) vars := mux.Vars(r) postID := vars["post"] diff --git a/request.go b/request.go index 4939f9c..2eb29f5 100644 --- a/request.go +++ b/request.go @@ -10,9 +10,13 @@ package writefreely -import "mime" +import ( + "mime" + "net/http" +) -func IsJSON(h string) bool { - ct, _, _ := mime.ParseMediaType(h) - return ct == "application/json" +func IsJSON(r *http.Request) bool { + ct, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) + accept := r.Header.Get("Accept") + return ct == "application/json" || accept == "application/json" } diff --git a/unregisteredusers.go b/unregisteredusers.go index 550c83b..b6f6ce6 100644 --- a/unregisteredusers.go +++ b/unregisteredusers.go @@ -13,13 +13,14 @@ package writefreely import ( "database/sql" "encoding/json" + "net/http" + "github.com/writeas/impart" "github.com/writeas/web-core/log" - "net/http" ) func handleWebSignup(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) // Get params var ur userRegistration @@ -71,7 +72,7 @@ func handleWebSignup(app *App, w http.ResponseWriter, r *http.Request) error { // { "username": "asdf" } // result: { code: 204 } func handleUsernameCheck(app *App, w http.ResponseWriter, r *http.Request) error { - reqJSON := IsJSON(r.Header.Get("Content-Type")) + reqJSON := IsJSON(r) // Get params var d struct { From 02dd1909459f06c085448ad8a231961820c8c940 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Wed, 2 Oct 2019 10:20:51 -0700 Subject: [PATCH 03/17] T697 default to user's first blog instead of draft --- templates/pad.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/pad.tmpl b/templates/pad.tmpl index 914d921..ea4246c 100644 --- a/templates/pad.tmpl +++ b/templates/pad.tmpl @@ -25,10 +25,10 @@ {{else}}
  • Draft
      -
    • description Draft
    • - {{if .Blogs}}{{range .Blogs}} -
    • public {{if .Title}}{{.Title}}{{else}}{{.Alias}}{{end}}
    • + {{if .Blogs}}{{range $idx, $el := .Blogs}} +
    • public {{if $el.Title}}{{$el.Title}}{{else}}{{$el.Alias}}{{end}}
    • {{end}}{{end}} +
    • description Draft

    • {{ if .SingleUser }}
    • launch View Blog
    • @@ -278,7 +278,7 @@ document.getElementById('target-name').innerText = newText.join(' '); }); } - var postTarget = H.get('postTarget', 'anonymous'); + var postTarget = H.get('postTarget', '{{if .Blogs}}{{$blog := index .Blogs 0}}{{$blog.Alias}}{{else}}anonymous{{end}}'); if (location.hash != '') { postTarget = location.hash.substring(1); // TODO: pushState to /pad (or whatever the URL is) so we live on a clean URL From aa9efc7b37219066ac767049b1c77e2d52fbc634 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Thu, 3 Oct 2019 13:41:50 -0700 Subject: [PATCH 04/17] allow admin to reset user passwords this adds a new button when viewing a user as an admin, that will generate and store a new password for the user --- admin.go | 58 +++++++++++++++++++++++++---- routes.go | 1 + templates/user/admin/view-user.tmpl | 45 +++++++++++++++++++++- 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/admin.go b/admin.go index fdbb82f..5b3acab 100644 --- a/admin.go +++ b/admin.go @@ -16,12 +16,14 @@ import ( "net/http" "runtime" "strconv" + "strings" "time" "github.com/gorilla/mux" "github.com/writeas/impart" "github.com/writeas/web-core/auth" "github.com/writeas/web-core/log" + "github.com/writeas/web-core/passgen" "github.com/writeas/writefreely/appstats" "github.com/writeas/writefreely/config" ) @@ -167,25 +169,34 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque p := struct { *UserPage - Config config.AppCfg - Message string - - User *User - Colls []inspectedCollection - LastPost string - - TotalPosts int64 + Config config.AppCfg + Message string + OwnUserPage bool + + User *User + Colls []inspectedCollection + LastPost string + NewPassword string + TotalPosts int64 }{ Config: app.cfg.App, Message: r.FormValue("m"), Colls: []inspectedCollection{}, } + flashes, _ := getSessionFlashes(app, w, r, nil) + for _, flash := range flashes { + if strings.HasPrefix(flash, "SUCCESS: ") { + p.NewPassword = strings.TrimPrefix(flash, "SUCCESS: ") + } + } + var err error p.User, err = app.db.GetUserForAuth(username) if err != nil { return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get user: %v", err)} } + p.OwnUserPage = u.ID == p.User.ID p.UserPage = NewUserPage(app, r, u, p.User.Username, nil) p.TotalPosts = app.db.GetUserPostsCount(p.User.ID) lp, err := app.db.GetUserLastPostTime(p.User.ID) @@ -230,6 +241,37 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque return nil } +func handleAdminResetUserPass(app *App, u *User, w http.ResponseWriter, r *http.Request) error { + vars := mux.Vars(r) + username := vars["username"] + if username == "" { + return impart.HTTPError{http.StatusFound, "/admin/users"} + } + // Generate new random password since none supplied + pass := passgen.New() + hashedPass, err := auth.HashPass([]byte(pass)) + if err != nil { + return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not create password hash: %v", err)} + } + + userIDVal := r.FormValue("user") + log.Info("ADMIN: Changing user %s password", userIDVal) + id, err := strconv.Atoi(userIDVal) + if err != nil { + return impart.HTTPError{http.StatusBadRequest, fmt.Sprintf("Invalid user ID: %v", err)} + } + + err = app.db.ChangePassphrase(int64(id), true, "", hashedPass) + if err != nil { + return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not update passphrase: %v", err)} + } + log.Info("ADMIN: Successfully changed.") + + addSessionFlash(app, w, r, fmt.Sprintf("SUCCESS: %s", pass), nil) + + return impart.HTTPError{http.StatusFound, fmt.Sprintf("/admin/user/%s", username)} +} + func handleViewAdminPages(app *App, u *User, w http.ResponseWriter, r *http.Request) error { p := struct { *UserPage diff --git a/routes.go b/routes.go index 0113e93..003b7d1 100644 --- a/routes.go +++ b/routes.go @@ -144,6 +144,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET") write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET") write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET") + write.HandleFunc("/admin/user/{username}", handler.Admin(handleAdminResetUserPass)).Methods("POST") write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET") write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET") write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST") diff --git a/templates/user/admin/view-user.tmpl b/templates/user/admin/view-user.tmpl index 2a74e5b..211297d 100644 --- a/templates/user/admin/view-user.tmpl +++ b/templates/user/admin/view-user.tmpl @@ -7,12 +7,32 @@ table.classy th { h3 { font-weight: normal; } +input.copy-text { + text-align: center; + font-size: 1.2em; + color: #555; + margin-left: 1rem; +} +button[type="submit"].danger { + padding-left: 2rem; + padding-right: 2rem; +}
      {{template "admin-header" .}}

      {{.User.Username}}

      - + {{if .NewPassword}}

      New password for user {{.User.Username}} is + +

      + You must share this new password with the user, this is the only time it will be displayed. +

      + {{end}} @@ -38,6 +58,21 @@ h3 { + + + +
      No.Last Post {{if .LastPost}}{{.LastPost}}{{else}}Never{{end}}
      Password + {{if not .OwnUserPage}} +
      + + +
      + {{else}} + Change your password + {{end}} +

      Blogs

      @@ -83,5 +118,13 @@ h3 { {{end}}
      + {{template "footer" .}} {{end}} From bf4f8793832df63f2e426ac9edc556029d6a738b Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 4 Nov 2019 14:06:24 -0500 Subject: [PATCH 05/17] Update hosting options in README Now: Write.as Pro and Write.as for Teams --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4f0b6bb..68da89b 100644 --- a/README.md +++ b/README.md @@ -47,15 +47,15 @@ It's designed to be flexible and share your writing widely, so it's built around ## Hosting -We offer two kinds of hosting services that make WriteFreely deployment painless: [Write.as](https://write.as) for individuals, and [WriteFreely.host](https://writefreely.host) for communities. Besides saving you time, as a customer you directly help fund WriteFreely development. +We offer two kinds of hosting services that make WriteFreely deployment painless: [Write.as Pro](https://write.as/pro) for individuals, and [Write.as for Teams](https://write.as/for/teams) for businesses. Besides saving you time and effort, both services directly fund WriteFreely development and ensure the long-term sustainability of our open source work. -### [![Write.as](https://write.as/img/writeas-wf-readme.png)](https://write.as/) +### [![Write.as Pro](https://writefreely.org/img/writeas-pro-readme.png)](https://write.as/pro) -Start a personal blog on [Write.as](https://write.as), our flagship instance. Built to eliminate setup friction and preserve your privacy, Write.as helps you start a blog in seconds. It supports custom domains (with SSL) and multiple blogs / pen names per account. [Read more here](https://write.as/pricing). +Start a personal blog on [Write.as](https://write.as), our flagship instance. Built to eliminate setup friction and preserve your privacy, Write.as helps you start a blog in seconds. It supports custom domains (with SSL) and multiple blogs / pen names per account. [Read more here](https://write.as/pro). -### [![WriteFreely.host](https://writefreely.host/img/wfhost-wf-readme.png)](https://writefreely.host) +### [![Write.as for Teams](https://writefreely.org/img/writeas-for-teams-readme.png)](https://write.as/for/teams) -[WriteFreely.host](https://writefreely.host) makes it easy to start a close-knit community — to share knowledge, complement your Mastodon instance, or publish updates in your organization. We take care of the hosting, upgrades, backups, and maintenance so you can focus on writing. +[Write.as for Teams](https://write.as/for/teams) gives your organization, business, or [open source project](https://write.as/for/open-source) a clutter-free space to share updates or proposals and build your collective knowledge. We take care of hosting, upgrades, backups, and maintenance so your team can focus on writing. ## Quick start From e1149cd1e95154e4e72aca606e5582ad5dcbc7cf Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Thu, 7 Nov 2019 17:24:04 +0900 Subject: [PATCH 06/17] Fix URLs in CSV exports This includes the instance's hostname in calls to export a CSV file and PublicPost.CanonicalURL(). It also fixes a panic in that method during CSV export caused by draft posts. --- account.go | 2 +- export.go | 5 +++-- posts.go | 6 +++--- read.go | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/account.go b/account.go index 920fc9d..180e9b0 100644 --- a/account.go +++ b/account.go @@ -625,7 +625,7 @@ func viewExportPosts(app *App, w http.ResponseWriter, r *http.Request) ([]byte, // Export as CSV if strings.HasSuffix(r.URL.Path, ".csv") { - data = exportPostsCSV(u, posts) + data = exportPostsCSV(app.cfg.App.Host, u, posts) return data, filename, err } if strings.HasSuffix(r.URL.Path, ".zip") { diff --git a/export.go b/export.go index 3b5ac49..592bc0c 100644 --- a/export.go +++ b/export.go @@ -20,7 +20,7 @@ import ( "github.com/writeas/web-core/log" ) -func exportPostsCSV(u *User, posts *[]PublicPost) []byte { +func exportPostsCSV(hostName string, u *User, posts *[]PublicPost) []byte { var b bytes.Buffer r := [][]string{ @@ -30,8 +30,9 @@ func exportPostsCSV(u *User, posts *[]PublicPost) []byte { var blog string if p.Collection != nil { blog = p.Collection.Alias + p.Collection.hostName = hostName } - f := []string{p.ID, p.Slug.String, blog, p.CanonicalURL(), p.Created8601(), p.Title.String, strings.Replace(p.Content, "\n", "\\n", -1)} + f := []string{p.ID, p.Slug.String, blog, p.CanonicalURL(hostName), p.Created8601(), p.Title.String, strings.Replace(p.Content, "\n", "\\n", -1)} r = append(r, f) } diff --git a/posts.go b/posts.go index 1f35eda..d004296 100644 --- a/posts.go +++ b/posts.go @@ -1061,9 +1061,9 @@ func (p *Post) processPost() PublicPost { return *res } -func (p *PublicPost) CanonicalURL() string { +func (p *PublicPost) CanonicalURL(hostName string) string { if p.Collection == nil || p.Collection.Alias == "" { - return p.Collection.hostName + "/" + p.ID + return hostName + "/" + p.ID } return p.Collection.CanonicalURL() + p.Slug.String } @@ -1072,7 +1072,7 @@ func (p *PublicPost) ActivityObject(cfg *config.Config) *activitystreams.Object o := activitystreams.NewArticleObject() o.ID = p.Collection.FederatedAPIBase() + "api/posts/" + p.ID o.Published = p.Created - o.URL = p.CanonicalURL() + o.URL = p.CanonicalURL(cfg.App.Host) o.AttributedTo = p.Collection.FederatedAccount() o.CC = []string{ p.Collection.FederatedAccount() + "/followers", diff --git a/read.go b/read.go index ec0305a..df24621 100644 --- a/read.go +++ b/read.go @@ -293,7 +293,7 @@ func viewLocalTimelineFeed(app *App, w http.ResponseWriter, req *http.Request) e } title = p.PlainDisplayTitle() - permalink = p.CanonicalURL() + permalink = p.CanonicalURL(app.cfg.App.Host) if p.Collection != nil { author = p.Collection.Title } else { From c0b75f6b65e375b04a100ca6b9963e579d4282d5 Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Fri, 8 Nov 2019 08:47:03 -0800 Subject: [PATCH 07/17] pass hostname to canonical url in post templates the change to take a hostname in Post.CanonicalURL broke a few template using that function. This adds a Hostname string to the Post being passed to templates and passes it to calls to Post.CanonicalURL --- posts.go | 2 ++ templates/chorus-collection-post.tmpl | 6 +++--- templates/chorus-collection.tmpl | 2 +- templates/collection-post.tmpl | 6 +++--- templates/collection-tags.tmpl | 2 +- templates/collection.tmpl | 2 +- templates/read.tmpl | 8 ++++---- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/posts.go b/posts.go index d004296..6cb76a2 100644 --- a/posts.go +++ b/posts.go @@ -1380,12 +1380,14 @@ Are you sure it was ever here?`, IsFound bool IsAdmin bool CanInvite bool + Hostname string }{ PublicPost: p, StaticPage: pageForReq(app, r), IsOwner: cr.isCollOwner, IsCustomDomain: cr.isCustomDomain, IsFound: postFound, + Hostname: app.cfg.App.Host, } tp.IsAdmin = u != nil && u.IsAdmin() tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin) diff --git a/templates/chorus-collection-post.tmpl b/templates/chorus-collection-post.tmpl index bab2e31..d229c62 100644 --- a/templates/chorus-collection-post.tmpl +++ b/templates/chorus-collection-post.tmpl @@ -8,7 +8,7 @@ - + @@ -25,7 +25,7 @@ - + {{range .Images}}{{else}}{{end}} @@ -77,7 +77,7 @@ article time.dt-published {


      diff --git a/templates/chorus-collection.tmpl b/templates/chorus-collection.tmpl index e36d3b5..ebee403 100644 --- a/templates/chorus-collection.tmpl +++ b/templates/chorus-collection.tmpl @@ -68,7 +68,7 @@ body#collection header nav.tabs a:first-child { {{/*end*/}} {{if .PinnedPosts}} + {{range .PinnedPosts}}{{.PlainDisplayTitle}}{{end}} {{end}} diff --git a/templates/collection-post.tmpl b/templates/collection-post.tmpl index 7075226..a4084b3 100644 --- a/templates/collection-post.tmpl +++ b/templates/collection-post.tmpl @@ -9,7 +9,7 @@ {{ if .IsFound }} - + @@ -26,7 +26,7 @@ - + {{range .Images}}{{else}}{{end}} @@ -50,7 +50,7 @@

      {{end}} diff --git a/templates/read.tmpl b/templates/read.tmpl index 9541ab5..91fbeb4 100644 --- a/templates/read.tmpl +++ b/templates/read.tmpl @@ -87,17 +87,17 @@ {{ if gt (len .Posts) 0 }}
      {{range .Posts}}
      - {{if .Title.String}}

      + {{if .Title.String}}

      {{else}} -

      +

      {{end}}

      {{if .Collection}}from {{.Collection.DisplayTitle}}{{else}}Anonymous{{end}}

      {{if .Excerpt}}
      {{.Excerpt}}
      - {{localstr "Read more..." .Language.String}}{{else}}
      {{ if not .HTMLContent }}

      {{.Content}}

      {{ else }}{{.HTMLContent}}{{ end }}
       
      + {{localstr "Read more..." .Language.String}}{{else}}
      {{ if not .HTMLContent }}

      {{.Content}}

      {{ else }}{{.HTMLContent}}{{ end }}
       
      - {{localstr "Read more..." .Language.String}}{{end}}
      + {{localstr "Read more..." .Language.String}}{{end}} {{end}}
      {{ else }} From f66d5bf1e8fa35330f52c2bb6b58f9720831029b Mon Sep 17 00:00:00 2001 From: Rob Loranger Date: Sat, 9 Nov 2019 11:41:39 -0800 Subject: [PATCH 08/17] use .Host instead of adding .Hostname --- posts.go | 2 -- templates/chorus-collection-post.tmpl | 6 +++--- templates/chorus-collection.tmpl | 2 +- templates/collection-post.tmpl | 6 +++--- templates/collection-tags.tmpl | 2 +- templates/collection.tmpl | 2 +- templates/read.tmpl | 8 ++++---- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/posts.go b/posts.go index 6cb76a2..d004296 100644 --- a/posts.go +++ b/posts.go @@ -1380,14 +1380,12 @@ Are you sure it was ever here?`, IsFound bool IsAdmin bool CanInvite bool - Hostname string }{ PublicPost: p, StaticPage: pageForReq(app, r), IsOwner: cr.isCollOwner, IsCustomDomain: cr.isCustomDomain, IsFound: postFound, - Hostname: app.cfg.App.Host, } tp.IsAdmin = u != nil && u.IsAdmin() tp.CanInvite = canUserInvite(app.cfg, tp.IsAdmin) diff --git a/templates/chorus-collection-post.tmpl b/templates/chorus-collection-post.tmpl index d229c62..18fb632 100644 --- a/templates/chorus-collection-post.tmpl +++ b/templates/chorus-collection-post.tmpl @@ -8,7 +8,7 @@ - + @@ -25,7 +25,7 @@ - + {{range .Images}}{{else}}{{end}} @@ -77,7 +77,7 @@ article time.dt-published {


      diff --git a/templates/chorus-collection.tmpl b/templates/chorus-collection.tmpl index ebee403..14d5fbd 100644 --- a/templates/chorus-collection.tmpl +++ b/templates/chorus-collection.tmpl @@ -68,7 +68,7 @@ body#collection header nav.tabs a:first-child { {{/*end*/}} {{if .PinnedPosts}} + {{range .PinnedPosts}}{{.PlainDisplayTitle}}{{end}} {{end}} diff --git a/templates/collection-post.tmpl b/templates/collection-post.tmpl index a4084b3..4af5cb8 100644 --- a/templates/collection-post.tmpl +++ b/templates/collection-post.tmpl @@ -9,7 +9,7 @@ {{ if .IsFound }} - + @@ -26,7 +26,7 @@ - + {{range .Images}}{{else}}{{end}} @@ -50,7 +50,7 @@

      {{end}} diff --git a/templates/read.tmpl b/templates/read.tmpl index 91fbeb4..f1cbf29 100644 --- a/templates/read.tmpl +++ b/templates/read.tmpl @@ -87,17 +87,17 @@ {{ if gt (len .Posts) 0 }}
      {{range .Posts}}
      - {{if .Title.String}}

      + {{if .Title.String}}

      {{else}} -

      +

      {{end}}

      {{if .Collection}}from {{.Collection.DisplayTitle}}{{else}}Anonymous{{end}}

      {{if .Excerpt}}
      {{.Excerpt}}
      - {{localstr "Read more..." .Language.String}}{{else}}
      {{ if not .HTMLContent }}

      {{.Content}}

      {{ else }}{{.HTMLContent}}{{ end }}
       
      + {{localstr "Read more..." .Language.String}}{{else}}
      {{ if not .HTMLContent }}

      {{.Content}}

      {{ else }}{{.HTMLContent}}{{ end }}
       
      - {{localstr "Read more..." .Language.String}}{{end}}
      + {{localstr "Read more..." .Language.String}}{{end}} {{end}}
      {{ else }} From 6e09fcb9e2a3088c9c5ad1cbbbb5cc5947d2122a Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Mon, 11 Nov 2019 16:02:22 +0900 Subject: [PATCH 09/17] Change password reset endpoint to /admin/user/{Username}/passphrase Ref T695 --- routes.go | 2 +- templates/user/admin/view-user.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routes.go b/routes.go index 003b7d1..de19ff2 100644 --- a/routes.go +++ b/routes.go @@ -144,7 +144,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { write.HandleFunc("/admin", handler.Admin(handleViewAdminDash)).Methods("GET") write.HandleFunc("/admin/users", handler.Admin(handleViewAdminUsers)).Methods("GET") write.HandleFunc("/admin/user/{username}", handler.Admin(handleViewAdminUser)).Methods("GET") - write.HandleFunc("/admin/user/{username}", handler.Admin(handleAdminResetUserPass)).Methods("POST") + write.HandleFunc("/admin/user/{username}/passphrase", handler.Admin(handleAdminResetUserPass)).Methods("POST") write.HandleFunc("/admin/pages", handler.Admin(handleViewAdminPages)).Methods("GET") write.HandleFunc("/admin/page/{slug}", handler.Admin(handleViewAdminPage)).Methods("GET") write.HandleFunc("/admin/update/config", handler.AdminApper(handleAdminUpdateConfig)).Methods("POST") diff --git a/templates/user/admin/view-user.tmpl b/templates/user/admin/view-user.tmpl index 211297d..91fdaf1 100644 --- a/templates/user/admin/view-user.tmpl +++ b/templates/user/admin/view-user.tmpl @@ -62,7 +62,7 @@ button[type="submit"].danger { Password {{if not .OwnUserPage}} -
      +