Skip to content

Commit

Permalink
Merge branch 'develop' into T670-local-time
Browse files Browse the repository at this point in the history
  • Loading branch information
thebaer committed Jan 29, 2020
2 parents 513765c + 5ddd73e commit 3e90246
Show file tree
Hide file tree
Showing 78 changed files with 3,503 additions and 194 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
@@ -1,7 +1,7 @@
language: go

go:
- "1.11.x"
- "1.13.x"

env:
- GO111MODULE=on
Expand Down
10 changes: 10 additions & 0 deletions Makefile
Expand Up @@ -47,6 +47,12 @@ build-arm7: deps
fi
xgo --targets=linux/arm-7, -dest build/ $(LDFLAGS) -tags='sqlite' -out writefreely ./cmd/writefreely

build-arm64: deps
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GOGET) -u github.com/karalabe/xgo; \
fi
xgo --targets=linux/arm64, -dest build/ $(LDFLAGS) -tags='sqlite' -out writefreely ./cmd/writefreely

build-docker :
$(DOCKERCMD) build -t $(IMAGE_NAME):latest -t $(IMAGE_NAME):$(GITREV) .

Expand Down Expand Up @@ -83,6 +89,10 @@ release : clean ui assets
mv build/$(BINARY_NAME)-linux-arm-7 $(BUILDPATH)/$(BINARY_NAME)
tar -cvzf $(BINARY_NAME)_$(GITREV)_linux_arm7.tar.gz -C build $(BINARY_NAME)
rm $(BUILDPATH)/$(BINARY_NAME)
$(MAKE) build-arm64
mv build/$(BINARY_NAME)-linux-arm64 $(BUILDPATH)/$(BINARY_NAME)
tar -cvzf $(BINARY_NAME)_$(GITREV)_linux_arm64.tar.gz -C build $(BINARY_NAME)
rm $(BUILDPATH)/$(BINARY_NAME)
$(MAKE) build-darwin
mv build/$(BINARY_NAME)-darwin-10.6-amd64 $(BUILDPATH)/$(BINARY_NAME)
tar -cvzf $(BINARY_NAME)_$(GITREV)_macos_amd64.tar.gz -C build $(BINARY_NAME)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Expand Up @@ -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

Expand Down
88 changes: 63 additions & 25 deletions account.go
Expand Up @@ -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
Expand Down Expand Up @@ -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 == "" {
Expand Down Expand Up @@ -156,17 +156,9 @@ func signupWithRegistration(app *App, signup userRegistration, w http.ResponseWr
Username: signup.Alias,
HashedPass: hashedPass,
HasPass: createdWithPass,
Email: zero.NewString("", signup.Email != ""),
Email: prepareUserEmail(signup.Email, app.keys.EmailKey),
Created: time.Now().Truncate(time.Second).UTC(),
}
if signup.Email != "" {
encEmail, err := data.Encrypt(app.keys.EmailKey, signup.Email)
if err != nil {
log.Error("Unable to encrypt email: %s\n", err)
} else {
u.Email.String = string(encEmail)
}
}

// Create actual user
if err := app.db.CreateUser(app.cfg, u, desiredUsername); err != nil {
Expand Down Expand Up @@ -314,12 +306,16 @@ func viewLogin(app *App, w http.ResponseWriter, r *http.Request) error {
Message template.HTML
Flashes []template.HTML
LoginUsername string
OauthSlack bool
OauthWriteAs bool
}{
pageForReq(app, r),
r.FormValue("to"),
template.HTML(""),
[]template.HTML{},
getTempInfo(app, "login-user", r, w),
app.Config().SlackOauth.ClientID != "",
app.Config().WriteAsOauth.ClientID != "",
}

if earlyError != "" {
Expand Down Expand Up @@ -377,7 +373,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 != "")

Expand Down Expand Up @@ -580,7 +576,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")
Expand Down Expand Up @@ -625,7 +621,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") {
Expand Down Expand Up @@ -662,7 +658,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"`
Expand All @@ -686,7 +682,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
}
Expand Down Expand Up @@ -717,7 +713,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
}
Expand Down Expand Up @@ -750,14 +746,20 @@ func viewArticles(app *App, u *User, w http.ResponseWriter, r *http.Request) err
log.Error("unable to fetch collections: %v", err)
}

