Merge branch 'develop' into chorus

This commit is contained in:
Matt Baer 2019-08-08 07:55:49 -04:00
commit deec914ccb
17 changed files with 90 additions and 44 deletions

View File

@ -168,7 +168,7 @@ func signupWithRegistration(app *App, signup userRegistration, w http.ResponseWr
}
// Create actual user
if err := app.db.CreateUser(u, desiredUsername); err != nil {
if err := app.db.CreateUser(app.cfg, u, desiredUsername); err != nil {
return nil, err
}

View File

@ -391,6 +391,7 @@ func handleAdminUpdateConfig(apper Apper, u *User, w http.ResponseWriter, r *htt
if apper.App().cfg.App.UserInvites == "none" {
apper.App().cfg.App.UserInvites = ""
}
apper.App().cfg.App.DefaultVisibility = r.FormValue("default_visibility")
m := "?cm=Configuration+saved."
err = apper.SaveConfig(apper.App().cfg)

4
app.go
View File

@ -558,7 +558,7 @@ func DoConfig(app *App, configSections string) {
// Create blog
log.Info("Creating user %s...\n", u.Username)
err = app.db.CreateUser(u, app.cfg.App.SiteName)
err = app.db.CreateUser(app.cfg, u, app.cfg.App.SiteName)
if err != nil {
log.Error("Unable to create user: %s", err)
os.Exit(1)
@ -753,7 +753,7 @@ func CreateUser(apper Apper, username, password string, isAdmin bool) error {
userType = "admin"
}
log.Info("Creating %s %s...", userType, usernameDesc)
err = apper.App().db.CreateUser(u, desiredUsername)
err = apper.App().db.CreateUser(apper.App().Config(), u, desiredUsername)
if err != nil {
return fmt.Errorf("Unable to create user: %s", err)
}

View File

@ -31,6 +31,7 @@ import (
"github.com/writeas/web-core/log"
waposts "github.com/writeas/web-core/posts"
"github.com/writeas/writefreely/author"
"github.com/writeas/writefreely/config"
"github.com/writeas/writefreely/page"
)
@ -126,6 +127,21 @@ const (
CollProtected
)
var collVisibilityStrings = map[string]collVisibility{
"unlisted": CollUnlisted,
"public": CollPublic,
"private": CollPrivate,
"protected": CollProtected,
}
func defaultVisibility(cfg *config.Config) collVisibility {
vis, ok := collVisibilityStrings[cfg.App.DefaultVisibility]
if !ok {
vis = CollUnlisted
}
return vis
}
func (cf *CollectionFormat) Ascending() bool {
return cf.Format == "novel"
}
@ -362,36 +378,32 @@ func newCollection(app *App, w http.ResponseWriter, r *http.Request) error {
return impart.HTTPError{http.StatusBadRequest, fmt.Sprintf("Parameter(s) %srequired.", missingParams)}
}
var userID int64
if reqJSON && !c.Web {
accessToken = r.Header.Get("Authorization")
if accessToken == "" {
return ErrNoAccessToken
}
userID = app.db.GetUserID(accessToken)
if userID == -1 {
return ErrBadAccessToken
}
} else {
u = getUserSession(app, r)
if u == nil {
return ErrNotLoggedIn
}
userID = u.ID
}
if !author.IsValidUsername(app.cfg, c.Alias) {
return impart.HTTPError{http.StatusPreconditionFailed, "Collection alias isn't valid."}
}
var coll *Collection
var err error
if accessToken != "" {
coll, err = app.db.CreateCollectionFromToken(c.Alias, c.Title, accessToken)
if err != nil {
// TODO: handle this
return err
}
} else {
coll, err = app.db.CreateCollection(c.Alias, c.Title, u.ID)
if err != nil {
// TODO: handle this
return err
}
coll, err := app.db.CreateCollection(app.cfg, c.Alias, c.Title, userID)
if err != nil {
// TODO: handle this
return err
}
res := &CollectionObj{Collection: *coll}

View File

@ -69,6 +69,7 @@ type (
WebFonts bool `ini:"webfonts"`
Landing string `ini:"landing"`
SimpleNav bool `ini:"simple_nav"`
WFModesty bool `ini:"wf_modesty"`
Chorus bool `ini:"chorus"`
// Users
@ -87,6 +88,9 @@ type (
// Additional functions
LocalTimeline bool `ini:"local_timeline"`
UserInvites string `ini:"user_invites"`
// Defaults
DefaultVisibility string `ini:"default_visibility"`
}
// Config holds the complete configuration for running a writefreely instance

View File

@ -45,7 +45,7 @@ var (
)
type writestore interface {
CreateUser(*User, string) error
CreateUser(*config.Config, *User, string) error
UpdateUserEmail(keys *key.Keychain, userID int64, email string) error
UpdateEncryptedUserEmail(int64, []byte) error
GetUserByID(int64) (*User, error)
@ -83,8 +83,8 @@ type writestore interface {
GetOwnedPost(id string, ownerID int64) (*PublicPost, error)
GetPostProperty(id string, collectionID int64, property string) (interface{}, error)
CreateCollectionFromToken(string, string, string) (*Collection, error)
CreateCollection(string, string, int64) (*Collection, error)
CreateCollectionFromToken(*config.Config, string, string, string) (*Collection, error)
CreateCollection(*config.Config, string, string, int64) (*Collection, error)
GetCollectionBy(condition string, value interface{}) (*Collection, error)
GetCollection(alias string) (*Collection, error)
GetCollectionForPad(alias string) (*Collection, error)
@ -103,7 +103,7 @@ type writestore interface {
CanCollect(cpr *ClaimPostRequest, userID int64) bool
AttemptClaim(p *ClaimPostRequest, query string, params []interface{}, slugIdx int) (sql.Result, error)
DispersePosts(userID int64, postIDs []string) (*[]ClaimPostResult, error)
ClaimPosts(userID int64, collAlias string, posts *[]ClaimPostRequest) (*[]ClaimPostResult, error)
ClaimPosts(cfg *config.Config, userID int64, collAlias string, posts *[]ClaimPostRequest) (*[]ClaimPostResult, error)
GetPostsCount(c *CollectionObj, includeFuture bool)
GetPosts(cfg *config.Config, c *Collection, page int, includeFuture, forceRecentFirst, includePinned bool) (*[]PublicPost, error)
@ -163,7 +163,7 @@ func (db *datastore) dateSub(l int, unit string) string {
return fmt.Sprintf("DATE_SUB(NOW(), INTERVAL %d %s)", l, unit)
}
func (db *datastore) CreateUser(u *User, collectionTitle string) error {
func (db *datastore) CreateUser(cfg *config.Config, u *User, collectionTitle string) error {
if db.PostIDExists(u.Username) {
return impart.HTTPError{http.StatusConflict, "Invalid collection name."}
}
@ -197,7 +197,7 @@ func (db *datastore) CreateUser(u *User, collectionTitle string) error {
if collectionTitle == "" {
collectionTitle = u.Username
}
res, err = t.Exec("INSERT INTO collections (alias, title, description, privacy, owner_id, view_count) VALUES (?, ?, ?, ?, ?, ?)", u.Username, collectionTitle, "", CollUnlisted, u.ID, 0)
res, err = t.Exec("INSERT INTO collections (alias, title, description, privacy, owner_id, view_count) VALUES (?, ?, ?, ?, ?, ?)", u.Username, collectionTitle, "", defaultVisibility(cfg), u.ID, 0)
if err != nil {
t.Rollback()
if db.isDuplicateKeyErr(err) {
@ -239,13 +239,13 @@ func (db *datastore) UpdateEncryptedUserEmail(userID int64, encEmail []byte) err
return nil
}
func (db *datastore) CreateCollectionFromToken(alias, title, accessToken string) (*Collection, error) {
func (db *datastore) CreateCollectionFromToken(cfg *config.Config, alias, title, accessToken string) (*Collection, error) {
userID := db.GetUserID(accessToken)
if userID == -1 {
return nil, ErrBadAccessToken
}
return db.CreateCollection(alias, title, userID)
return db.CreateCollection(cfg, alias, title, userID)
}
func (db *datastore) GetUserCollectionCount(userID int64) (uint64, error) {
@ -262,13 +262,13 @@ func (db *datastore) GetUserCollectionCount(userID int64) (uint64, error) {
return collCount, nil
}
func (db *datastore) CreateCollection(alias, title string, userID int64) (*Collection, error) {
func (db *datastore) CreateCollection(cfg *config.Config, alias, title string, userID int64) (*Collection, error) {
if db.PostIDExists(alias) {
return nil, impart.HTTPError{http.StatusConflict, "Invalid collection name."}
}
// All good, so create new collection
res, err := db.Exec("INSERT INTO collections (alias, title, description, privacy, owner_id, view_count) VALUES (?, ?, ?, ?, ?, ?)", alias, title, "", CollUnlisted, userID, 0)
res, err := db.Exec("INSERT INTO collections (alias, title, description, privacy, owner_id, view_count) VALUES (?, ?, ?, ?, ?, ?)", alias, title, "", defaultVisibility(cfg), userID, 0)
if err != nil {
if db.isDuplicateKeyErr(err) {
return nil, impart.HTTPError{http.StatusConflict, "Collection already exists."}
@ -282,6 +282,7 @@ func (db *datastore) CreateCollection(alias, title string, userID int64) (*Colle
Title: title,
OwnerID: userID,
PublicOwner: false,
Public: defaultVisibility(cfg) == CollPublic,
}
c.ID, err = res.LastInsertId()
@ -1326,7 +1327,7 @@ func (db *datastore) DispersePosts(userID int64, postIDs []string) (*[]ClaimPost
return &res, nil
}
func (db *datastore) ClaimPosts(userID int64, collAlias string, posts *[]ClaimPostRequest) (*[]ClaimPostResult, error) {
func (db *datastore) ClaimPosts(cfg *config.Config, userID int64, collAlias string, posts *[]ClaimPostRequest) (*[]ClaimPostResult, error) {
postClaimReqs := map[string]bool{}
res := []ClaimPostResult{}
postCollAlias := collAlias
@ -1383,7 +1384,7 @@ func (db *datastore) ClaimPosts(userID int64, collAlias string, posts *[]ClaimPo
// This is a new collection
// TODO: consider removing this. This seriously complicates this
// method and adds another (unnecessary?) logic path.
coll, err = db.CreateCollection(postCollAlias, "", userID)
coll, err = db.CreateCollection(cfg, postCollAlias, "", userID)
if err != nil {
if err, ok := err.(impart.HTTPError); ok {
r.Code = err.Status

4
pad.go
View File

@ -60,6 +60,10 @@ func handleViewPad(app *App, w http.ResponseWriter, r *http.Request) error {
if action == "" && slug == "" {
// Not editing any post; simply render the Pad
if templates[padTmpl] == nil {
log.Info("No template '%s' found. Falling back to default 'pad' template.", padTmpl)
padTmpl = "pad"
}
if err = templates[padTmpl].ExecuteTemplate(w, "pad", appData); err != nil {
log.Error("Unable to execute template: %v", err)
}

View File

@ -65,9 +65,9 @@ func defaultPrivacyTitle() sql.NullString {
func defaultAboutPage(cfg *config.Config) string {
if cfg.App.Federation {
return `_` + cfg.App.SiteName + `_ is an interconnected place for you to write and publish, powered by WriteFreely and ActivityPub.`
return `_` + cfg.App.SiteName + `_ is an interconnected place for you to write and publish, powered by [WriteFreely](https://writefreely.org) and ActivityPub.`
}
return `_` + cfg.App.SiteName + `_ is a place for you to write and publish, powered by WriteFreely.`
return `_` + cfg.App.SiteName + `_ is a place for you to write and publish, powered by [WriteFreely](https://writefreely.org).`
}
func defaultPrivacyPolicy(cfg *config.Config) string {

View File

@ -12,6 +12,7 @@
<p><em>{{.SiteName}}</em> is home to <strong>{{largeNumFmt .AboutStats.NumPosts}}</strong> {{pluralize "article" "articles" .AboutStats.NumPosts}} across <strong>{{largeNumFmt .AboutStats.NumBlogs}}</strong> {{pluralize "blog" "blogs" .AboutStats.NumBlogs}}.</p>
{{end}}
{{if not .WFModesty}}
<h2 style="margin-top:2em">About WriteFreely</h2>
<p><a href="https://writefreely.org">WriteFreely</a> is a self-hosted, decentralized blogging platform for publishing beautiful, simple blogs.</p>
<p>It lets you publish a single blog, or host a community of writers who can create multiple blogs under one account. You can also enable federation, which allows people in the fediverse to follow your blog, bookmark your posts, and share them with others.</p>
@ -23,5 +24,6 @@
<p><a href="https://writefreely.org">WriteFreely</a></p>
</div>
</div>
{{end}}
</div>
{{end}}

View File

@ -870,7 +870,7 @@ func addPost(app *App, w http.ResponseWriter, r *http.Request) error {
collAlias := vars["alias"]
// Update all given posts
res, err := app.db.ClaimPosts(ownerID, collAlias, claims)
res, err := app.db.ClaimPosts(app.cfg, ownerID, collAlias, claims)
if err != nil {
return err
}

View File

@ -141,7 +141,7 @@ function pinPost(e, postID, slug, title) {
var $header = document.getElementsByTagName('header')[0];
var $pinnedNavs = $header.getElementsByTagName('nav');
// Add link to nav
var link = '<a class="pinned" href="/{{.Alias}}/'+slug+'">'+title+'</a>';
var link = '<a class="pinned" href="{{if not .SingleUser}}/{{.Alias}}/{{end}}'+slug+'">'+title+'</a>';
if ($pinnedNavs.length == 0) {
$header.insertAdjacentHTML("beforeend", '<nav>'+link+'</nav>');
} else {

View File

@ -177,7 +177,7 @@ function pinPost(e, postID, slug, title) {
var $header = document.getElementsByTagName('header')[0];
var $pinnedNavs = $header.getElementsByTagName('nav');
// Add link to nav
var link = '<a class="pinned" href="/{{.Alias}}/'+slug+'">'+title+'</a>';
var link = '<a class="pinned" href="{{if not .SingleUser}}/{{.Alias}}/{{end}}'+slug+'">'+title+'</a>';
if ($pinnedNavs.length == 0) {
$header.insertAdjacentHTML("beforeend", '<nav>'+link+'</nav>');
} else {

View File

@ -1,13 +1,21 @@
{{define "footer"}}
<footer{{if not .SingleUser}} class="contain-me"{{end}}>
<footer{{if not (or .SingleUser .WFModesty)}} class="contain-me"{{end}}>
<hr />
{{if .SingleUser}}
{{if or .SingleUser .WFModesty}}
<nav>
<a class="home" href="/">{{.SiteName}}</a>
<a href="https://writefreely.org/guide/{{.OfficialVersion}}" target="guide">writer's guide</a>
<a href="https://developers.write.as/" title="Build on WriteFreely with our open developer API.">developers</a>
<a href="https://github.com/writeas/writefreely">source code</a>
<a href="https://writefreely.org">writefreely {{.Version}}</a>
{{if not .SingleUser}}
<a href="/about">about</a>
{{if .LocalTimeline}}<a href="/read">reader</a>{{end}}
{{if .Username}}<a href="https://writefreely.org/guide/{{.OfficialVersion}}" target="guide">writer's guide</a>{{end}}
<a href="/privacy">privacy</a>
<p style="font-size: 0.9em">powered by <a href="https://writefreely.org">writefreely</a></p>
{{else}}
<a href="https://writefreely.org/guide/{{.OfficialVersion}}" target="guide">writer's guide</a>
<a href="https://developers.write.as/" title="Build on WriteFreely with our open developer API.">developers</a>
<a href="https://github.com/writeas/writefreely">source code</a>
<a href="https://writefreely.org">writefreely {{.Version}}</a>
{{end}}
</nav>
{{else}}
<div class="marketing-section">

View File

@ -95,6 +95,14 @@ p.docs {
<option value="admin" {{if eq .Config.UserInvites "admin"}}selected="selected"{{end}}>Admins</option>
</select>
</dd>
<dt{{if .Config.SingleUser}} class="invisible"{{end}}><label for="default_visibility">Default blog visibility</label></dt>
<dd{{if .Config.SingleUser}} class="invisible"{{end}}>
<select name="default_visibility" id="default_visibility">
<option value="unlisted" {{if eq .Config.DefaultVisibility "unlisted"}}selected="selected"{{end}}>Unlisted</option>
<option value="public" {{if eq .Config.DefaultVisibility "public"}}selected="selected"{{end}}>Public</option>
<option value="private" {{if eq .Config.DefaultVisibility "private"}}selected="selected"{{end}}>Private</option>
</select>
</dd>
</dl>
<input type="submit" value="Save Configuration" />
</div>

View File

@ -34,8 +34,8 @@
{{if .Summary}}<p>{{.Summary}}</p>{{end}}
</div>{{end}}
</div>{{ else }}<div id="no-posts-published"><p>You haven't saved any drafts yet.</p>
<p>They'll show up here once you do. Find your blog posts from the <a href="/me/c/">Blogs</a> page.</p>
<p class="text-cta"><a href="/">Start writing</a></p></div>{{ end }}
<p>They'll show up here once you do. {{if not .SingleUser}}Find your blog posts from the <a href="/me/c/">Blogs</a> page.{{end}}</p>
<p class="text-cta"><a href="{{if .SingleUser}}/me/new{{else}}/{{end}}">Start writing</a></p></div>{{ end }}
<div id="moving"></div>

View File

@ -58,6 +58,7 @@
</label>
<p>A password is required to read this blog.</p>
</li>
{{if not .SingleUser}}
<li>
<label class="option-text{{if not .LocalTimeline}} disabled{{end}}"><input type="radio" name="visibility" id="visibility-public" value="1" {{if .IsPublic}}checked="checked"{{end}} {{if not .LocalTimeline}}disabled="disabled"{{end}} />
Public
@ -65,6 +66,7 @@
{{if .LocalTimeline}}<p>This blog is displayed on the public <a href="/read">reader</a>, and is visible to {{if .Private}}any registered user on this instance{{else}}anyone with its link{{end}}.</p>
{{else}}<p>The public reader is currently turned off for this community.</p>{{end}}
</li>
{{end}}
</ul>
</div>
</div>

View File

@ -8,11 +8,15 @@
<hr />
<nav>
<a class="home" href="/">{{.SiteName}}</a>
<a href="/about">about</a>
{{if not .SingleUser}}<a href="/about">about</a>{{end}}
{{if and (not .SingleUser) .LocalTimeline}}<a href="/read">reader</a>{{end}}
<a href="https://writefreely.org/guide/{{.OfficialVersion}}" target="guide">writer's guide</a>
<a href="/privacy">privacy</a>
{{if not .SingleUser}}<a href="/privacy">privacy</a>{{end}}
{{if .WFModesty}}
<p style="font-size: 0.9em">powered by <a href="https://writefreely.org">writefreely</a></p>
{{else}}
<a href="https://writefreely.org">writefreely {{.Version}}</a>
{{end}}
</nav>
</footer>