mediaupdate.go (5144B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package media 19 20 import ( 21 "errors" 22 "fmt" 23 "net/http" 24 25 "github.com/gin-gonic/gin" 26 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 27 apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" 28 "github.com/superseriousbusiness/gotosocial/internal/config" 29 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 30 "github.com/superseriousbusiness/gotosocial/internal/oauth" 31 ) 32 33 // MediaPUTHandler swagger:operation PUT /api/v1/media/{id} mediaUpdate 34 // 35 // Update a media attachment. 36 // 37 // You must own the media attachment, and the attachment must not yet be attached to a status. 38 // 39 // The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'. 40 // The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'. 41 // 42 // --- 43 // tags: 44 // - media 45 // 46 // consumes: 47 // - application/json 48 // - application/xml 49 // - application/x-www-form-urlencoded 50 // 51 // produces: 52 // - application/json 53 // 54 // parameters: 55 // - 56 // name: id 57 // description: id of the attachment to update 58 // type: string 59 // in: path 60 // required: true 61 // - 62 // name: description 63 // in: formData 64 // description: >- 65 // Image or media description to use as alt-text on the attachment. 66 // This is very useful for users of screenreaders! 67 // May or may not be required, depending on your instance settings. 68 // type: string 69 // allowEmptyValue: true 70 // - 71 // name: focus 72 // in: formData 73 // description: >- 74 // Focus of the media file. 75 // If present, it should be in the form of two comma-separated floats between -1 and 1. 76 // For example: `-0.5,0.25`. 77 // type: string 78 // allowEmptyValue: true 79 // default: "0,0" 80 // 81 // security: 82 // - OAuth2 Bearer: 83 // - write:media 84 // 85 // responses: 86 // '200': 87 // description: The newly-updated media attachment. 88 // schema: 89 // "$ref": "#/definitions/attachment" 90 // '400': 91 // description: bad request 92 // '401': 93 // description: unauthorized 94 // '404': 95 // description: not found 96 // '406': 97 // description: not acceptable 98 // '500': 99 // description: internal server error 100 func (m *Module) MediaPUTHandler(c *gin.Context) { 101 if apiVersion := c.Param(APIVersionKey); apiVersion != APIv1 { 102 err := errors.New("api version must be one v1 for this path") 103 apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGetV1) 104 return 105 } 106 107 authed, err := oauth.Authed(c, true, true, true, true) 108 if err != nil { 109 apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1) 110 return 111 } 112 113 if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil { 114 apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1) 115 return 116 } 117 118 attachmentID := c.Param(IDKey) 119 if attachmentID == "" { 120 err := errors.New("no attachment id specified") 121 apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) 122 return 123 } 124 125 form := &apimodel.AttachmentUpdateRequest{} 126 if err := c.ShouldBind(form); err != nil { 127 apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) 128 return 129 } 130 131 if err := validateUpdateMedia(form); err != nil { 132 apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1) 133 return 134 } 135 136 attachment, errWithCode := m.processor.Media().Update(c.Request.Context(), authed.Account, attachmentID, form) 137 if errWithCode != nil { 138 apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1) 139 return 140 } 141 142 c.JSON(http.StatusOK, attachment) 143 } 144 145 func validateUpdateMedia(form *apimodel.AttachmentUpdateRequest) error { 146 minDescriptionChars := config.GetMediaDescriptionMinChars() 147 maxDescriptionChars := config.GetMediaDescriptionMaxChars() 148 149 if form.Description != nil { 150 if length := len([]rune(*form.Description)); length < minDescriptionChars || length > maxDescriptionChars { 151 return fmt.Errorf("image description length must be between %d and %d characters (inclusive), but provided image description was %d chars", minDescriptionChars, maxDescriptionChars, length) 152 } 153 } 154 155 if form.Focus == nil && form.Description == nil { 156 return errors.New("focus and description were both nil, there's nothing to update") 157 } 158 159 return nil 160 }