fixin' bugs and takin' names
This commit is contained in:
parent
9eb8878e94
commit
1bf56e0a52
@ -27,6 +27,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/distributor"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
@ -36,12 +37,12 @@ import (
|
||||
|
||||
type advancedStatusCreateForm struct {
|
||||
mastotypes.StatusCreateRequest
|
||||
AdvancedVisibility *advancedVisibilityFlagsForm `form:"visibility_advanced"`
|
||||
advancedVisibilityFlagsForm
|
||||
}
|
||||
|
||||
type advancedVisibilityFlagsForm struct {
|
||||
// The gotosocial visibility model
|
||||
Visibility *model.Visibility
|
||||
VisibilityAdvanced *model.Visibility `form:"visibility_advanced"`
|
||||
// This status will be federated beyond the local timeline(s)
|
||||
Federated *bool `form:"federated"`
|
||||
// This status can be boosted/reblogged
|
||||
@ -70,7 +71,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
// extract the status create form from the request context
|
||||
l.Trace("parsing request form")
|
||||
l.Tracef("parsing request form: %s", c.Request.Form)
|
||||
form := &advancedStatusCreateForm{}
|
||||
if err := c.ShouldBind(form); err != nil || form == nil {
|
||||
l.Debugf("could not parse form from request: %s", err)
|
||||
@ -128,6 +129,12 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// handle language settings
|
||||
if err := parseLanguage(form, authed.Account.Language, newStatus); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// convert mentions to *model.Mention
|
||||
menchies, err := m.db.MentionStringsToMentions(util.DeriveMentions(form.Status), authed.Account.ID, thisStatusID)
|
||||
if err != nil {
|
||||
@ -186,8 +193,9 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
|
||||
URI: newStatus.URI,
|
||||
URL: newStatus.URL,
|
||||
Content: newStatus.Content,
|
||||
Application: authed.Application.ToMasto(),
|
||||
Application: authed.Application.ToMastoPublic(),
|
||||
Account: mastoAccount,
|
||||
// MediaAttachments: ,
|
||||
Text: form.Status,
|
||||
}
|
||||
c.JSON(http.StatusOK, mastoStatus)
|
||||
@ -260,8 +268,8 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
|
||||
// Advanced takes priority if it's set.
|
||||
// If it's not set, take whatever masto visibility is set.
|
||||
// If *that's* not set either, then just take the account default.
|
||||
if form.AdvancedVisibility != nil && form.AdvancedVisibility.Visibility != nil {
|
||||
gtsBasicVis = *form.AdvancedVisibility.Visibility
|
||||
if form.VisibilityAdvanced != nil {
|
||||
gtsBasicVis = *form.VisibilityAdvanced
|
||||
} else if form.Visibility != "" {
|
||||
gtsBasicVis = util.ParseGTSVisFromMastoVis(form.Visibility)
|
||||
} else {
|
||||
@ -274,40 +282,38 @@ func parseVisibility(form *advancedStatusCreateForm, accountDefaultVis model.Vis
|
||||
break
|
||||
case model.VisibilityUnlocked:
|
||||
// for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them
|
||||
if form.AdvancedVisibility != nil {
|
||||
if form.AdvancedVisibility.Federated != nil {
|
||||
gtsAdvancedVis.Federated = *form.AdvancedVisibility.Federated
|
||||
}
|
||||
|
||||
if form.AdvancedVisibility.Boostable != nil {
|
||||
gtsAdvancedVis.Boostable = *form.AdvancedVisibility.Boostable
|
||||
}
|
||||
|
||||
if form.AdvancedVisibility.Replyable != nil {
|
||||
gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable
|
||||
}
|
||||
|
||||
if form.AdvancedVisibility.Likeable != nil {
|
||||
gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable
|
||||
}
|
||||
if form.Federated != nil {
|
||||
gtsAdvancedVis.Federated = *form.Federated
|
||||
}
|
||||
|
||||
if form.Boostable != nil {
|
||||
gtsAdvancedVis.Boostable = *form.Boostable
|
||||
}
|
||||
|
||||
if form.Replyable != nil {
|
||||
gtsAdvancedVis.Replyable = *form.Replyable
|
||||
}
|
||||
|
||||
if form.Likeable != nil {
|
||||
gtsAdvancedVis.Likeable = *form.Likeable
|
||||
}
|
||||
|
||||
case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly:
|
||||
// for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them
|
||||
gtsAdvancedVis.Boostable = false
|
||||
|
||||
if form.AdvancedVisibility != nil {
|
||||
if form.AdvancedVisibility.Federated != nil {
|
||||
gtsAdvancedVis.Federated = *form.AdvancedVisibility.Federated
|
||||
}
|
||||
|
||||
if form.AdvancedVisibility.Replyable != nil {
|
||||
gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable
|
||||
}
|
||||
|
||||
if form.AdvancedVisibility.Likeable != nil {
|
||||
gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable
|
||||
}
|
||||
if form.Federated != nil {
|
||||
gtsAdvancedVis.Federated = *form.Federated
|
||||
}
|
||||
|
||||
if form.Replyable != nil {
|
||||
gtsAdvancedVis.Replyable = *form.Replyable
|
||||
}
|
||||
|
||||
if form.Likeable != nil {
|
||||
gtsAdvancedVis.Likeable = *form.Likeable
|
||||
}
|
||||
|
||||
case model.VisibilityDirect:
|
||||
// direct is pretty easy: there's only one possible setting so return it
|
||||
gtsAdvancedVis.Federated = true
|
||||
@ -336,9 +342,18 @@ func (m *statusModule) parseReplyToID(form *advancedStatusCreateForm, thisAccoun
|
||||
repliedStatus := &model.Status{}
|
||||
repliedAccount := &model.Account{}
|
||||
// check replied status exists + is replyable
|
||||
if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil || !repliedStatus.VisibilityAdvanced.Replyable {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
if err := m.db.GetByID(form.InReplyToID, repliedStatus); err != nil {
|
||||
if _, ok := err.(db.ErrNoEntries); ok {
|
||||
return fmt.Errorf("status with id %s not replyable because it doesn't exist", form.InReplyToID)
|
||||
} else {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !repliedStatus.VisibilityAdvanced.Replyable {
|
||||
return fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID)
|
||||
}
|
||||
|
||||
// check replied account is known to us
|
||||
if err := m.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil {
|
||||
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||
@ -373,3 +388,15 @@ func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccount
|
||||
status.Attachments = attachments
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseLanguage(form *advancedStatusCreateForm, accountDefaultLanguage string, status *model.Status) error {
|
||||
if form.Language != "" {
|
||||
status.Language = form.Language
|
||||
} else {
|
||||
status.Language = accountDefaultLanguage
|
||||
}
|
||||
if status.Language == "" {
|
||||
return errors.New("no language given either in status create form or account default")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -165,27 +165,13 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"this is a brand new status!"},
|
||||
"spoiler_text": {"hello hello"},
|
||||
"sensitive": {"true"},
|
||||
"visibility": {"public"},
|
||||
// Status string `form:"status"`
|
||||
// // Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used.
|
||||
// MediaIDs []string `form:"media_ids"`
|
||||
// // Poll to include with this status.
|
||||
// Poll *PollRequest `form:"poll"`
|
||||
// // ID of the status being replied to, if status is a reply
|
||||
// InReplyToID string `form:"in_reply_to_id"`
|
||||
// // Mark status and attached media as sensitive?
|
||||
// Sensitive bool `form:"sensitive"`
|
||||
// // Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.
|
||||
// SpoilerText string `form:"spoiler_text"`
|
||||
// // Visibility of the posted status. Enumerable oneOf public, unlisted, private, direct.
|
||||
// Visibility Visibility `form:"visibility"`
|
||||
// // ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future.
|
||||
// ScheduledAt string `form:"scheduled_at"`
|
||||
// // ISO 639 language code for this status.
|
||||
// Language string `form:"language"`
|
||||
"status": {"this is a brand new status!"},
|
||||
"spoiler_text": {"hello hello"},
|
||||
"sensitive": {"true"},
|
||||
"visibility_advanced": {"mutuals_only"},
|
||||
"likeable": {"false"},
|
||||
"replyable": {"false"},
|
||||
"federated": {"false"},
|
||||
}
|
||||
suite.statusModule.statusCreatePOSTHandler(ctx)
|
||||
|
||||
@ -198,6 +184,7 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
fmt.Println(string(b))
|
||||
|
||||
statusReply := &mastotypes.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
@ -206,7 +193,47 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
|
||||
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content)
|
||||
assert.True(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastotypes.VisibilityPublic, statusReply.Visibility)
|
||||
assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
|
||||
}
|
||||
|
||||
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() {
|
||||
t := suite.testTokens["local_account_1"]
|
||||
oauthToken := oauth.PGTokenToOauthToken(t)
|
||||
|
||||
// setup
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"this is a reply to a status that doesn't exist"},
|
||||
"spoiler_text": {"don't open cuz it won't work"},
|
||||
"in_reply_to_id": {"3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50"},
|
||||
}
|
||||
suite.statusModule.statusCreatePOSTHandler(ctx)
|
||||
|
||||
// check response
|
||||
|
||||
// 1. we should have OK from our call to the function
|
||||
suite.EqualValues(http.StatusOK, recorder.Code)
|
||||
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
b, err := ioutil.ReadAll(result.Body)
|
||||
assert.NoError(suite.T(), err)
|
||||
fmt.Println(string(b))
|
||||
|
||||
statusReply := &mastotypes.Status{}
|
||||
err = json.Unmarshal(b, statusReply)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), "hello hello", statusReply.SpoilerText)
|
||||
assert.Equal(suite.T(), "this is a brand new status!", statusReply.Content)
|
||||
assert.True(suite.T(), statusReply.Sensitive)
|
||||
assert.Equal(suite.T(), mastotypes.VisibilityPrivate, statusReply.Visibility)
|
||||
}
|
||||
|
||||
func TestStatusCreateTestSuite(t *testing.T) {
|
||||
|
@ -63,12 +63,36 @@ func GenerateURIs(username string, protocol string, host string) *URIs {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseGTSVisFromMastoVis converts a mastodon visibility into its gts equivalent.
|
||||
func ParseGTSVisFromMastoVis(m mastotypes.Visibility) model.Visibility {
|
||||
// TODO: convert a masto vis into a gts vis
|
||||
switch m {
|
||||
case mastotypes.VisibilityPublic:
|
||||
return model.VisibilityPublic
|
||||
case mastotypes.VisibilityUnlisted:
|
||||
return model.VisibilityUnlocked
|
||||
case mastotypes.VisibilityPrivate:
|
||||
return model.VisibilityFollowersOnly
|
||||
case mastotypes.VisibilityDirect:
|
||||
return model.VisibilityDirect
|
||||
default:
|
||||
break
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ParseMastoVisFromGTSVis converts a gts visibility into its mastodon equivalent
|
||||
func ParseMastoVisFromGTSVis(m model.Visibility) mastotypes.Visibility {
|
||||
// TODO: convert a gts vis into a masto vis
|
||||
switch m {
|
||||
case model.VisibilityPublic:
|
||||
return mastotypes.VisibilityPublic
|
||||
case model.VisibilityUnlocked:
|
||||
return mastotypes.VisibilityUnlisted
|
||||
case model.VisibilityFollowersOnly, model.VisibilityMutualsOnly:
|
||||
return mastotypes.VisibilityPrivate
|
||||
case model.VisibilityDirect:
|
||||
return mastotypes.VisibilityDirect
|
||||
default:
|
||||
break
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user