mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-07-22 07:00:04 +02:00
Compare commits
21 commits
d0e4cdc193
...
ae752e5446
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ae752e5446 | ||
![]() |
8fc7295869 | ||
![]() |
6ee4dd753a | ||
![]() |
9b470d2709 | ||
![]() |
95e8bbd5f0 | ||
![]() |
b83a554921 | ||
![]() |
4c34615e4e | ||
![]() |
a16204d99f | ||
![]() |
a5f7acdd2e | ||
![]() |
1b21719897 | ||
![]() |
8c8d646099 | ||
![]() |
b705cfcdd8 | ||
![]() |
fce430c6ba | ||
![]() |
bc4c1d64cb | ||
![]() |
fe6ba0c7c9 | ||
![]() |
f27b46436c | ||
![]() |
eaea89b7f0 | ||
![]() |
88c3b6dead | ||
![]() |
6e9b97e377 | ||
![]() |
c9949fbc64 | ||
![]() |
27e853454d |
36 changed files with 429 additions and 213 deletions
|
@ -28,7 +28,7 @@ jobs:
|
|||
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
- id: forgejo
|
||||
uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4
|
||||
uses: https://data.forgejo.org/actions/setup-forgejo@v3.0.1
|
||||
with:
|
||||
user: root
|
||||
password: admin1234
|
||||
|
|
|
@ -201,7 +201,7 @@ jobs:
|
|||
|
||||
- name: end-to-end tests
|
||||
if: ${{ secrets.TOKEN != '' && vars.ROLE == 'forgejo-integration' && vars.SKIP_END_TO_END != 'true' }}
|
||||
uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0
|
||||
uses: https://data.forgejo.org/actions/cascading-pr@v2.2.1
|
||||
with:
|
||||
origin-url: ${{ env.GITHUB_SERVER_URL }}
|
||||
origin-repo: ${{ github.repository }}
|
||||
|
|
|
@ -41,7 +41,7 @@ jobs:
|
|||
with:
|
||||
fetch-depth: '0'
|
||||
show-progress: 'false'
|
||||
- uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0
|
||||
- uses: https://data.forgejo.org/actions/cascading-pr@v2.2.1
|
||||
with:
|
||||
origin-url: ${{ env.GITHUB_SERVER_URL }}
|
||||
origin-repo: ${{ github.repository }}
|
||||
|
|
|
@ -5,32 +5,34 @@ on:
|
|||
- cron: '@daily'
|
||||
|
||||
env:
|
||||
RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
RNA_WORKDIR: /srv/rna
|
||||
RNA_VERSION: v1.3.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
|
||||
jobs:
|
||||
release-notes:
|
||||
if: vars.ROLE == 'forgejo-coding'
|
||||
runs-on: docker
|
||||
container:
|
||||
image: 'data.forgejo.org/oci/node:22-bookworm'
|
||||
image: 'data.forgejo.org/oci/ci:1'
|
||||
steps:
|
||||
- uses: https://data.forgejo.org/actions/checkout@v4
|
||||
|
||||
- uses: https://data.forgejo.org/actions/setup-go@v5
|
||||
- uses: https://data.forgejo.org/actions/cache@v4
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: false
|
||||
key: rna-${{ env.RNA_VERSION }}
|
||||
path: ${{ env.RNA_WORKDIR }}
|
||||
|
||||
- name: apt install jq
|
||||
- name: install release-notes-assistant
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get -q install -y -qq jq
|
||||
set -x
|
||||
wget -O /usr/local/bin/rna https://code.forgejo.org/forgejo/release-notes-assistant/releases/download/${{ env.RNA_VERSION}}/release-notes-assistant
|
||||
chmod +x /usr/local/bin/rna
|
||||
|
||||
- name: update open milestones
|
||||
run: |
|
||||
set -x
|
||||
curl -sS $GITHUB_SERVER_URL/api/v1/repos/$GITHUB_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do
|
||||
mkdir -p ${{ env.RNA_WORKDIR }}
|
||||
curl -sS $FORGEJO_SERVER_URL/api/v1/repos/$FORGEJO_REPOSITORY/milestones?state=open | jq -r '.[] | .title' | while read forgejo version ; do
|
||||
milestone="$forgejo $version"
|
||||
go run code.forgejo.org/forgejo/release-notes-assistant@$RNA_VERSION --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $GITHUB_SERVER_URL --repository $GITHUB_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version
|
||||
rna --workdir ${{ env.RNA_WORKDIR }} --config .release-notes-assistant.yaml --storage milestone --storage-location "$milestone" --forgejo-url $FORGEJO_SERVER_URL --repository $FORGEJO_REPOSITORY --token ${{ secrets.RELEASE_NOTES_ASSISTANT_TOKEN }} release $version
|
||||
done
|
||||
|
|
|
@ -8,7 +8,7 @@ on:
|
|||
- labeled
|
||||
|
||||
env:
|
||||
RNA_VERSION: v1.2.5 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
RNA_VERSION: v1.3.1 # renovate: datasource=gitea-releases depName=forgejo/release-notes-assistant registryUrl=https://code.forgejo.org
|
||||
|
||||
jobs:
|
||||
release-notes:
|
||||
|
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
|
||||
runs-on: docker
|
||||
container:
|
||||
image: data.forgejo.org/renovate/renovate:41.32.1
|
||||
image: data.forgejo.org/renovate/renovate:41.40.0
|
||||
|
||||
steps:
|
||||
- name: Load renovate repo cache
|
||||
|
|
6
Makefile
6
Makefile
|
@ -39,15 +39,15 @@ XGO_VERSION := go-1.21.x
|
|||
AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.3.0 # renovate: datasource=go
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.1 # renovate: datasource=go
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.2.2 # renovate: datasource=go
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
|
||||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go
|
||||
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.35.0 # renovate: datasource=go
|
||||
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@41.32.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
RENOVATE_NPM_PACKAGE ?= renovate@41.40.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
|
||||
|
||||
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
|
||||
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"forgejo.org/modules/graceful"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/process"
|
||||
"forgejo.org/modules/proxy"
|
||||
"forgejo.org/modules/setting"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
|
@ -76,6 +77,12 @@ func runACME(listenAddr string, m http.Handler) error {
|
|||
ListenHost: setting.HTTPAddr,
|
||||
AltTLSALPNPort: altTLSALPNPort,
|
||||
AltHTTPPort: altHTTPPort,
|
||||
HTTPProxy: proxy.Proxy(),
|
||||
}
|
||||
|
||||
// Preserve behavior to use Let's encrypt test CA when Let's encrypt is CA.
|
||||
if certmagic.DefaultACME.CA == certmagic.LetsEncryptProductionCA {
|
||||
certmagic.DefaultACME.TestCA = certmagic.LetsEncryptStagingCA
|
||||
}
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
|
|
6
go.mod
6
go.mod
|
@ -78,7 +78,7 @@ require (
|
|||
github.com/minio/minio-go/v7 v7.0.94
|
||||
github.com/msteinert/pam/v2 v2.1.0
|
||||
github.com/nektos/act v0.2.52
|
||||
github.com/niklasfasching/go-org v1.9.0
|
||||
github.com/niklasfasching/go-org v1.9.1
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
|
@ -235,9 +235,9 @@ require (
|
|||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
12
go.sum
12
go.sum
|
@ -426,8 +426,8 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG
|
|||
github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/niklasfasching/go-org v1.9.0 h1:4/Sr68Qx06hjC9MVDB/4etGP67JionLHGscLMOClpnk=
|
||||
github.com/niklasfasching/go-org v1.9.0/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
|
||||
github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0=
|
||||
github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
|
@ -597,8 +597,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -688,8 +688,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
21
models/fixtures/ModerationFeatures/abuse_report.yml
Normal file
21
models/fixtures/ModerationFeatures/abuse_report.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
-
|
||||
id: 1
|
||||
status: 1
|
||||
reporter_id: 2 # @user2
|
||||
content_type: 4 # Comment
|
||||
content_id: 18 # user2/repo2/issues/2#issuecomment-18
|
||||
category: 2 # Spam
|
||||
remarks: The comment I'm reporting is pure SPAM.
|
||||
shadow_copy_id: null
|
||||
created_unix: 1752697980 # 2025-07-16 20:33:00
|
||||
|
||||
-
|
||||
id: 2
|
||||
status: 1 # Open
|
||||
reporter_id: 2 # @user2
|
||||
content_type: 1 # User (users or organizations)
|
||||
content_id: 1002 # @alexsmith
|
||||
category: 2 # Spam
|
||||
remarks: This user just posted a spammy comment on my issue.
|
||||
shadow_copy_id: null
|
||||
created_unix: 1752698010 # 2025-07-16 20:33:30
|
7
models/fixtures/ModerationFeatures/comment.yml
Normal file
7
models/fixtures/ModerationFeatures/comment.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
- # This is a spam comment (abusive content), created for testing moderation functionalities.
|
||||
id: 18
|
||||
type: 0 # Standard comment
|
||||
poster_id: 1002 # @alexsmith
|
||||
issue_id: 7 # user2/repo2#2
|
||||
content: If anyone needs help for promoting their business online using SEO, just contact me (check my profile page).
|
||||
created_unix: 1752697860 # 2025-07-16 20:31:00
|
22
models/fixtures/ModerationFeatures/user.yml
Normal file
22
models/fixtures/ModerationFeatures/user.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
- # This user is a spammer and will create abusive content (for testing moderation functionalities).
|
||||
id: 1002
|
||||
lower_name: alexsmith
|
||||
name: alexsmith
|
||||
full_name: Alex Smith
|
||||
email: alexsmith@example.org
|
||||
keep_email_private: false
|
||||
passwd: passwdSalt:password
|
||||
passwd_hash_algo: dummy
|
||||
type: 0
|
||||
location: '@master@seo.net'
|
||||
website: http://promote-your-business.biz
|
||||
pronouns: SEO
|
||||
salt: passwdSalt
|
||||
description: I can help you promote your business online using SEO.
|
||||
created_unix: 1752697800 # 2025-07-16 20:30:00
|
||||
is_active: true
|
||||
is_admin: false
|
||||
is_restricted: false
|
||||
avatar: avatar-hash-1002
|
||||
avatar_email: alexsmith@example.org
|
||||
use_custom_avatar: false
|
|
@ -1156,7 +1156,7 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us
|
|||
defer committer.Close()
|
||||
|
||||
// If the comment was reported as abusive, a shadow copy should be created before first update.
|
||||
if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil {
|
||||
if err := IfNeededCreateShadowCopyForComment(ctx, c, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1197,7 +1197,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error {
|
|||
e := db.GetEngine(ctx)
|
||||
|
||||
// If the comment was reported as abusive, a shadow copy should be created before deletion.
|
||||
if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil {
|
||||
if err := IfNeededCreateShadowCopyForComment(ctx, comment, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -87,13 +87,19 @@ func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error {
|
|||
// IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted
|
||||
// and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s).
|
||||
// This function should be called before a comment is deleted or updated.
|
||||
func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error {
|
||||
func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment, forUpdates bool) error {
|
||||
shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if shadowCopyNeeded {
|
||||
if forUpdates {
|
||||
// get the unaltered comment fields (for updates the provided variable is already altered but not yet saved)
|
||||
if comment, err = GetCommentByID(ctx, comment.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
commentData := newCommentData(comment)
|
||||
content, err := json.Marshal(commentData)
|
||||
if err != nil {
|
||||
|
|
|
@ -73,16 +73,20 @@ var userDataColumnNames = sync.OnceValue(func() []string {
|
|||
// and if found a shadow copy of relevant user fields will be stored into DB and linked to the above report(s).
|
||||
// This function should be called before a user is deleted or updated.
|
||||
//
|
||||
// In case the User object was already altered before calling this method, just provide the userID and
|
||||
// nil for unalteredUser; when it is decided that a shadow copy should be created and unalteredUser is nil,
|
||||
// the user will be retrieved from DB based on the provided userID.
|
||||
//
|
||||
// For deletions alteredCols argument must be omitted.
|
||||
//
|
||||
// In case of updates it will first checks whether any of the columns being updated (alteredCols argument)
|
||||
// is relevant for moderation purposes (i.e. included in the UserData struct).
|
||||
func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCols ...string) error {
|
||||
func IfNeededCreateShadowCopyForUser(ctx context.Context, userID int64, unalteredUser *User, alteredCols ...string) error {
|
||||
// TODO: this can be triggered quite often (e.g. by routers/web/repo/middlewares.go SetDiffViewStyle())
|
||||
|
||||
shouldCheckIfNeeded := len(alteredCols) == 0 // no columns being updated, therefore a deletion
|
||||
if !shouldCheckIfNeeded {
|
||||
// for updates we need to go further only if certain column are being changed
|
||||
// for updates we need to go further only if certain columns are being changed
|
||||
for _, colName := range userDataColumnNames() {
|
||||
if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded {
|
||||
break
|
||||
|
@ -94,18 +98,23 @@ func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCol
|
|||
return nil
|
||||
}
|
||||
|
||||
shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID)
|
||||
shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if shadowCopyNeeded {
|
||||
userData := newUserData(user)
|
||||
if unalteredUser == nil {
|
||||
if unalteredUser, err = GetUserByID(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
userData := newUserData(unalteredUser)
|
||||
content, err := json.Marshal(userData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return moderation.CreateShadowCopyForUser(ctx, user.ID, string(content))
|
||||
return moderation.CreateShadowCopyForUser(ctx, userID, string(content))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -927,7 +927,9 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error {
|
|||
|
||||
// If the user was reported as abusive and any of the columns being updated is relevant
|
||||
// for moderation purposes a shadow copy should be created before first update.
|
||||
if err := IfNeededCreateShadowCopyForUser(ctx, u, cols...); err != nil {
|
||||
// Since u is already altered at this point we are sending nil instead as an argument
|
||||
// so that the unaltered version will be retrieved from DB.
|
||||
if err := IfNeededCreateShadowCopyForUser(ctx, u.ID, nil, cols...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import (
|
|||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/util"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
)
|
||||
|
||||
// Scheme describes protocol types
|
||||
|
@ -206,7 +208,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
|||
EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false)
|
||||
}
|
||||
if EnableAcme {
|
||||
AcmeURL = sec.Key("ACME_URL").MustString("")
|
||||
AcmeURL = sec.Key("ACME_URL").MustString(certmagic.LetsEncryptProductionCA)
|
||||
AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("")
|
||||
|
||||
if sec.HasKey("ACME_ACCEPTTOS") {
|
||||
|
|
217
package-lock.json
generated
217
package-lock.json
generated
|
@ -63,15 +63,15 @@
|
|||
"devDependencies": {
|
||||
"@axe-core/playwright": "4.10.2",
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
|
||||
"@playwright/test": "1.53.2",
|
||||
"@playwright/test": "1.54.1",
|
||||
"@stoplight/spectral-cli": "6.15.0",
|
||||
"@stylistic/eslint-plugin": "5.1.0",
|
||||
"@stylistic/eslint-plugin": "5.2.0",
|
||||
"@stylistic/stylelint-plugin": "3.1.3",
|
||||
"@vitejs/plugin-vue": "6.0.0",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/eslint-plugin": "1.3.4",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"eslint": "9.30.1",
|
||||
"eslint": "9.31.0",
|
||||
"eslint-import-resolver-typescript": "4.4.4",
|
||||
"eslint-plugin-array-func": "5.0.2",
|
||||
"eslint-plugin-import-x": "4.16.1",
|
||||
|
@ -92,14 +92,14 @@
|
|||
"markdownlint-cli": "0.45.0",
|
||||
"postcss-html": "1.8.0",
|
||||
"sharp": "0.34.3",
|
||||
"stylelint": "16.21.1",
|
||||
"stylelint": "16.22.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||
"stylelint-declaration-strict-value": "1.10.11",
|
||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||
"svgo": "4.0.0",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.35.1",
|
||||
"vite-string-plugin": "1.3.4",
|
||||
"typescript-eslint": "8.37.0",
|
||||
"vite-string-plugin": "1.4.6",
|
||||
"vitest": "3.2.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1075,9 +1075,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/core": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
|
||||
"integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
|
||||
"version": "0.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
|
||||
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
@ -1162,9 +1162,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.30.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz",
|
||||
"integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==",
|
||||
"version": "9.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz",
|
||||
"integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -1198,19 +1198,6 @@
|
|||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
|
||||
"version": "0.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
|
||||
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@github/combobox-nav": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@github/combobox-nav/-/combobox-nav-2.3.1.tgz",
|
||||
|
@ -2213,13 +2200,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz",
|
||||
"integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==",
|
||||
"version": "1.54.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz",
|
||||
"integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.53.2"
|
||||
"playwright": "1.54.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
@ -3089,18 +3076,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@stylistic/eslint-plugin": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.1.0.tgz",
|
||||
"integrity": "sha512-TJRJul4u/lmry5N/kyCU+7RWWOk0wyXN+BncRlDYBqpLFnzXkd7QGVfN7KewarFIXv0IX0jSF/Ksu7aHWEDeuw==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.0.tgz",
|
||||
"integrity": "sha512-RCEdbREv9EBiToUBQTlRhVYKG093I6ZnnQ990j08eJ6uRZh71DXkOnoxtTLfDQ6utVCVQzrhZFHZP0zfrfOIjA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/types": "^8.34.1",
|
||||
"@typescript-eslint/types": "^8.37.0",
|
||||
"eslint-visitor-keys": "^4.2.1",
|
||||
"espree": "^10.4.0",
|
||||
"estraverse": "^5.3.0",
|
||||
"picomatch": "^4.0.2"
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -3110,9 +3097,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -3585,17 +3572,17 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz",
|
||||
"integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz",
|
||||
"integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.35.1",
|
||||
"@typescript-eslint/type-utils": "8.35.1",
|
||||
"@typescript-eslint/utils": "8.35.1",
|
||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
||||
"@typescript-eslint/scope-manager": "8.37.0",
|
||||
"@typescript-eslint/type-utils": "8.37.0",
|
||||
"@typescript-eslint/utils": "8.37.0",
|
||||
"@typescript-eslint/visitor-keys": "8.37.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
|
@ -3609,7 +3596,7 @@
|
|||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.35.1",
|
||||
"@typescript-eslint/parser": "^8.37.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
|
@ -3625,16 +3612,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz",
|
||||
"integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz",
|
||||
"integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.35.1",
|
||||
"@typescript-eslint/types": "8.35.1",
|
||||
"@typescript-eslint/typescript-estree": "8.35.1",
|
||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
||||
"@typescript-eslint/scope-manager": "8.37.0",
|
||||
"@typescript-eslint/types": "8.37.0",
|
||||
"@typescript-eslint/typescript-estree": "8.37.0",
|
||||
"@typescript-eslint/visitor-keys": "8.37.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -3650,14 +3637,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz",
|
||||
"integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz",
|
||||
"integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.35.1",
|
||||
"@typescript-eslint/types": "^8.35.1",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.37.0",
|
||||
"@typescript-eslint/types": "^8.37.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -3672,14 +3659,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz",
|
||||
"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz",
|
||||
"integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.35.1",
|
||||
"@typescript-eslint/visitor-keys": "8.35.1"
|
||||
"@typescript-eslint/types": "8.37.0",
|
||||
"@typescript-eslint/visitor-keys": "8.37.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -3690,9 +3677,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz",
|
||||
"integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz",
|
||||
"integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -3707,14 +3694,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz",
|
||||
"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz",
|
||||
"integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "8.35.1",
|
||||
"@typescript-eslint/utils": "8.35.1",
|
||||
"@typescript-eslint/types": "8.37.0",
|
||||
"@typescript-eslint/typescript-estree": "8.37.0",
|
||||
"@typescript-eslint/utils": "8.37.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
|
@ -3731,9 +3719,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz",
|
||||
"integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz",
|
||||
"integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
@ -3745,16 +3733,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz",
|
||||
"integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz",
|
||||
"integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.35.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.35.1",
|
||||
"@typescript-eslint/types": "8.35.1",
|
||||
"@typescript-eslint/visitor-keys": "8.35.1",
|
||||
"@typescript-eslint/project-service": "8.37.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.37.0",
|
||||
"@typescript-eslint/types": "8.37.0",
|
||||
"@typescript-eslint/visitor-keys": "8.37.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -3807,16 +3795,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz",
|
||||
"integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz",
|
||||
"integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.35.1",
|
||||
"@typescript-eslint/types": "8.35.1",
|
||||
"@typescript-eslint/typescript-estree": "8.35.1"
|
||||
"@typescript-eslint/scope-manager": "8.37.0",
|
||||
"@typescript-eslint/types": "8.37.0",
|
||||
"@typescript-eslint/typescript-estree": "8.37.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -3831,13 +3819,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz",
|
||||
"integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz",
|
||||
"integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.35.1",
|
||||
"@typescript-eslint/types": "8.37.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -7302,9 +7290,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.30.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz",
|
||||
"integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==",
|
||||
"version": "9.31.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz",
|
||||
"integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
@ -7312,9 +7300,9 @@
|
|||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.21.0",
|
||||
"@eslint/config-helpers": "^0.3.0",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/core": "^0.15.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.30.1",
|
||||
"@eslint/js": "9.31.0",
|
||||
"@eslint/plugin-kit": "^0.3.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
|
@ -12002,13 +11990,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz",
|
||||
"integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==",
|
||||
"version": "1.54.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz",
|
||||
"integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.53.2"
|
||||
"playwright-core": "1.54.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
|
@ -12021,9 +12009,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.53.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz",
|
||||
"integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==",
|
||||
"version": "1.54.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz",
|
||||
"integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -13850,9 +13838,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/stylelint": {
|
||||
"version": "16.21.1",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.21.1.tgz",
|
||||
"integrity": "sha512-WCXdXnYK2tpCbebgMF0Bme3YZH/Rh/UXerj75twYo4uLULlcrLwFVdZTvTEF8idFnAcW21YUDJFyKOfaf6xJRw==",
|
||||
"version": "16.22.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.22.0.tgz",
|
||||
"integrity": "sha512-SVEMTdjKNV4ollUrIY9ordZ36zHv2/PHzPjfPMau370MlL2VYXeLgSNMMiEbLGRO8RmD2R8/BVUeF2DfnfkC0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -14936,15 +14924,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.35.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.35.1.tgz",
|
||||
"integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==",
|
||||
"version": "8.37.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz",
|
||||
"integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.35.1",
|
||||
"@typescript-eslint/parser": "8.35.1",
|
||||
"@typescript-eslint/utils": "8.35.1"
|
||||
"@typescript-eslint/eslint-plugin": "8.37.0",
|
||||
"@typescript-eslint/parser": "8.37.0",
|
||||
"@typescript-eslint/typescript-estree": "8.37.0",
|
||||
"@typescript-eslint/utils": "8.37.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
@ -15255,9 +15244,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite-string-plugin": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.3.4.tgz",
|
||||
"integrity": "sha512-mHvcooHgZ0nVbHtj9o+c5dzD2/nclr/SOG023EFYF/zRnO8bxB63bV9WUA9X+njlgLpOwCJ3LI2IdihKoi0gZQ==",
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.4.6.tgz",
|
||||
"integrity": "sha512-Csjtny8/uVIynzlaRRj4RpHrPAakNwlH9jw6kgQ8tQhc2f0zzA6bCbAgWD0y84EgB8aLNrz7pZFUqSt3LOtk+w==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
|
|
12
package.json
12
package.json
|
@ -62,15 +62,15 @@
|
|||
"devDependencies": {
|
||||
"@axe-core/playwright": "4.10.2",
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.5.0",
|
||||
"@playwright/test": "1.53.2",
|
||||
"@playwright/test": "1.54.1",
|
||||
"@stoplight/spectral-cli": "6.15.0",
|
||||
"@stylistic/eslint-plugin": "5.1.0",
|
||||
"@stylistic/eslint-plugin": "5.2.0",
|
||||
"@stylistic/stylelint-plugin": "3.1.3",
|
||||
"@vitejs/plugin-vue": "6.0.0",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"@vitest/eslint-plugin": "1.3.4",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"eslint": "9.30.1",
|
||||
"eslint": "9.31.0",
|
||||
"eslint-import-resolver-typescript": "4.4.4",
|
||||
"eslint-plugin-array-func": "5.0.2",
|
||||
"eslint-plugin-import-x": "4.16.1",
|
||||
|
@ -91,14 +91,14 @@
|
|||
"markdownlint-cli": "0.45.0",
|
||||
"postcss-html": "1.8.0",
|
||||
"sharp": "0.34.3",
|
||||
"stylelint": "16.21.1",
|
||||
"stylelint": "16.22.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||
"stylelint-declaration-strict-value": "1.10.11",
|
||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||
"svgo": "4.0.0",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.35.1",
|
||||
"vite-string-plugin": "1.3.4",
|
||||
"typescript-eslint": "8.37.0",
|
||||
"vite-string-plugin": "1.4.6",
|
||||
"vitest": "3.2.4"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
|
@ -64,7 +64,7 @@ function test_main() {
|
|||
test "$(categorize)" = 'ZE Other changes without a feature or bug label'
|
||||
|
||||
test_payload_labels
|
||||
test "$(categorize)" = 'ZF Included for completeness but not worth a release note'
|
||||
test "$(categorize)" = 'ZF Included for completeness but not user-facing (chores, etc.)'
|
||||
|
||||
test_payload_draft "fix(security)!: breaking security bug fix"
|
||||
test "$(categorize)" = 'AA Breaking security bug fixes'
|
||||
|
@ -117,11 +117,12 @@ function categorize() {
|
|||
|
||||
#
|
||||
# If there was no release-notes/N.md file and it is not
|
||||
# worth a release note, just forget about it.
|
||||
# directly user-facing, we include it in a separate section
|
||||
# for completeness.
|
||||
#
|
||||
if test -z "$(jq --raw-output .Draft <$payload)"; then
|
||||
if ! $worth; then
|
||||
echo -n ZF Included for completeness but not worth a release note
|
||||
echo -n ZF Included for completeness but not user-facing \(chores, etc.\)
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -53,19 +53,7 @@
|
|||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7245): <!--number 7245 --><!--line 0 --><!--description YWxsb3cgdXNlciB3aXRoIGFjdGlvbnMgd3JpdGUgcGVybWlzc2lvbiB0byBydW4gYSB3b3JrZmxvdyBmcm9tIHRoZSB3ZWIgVUk=-->allow user with actions write permission to run a workflow from the web UI.<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6799): <!--number 6799 --><!--line 0 --><!--description Zml4KHVpKTogZW5zdXJlIHNhbWUgd2lkdGggb2YgdXNlcmNhcmRzIGluIGdyaWQ=-->ensure usercards in grid have the same width.<!--description-->
|
||||
- Localization
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8238) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8294)): <!--number 8294 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8178): <!--number 8178 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8077): <!--number 8077 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7969): <!--number 7969 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7895): <!--number 7895 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7819): <!--number 7819 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7742): <!--number 7742 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7637): <!--number 7637 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7572): <!--number 7572 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7507): <!--number 7507 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7438): <!--number 7438 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7363): <!--number 7363 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7275): <!--number 7275 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
|
||||
- Updates from Codeberg Translate: [#7275](https://codeberg.org/forgejo/forgejo/pulls/7275), [#7363](https://codeberg.org/forgejo/forgejo/pulls/7363), [#7438](https://codeberg.org/forgejo/forgejo/pulls/7438), [#7507](https://codeberg.org/forgejo/forgejo/pulls/7507), [#7572](https://codeberg.org/forgejo/forgejo/pulls/7572), [#7637](https://codeberg.org/forgejo/forgejo/pulls/7637), [#7742](https://codeberg.org/forgejo/forgejo/pulls/7742), [#7819](https://codeberg.org/forgejo/forgejo/pulls/7819), [#7895](https://codeberg.org/forgejo/forgejo/pulls/7895), [#7969](https://codeberg.org/forgejo/forgejo/pulls/7969), [#8077](https://codeberg.org/forgejo/forgejo/pulls/8077), [#8178](https://codeberg.org/forgejo/forgejo/pulls/8178), [#8294](https://codeberg.org/forgejo/forgejo/pulls/8294) (backport of [#8238](https://codeberg.org/forgejo/forgejo/pulls/8238)), [#8534](https://codeberg.org/forgejo/forgejo/pulls/8534) (backport of [#8295](https://codeberg.org/forgejo/forgejo/issues/8295), [#8410](https://codeberg.org/forgejo/forgejo/issues/8410), [#8490](https://codeberg.org/forgejo/forgejo/issues/8490))
|
||||
- Features
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8179): <!--number 8179 --><!--line 0 --><!--description QVBJOiBuZXcgYEdFVCAvcmVwb3Mve293bmVyfS97cmVwb30vZ2l0L2Jsb2JzYCBlbmRwb2ludCB0byByZXRyaWV2ZSBtdWx0aXBsZSBibG9icyBhdCBvbmNl-->new `GET /repos/{owner}/{repo}/git/blobs` API endpoint to retrieve multiple blobs at once.<!--description-->
|
||||
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8177): <!--number 8177 --><!--line 0 --><!--description YWx3YXlzIHB1Ymxpc2ggdGhlIGxpbmsgdG8gdGhlIGNvbW1pdCBzdGF0dXM=-->always publish the link to the commit status.<!--description-->
|
||||
|
|
|
@ -71,6 +71,9 @@ func SettingsPost(ctx *context.Context) {
|
|||
ctx.Data["PageIsSettingsOptions"] = true
|
||||
ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility
|
||||
ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod
|
||||
ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize
|
||||
ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth
|
||||
ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(http.StatusOK, tplSettingsOptions)
|
||||
|
|
|
@ -8,9 +8,11 @@ import (
|
|||
|
||||
"forgejo.org/models/db"
|
||||
issues_model "forgejo.org/models/issues"
|
||||
"forgejo.org/models/moderation"
|
||||
"forgejo.org/models/unittest"
|
||||
user_model "forgejo.org/models/user"
|
||||
webhook_model "forgejo.org/models/webhook"
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
issue_service "forgejo.org/services/issue"
|
||||
|
@ -148,3 +150,40 @@ func TestUpdateComment(t *testing.T) {
|
|||
unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID})
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateShadowCopyOnCommentUpdate(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
userAlexSmithID := int64(1002)
|
||||
spamCommentID := int64(18) // posted by @alexsmith
|
||||
abuseReportID := int64(1) // submitted for above comment
|
||||
newCommentContent := "If anyone needs help, just contact me."
|
||||
|
||||
// Retrieve the abusive user (@alexsmith), their SPAM comment and the abuse report already created for this comment.
|
||||
poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID})
|
||||
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: spamCommentID, PosterID: poster.ID})
|
||||
report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{
|
||||
ID: abuseReportID,
|
||||
ContentType: moderation.ReportedContentTypeComment,
|
||||
ContentID: comment.ID,
|
||||
})
|
||||
// The report should not already have a shadow copy linked.
|
||||
assert.False(t, report.ShadowCopyID.Valid)
|
||||
|
||||
// The abusive user is updating their comment.
|
||||
oldContent := comment.Content
|
||||
comment.Content = newCommentContent
|
||||
require.NoError(t, issue_service.UpdateComment(t.Context(), comment, 0, poster, oldContent))
|
||||
|
||||
// Reload the report.
|
||||
report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID})
|
||||
// A shadow copy should have been created and linked to our report.
|
||||
assert.True(t, report.ShadowCopyID.Valid)
|
||||
// Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values.
|
||||
shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64})
|
||||
shadowCopyCommentData := new(issues_model.CommentData)
|
||||
require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyCommentData))
|
||||
// Check to see if the initial content of the comment was stored within the shadow copy.
|
||||
assert.Equal(t, oldContent, shadowCopyCommentData.Content)
|
||||
}
|
||||
|
|
|
@ -218,7 +218,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
|
|||
// ***** END: ExternalLoginUser *****
|
||||
|
||||
// If the user was reported as abusive, a shadow copy should be created before deletion.
|
||||
if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u); err != nil {
|
||||
if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary
|
|||
oldPrimaryEmail := u.Email
|
||||
|
||||
// If the user was reported as abusive, a shadow copy should be created before first update (of certain columns).
|
||||
if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil {
|
||||
if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u, "email"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -15,10 +15,13 @@ import (
|
|||
asymkey_model "forgejo.org/models/asymkey"
|
||||
"forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/moderation"
|
||||
"forgejo.org/models/organization"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
"forgejo.org/models/unittest"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
"forgejo.org/modules/timeutil"
|
||||
|
@ -277,3 +280,56 @@ func TestDeleteInactiveUsers(t *testing.T) {
|
|||
unittest.AssertExistsIf(t, true, newUser)
|
||||
unittest.AssertExistsIf(t, true, newEmail)
|
||||
}
|
||||
|
||||
func TestCreateShadowCopyOnUserUpdate(t *testing.T) {
|
||||
defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")()
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
userAlexSmithID := int64(1002)
|
||||
abuseReportID := int64(2) // submitted for @alexsmith
|
||||
newDummyValue := "[REDACTED]" // used for updating profile text fields
|
||||
|
||||
// Retrieve the abusive user (@alexsmith) and the abuse report already created for this user.
|
||||
abuser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID})
|
||||
report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{
|
||||
ID: abuseReportID,
|
||||
ContentType: moderation.ReportedContentTypeUser,
|
||||
ContentID: abuser.ID,
|
||||
})
|
||||
// The report should not already have a shadow copy linked.
|
||||
assert.False(t, report.ShadowCopyID.Valid)
|
||||
|
||||
// Keep a copy of old field values before updating them.
|
||||
oldUserData := user_model.UserData{
|
||||
FullName: abuser.FullName,
|
||||
Location: abuser.Location,
|
||||
Website: abuser.Website,
|
||||
Pronouns: abuser.Pronouns,
|
||||
Description: abuser.Description,
|
||||
}
|
||||
|
||||
// The abusive user is updating their profile.
|
||||
opts := &UpdateOptions{
|
||||
FullName: optional.Some(newDummyValue),
|
||||
Location: optional.Some(newDummyValue),
|
||||
Website: optional.Some(newDummyValue),
|
||||
Pronouns: optional.Some(newDummyValue),
|
||||
Description: optional.Some(newDummyValue),
|
||||
}
|
||||
require.NoError(t, UpdateUser(t.Context(), abuser, opts))
|
||||
|
||||
// Reload the report.
|
||||
report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID})
|
||||
// A shadow copy should have been created and linked to our report.
|
||||
assert.True(t, report.ShadowCopyID.Valid)
|
||||
// Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values.
|
||||
shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64})
|
||||
shadowCopyUserData := new(user_model.UserData)
|
||||
require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyUserData))
|
||||
// Check to see if the initial field values of the user were stored within the shadow copy.
|
||||
assert.Equal(t, oldUserData.FullName, shadowCopyUserData.FullName)
|
||||
assert.Equal(t, oldUserData.Location, shadowCopyUserData.Location)
|
||||
assert.Equal(t, oldUserData.Website, shadowCopyUserData.Website)
|
||||
assert.Equal(t, oldUserData.Pronouns, shadowCopyUserData.Pronouns)
|
||||
assert.Equal(t, oldUserData.Description, shadowCopyUserData.Description)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
{{end}}
|
||||
<div class="item context js-aria-clickable" data-clipboard-text-type="url" data-clipboard-text="{{$referenceUrl}}">{{ctx.Locale.Tr "repo.issues.context.copy_link"}}</div>
|
||||
{{if and .ctxData.IsSigned (not .ctxData.Repository.IsArchived)}}
|
||||
<div class="item context js-aria-clickable quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-content" data-author="{{.item.Poster.Name}}" data-reference-url="{{$referenceUrl}}">{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}</div>
|
||||
<div class="item context js-aria-clickable quote-reply {{if .diff}}quote-reply-diff{{end}}" data-target="{{.item.HashTag}}-content" data-author="{{.item.Poster.Name}}" data-reference-url="{{$referenceUrl}}" data-context="{{.ctxData.Repository.Link}}">{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}</div>
|
||||
{{if not .ctxData.UnitIssuesGlobalDisabled}}
|
||||
<div class="item context js-aria-clickable reference-issue" data-target="{{.item.HashTag}}-raw" data-modal="#reference-issue-modal" data-poster="{{.item.Poster.GetDisplayName}}" data-poster-username="{{.item.Poster.Name}}" data-reference="{{$referenceUrl}}">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</div>
|
||||
{{end}}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
type: 0 # comment
|
||||
poster_id: 2
|
||||
issue_id: 1 # in repo_id 1
|
||||
content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:"
|
||||
content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:\n\n\n<img src='/user2/repo1/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2' alt='something something' width='500' height='500' />"
|
||||
created_unix: 946684811
|
||||
updated_unix: 946684811
|
||||
content_version: 1
|
||||
|
@ -13,7 +13,7 @@
|
|||
type: 21 # code comment
|
||||
poster_id: 2
|
||||
issue_id: 19
|
||||
content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:"
|
||||
content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:\n\n\n<img src='/user2/commitsonpr/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2' alt='something something' width='500' height='500' />"
|
||||
review_id: 1001
|
||||
line: 1
|
||||
tree_path: "test1.txt"
|
||||
|
|
|
@ -123,7 +123,8 @@ test('Quote reply', async ({page}, workerInfo) => {
|
|||
"> alert('evil')\n" +
|
||||
'> ```\n' +
|
||||
'> \n' +
|
||||
'> :+1: :100:\n\n');
|
||||
'> :+1: :100: [](/user2/repo1/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n' +
|
||||
'> <img alt="something something" width="500" height="500" src="/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2">\n\n');
|
||||
|
||||
await editorTextarea.fill('');
|
||||
|
||||
|
@ -197,7 +198,8 @@ test('Pull quote reply', async ({page}, workerInfo) => {
|
|||
"> alert('evil')\n" +
|
||||
'> ```\n' +
|
||||
'> \n' +
|
||||
'> :+1: :100:\n\n');
|
||||
'> :+1: :100: [](/user2/commitsonpr/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n' +
|
||||
'> <img alt="something something" width="500" height="500" src="/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2">\n\n');
|
||||
|
||||
await editorTextarea.fill('');
|
||||
});
|
||||
|
|
48
tests/e2e/issue-timetracking.test.e2e.ts
Normal file
48
tests/e2e/issue-timetracking.test.e2e.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
// @watch start
|
||||
// web_src/js/features/comp/**
|
||||
// web_src/js/features/repo-**
|
||||
// templates/repo/issue/view_content/*
|
||||
// @watch end
|
||||
|
||||
import {expect} from '@playwright/test';
|
||||
import {test, save_visual} from './utils_e2e.ts';
|
||||
|
||||
test.use({user: 'user2'});
|
||||
|
||||
test('Issue timetracking', async ({page}) => {
|
||||
await page.goto('/user2/repo1/issues/new');
|
||||
|
||||
// Create temporary issue.
|
||||
await page.getByPlaceholder('Title').fill('Just a title');
|
||||
await page.getByPlaceholder('Leave a comment').fill('Hi, have you considered using a rotating fish as logo?');
|
||||
await page.getByRole('button', {name: 'Create issue'}).click();
|
||||
await expect(page).toHaveURL(/\/user2\/repo1\/issues\/\d+$/);
|
||||
|
||||
// Manually add time to the time tracker.
|
||||
await page.getByRole('button', {name: 'Add time'}).click();
|
||||
await page.getByPlaceholder('Hours').fill('5');
|
||||
await page.getByPlaceholder('Minutes').fill('32');
|
||||
await page.getByRole('button', {name: 'Add time', exact: true}).click();
|
||||
|
||||
// Verify this was added in the timeline.
|
||||
await expect(page.locator('.ui.timeline')).toContainText('added spent time');
|
||||
await expect(page.locator('.ui.timeline')).toContainText('5 hours 32 minutes');
|
||||
|
||||
// Verify it is shown in the issue sidebar
|
||||
await expect(page.locator('.issue-content-right .comments')).toContainText('Total time spent: 5 hours 32 minutes');
|
||||
|
||||
await save_visual(page);
|
||||
|
||||
// Delete the added time.
|
||||
await page.getByRole('button', {name: 'Delete this time log'}).click();
|
||||
await page.getByRole('button', {name: 'Yes'}).click();
|
||||
|
||||
// Verify this was removed in the timeline.
|
||||
await expect(page.locator('.ui.timeline')).toContainText('deleted spent time');
|
||||
await expect(page.locator('.ui.timeline')).toContainText('- 5 hours 32 minutes');
|
||||
|
||||
// Delete the issue.
|
||||
await page.getByRole('button', {name: 'Delete'}).click();
|
||||
await page.getByRole('button', {name: 'Yes'}).click();
|
||||
await expect(page).toHaveURL('/user2/repo1/issues');
|
||||
});
|
|
@ -57,7 +57,9 @@ func TestOrgSettingsChangeEmail(t *testing.T) {
|
|||
settings := getOrgSettingsFormData(t, session, orgName)
|
||||
|
||||
settings["email"] = "invalid"
|
||||
session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusOK)
|
||||
doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusOK).Body)
|
||||
doc.AssertElement(t, ".status-page-500", false)
|
||||
doc.AssertElement(t, ".flash-error", true)
|
||||
|
||||
org := getOrgSettings(t, token, orgName)
|
||||
assert.Equal(t, "org3@example.com", org.Email)
|
||||
|
@ -69,7 +71,10 @@ func TestOrgSettingsChangeEmail(t *testing.T) {
|
|||
settings := getOrgSettingsFormData(t, session, orgName)
|
||||
|
||||
settings["email"] = "example@example.com"
|
||||
session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther)
|
||||
doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther).Body)
|
||||
doc.AssertElement(t, "body", true)
|
||||
doc.AssertElement(t, ".status-page-500", false)
|
||||
doc.AssertElement(t, ".flash-error", false)
|
||||
|
||||
org := getOrgSettings(t, token, orgName)
|
||||
assert.Equal(t, "example@example.com", org.Email)
|
||||
|
@ -81,7 +86,10 @@ func TestOrgSettingsChangeEmail(t *testing.T) {
|
|||
settings := getOrgSettingsFormData(t, session, orgName)
|
||||
|
||||
settings["email"] = ""
|
||||
session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther)
|
||||
doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther).Body)
|
||||
doc.AssertElement(t, "body", true)
|
||||
doc.AssertElement(t, ".status-page-500", false)
|
||||
doc.AssertElement(t, ".flash-error", false)
|
||||
|
||||
org := getOrgSettings(t, token, orgName)
|
||||
assert.Empty(t, org.Email)
|
||||
|
|
|
@ -546,11 +546,6 @@ a.label,
|
|||
background: var(--color-active);
|
||||
}
|
||||
|
||||
.ui.secondary.menu.tight .item {
|
||||
padding-left: 0.85714286em;
|
||||
padding-right: 0.85714286em;
|
||||
}
|
||||
|
||||
/* remove the menu clearfix so that it won't add undesired gaps when using "gap" */
|
||||
.ui.menu::after {
|
||||
content: normal;
|
||||
|
|
|
@ -457,7 +457,7 @@ export default sfc; // activate the IDE's Vue plugin
|
|||
<svg-icon name="octicon-organization" :size="16" class="repo-list-icon"/>
|
||||
<div class="text truncate">{{ org.name }}</div>
|
||||
<div><!-- div to prevent underline of label on hover -->
|
||||
<span class="ui tiny basic label" v-if="org.org_visibility !== 'public'">
|
||||
<span class="ui label" v-if="org.org_visibility !== 'public'">
|
||||
{{ org.org_visibility === 'limited' ? textOrgVisibilityLimited: textOrgVisibilityPrivate }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -57,7 +57,7 @@ export function initRepoIssueTimeTracking() {
|
|||
$(sel).modal({
|
||||
duration: 200,
|
||||
onApprove() {
|
||||
document.getElementById(`${sel} form`).requestSubmit();
|
||||
document.querySelector(`${sel} form`).requestSubmit();
|
||||
},
|
||||
}).modal('show');
|
||||
});
|
||||
|
|
|
@ -531,6 +531,13 @@ const filters = {
|
|||
}
|
||||
return el;
|
||||
},
|
||||
IMG(el, context) {
|
||||
const src = el.getAttribute('src');
|
||||
if (src?.startsWith(context)) {
|
||||
el.src = src.slice(context.length);
|
||||
}
|
||||
return el;
|
||||
},
|
||||
};
|
||||
|
||||
function hasContent(node) {
|
||||
|
@ -538,32 +545,34 @@ function hasContent(node) {
|
|||
}
|
||||
|
||||
// This code matches that of what is done by @github/quote-selection
|
||||
function preprocessFragment(fragment) {
|
||||
const nodeIterator = document.createNodeIterator(fragment, NodeFilter.SHOW_ELEMENT, {
|
||||
acceptNode(node) {
|
||||
if (node.nodeName in filters && hasContent(node)) {
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
function preprocessFragment(context) {
|
||||
return function(fragment) {
|
||||
const nodeIterator = document.createNodeIterator(fragment, NodeFilter.SHOW_ELEMENT, {
|
||||
acceptNode(node) {
|
||||
if (node.nodeName in filters && hasContent(node)) {
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
}
|
||||
|
||||
return NodeFilter.FILTER_SKIP;
|
||||
},
|
||||
});
|
||||
const results = [];
|
||||
let node = nodeIterator.nextNode();
|
||||
|
||||
while (node) {
|
||||
if (node instanceof HTMLElement) {
|
||||
results.push(node);
|
||||
}
|
||||
|
||||
return NodeFilter.FILTER_SKIP;
|
||||
},
|
||||
});
|
||||
const results = [];
|
||||
let node = nodeIterator.nextNode();
|
||||
|
||||
while (node) {
|
||||
if (node instanceof HTMLElement) {
|
||||
results.push(node);
|
||||
node = nodeIterator.nextNode();
|
||||
}
|
||||
node = nodeIterator.nextNode();
|
||||
}
|
||||
|
||||
// process deepest matches first
|
||||
results.reverse();
|
||||
results.reverse();
|
||||
|
||||
for (const el of results) {
|
||||
el.replaceWith(filters[el.nodeName](el));
|
||||
}
|
||||
for (const el of results) {
|
||||
el.replaceWith(filters[el.nodeName](el, context));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function initRepoIssueCommentEdit() {
|
||||
|
@ -573,7 +582,7 @@ function initRepoIssueCommentEdit() {
|
|||
// Quote reply
|
||||
$(document).on('click', '.quote-reply', async (event) => {
|
||||
event.preventDefault();
|
||||
const quote = new MarkdownQuote('', preprocessFragment);
|
||||
const quote = new MarkdownQuote('', preprocessFragment(event.target.getAttribute('data-context')));
|
||||
|
||||
let editorTextArea;
|
||||
if (event.target.classList.contains('quote-reply-diff')) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue