Merge pull request #775 from writefreely/subscriber-insights
Add Subscribers page Closes T826
This commit is contained in:
commit
26ba79ff02
65
account.go
65
account.go
|
@ -1063,12 +1063,15 @@ func viewStats(app *App, u *User, w http.ResponseWriter, r *http.Request) error
|
|||
Collection *Collection
|
||||
TopPosts *[]PublicPost
|
||||
APFollowers int
|
||||
EmailEnabled bool
|
||||
EmailSubscribers int
|
||||
Silenced bool
|
||||
}{
|
||||
UserPage: NewUserPage(app, r, u, titleStats+"Stats", flashes),
|
||||
VisitsBlog: alias,
|
||||
Collection: c,
|
||||
TopPosts: topPosts,
|
||||
EmailEnabled: app.cfg.Email.Enabled(),
|
||||
Silenced: silenced,
|
||||
}
|
||||
obj.UserPage.CollAlias = c.Alias
|
||||
|
@ -1079,11 +1082,73 @@ func viewStats(app *App, u *User, w http.ResponseWriter, r *http.Request) error
|
|||
}
|
||||
obj.APFollowers = len(*folls)
|
||||
}
|
||||
if obj.EmailEnabled {
|
||||
subs, err := app.db.GetEmailSubscribers(c.ID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj.EmailSubscribers = len(subs)
|
||||
}
|
||||
|
||||
showUserPage(w, "stats", obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleViewSubscribers(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
||||
vars := mux.Vars(r)
|
||||
c, err := app.db.GetCollection(vars["collection"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filter := r.FormValue("filter")
|
||||
|
||||
flashes, _ := getSessionFlashes(app, w, r, nil)
|
||||
obj := struct {
|
||||
*UserPage
|
||||
Collection CollectionNav
|
||||
EmailSubs []*EmailSubscriber
|
||||
Followers *[]RemoteUser
|
||||
Silenced bool
|
||||
|
||||
Filter string
|
||||
FederationEnabled bool
|
||||
CanEmailSub bool
|
||||
CanAddSubs bool
|
||||
EmailSubsEnabled bool
|
||||
}{
|
||||
UserPage: NewUserPage(app, r, u, c.DisplayTitle()+" Subscribers", flashes),
|
||||
Collection: CollectionNav{
|
||||
Collection: c,
|
||||
Path: r.URL.Path,
|
||||
SingleUser: app.cfg.App.SingleUser,
|
||||
},
|
||||
Silenced: u.IsSilenced(),
|
||||
Filter: filter,
|
||||
FederationEnabled: app.cfg.App.Federation,
|
||||
CanEmailSub: app.cfg.Email.Enabled(),
|
||||
EmailSubsEnabled: c.EmailSubsEnabled(),
|
||||
}
|
||||
|
||||
obj.Followers, err = app.db.GetAPFollowers(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj.EmailSubs, err = app.db.GetEmailSubscribers(c.ID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if obj.Filter == "" {
|
||||
// Set permission to add email subscribers
|
||||
//obj.CanAddSubs = app.db.GetUserAttribute(c.OwnerID, userAttrCanAddEmailSubs) == "1"
|
||||
}
|
||||
|
||||
showUserPage(w, "subscribers", obj)
|
||||
return nil
|
||||
}
|
||||
|
||||
func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) error {
|
||||
fullUser, err := app.db.GetUserByID(u.ID)
|
||||
if err != nil {
|
||||
|
|
|
@ -84,6 +84,14 @@ type (
|
|||
TotalPages int
|
||||
Silenced bool
|
||||
}
|
||||
|
||||
CollectionNav struct {
|
||||
*Collection
|
||||
Path string
|
||||
SingleUser bool
|
||||
CanPost bool
|
||||
}
|
||||
|
||||
SubmittedCollection struct {
|
||||
// Data used for updating a given collection
|
||||
ID int64
|
||||
|
|
|
@ -60,6 +60,35 @@ nav#admin {
|
|||
background: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
&.sub {
|
||||
margin: 1em 0 2em;
|
||||
a:not(.toggle) {
|
||||
border: 0;
|
||||
border-bottom: 2px transparent solid;
|
||||
.rounded(0);
|
||||
padding: 0.5em;
|
||||
margin-left: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
|
||||
&:hover {
|
||||
color: @primary;
|
||||
background: transparent;
|
||||
}
|
||||
&.selected {
|
||||
color: @primary;
|
||||
background: transparent;
|
||||
border-bottom-color: @primary;
|
||||
}
|
||||
&+a {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
a.toggle {
|
||||
margin-top: -0.5em;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-actions {
|
||||
|
|
|
@ -99,6 +99,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
|||
me.HandleFunc("/c/", handler.User(viewCollections)).Methods("GET")
|
||||
me.HandleFunc("/c/{collection}", handler.User(viewEditCollection)).Methods("GET")
|
||||
me.HandleFunc("/c/{collection}/stats", handler.User(viewStats)).Methods("GET")
|
||||
me.HandleFunc("/c/{collection}/subscribers", handler.User(handleViewSubscribers)).Methods("GET")
|
||||
me.Path("/delete").Handler(csrf.Protect(apper.App().keys.CSRFKey)(handler.User(handleUserDelete))).Methods("POST")
|
||||
me.HandleFunc("/posts", handler.Redirect("/me/posts/", UserLevelUser)).Methods("GET")
|
||||
me.HandleFunc("/posts/", handler.User(viewArticles)).Methods("GET")
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
{{if .SimpleNav}}<li><a href="/new#{{.Alias}}">New Post</a></li>{{end}}
|
||||
<li><a href="/me/c/{{.Alias}}">Customize</a></li>
|
||||
<li><a href="/me/c/{{.Alias}}/stats">Stats</a></li>
|
||||
<li><a href="/me/c/{{.Alias}}/subscribers">Subscribers</a></li>
|
||||
<li class="separator"><hr /></li>
|
||||
{{if not .SingleUser}}<li><a href="/me/c/"><img class="ic-18dp" src="/img/ic_blogs_dark@2x.png" /> View Blogs</a></li>{{end}}
|
||||
<li><a href="/me/posts/"><img class="ic-18dp" src="/img/ic_list_dark@2x.png" /> View Drafts</a></li>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<nav class="tabs">
|
||||
<a href="/me/c/{{.Username}}" {{if and (hasPrefix .Path "/me/c/") (hasSuffix .Path .Username)}}class="selected"{{end}}>Customize</a>
|
||||
<a href="/me/c/{{.Username}}/stats" {{if hasSuffix .Path "/stats"}}class="selected"{{end}}>Stats</a>
|
||||
<a href="/me/c/{{.Username}}/subscribers" {{if hasSuffix .Path "/subscribers"}}class="selected"{{end}}>Subscribers</a>
|
||||
<a href="/me/posts/"{{if eq .Path "/me/posts/"}} class="selected"{{end}}>Drafts</a>
|
||||
</nav>
|
||||
</nav>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
{{if .CanPost}}<a href="{{if .SingleUser}}/me/new{{else}}/#{{.Alias}}{{end}}" class="btn gentlecta">New Post</a>{{end}}
|
||||
<a href="/me/c/{{.Alias}}" {{if and (hasPrefix .Path "/me/c/") (hasSuffix .Path .Alias)}}class="selected"{{end}}>Customize</a>
|
||||
<a href="/me/c/{{.Alias}}/stats" {{if hasSuffix .Path "/stats"}}class="selected"{{end}}>Stats</a>
|
||||
<a href="/me/c/{{.Alias}}/subscribers" {{if hasSuffix .Path "/subscribers"}}class="selected"{{end}}>Subscribers</a>
|
||||
<a href="{{if .SingleUser}}/{{else}}/{{.Alias}}/{{end}}">View Blog →</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -31,14 +31,16 @@ td.none {
|
|||
|
||||
<p>Stats for all time.</p>
|
||||
|
||||
{{if .Federation}}
|
||||
<h3>Fediverse stats</h3>
|
||||
{{if or .Federation .EmailEnabled}}
|
||||
<h3>Subscribers</h3>
|
||||
<table id="fediverse" class="classy export">
|
||||
<tr>
|
||||
<th>Followers</th>
|
||||
{{if .Federation}}<th>Fediverse Followers</th>{{end}}
|
||||
{{if .EmailEnabled}}<th>Email Subscribers</th>{{end}}
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{.APFollowers}}</td>
|
||||
{{if .Federation}}<td><a href="/me/c/{{.Collection.Alias}}/subscribers?filter=fediverse">{{.APFollowers}}</a></td>{{end}}
|
||||
{{if .EmailEnabled}}<td><a href="/me/c/{{.Collection.Alias}}/subscribers">{{.EmailSubscribers}}</a></td>{{end}}
|
||||
</tr>
|
||||
</table>
|
||||
{{end}}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
{{define "subscribers"}}
|
||||
{{template "header" .}}
|
||||
|
||||
<style>
|
||||
.toolbar {
|
||||
text-align: right;
|
||||
margin: 1em 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="snug content-container {{if not .CanEmailSub}}clean{{end}}">
|
||||
{{if .Silenced}}
|
||||
{{template "user-silenced"}}
|
||||
{{end}}
|
||||
|
||||
{{if .Collection.Collection}}{{template "collection-breadcrumbs" .}}{{end}}
|
||||
|
||||
<h1>Subscribers</h1>
|
||||
{{if .Collection.Collection}}
|
||||
{{template "collection-nav" .Collection}}
|
||||
|
||||
<nav class="pager sub">
|
||||
<a href="/me/c/{{.Collection.Alias}}/subscribers" {{if eq .Filter ""}}class="selected"{{end}}>Email ({{len .EmailSubs}})</a>
|
||||
<a href="/me/c/{{.Collection.Alias}}/subscribers?filter=fediverse" {{if eq .Filter "fediverse"}}class="selected"{{end}}>Followers ({{len .Followers}})</a>
|
||||
</nav>
|
||||
{{end}}
|
||||
|
||||
{{if .Flashes -}}
|
||||
<ul class="errors">
|
||||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
|
||||
</ul>
|
||||
{{- end}}
|
||||
|
||||
{{ if eq .Filter "fediverse" }}
|
||||
<table class="classy export">
|
||||
<tr>
|
||||
<th style="width: 60%">Username</th>
|
||||
<th colspan="2">Since</th>
|
||||
</tr>
|
||||
|
||||
{{if and (gt (len .Followers) 0) (not .FederationEnabled)}}
|
||||
<div class="alert info">
|
||||
<p><strong>Federation is disabled on this server</strong>, so followers won't receive any new posts.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{ if gt (len .Followers) 0 }}
|
||||
{{range $el := .Followers}}
|
||||
<tr>
|
||||
<td><a href="{{.ActorID}}">@{{.EstimatedHandle}}</a></td>
|
||||
<td>{{.CreatedFriendly}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{ else }}
|
||||
<tr>
|
||||
<td colspan="2">No followers yet.</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
{{ else }}
|
||||
{{if or .CanEmailSub .EmailSubs}}
|
||||
{{if not .CanEmailSub}}
|
||||
<div class="alert info">
|
||||
<p><strong>Email subscriptions are disabled on this server</strong>, so no new emails will be sent out.</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if not .EmailSubsEnabled}}
|
||||
<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>
|
||||
</div>
|
||||
{{end}}
|
||||
<table class="classy export">
|
||||
<tr>
|
||||
<th style="width: 60%">Email Address</th>
|
||||
<th colspan="2">Since</th>
|
||||
</tr>
|
||||
|
||||
{{ if .EmailSubs }}
|
||||
{{range $el := .EmailSubs}}
|
||||
<tr>
|
||||
<td><a href="mailto:{{.Email.String}}">{{.Email.String}}</a></td>
|
||||
<td>{{.SubscribedFriendly}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{ else }}
|
||||
<tr>
|
||||
<td colspan="2">No subscribers yet.</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
{{end}}
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
|
||||
{{template "foot" .}}
|
||||
|
||||
{{template "body-end" .}}
|
||||
{{end}}
|
Loading…
Reference in New Issue