mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:32:25 -05:00 
			
		
		
		
	fixin' bugs and takin' names
This commit is contained in:
		
					parent
					
						
							
								9eb8878e94
							
						
					
				
			
			
				commit
				
					
						1bf56e0a52
					
				
			
		
					 3 changed files with 138 additions and 60 deletions
				
			
		|  | @ -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.Federated != nil { | ||||
| 			gtsAdvancedVis.Federated = *form.Federated | ||||
| 		} | ||||
| 
 | ||||
| 			if form.AdvancedVisibility.Boostable != nil { | ||||
| 				gtsAdvancedVis.Boostable = *form.AdvancedVisibility.Boostable | ||||
| 		if form.Boostable != nil { | ||||
| 			gtsAdvancedVis.Boostable = *form.Boostable | ||||
| 		} | ||||
| 
 | ||||
| 			if form.AdvancedVisibility.Replyable != nil { | ||||
| 				gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable | ||||
| 		if form.Replyable != nil { | ||||
| 			gtsAdvancedVis.Replyable = *form.Replyable | ||||
| 		} | ||||
| 
 | ||||
| 			if form.AdvancedVisibility.Likeable != nil { | ||||
| 				gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable | ||||
| 			} | ||||
| 		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.Federated != nil { | ||||
| 			gtsAdvancedVis.Federated = *form.Federated | ||||
| 		} | ||||
| 
 | ||||
| 			if form.AdvancedVisibility.Replyable != nil { | ||||
| 				gtsAdvancedVis.Replyable = *form.AdvancedVisibility.Replyable | ||||
| 		if form.Replyable != nil { | ||||
| 			gtsAdvancedVis.Replyable = *form.Replyable | ||||
| 		} | ||||
| 
 | ||||
| 			if form.AdvancedVisibility.Likeable != nil { | ||||
| 				gtsAdvancedVis.Likeable = *form.AdvancedVisibility.Likeable | ||||
| 			} | ||||
| 		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 { | ||||
| 	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 | ||||
| } | ||||
|  |  | |||
|  | @ -168,24 +168,10 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() { | |||
| 		"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"` | ||||
| 		"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…
	
	Add table
		Add a link
		
	
		Reference in a new issue