Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Matt Baer
f545b3353e Support exporting email subscribers list
This adds a new /api/collections/{alias} endpoint that downloads all
collection subscribers as a CSV file.

Ref T828
2025-02-18 18:42:46 -05:00
4 changed files with 42 additions and 1 deletions

View file

@ -11,6 +11,7 @@
package writefreely package writefreely
import ( import (
"bytes"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -180,6 +181,40 @@ func handleCreateEmailSubscription(app *App, w http.ResponseWriter, r *http.Requ
return impart.WriteSuccess(w, "", http.StatusAccepted) return impart.WriteSuccess(w, "", http.StatusAccepted)
} }
func handleExportEmailSubscriptions(app *App, w http.ResponseWriter, r *http.Request) ([]byte, string, error) {
vars := mux.Vars(r)
var err error
alias := vars["alias"]
filename := ""
u := getUserSession(app, r)
if u == nil {
return nil, filename, ErrNotLoggedIn
}
c, err := app.db.GetCollection(alias)
if err != nil {
return nil, filename, err
}
// Verify permissions / ownership
if u.ID != c.OwnerID {
return nil, filename, ErrForbiddenCollectionAccess
}
filename = "subscribers-" + alias + "-" + time.Now().Truncate(time.Second).UTC().Format("200601021504")
subs, err := app.db.GetEmailSubscribers(c.ID, true)
if err != nil {
return nil, filename, err
}
var data []byte
for _, sub := range subs {
data = append(data, []byte(sub.Email.String+"\n")...)
}
data = bytes.TrimRight(data, "\n")
return data, filename, err
}
func handleDeleteEmailSubscription(app *App, w http.ResponseWriter, r *http.Request) error { func handleDeleteEmailSubscription(app *App, w http.ResponseWriter, r *http.Request) error {
alias := collectionAliasFromReq(r) alias := collectionAliasFromReq(r)

View file

@ -26,6 +26,7 @@ var (
ErrNotLoggedIn = impart.HTTPError{http.StatusUnauthorized, "Not logged in."} ErrNotLoggedIn = impart.HTTPError{http.StatusUnauthorized, "Not logged in."}
ErrForbiddenCollection = impart.HTTPError{http.StatusForbidden, "You don't have permission to add to this collection."} ErrForbiddenCollection = impart.HTTPError{http.StatusForbidden, "You don't have permission to add to this collection."}
ErrForbiddenCollectionAccess = impart.HTTPError{http.StatusForbidden, "You don't have permission to access this collection."}
ErrForbiddenEditPost = impart.HTTPError{http.StatusForbidden, "You don't have permission to update this post."} ErrForbiddenEditPost = impart.HTTPError{http.StatusForbidden, "You don't have permission to update this post."}
ErrUnauthorizedEditPost = impart.HTTPError{http.StatusUnauthorized, "Invalid editing credentials."} ErrUnauthorizedEditPost = impart.HTTPError{http.StatusUnauthorized, "Invalid editing credentials."}
ErrUnauthorizedGeneral = impart.HTTPError{http.StatusUnauthorized, "You don't have permission to do that."} ErrUnauthorizedGeneral = impart.HTTPError{http.StatusUnauthorized, "You don't have permission to do that."}

View file

@ -148,6 +148,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
apiColls.HandleFunc("/{alias}/collect", handler.All(addPost)).Methods("POST") apiColls.HandleFunc("/{alias}/collect", handler.All(addPost)).Methods("POST")
apiColls.HandleFunc("/{alias}/pin", handler.All(pinPost)).Methods("POST") apiColls.HandleFunc("/{alias}/pin", handler.All(pinPost)).Methods("POST")
apiColls.HandleFunc("/{alias}/unpin", handler.All(pinPost)).Methods("POST") apiColls.HandleFunc("/{alias}/unpin", handler.All(pinPost)).Methods("POST")
apiColls.HandleFunc("/{alias}/email/subscribers/export.csv", handler.Download(handleExportEmailSubscriptions, UserLevelUser)).Methods("GET")
apiColls.HandleFunc("/{alias}/email/subscribe", handler.All(handleCreateEmailSubscription)).Methods("POST") apiColls.HandleFunc("/{alias}/email/subscribe", handler.All(handleCreateEmailSubscription)).Methods("POST")
apiColls.HandleFunc("/{alias}/email/subscribe", handler.All(handleDeleteEmailSubscription)).Methods("DELETE") apiColls.HandleFunc("/{alias}/email/subscribe", handler.All(handleDeleteEmailSubscription)).Methods("DELETE")
apiColls.HandleFunc("/{collection}/email/unsubscribe", handler.All(handleDeleteEmailSubscription)).Methods("GET") apiColls.HandleFunc("/{collection}/email/unsubscribe", handler.All(handleDeleteEmailSubscription)).Methods("GET")

View file

@ -63,7 +63,11 @@
<p><strong>Email subscriptions are disabled on this server</strong>, so no new emails will be sent out.</p> <p><strong>Email subscriptions are disabled on this server</strong>, so no new emails will be sent out.</p>
</div> </div>
{{end}} {{end}}
{{if not .EmailSubsEnabled}} {{if .EmailSubsEnabled}}
<div class="row toolbar ends">
<a href="/api/collections/{{.Collection.Alias}}/email/subscribers/export.csv">Export list</a>
</div>
{{else}}
<div class="alert info"> <div class="alert info">
<p><strong>Email subscriptions are disabled</strong>. {{if .EmailSubs}}No new emails will be sent out.{{end}} To enable email subscriptions, turn the option on from your blog's <a href="/me/c/{{.Collection.Alias}}#updates">Customize</a> page.</p> <p><strong>Email subscriptions are disabled</strong>. {{if .EmailSubs}}No new emails will be sent out.{{end}} To enable email subscriptions, turn the option on from your blog's <a href="/me/c/{{.Collection.Alias}}#updates">Customize</a> page.</p>
</div> </div>