suspended, err := app.db.IsUserSuspended(u.ID)
if err != nil {
log.Error("view articles: %v", err)
}
d := struct {
*UserPage
AnonymousPosts *[]PublicPost
Collections *[]Collection
Suspended bool
}{
UserPage: NewUserPage(app, r, u, u.Username+"'s Posts", f),
AnonymousPosts: p,
Collections: c,
Suspended: suspended,
}
d.UserPage.SetMessaging(u)
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
Expand All @@ -779,18 +781,25 @@ func viewCollections(app *App, u *User, w http.ResponseWriter, r *http.Request)
uc, _ := app.db.GetUserCollectionCount(u.ID)
// TODO: handle any errors

suspended, err := app.db.IsUserSuspended(u.ID)
if err != nil {
log.Error("view collections %v", err)
return fmt.Errorf("view collections: %v", err)
}
d := struct {
*UserPage
Collections *[]Collection

UsedCollections, TotalCollections int

NewBlogsDisabled bool
Suspended bool
}{
UserPage: NewUserPage(app, r, u, u.Username+"'s Blogs", f),
Collections: c,
UsedCollections: int(uc),
NewBlogsDisabled: !app.cfg.App.CanCreateBlogs(uc),
Suspended: suspended,
}
d.UserPage.SetMessaging(u)
showUserPage(w, "collections", d)
Expand All @@ -808,21 +817,28 @@ func viewEditCollection(app *App, u *User, w http.ResponseWriter, r *http.Reques
return ErrCollectionNotFound
}

suspended, err := app.db.IsUserSuspended(u.ID)
if err != nil {
log.Error("view edit collection %v", err)
return fmt.Errorf("view edit collection: %v", err)
}
flashes, _ := getSessionFlashes(app, w, r, nil)
obj := struct {
*UserPage
*Collection
Suspended bool
}{
UserPage: NewUserPage(app, r, u, "Edit "+c.DisplayTitle(), flashes),
Collection: c,
Suspended: suspended,
}

showUserPage(w, "collection", obj)
return nil
}

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
Expand Down Expand Up @@ -976,17 +992,24 @@ func viewStats(app *App, u *User, w http.ResponseWriter, r *http.Request) error
titleStats = c.DisplayTitle() + " "
}

suspended, err := app.db.IsUserSuspended(u.ID)
if err != nil {
log.Error("view stats: %v", err)
return err
}
obj := struct {
*UserPage
VisitsBlog string
Collection *Collection
TopPosts *[]PublicPost
APFollowers int
Suspended bool
}{
UserPage: NewUserPage(app, r, u, titleStats+"Stats", flashes),
VisitsBlog: alias,
Collection: c,
TopPosts: topPosts,
Suspended: suspended,
}
if app.cfg.App.Federation {
folls, err := app.db.GetAPFollowers(c)
Expand Down Expand Up @@ -1017,14 +1040,16 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err

obj := struct {
*UserPage
Email string
HasPass bool
IsLogOut bool
Email string
HasPass bool
IsLogOut bool
Suspended bool
}{
UserPage: NewUserPage(app, r, u, "Account Settings", flashes),
Email: fullUser.EmailClear(app.keys),
HasPass: passIsSet,
IsLogOut: r.FormValue("logout") == "1",
UserPage: NewUserPage(app, r, u, "Account Settings", flashes),
Email: fullUser.EmailClear(app.keys),
HasPass: passIsSet,
IsLogOut: r.FormValue("logout") == "1",
Suspended: fullUser.IsSilenced(),
}

showUserPage(w, "settings", obj)
Expand Down Expand Up @@ -1068,3 +1093,16 @@ func getTempInfo(app *App, key string, r *http.Request, w http.ResponseWriter) s
// Return value
return s
}

func prepareUserEmail(input string, emailKey []byte) zero.String {
email := zero.NewString("", input != "")
if len(input) > 0 {
encEmail, err := data.Encrypt(emailKey, input)
if err != nil {
log.Error("Unable to encrypt email: %s\n", err)
} else {
email.String = string(encEmail)
}
}
return email
}

0 comments on commit 3e90246

Please sign in to comment.