mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-18 09:07:28 -06:00
[feature] add support for clients editing statuses and fetching status revision history (#3628)
* start adding client support for making status edits and viewing history * modify 'freshest' freshness window to be 5s, add typeutils test for status -> api edits * only populate the status edits when specifically requested * start adding some simple processor status edit tests * add test editing status but adding a poll * test edits appropriately adding poll expiry handlers * finish adding status edit tests * store both new and old revision emojis in status * add code comment * ensure the requester's account is populated before status edits * add code comments for status edit tests * update status edit form swagger comments * remove unused function * fix status source test * add more code comments, move media description check back to media process in status create * fix tests, add necessary form struct tag
This commit is contained in:
parent
1aa7f70660
commit
fe8d5f2307
29 changed files with 2546 additions and 523 deletions
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"codeberg.org/gruf/go-iotools"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
|
|
@ -45,10 +46,21 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
|
|||
}
|
||||
|
||||
// Parse focus details from API form input.
|
||||
focusX, focusY, err := parseFocus(form.Focus)
|
||||
if err != nil {
|
||||
text := fmt.Sprintf("could not parse focus value %s: %s", form.Focus, err)
|
||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||
focusX, focusY, errWithCode := apiutil.ParseFocus(form.Focus)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
// If description provided,
|
||||
// process and validate it.
|
||||
//
|
||||
// This may not yet be set as it
|
||||
// is often set on status post.
|
||||
if form.Description != "" {
|
||||
form.Description, errWithCode = processDescription(form.Description)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
}
|
||||
|
||||
// Open multipart file reader.
|
||||
|
|
@ -58,7 +70,7 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
|
|||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Wrap the multipart file reader to ensure is limited to max.
|
||||
// Wrap multipart file reader to ensure is limited to max size.
|
||||
rc, _, _ := iotools.UpdateReadCloserLimit(mpfile, maxszInt64)
|
||||
|
||||
// Create local media and write to instance storage.
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
|
|
@ -47,17 +49,27 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, media
|
|||
var updatingColumns []string
|
||||
|
||||
if form.Description != nil {
|
||||
attachment.Description = text.SanitizeToPlaintext(*form.Description)
|
||||
// Sanitize and validate incoming description.
|
||||
description, errWithCode := processDescription(
|
||||
*form.Description,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
attachment.Description = description
|
||||
updatingColumns = append(updatingColumns, "description")
|
||||
}
|
||||
|
||||
if form.Focus != nil {
|
||||
focusx, focusy, err := parseFocus(*form.Focus)
|
||||
if err != nil {
|
||||
return nil, gtserror.NewErrorBadRequest(err)
|
||||
// Parse focus details from API form input.
|
||||
focusX, focusY, errWithCode := apiutil.ParseFocus(*form.Focus)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
attachment.FileMeta.Focus.X = focusx
|
||||
attachment.FileMeta.Focus.Y = focusy
|
||||
|
||||
attachment.FileMeta.Focus.X = focusX
|
||||
attachment.FileMeta.Focus.Y = focusY
|
||||
updatingColumns = append(updatingColumns, "focus_x", "focus_y")
|
||||
}
|
||||
|
||||
|
|
@ -72,3 +84,21 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, media
|
|||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
// processDescription will sanitize and valid description against server configuration.
|
||||
func processDescription(description string) (string, gtserror.WithCode) {
|
||||
description = text.SanitizeToPlaintext(description)
|
||||
chars := len([]rune(description))
|
||||
|
||||
if min := config.GetMediaDescriptionMinChars(); chars < min {
|
||||
text := fmt.Sprintf("media description less than min chars (%d)", min)
|
||||
return "", gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||
}
|
||||
|
||||
if max := config.GetMediaDescriptionMaxChars(); chars > max {
|
||||
text := fmt.Sprintf("media description exceeds max chars (%d)", max)
|
||||
return "", gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||
}
|
||||
|
||||
return description, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package media
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseFocus(focus string) (focusx, focusy float32, err error) {
|
||||
if focus == "" {
|
||||
return
|
||||
}
|
||||
spl := strings.Split(focus, ",")
|
||||
if len(spl) != 2 {
|
||||
err = fmt.Errorf("improperly formatted focus %s", focus)
|
||||
return
|
||||
}
|
||||
xStr := spl[0]
|
||||
yStr := spl[1]
|
||||
if xStr == "" || yStr == "" {
|
||||
err = fmt.Errorf("improperly formatted focus %s", focus)
|
||||
return
|
||||
}
|
||||
fx, err := strconv.ParseFloat(xStr, 32)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("improperly formatted focus %s: %s", focus, err)
|
||||
return
|
||||
}
|
||||
if fx > 1 || fx < -1 {
|
||||
err = fmt.Errorf("improperly formatted focus %s", focus)
|
||||
return
|
||||
}
|
||||
focusx = float32(fx)
|
||||
fy, err := strconv.ParseFloat(yStr, 32)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("improperly formatted focus %s: %s", focus, err)
|
||||
return
|
||||
}
|
||||
if fy > 1 || fy < -1 {
|
||||
err = fmt.Errorf("improperly formatted focus %s", focus)
|
||||
return
|
||||
}
|
||||
focusy = float32(fy)
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue